Merge changes from topic "fro" into main
* changes: aconfig: add ForceReadOnly mode to aconfig java codegen aconfig: add new codegen mode force_read_only
This commit is contained in:
@@ -96,6 +96,12 @@ aconfig_declarations {
|
|||||||
srcs: ["tests/test_exported.aconfig"],
|
srcs: ["tests/test_exported.aconfig"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aconfig_declarations {
|
||||||
|
name: "aconfig.test.forcereadonly.flags",
|
||||||
|
package: "com.android.aconfig.test.forcereadonly",
|
||||||
|
srcs: ["tests/test_force_read_only.aconfig"],
|
||||||
|
}
|
||||||
|
|
||||||
aconfig_values {
|
aconfig_values {
|
||||||
name: "aconfig.test.flag.values",
|
name: "aconfig.test.flag.values",
|
||||||
package: "com.android.aconfig.test",
|
package: "com.android.aconfig.test",
|
||||||
@@ -125,6 +131,12 @@ java_aconfig_library {
|
|||||||
mode: "exported",
|
mode: "exported",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
java_aconfig_library {
|
||||||
|
name: "aconfig_test_java_library_forcereadonly",
|
||||||
|
aconfig_declarations: "aconfig.test.forcereadonly.flags",
|
||||||
|
mode: "force-read-only",
|
||||||
|
}
|
||||||
|
|
||||||
android_test {
|
android_test {
|
||||||
name: "aconfig.test.java",
|
name: "aconfig.test.java",
|
||||||
srcs: [
|
srcs: [
|
||||||
@@ -135,6 +147,7 @@ android_test {
|
|||||||
static_libs: [
|
static_libs: [
|
||||||
"aconfig_test_java_library",
|
"aconfig_test_java_library",
|
||||||
"aconfig_test_java_library_exported",
|
"aconfig_test_java_library_exported",
|
||||||
|
"aconfig_test_java_library_forcereadonly",
|
||||||
"androidx.test.rules",
|
"androidx.test.rules",
|
||||||
"testng",
|
"testng",
|
||||||
],
|
],
|
||||||
|
@@ -833,6 +833,228 @@ mod tests {
|
|||||||
assert!(file_set.is_empty());
|
assert!(file_set.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_java_code_force_read_only() {
|
||||||
|
let parsed_flags = crate::test::parse_test_flags();
|
||||||
|
let mode = CodegenMode::ForceReadOnly;
|
||||||
|
let modified_parsed_flags =
|
||||||
|
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
|
||||||
|
let generated_files =
|
||||||
|
generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
|
||||||
|
.unwrap();
|
||||||
|
let expect_featureflags_content = r#"
|
||||||
|
package com.android.aconfig.test;
|
||||||
|
// TODO(b/303773055): Remove the annotation after access issue is resolved.
|
||||||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
|
/** @hide */
|
||||||
|
public interface FeatureFlags {
|
||||||
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
boolean disabledRo();
|
||||||
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
boolean disabledRw();
|
||||||
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
boolean disabledRwInOtherNamespace();
|
||||||
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
boolean enabledFixedRo();
|
||||||
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
boolean enabledRo();
|
||||||
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
boolean enabledRw();
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let expect_featureflagsimpl_content = r#"
|
||||||
|
package com.android.aconfig.test;
|
||||||
|
// TODO(b/303773055): Remove the annotation after access issue is resolved.
|
||||||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
|
/** @hide */
|
||||||
|
public final class FeatureFlagsImpl implements FeatureFlags {
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean disabledRo() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean disabledRw() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean disabledRwInOtherNamespace() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean enabledFixedRo() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean enabledRo() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean enabledRw() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let expect_flags_content = r#"
|
||||||
|
package com.android.aconfig.test;
|
||||||
|
// TODO(b/303773055): Remove the annotation after access issue is resolved.
|
||||||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
|
/** @hide */
|
||||||
|
public final class Flags {
|
||||||
|
/** @hide */
|
||||||
|
public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
|
||||||
|
/** @hide */
|
||||||
|
public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
|
||||||
|
/** @hide */
|
||||||
|
public static final String FLAG_DISABLED_RW_IN_OTHER_NAMESPACE = "com.android.aconfig.test.disabled_rw_in_other_namespace";
|
||||||
|
/** @hide */
|
||||||
|
public static final String FLAG_ENABLED_FIXED_RO = "com.android.aconfig.test.enabled_fixed_ro";
|
||||||
|
/** @hide */
|
||||||
|
public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
|
||||||
|
/** @hide */
|
||||||
|
public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
|
||||||
|
|
||||||
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public static boolean disabledRo() {
|
||||||
|
return FEATURE_FLAGS.disabledRo();
|
||||||
|
}
|
||||||
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public static boolean disabledRw() {
|
||||||
|
return FEATURE_FLAGS.disabledRw();
|
||||||
|
}
|
||||||
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public static boolean disabledRwInOtherNamespace() {
|
||||||
|
return FEATURE_FLAGS.disabledRwInOtherNamespace();
|
||||||
|
}
|
||||||
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public static boolean enabledFixedRo() {
|
||||||
|
return FEATURE_FLAGS.enabledFixedRo();
|
||||||
|
}
|
||||||
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public static boolean enabledRo() {
|
||||||
|
return FEATURE_FLAGS.enabledRo();
|
||||||
|
}
|
||||||
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public static boolean enabledRw() {
|
||||||
|
return FEATURE_FLAGS.enabledRw();
|
||||||
|
}
|
||||||
|
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let expect_fakefeatureflags_content = r#"
|
||||||
|
package com.android.aconfig.test;
|
||||||
|
// TODO(b/303773055): Remove the annotation after access issue is resolved.
|
||||||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
/** @hide */
|
||||||
|
public class FakeFeatureFlagsImpl implements FeatureFlags {
|
||||||
|
public FakeFeatureFlagsImpl() {
|
||||||
|
resetAll();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean disabledRo() {
|
||||||
|
return getValue(Flags.FLAG_DISABLED_RO);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean disabledRw() {
|
||||||
|
return getValue(Flags.FLAG_DISABLED_RW);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean disabledRwInOtherNamespace() {
|
||||||
|
return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean enabledFixedRo() {
|
||||||
|
return getValue(Flags.FLAG_ENABLED_FIXED_RO);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean enabledRo() {
|
||||||
|
return getValue(Flags.FLAG_ENABLED_RO);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@UnsupportedAppUsage
|
||||||
|
public boolean enabledRw() {
|
||||||
|
return getValue(Flags.FLAG_ENABLED_RW);
|
||||||
|
}
|
||||||
|
public void setFlag(String flagName, boolean value) {
|
||||||
|
if (!this.mFlagMap.containsKey(flagName)) {
|
||||||
|
throw new IllegalArgumentException("no such flag " + flagName);
|
||||||
|
}
|
||||||
|
this.mFlagMap.put(flagName, value);
|
||||||
|
}
|
||||||
|
public void resetAll() {
|
||||||
|
for (Map.Entry entry : mFlagMap.entrySet()) {
|
||||||
|
entry.setValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private boolean getValue(String flagName) {
|
||||||
|
Boolean value = this.mFlagMap.get(flagName);
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException(flagName + " is not set");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
private Map<String, Boolean> mFlagMap = new HashMap<>(
|
||||||
|
Map.ofEntries(
|
||||||
|
Map.entry(Flags.FLAG_DISABLED_RO, false),
|
||||||
|
Map.entry(Flags.FLAG_DISABLED_RW, false),
|
||||||
|
Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
|
||||||
|
Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
|
||||||
|
Map.entry(Flags.FLAG_ENABLED_RO, false),
|
||||||
|
Map.entry(Flags.FLAG_ENABLED_RW, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut file_set = HashMap::from([
|
||||||
|
("com/android/aconfig/test/Flags.java", expect_flags_content),
|
||||||
|
("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
|
||||||
|
("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content),
|
||||||
|
("com/android/aconfig/test/FakeFeatureFlagsImpl.java", expect_fakefeatureflags_content),
|
||||||
|
]);
|
||||||
|
|
||||||
|
for file in generated_files {
|
||||||
|
let file_path = file.path.to_str().unwrap();
|
||||||
|
assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
|
||||||
|
assert_eq!(
|
||||||
|
None,
|
||||||
|
crate::test::first_significant_code_diff(
|
||||||
|
file_set.get(file_path).unwrap(),
|
||||||
|
&String::from_utf8(file.contents).unwrap()
|
||||||
|
),
|
||||||
|
"File {} content is not correct",
|
||||||
|
file_path
|
||||||
|
);
|
||||||
|
file_set.remove(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(file_set.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_java_method_name() {
|
fn test_format_java_method_name() {
|
||||||
let expected = "someSnakeName";
|
let expected = "someSnakeName";
|
||||||
|
@@ -56,6 +56,7 @@ pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<Stri
|
|||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
|
||||||
pub enum CodegenMode {
|
pub enum CodegenMode {
|
||||||
Exported,
|
Exported,
|
||||||
|
ForceReadOnly,
|
||||||
Production,
|
Production,
|
||||||
Test,
|
Test,
|
||||||
}
|
}
|
||||||
@@ -64,6 +65,7 @@ impl std::fmt::Display for CodegenMode {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
CodegenMode::Exported => write!(f, "exported"),
|
CodegenMode::Exported => write!(f, "exported"),
|
||||||
|
CodegenMode::ForceReadOnly => write!(f, "force-read-only"),
|
||||||
CodegenMode::Production => write!(f, "production"),
|
CodegenMode::Production => write!(f, "production"),
|
||||||
CodegenMode::Test => write!(f, "test"),
|
CodegenMode::Test => write!(f, "test"),
|
||||||
}
|
}
|
||||||
|
@@ -47,6 +47,7 @@ where
|
|||||||
CodegenMode::Production => include_str!("../../templates/rust_prod.template"),
|
CodegenMode::Production => include_str!("../../templates/rust_prod.template"),
|
||||||
CodegenMode::Test => include_str!("../../templates/rust_test.template"),
|
CodegenMode::Test => include_str!("../../templates/rust_test.template"),
|
||||||
CodegenMode::Exported => include_str!("../../templates/rust_exported.template"),
|
CodegenMode::Exported => include_str!("../../templates/rust_exported.template"),
|
||||||
|
CodegenMode::ForceReadOnly => todo!(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let contents = template.render("rust_code_gen", &context)?;
|
let contents = template.render("rust_code_gen", &context)?;
|
||||||
@@ -569,6 +570,7 @@ pub fn enabled_ro_exported() -> bool {
|
|||||||
CodegenMode::Production => PROD_EXPECTED,
|
CodegenMode::Production => PROD_EXPECTED,
|
||||||
CodegenMode::Test => TEST_EXPECTED,
|
CodegenMode::Test => TEST_EXPECTED,
|
||||||
CodegenMode::Exported => EXPORTED_EXPECTED,
|
CodegenMode::Exported => EXPORTED_EXPECTED,
|
||||||
|
codegen::CodegenMode::ForceReadOnly => todo!(),
|
||||||
},
|
},
|
||||||
&String::from_utf8(generated.contents).unwrap()
|
&String::from_utf8(generated.contents).unwrap()
|
||||||
)
|
)
|
||||||
|
@@ -332,6 +332,11 @@ pub fn modify_parsed_flags_based_on_mode(
|
|||||||
parsed_flag
|
parsed_flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn force_read_only_mode_flag_modifier(mut parsed_flag: ProtoParsedFlag) -> ProtoParsedFlag {
|
||||||
|
parsed_flag.set_permission(ProtoFlagPermission::READ_ONLY);
|
||||||
|
parsed_flag
|
||||||
|
}
|
||||||
|
|
||||||
let modified_parsed_flags: Vec<_> = match codegen_mode {
|
let modified_parsed_flags: Vec<_> = match codegen_mode {
|
||||||
CodegenMode::Exported => parsed_flags
|
CodegenMode::Exported => parsed_flags
|
||||||
.parsed_flag
|
.parsed_flag
|
||||||
@@ -339,6 +344,12 @@ pub fn modify_parsed_flags_based_on_mode(
|
|||||||
.filter(|pf| pf.is_exported())
|
.filter(|pf| pf.is_exported())
|
||||||
.map(exported_mode_flag_modifier)
|
.map(exported_mode_flag_modifier)
|
||||||
.collect(),
|
.collect(),
|
||||||
|
CodegenMode::ForceReadOnly => parsed_flags
|
||||||
|
.parsed_flag
|
||||||
|
.into_iter()
|
||||||
|
.filter(|pf| !pf.is_exported())
|
||||||
|
.map(force_read_only_mode_flag_modifier)
|
||||||
|
.collect(),
|
||||||
CodegenMode::Production | CodegenMode::Test => {
|
CodegenMode::Production | CodegenMode::Test => {
|
||||||
parsed_flags.parsed_flag.into_iter().collect()
|
parsed_flags.parsed_flag.into_iter().collect()
|
||||||
}
|
}
|
||||||
@@ -694,4 +705,25 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
assert_eq!(flag_ids, expected_flag_ids);
|
assert_eq!(flag_ids, expected_flag_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_modify_parsed_flags_based_on_mode_force_read_only() {
|
||||||
|
let parsed_flags = crate::test::parse_test_flags();
|
||||||
|
let p_parsed_flags =
|
||||||
|
modify_parsed_flags_based_on_mode(parsed_flags.clone(), CodegenMode::ForceReadOnly)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(6, p_parsed_flags.len());
|
||||||
|
for pf in p_parsed_flags {
|
||||||
|
assert_eq!(ProtoFlagPermission::READ_ONLY, pf.permission());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parsed_flags = crate::test::parse_test_flags();
|
||||||
|
parsed_flags.parsed_flag.retain_mut(|pf| pf.is_exported());
|
||||||
|
let error = modify_parsed_flags_based_on_mode(parsed_flags, CodegenMode::ForceReadOnly)
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
"force-read-only library contains no force-read-only flags",
|
||||||
|
format!("{:?}", error)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ import static com.android.aconfig.test.Flags.enabledRo;
|
|||||||
import static com.android.aconfig.test.Flags.enabledRw;
|
import static com.android.aconfig.test.Flags.enabledRw;
|
||||||
import static com.android.aconfig.test.exported.Flags.exportedFlag;
|
import static com.android.aconfig.test.exported.Flags.exportedFlag;
|
||||||
import static com.android.aconfig.test.exported.Flags.FLAG_EXPORTED_FLAG;
|
import static com.android.aconfig.test.exported.Flags.FLAG_EXPORTED_FLAG;
|
||||||
|
import static com.android.aconfig.test.forcereadonly.Flags.froRw;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
@@ -66,4 +67,9 @@ public final class AconfigTest {
|
|||||||
assertEquals("com.android.aconfig.test.exported.exported_flag", FLAG_EXPORTED_FLAG);
|
assertEquals("com.android.aconfig.test.exported.exported_flag", FLAG_EXPORTED_FLAG);
|
||||||
assertFalse(exportedFlag());
|
assertFalse(exportedFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForceReadOnly() {
|
||||||
|
assertFalse(froRw());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
17
tools/aconfig/tests/test_force_read_only.aconfig
Normal file
17
tools/aconfig/tests/test_force_read_only.aconfig
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package: "com.android.aconfig.test.forcereadonly"
|
||||||
|
container: "system"
|
||||||
|
|
||||||
|
flag {
|
||||||
|
name: "fro_exported"
|
||||||
|
namespace: "aconfig_test"
|
||||||
|
description: "This is an exported flag"
|
||||||
|
is_exported: true
|
||||||
|
bug: "888"
|
||||||
|
}
|
||||||
|
|
||||||
|
flag {
|
||||||
|
name: "fro_rw"
|
||||||
|
namespace: "aconfig_test"
|
||||||
|
description: "This flag is not exported"
|
||||||
|
bug: "777"
|
||||||
|
}
|
Reference in New Issue
Block a user