/*
* Copyright ( C ) 2023 The Android Open Source Project
*
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
#[ cfg(test)]
pub use test_utils::*;
#[ cfg(test)]
pub mod test_utils {
use crate ::commands::Input;
use aconfig_protos::ProtoParsedFlags;
use itertools;
pub const TEST_PACKAGE: &str = "com.android.aconfig.test" ;
pub const TEST_FLAGS_TEXTPROTO: &str = r#"
parsed_flag {
package: "com.android.aconfig.test"
name: "disabled_ro"
namespace: "aconfig_test"
description: "This flag is DISABLED + READ_ONLY"
bug: "123"
state: DISABLED
permission: READ_ONLY
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/first.values"
state: DISABLED
permission: READ_ONLY
}
is_fixed_read_only: false
is_exported: false
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: NONE
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "disabled_rw"
namespace: "aconfig_test"
description: "This flag is DISABLED + READ_WRITE"
bug: "456"
state: DISABLED
permission: READ_WRITE
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
is_fixed_read_only: false
is_exported: false
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: ACONFIGD
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "disabled_rw_exported"
namespace: "aconfig_test"
description: "This flag is DISABLED + READ_WRITE and exported"
bug: "111"
state: DISABLED
permission: READ_WRITE
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/first.values"
state: DISABLED
permission: READ_WRITE
}
is_fixed_read_only: false
is_exported: true
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: ACONFIGD
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "disabled_rw_in_other_namespace"
namespace: "other_namespace"
description: "This flag is DISABLED + READ_WRITE, and is defined in another namespace"
bug: "999"
state: DISABLED
permission: READ_WRITE
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/first.values"
state: DISABLED
permission: READ_WRITE
}
is_fixed_read_only: false
is_exported: false
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: ACONFIGD
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "enabled_fixed_ro"
namespace: "aconfig_test"
description: "This flag is fixed READ_ONLY + ENABLED"
bug: ""
state: ENABLED
permission: READ_ONLY
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_ONLY
}
trace {
source: "tests/first.values"
state: ENABLED
permission: READ_ONLY
}
is_fixed_read_only: true
is_exported: false
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: NONE
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "enabled_fixed_ro_exported"
namespace: "aconfig_test"
description: "This flag is fixed ENABLED + READ_ONLY and exported"
bug: "111"
state: ENABLED
permission: READ_ONLY
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_ONLY
}
trace {
source: "tests/first.values"
state: ENABLED
permission: READ_ONLY
}
is_fixed_read_only: true
is_exported: true
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: NONE
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "enabled_ro"
namespace: "aconfig_test"
description: "This flag is ENABLED + READ_ONLY"
bug: "abc"
state: ENABLED
permission: READ_ONLY
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/first.values"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/second.values"
state: ENABLED
permission: READ_ONLY
}
is_fixed_read_only: false
is_exported: false
container: "system"
metadata {
purpose: PURPOSE_BUGFIX
storage: NONE
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "enabled_ro_exported"
namespace: "aconfig_test"
description: "This flag is ENABLED + READ_ONLY and exported"
bug: "111"
state: ENABLED
permission: READ_ONLY
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/first.values"
state: ENABLED
permission: READ_ONLY
}
is_fixed_read_only: false
is_exported: true
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: NONE
}
}
parsed_flag {
package: "com.android.aconfig.test"
name: "enabled_rw"
namespace: "aconfig_test"
description: "This flag is ENABLED + READ_WRITE"
bug: ""
state: ENABLED
permission: READ_WRITE
trace {
source: "tests/test.aconfig"
state: DISABLED
permission: READ_WRITE
}
trace {
source: "tests/first.values"
state: ENABLED
permission: READ_WRITE
}
is_fixed_read_only: false
is_exported: false
container: "system"
metadata {
purpose: PURPOSE_UNSPECIFIED
storage: ACONFIGD
}
}
"#;
pub fn parse_read_only_test_flags() -> ProtoParsedFlags {
let extended_permissions_options = crate ::commands::ExtendedPermissionsOptions {
default_permission: crate ::commands::DEFAULT_FLAG_PERMISSION,
allow_read_write: true ,
force_read_only: false ,
};
let bytes = crate ::commands::parse_flags(
"com.android.aconfig.test" ,
"system" ,
vec![Input {
source: "tests/read_only_test.aconfig" .to_string(),
reader: Box ::new(include_bytes!("../tests/read_only_test.aconfig" ).as_slice()),
}],
vec![Input {
source: "tests/read_only_test.values" .to_string(),
reader: Box ::new(include_bytes!("../tests/read_only_test.values" ).as_slice()),
}],
None,
extended_permissions_options,
)
.unwrap();
aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
}
pub fn parse_test_flags() -> ProtoParsedFlags {
let extended_permissions_options = crate ::commands::ExtendedPermissionsOptions {
default_permission: crate ::commands::DEFAULT_FLAG_PERMISSION,
allow_read_write: true ,
force_read_only: false ,
};
let bytes = crate ::commands::parse_flags(
"com.android.aconfig.test" ,
"system" ,
vec![Input {
source: "tests/test.aconfig" .to_string(),
reader: Box ::new(include_bytes!("../tests/test.aconfig" ).as_slice()),
}],
vec![
Input {
source: "tests/first.values" .to_string(),
reader: Box ::new(include_bytes!("../tests/first.values" ).as_slice()),
},
Input {
source: "tests/second.values" .to_string(),
reader: Box ::new(include_bytes!("../tests/second.values" ).as_slice()),
},
],
None,
extended_permissions_options,
)
.unwrap();
aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
}
pub fn parse_second_package_flags() -> ProtoParsedFlags {
let extended_permissions_options = crate ::commands::ExtendedPermissionsOptions {
default_permission: crate ::commands::DEFAULT_FLAG_PERMISSION,
allow_read_write: true ,
force_read_only: false ,
};
let bytes = crate ::commands::parse_flags(
"com.android.aconfig.second_test" ,
"system" ,
vec![Input {
source: "tests/test_second_package.aconfig" .to_string(),
reader: Box ::new(include_bytes!("../tests/test_second_package.aconfig" ).as_slice()),
}],
vec![Input {
source: "tests/third.values" .to_string(),
reader: Box ::new(include_bytes!("../tests/third.values" ).as_slice()),
}],
None,
extended_permissions_options,
)
.unwrap();
aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
}
pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
match itertools::diff_with(a, b, |left, right| left == right) {
Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => {
Some(format!("'{}' vs '{}'" , left.next().unwrap(), right.next().unwrap()))
}
Some(itertools::Diff::Shorter(_, mut left)) => {
Some(format!("LHS trailing data: '{}'" , left.next().unwrap()))
}
Some(itertools::Diff::Longer(_, mut right)) => {
Some(format!("RHS trailing data: '{}'" , right.next().unwrap()))
}
None => None,
}
}
/// Asserts that the two strings are equivalent. For use in tests. Fails
/// with formatted error message for easier debugging.
pub fn assert_no_significant_code_diff(expected: &str, actual: &str) {
let expected =
expected.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
let actual = actual.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
let fail_message: Option<String> =
match itertools::diff_with(expected, actual, |left, right| left == right) {
Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => Some(format!(
"DOES NOT MATCH: 1) expected, 2) actual:\n{}\n{}" ,
left.next().unwrap(),
right.next().unwrap()
)),
Some(itertools::Diff::Shorter(_, mut left)) => {
Some(format!("LHS trailing data: '{}'" , left.next().unwrap()))
}
Some(itertools::Diff::Longer(_, mut right)) => {
Some(format!("RHS trailing data: '{}'" , right.next().unwrap()))
}
None => None,
};
assert!(fail_message.is_none(), "{}" , fail_message.unwrap());
}
#[ test]
fn test_first_significant_code_diff() {
assert!(first_significant_code_diff("" , "" ).is_none());
assert!(first_significant_code_diff(" a" , "\n\na\n" ).is_none());
let a = r#"
public class A {
private static final String FOO = "FOO" ;
public static void main(String[] args) {
System.out.println("FOO=" + FOO);
}
}
"#;
let b = r#"
public class A {
private static final String FOO = "BAR" ;
public static void main(String[] args) {
System.out.println("foo=" + FOO);
}
}
"#;
assert_eq!(Some(r#" 'private static final String FOO = "FOO";' vs 'private static final String FOO = "BAR";' "#.to_string()), first_significant_code_diff(a, b));
assert_eq!(
Some("LHS trailing data: 'b'" .to_string()),
first_significant_code_diff("a\nb" , "a" )
);
assert_eq!(
Some("RHS trailing data: 'b'" .to_string()),
first_significant_code_diff("a" , "a\nb" )
);
}
}
Messung V0.5 in Prozent C=96 H=95 G=95
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet am 2026-06-28)
¤
*© Formatika GbR, Deutschland