diff options
author | Jeff DeCew <jeffdq@google.com> | 2024-04-24 14:43:27 +0000 |
---|---|---|
committer | Jeff DeCew <jeffdq@google.com> | 2024-04-24 14:43:27 +0000 |
commit | c3bc24f33b09ea8a841663570949efb4738ac000 (patch) | |
tree | d27ead9d27b7a5eea66f704b5357f58113cdc35f | |
parent | e0c74fbdd640865c58ff378ac4c41235e9199248 (diff) | |
download | build-c3bc24f33b09ea8a841663570949efb4738ac000.tar.gz |
Generate CustomFeatureFlags
* Creates a new general-purpose CustomFeatureFlags class
* Simplifies FakeFeatureFlagsImpl to be based on that
* This allows teams to fake FeatureFlags without having to make changes per flag.
* This allows SetFlagsRule to inject an instance of FeatureFlags that would detect if a flag has been read.
Bug: 336768870
Flag: none
Test: presubmit
Change-Id: Id3c2530d484fa5584c46d11381fcfc0ab294f33f
NOTE FOR REVIEWERS - original patch and result patch are not identical.
PLEASE REVIEW CAREFULLY.
Diffs between the patches:
"CustomFeatureFlags.java",
> + include_str!("../../templates/CustomFeatureFlags.java.template"),
> + )?;
> + template.add_template(
> - ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"]
> - .iter()
> - .map(|file| {
> - Ok(OutputFile {
> - contents: template.render(file, &context)?.into(),
> - path: path.join(file),
> - })
> - })
> - .collect::<Result<Vec<OutputFile>>>()
> + [
> + "Flags.java",
> + "FeatureFlags.java",
> + "FeatureFlagsImpl.java",
> + "CustomFeatureFlags.java",
> + "FakeFeatureFlagsImpl.java",
> + ]
> + .iter()
> + .map(|file| {
> + Ok(OutputFile { contents: template.render(file, &context)?.into(), path: path.join(file) })
> + })
> + .collect::<Result<Vec<OutputFile>>>()
> - const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
> + const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = r#"
> +
> - import java.util.HashMap;
> - import java.util.Map;
> + import java.util.List;
> + import java.util.function.BiPredicate;
> + import java.util.function.Predicate;
> +
> - public class FakeFeatureFlagsImpl implements FeatureFlags {
> - public FakeFeatureFlagsImpl() {
> - resetAll();
> + public class CustomFeatureFlags implements FeatureFlags {
> +
> + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
> +
> + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
> + mGetValueImpl = getValueImpl;
> +
> - return getValue(Flags.FLAG_DISABLED_RO);
> + return getValue(Flags.FLAG_DISABLED_RO,
> + FeatureFlags::disabledRo);
> - return getValue(Flags.FLAG_DISABLED_RW);
> + return getValue(Flags.FLAG_DISABLED_RW,
> + FeatureFlags::disabledRw);
> - return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
> + return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
> + FeatureFlags::disabledRwExported);
> - return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
> + return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
> + FeatureFlags::disabledRwInOtherNamespace);
> - return getValue(Flags.FLAG_ENABLED_FIXED_RO);
> + return getValue(Flags.FLAG_ENABLED_FIXED_RO,
> + FeatureFlags::enabledFixedRo);
> - return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
> + return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
> + FeatureFlags::enabledFixedRoExported);
> - return getValue(Flags.FLAG_ENABLED_RO);
> + return getValue(Flags.FLAG_ENABLED_RO,
> + FeatureFlags::enabledRo);
> - return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
> + return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
> + FeatureFlags::enabledRoExported);
> - return getValue(Flags.FLAG_ENABLED_RW);
> + return getValue(Flags.FLAG_ENABLED_RW,
> + FeatureFlags::enabledRw);
> - 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;
> +
> + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
> + return mGetValueImpl.test(flagName, getter);
> - 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_EXPORTED, false),
> - Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
> - Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
> - Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false),
> - Map.entry(Flags.FLAG_ENABLED_RO, false),
> - Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false),
> - Map.entry(Flags.FLAG_ENABLED_RW, false)
> - )
> - );
> +
> + public List<String> getFlagNames() {
> + return Arrays.asList(
> + Flags.FLAG_DISABLED_RO,
> + Flags.FLAG_DISABLED_RW,
> + Flags.FLAG_DISABLED_RW_EXPORTED,
> + Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
> + Flags.FLAG_ENABLED_FIXED_RO,
> + Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
> + Flags.FLAG_ENABLED_RO,
> + Flags.FLAG_ENABLED_RO_EXPORTED,
> + Flags.FLAG_ENABLED_RW
> + );
> + }
> +
> + const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
> + package com.android.aconfig.test;
> +
> + import java.util.HashMap;
> + import java.util.Map;
> + import java.util.function.Predicate;
> +
> + /** @hide */
> + public class FakeFeatureFlagsImpl extends CustomFeatureFlags {
> + private Map<String, Boolean> mFlagMap = new HashMap<>();
> +
> + public FakeFeatureFlagsImpl() {
> + super(null);
> + // Initialize the map with null values
> + for (String flagName : getFlagNames()) {
> + mFlagMap.put(flagName, null);
> + }
> + }
> +
> + @Override
> + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
> + Boolean value = this.mFlagMap.get(flagName);
> + if (value == null) {
> + throw new IllegalArgumentException(flagName + " is not set");
> + }
> + return value;
> + }
> +
> + 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);
> + }
> + }
> + }
> + "#;
> +
> + "com/android/aconfig/test/CustomFeatureFlags.java",
> + EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
> + ),
> + (
> - let expect_fake_feature_flags_impl_content = r#"
> + let expect_custom_feature_flags_content = r#"
> +
> - import java.util.HashMap;
> - import java.util.Map;
> + import java.util.List;
> + import java.util.function.BiPredicate;
> + import java.util.function.Predicate;
> +
> - public class FakeFeatureFlagsImpl implements FeatureFlags {
> - public FakeFeatureFlagsImpl() {
> - resetAll();
> + public class CustomFeatureFlags implements FeatureFlags {
> +
> + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
> +
> + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
> + mGetValueImpl = getValueImpl;
> +
> - return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
> + return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
> + FeatureFlags::disabledRwExported);
> - return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
> + return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
> + FeatureFlags::enabledFixedRoExported);
> - return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
> + return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
> + FeatureFlags::enabledRoExported);
> - public void setFlag(String flagName, boolean value) {
> - if (!this.mFlagMap.containsKey(flagName)) {
> - throw new IllegalArgumentException("no such flag " + flagName);
> - }
> - this.mFlagMap.put(flagName, value);
> +
> + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
> + return mGetValueImpl.test(flagName, getter);
> - public void resetAll() {
> - for (Map.Entry entry : mFlagMap.entrySet()) {
> - entry.setValue(null);
> - }
> +
> + public List<String> getFlagNames() {
> + return Arrays.asList(
> + Flags.FLAG_DISABLED_RW_EXPORTED,
> + Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
> + Flags.FLAG_ENABLED_RO_EXPORTED
> + );
> - 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_RW_EXPORTED, false),
> - Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false),
> - Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false)
> - )
> - );
> +
> + "com/android/aconfig/test/CustomFeatureFlags.java",
> + expect_custom_feature_flags_content,
> + ),
> + (
> - expect_fake_feature_flags_impl_content,
> + EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
> + "com/android/aconfig/test/CustomFeatureFlags.java",
> + EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
> + ),
> + (
> - let expect_fakefeatureflags_content = r#"
> + let expect_customfeatureflags_content = r#"
> +
> - import java.util.HashMap;
> - import java.util.Map;
> + import java.util.List;
> + import java.util.function.BiPredicate;
> + import java.util.function.Predicate;
> +
> - public class FakeFeatureFlagsImpl implements FeatureFlags {
> - public FakeFeatureFlagsImpl() {
> - resetAll();
> + public class CustomFeatureFlags implements FeatureFlags {
> +
> + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
> +
> + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
> + mGetValueImpl = getValueImpl;
> +
> - return getValue(Flags.FLAG_DISABLED_RO);
> + return getValue(Flags.FLAG_DISABLED_RO,
> + FeatureFlags::disabledRo);
> - return getValue(Flags.FLAG_DISABLED_RW);
> + return getValue(Flags.FLAG_DISABLED_RW,
> + FeatureFlags::disabledRw);
> - return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
> + return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
> + FeatureFlags::disabledRwInOtherNamespace);
> - return getValue(Flags.FLAG_ENABLED_FIXED_RO);
> + return getValue(Flags.FLAG_ENABLED_FIXED_RO,
> + FeatureFlags::enabledFixedRo);
> - return getValue(Flags.FLAG_ENABLED_RO);
> + return getValue(Flags.FLAG_ENABLED_RO,
> + FeatureFlags::enabledRo);
> - return getValue(Flags.FLAG_ENABLED_RW);
> + return getValue(Flags.FLAG_ENABLED_RW,
> + FeatureFlags::enabledRw);
> - 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;
> +
> + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
> + return mGetValueImpl.test(flagName, getter);
> - 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)
> - )
> - );
> +
> + public List<String> getFlagNames() {
> + return Arrays.asList(
> + Flags.FLAG_DISABLED_RO,
> + Flags.FLAG_DISABLED_RW,
> + Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
> + Flags.FLAG_ENABLED_FIXED_RO,
> + Flags.FLAG_ENABLED_RO,
> + Flags.FLAG_ENABLED_RW
> + );
> + }
> +
> +
> - ("com/android/aconfig/test/FakeFeatureFlagsImpl.java", expect_fakefeatureflags_content),
> + ("com/android/aconfig/test/CustomFeatureFlags.java", expect_customfeatureflags_content),
> + (
> + "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
> + EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
> + ),
> --- /dev/null
> +++ tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
> +package {package_name};
> +
> +{{ if not library_exported- }}
> +// TODO(b/303773055): Remove the annotation after access issue is resolved.
> +import android.compat.annotation.UnsupportedAppUsage;
> +{{ -endif }}
> +import java.util.Arrays;
> +import java.util.HashSet;
> +import java.util.List;
> +import java.util.Set;
> +import java.util.function.BiPredicate;
> +import java.util.function.Predicate;
> +
> +/** @hide */
> +public class CustomFeatureFlags implements FeatureFlags \{
> +
> + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
> +
> + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) \{
> + mGetValueImpl = getValueImpl;
> + }
> +
> +{{ -for item in flag_elements}}
> + @Override
> +{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }}
> + public boolean {item.method_name}() \{
> + return getValue(Flags.FLAG_{item.flag_name_constant_suffix},
> + FeatureFlags::{item.method_name});
> + }
> +{{ endfor }}
> +
> +{{ -if not library_exported }}
> + public boolean isFlagReadOnlyOptimized(String flagName) \{
> + if (mReadOnlyFlagsSet.contains(flagName) &&
> + isOptimizationEnabled()) \{
> + return true;
> + }
> + return false;
> + }
> +
> + @com.android.aconfig.annotations.AssumeTrueForR8
> + private boolean isOptimizationEnabled() \{
> + return false;
> + }
> +{{ -endif }}
> +
> + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
> + return mGetValueImpl.test(flagName, getter);
> + }
> +
> + public List<String> getFlagNames() \{
> + return Arrays.asList(
> + {{ -for item in flag_elements }}
> + Flags.FLAG_{item.flag_name_constant_suffix}
> + {{ -if not @last }},{{ endif }}
> + {{ -endfor }}
> + );
> + }
> +
> + private Set<String> mReadOnlyFlagsSet = new HashSet<>(
> + Arrays.asList(
> + {{ -for item in flag_elements }}
> + {{ -if not item.is_read_write }}
> + Flags.FLAG_{item.flag_name_constant_suffix},
> + {{ -endif }}
> + {{ -endfor }}
> + ""{# The empty string here is to resolve the ending comma #}
> + )
> + );
> +}
> --- tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
> +++ tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
> -{{ if not library_exported- }}
> -// TODO(b/303773055): Remove the annotation after access issue is resolved.
> -import android.compat.annotation.UnsupportedAppUsage;
> -{{ -endif }}
> -import java.util.Arrays;
> +
> -import java.util.HashSet;
> -import java.util.Set;
> +import java.util.function.Predicate;
> -public class FakeFeatureFlagsImpl implements FeatureFlags \{
> +public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{
> + private Map<String, Boolean> mFlagMap = new HashMap<>();
> +
> - resetAll();
> + super(null);
> + // Initialize the map with null values
> + for (String flagName : getFlagNames()) \{
> + mFlagMap.put(flagName, null);
> + }
> -{{ for item in flag_elements}}
> -{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }}
> - public boolean {item.method_name}() \{
> - return getValue(Flags.FLAG_{item.flag_name_constant_suffix});
> + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
> + Boolean value = this.mFlagMap.get(flagName);
> + if (value == null) \{
> + throw new IllegalArgumentException(flagName + " is not set");
> + }
> + return value;
> -{{ endfor}}
> +
> -{{ if not library_exported }}
> - public boolean isFlagReadOnlyOptimized(String flagName) \{
> - if (mReadOnlyFlagsSet.contains(flagName) &&
> - isOptimizationEnabled()) \{
> - return true;
> - }
> - return false;
> - }
> -
> - @com.android.aconfig.annotations.AssumeTrueForR8
> - private boolean isOptimizationEnabled() \{
> - return false;
> - }
> -{{ -endif }}
> - 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(
> - {{ -for item in flag_elements }}
> - Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false)
> - {{ -if not @last }},{{ endif }}
> - {{ -endfor }}
> - )
> - );
> -
> - private Set<String> mReadOnlyFlagsSet = new HashSet<>(
> - Arrays.asList(
> - {{ -for item in flag_elements }}
> - {{ -if not item.is_read_write }}
> - Flags.FLAG_{item.flag_name_constant_suffix},
> - {{ -endif }}
> - {{ -endfor }}
> - ""{# The empty string here is to resolve the ending comma #}
> - )
> - );
Original patch:
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
old mode 100644
new mode 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -63,21 +63,28 @@
"FeatureFlags.java",
include_str!("../../templates/FeatureFlags.java.template"),
)?;
+ template.add_template(
+ "CustomFeatureFlags.java",
+ include_str!("../../templates/CustomFeatureFlags.java.template"),
+ )?;
template.add_template(
"FakeFeatureFlagsImpl.java",
include_str!("../../templates/FakeFeatureFlagsImpl.java.template"),
)?;
let path: PathBuf = package.split('.').collect();
- ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"]
- .iter()
- .map(|file| {
- Ok(OutputFile {
- contents: template.render(file, &context)?.into(),
- path: path.join(file),
- })
- })
- .co
[[[Original patch trimmed due to size. Decoded string size: 26318. Decoded string SHA1: 7db34b7baf0a0bbaa1cff48b6ccab9c65408e743.]]]
Result patch:
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 18a4be5..9abc892 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -64,20 +64,27 @@
include_str!("../../templates/FeatureFlags.java.template"),
)?;
template.add_template(
+ "CustomFeatureFlags.java",
+ include_str!("../../templates/CustomFeatureFlags.java.template"),
+ )?;
+ template.add_template(
"FakeFeatureFlagsImpl.java",
include_str!("../../templates/FakeFeatureFlagsImpl.java.template"),
)?;
let path: PathBuf = package.split('.').collect();
- ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"]
- .iter()
- .map(|file| {
- Ok(OutputFile {
- contents: template.render(file, &context)?.into(),
- path: path.join(file),
- })
- })
- .collect::<Result<Vec<OutputFile>>>
[[[Result patch trimmed due to size. Decoded string size: 26308. Decoded string SHA1: bb07ee948a630a6bc4cfbbf2a8df178f3e6d3103.]]]
Change-Id: I94184633303b9d76f7943451fb3b2c93d071ec1c
3 files changed, 289 insertions, 184 deletions
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs index 18a4be5094..9abc892908 100644 --- a/tools/aconfig/aconfig/src/codegen/java.rs +++ b/tools/aconfig/aconfig/src/codegen/java.rs @@ -64,20 +64,27 @@ where include_str!("../../templates/FeatureFlags.java.template"), )?; template.add_template( + "CustomFeatureFlags.java", + include_str!("../../templates/CustomFeatureFlags.java.template"), + )?; + template.add_template( "FakeFeatureFlagsImpl.java", include_str!("../../templates/FakeFeatureFlagsImpl.java.template"), )?; let path: PathBuf = package.split('.').collect(); - ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"] - .iter() - .map(|file| { - Ok(OutputFile { - contents: template.render(file, &context)?.into(), - path: path.join(file), - }) - }) - .collect::<Result<Vec<OutputFile>>>() + [ + "Flags.java", + "FeatureFlags.java", + "FeatureFlagsImpl.java", + "CustomFeatureFlags.java", + "FakeFeatureFlagsImpl.java", + ] + .iter() + .map(|file| { + Ok(OutputFile { contents: template.render(file, &context)?.into(), path: path.join(file) }) + }) + .collect::<Result<Vec<OutputFile>>>() } fn gen_flags_by_namespace(flags: &[FlagElement]) -> Vec<NamespaceFlags> { @@ -292,76 +299,82 @@ mod tests { } "#; - const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#" + const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = 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.Arrays; - import java.util.HashMap; import java.util.HashSet; - import java.util.Map; + import java.util.List; import java.util.Set; + import java.util.function.BiPredicate; + import java.util.function.Predicate; + /** @hide */ - public class FakeFeatureFlagsImpl implements FeatureFlags { - public FakeFeatureFlagsImpl() { - resetAll(); + public class CustomFeatureFlags implements FeatureFlags { + + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) { + mGetValueImpl = getValueImpl; } + @Override @UnsupportedAppUsage public boolean disabledRo() { - return getValue(Flags.FLAG_DISABLED_RO); + return getValue(Flags.FLAG_DISABLED_RO, + FeatureFlags::disabledRo); } @Override @UnsupportedAppUsage public boolean disabledRw() { - return getValue(Flags.FLAG_DISABLED_RW); + return getValue(Flags.FLAG_DISABLED_RW, + FeatureFlags::disabledRw); } @Override @UnsupportedAppUsage public boolean disabledRwExported() { - return getValue(Flags.FLAG_DISABLED_RW_EXPORTED); + return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, + FeatureFlags::disabledRwExported); } @Override @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { - return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE); + return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + FeatureFlags::disabledRwInOtherNamespace); } @Override @UnsupportedAppUsage public boolean enabledFixedRo() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO); + return getValue(Flags.FLAG_ENABLED_FIXED_RO, + FeatureFlags::enabledFixedRo); } @Override @UnsupportedAppUsage public boolean enabledFixedRoExported() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + FeatureFlags::enabledFixedRoExported); } @Override @UnsupportedAppUsage public boolean enabledRo() { - return getValue(Flags.FLAG_ENABLED_RO); + return getValue(Flags.FLAG_ENABLED_RO, + FeatureFlags::enabledRo); } @Override @UnsupportedAppUsage public boolean enabledRoExported() { - return getValue(Flags.FLAG_ENABLED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, + FeatureFlags::enabledRoExported); } @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); - } + return getValue(Flags.FLAG_ENABLED_RW, + FeatureFlags::enabledRw); } + public boolean isFlagReadOnlyOptimized(String flagName) { if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) { @@ -369,30 +382,30 @@ mod tests { } return false; } + @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() { return false; } - private boolean getValue(String flagName) { - Boolean value = this.mFlagMap.get(flagName); - if (value == null) { - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; + + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) { + return mGetValueImpl.test(flagName, getter); } - 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_EXPORTED, false), - Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_RO, false), - Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_RW, false) - ) - ); + + public List<String> getFlagNames() { + return Arrays.asList( + Flags.FLAG_DISABLED_RO, + Flags.FLAG_DISABLED_RW, + Flags.FLAG_DISABLED_RW_EXPORTED, + Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + Flags.FLAG_ENABLED_FIXED_RO, + Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + Flags.FLAG_ENABLED_RO, + Flags.FLAG_ENABLED_RO_EXPORTED, + Flags.FLAG_ENABLED_RW + ); + } + private Set<String> mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( Flags.FLAG_DISABLED_RO, @@ -406,6 +419,49 @@ mod tests { } "#; + const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#" + package com.android.aconfig.test; + + import java.util.HashMap; + import java.util.Map; + import java.util.function.Predicate; + + /** @hide */ + public class FakeFeatureFlagsImpl extends CustomFeatureFlags { + private Map<String, Boolean> mFlagMap = new HashMap<>(); + + public FakeFeatureFlagsImpl() { + super(null); + // Initialize the map with null values + for (String flagName : getFlagNames()) { + mFlagMap.put(flagName, null); + } + } + + @Override + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) { + Boolean value = this.mFlagMap.get(flagName); + if (value == null) { + throw new IllegalArgumentException(flagName + " is not set"); + } + return value; + } + + 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); + } + } + } + "#; + #[test] fn test_generate_java_code_production() { let parsed_flags = crate::test::parse_test_flags(); @@ -549,6 +605,10 @@ mod tests { ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT), ( + "com/android/aconfig/test/CustomFeatureFlags.java", + EXPECTED_CUSTOMFEATUREFLAGS_CONTENT, + ), + ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), @@ -671,55 +731,53 @@ mod tests { } }"#; - let expect_fake_feature_flags_impl_content = r#" + let expect_custom_feature_flags_content = r#" package com.android.aconfig.test; + import java.util.Arrays; - import java.util.HashMap; import java.util.HashSet; - import java.util.Map; + import java.util.List; import java.util.Set; + import java.util.function.BiPredicate; + import java.util.function.Predicate; + /** @hide */ - public class FakeFeatureFlagsImpl implements FeatureFlags { - public FakeFeatureFlagsImpl() { - resetAll(); + public class CustomFeatureFlags implements FeatureFlags { + + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) { + mGetValueImpl = getValueImpl; } + @Override public boolean disabledRwExported() { - return getValue(Flags.FLAG_DISABLED_RW_EXPORTED); + return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, + FeatureFlags::disabledRwExported); } @Override public boolean enabledFixedRoExported() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + FeatureFlags::enabledFixedRoExported); } @Override public boolean enabledRoExported() { - return getValue(Flags.FLAG_ENABLED_RO_EXPORTED); - } - public void setFlag(String flagName, boolean value) { - if (!this.mFlagMap.containsKey(flagName)) { - throw new IllegalArgumentException("no such flag " + flagName); - } - this.mFlagMap.put(flagName, value); + return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, + FeatureFlags::enabledRoExported); } - public void resetAll() { - for (Map.Entry entry : mFlagMap.entrySet()) { - entry.setValue(null); - } + + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) { + return mGetValueImpl.test(flagName, getter); } - private boolean getValue(String flagName) { - Boolean value = this.mFlagMap.get(flagName); - if (value == null) { - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; + + public List<String> getFlagNames() { + return Arrays.asList( + Flags.FLAG_DISABLED_RW_EXPORTED, + Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + Flags.FLAG_ENABLED_RO_EXPORTED + ); } - private Map<String, Boolean> mFlagMap = new HashMap<>( - Map.ofEntries( - Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false) - ) - ); + private Set<String> mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( "" @@ -733,8 +791,12 @@ mod tests { ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content), ( + "com/android/aconfig/test/CustomFeatureFlags.java", + expect_custom_feature_flags_content, + ), + ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", - expect_fake_feature_flags_impl_content, + EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); @@ -854,6 +916,10 @@ mod tests { ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), ( + "com/android/aconfig/test/CustomFeatureFlags.java", + EXPECTED_CUSTOMFEATUREFLAGS_CONTENT, + ), + ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), @@ -1020,61 +1086,64 @@ mod tests { private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); }"#; - let expect_fakefeatureflags_content = r#" + let expect_customfeatureflags_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.Arrays; - import java.util.HashMap; import java.util.HashSet; - import java.util.Map; + import java.util.List; import java.util.Set; + import java.util.function.BiPredicate; + import java.util.function.Predicate; + /** @hide */ - public class FakeFeatureFlagsImpl implements FeatureFlags { - public FakeFeatureFlagsImpl() { - resetAll(); + public class CustomFeatureFlags implements FeatureFlags { + + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) { + mGetValueImpl = getValueImpl; } + @Override @UnsupportedAppUsage public boolean disabledRo() { - return getValue(Flags.FLAG_DISABLED_RO); + return getValue(Flags.FLAG_DISABLED_RO, + FeatureFlags::disabledRo); } @Override @UnsupportedAppUsage public boolean disabledRw() { - return getValue(Flags.FLAG_DISABLED_RW); + return getValue(Flags.FLAG_DISABLED_RW, + FeatureFlags::disabledRw); } @Override @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { - return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE); + return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + FeatureFlags::disabledRwInOtherNamespace); } @Override @UnsupportedAppUsage public boolean enabledFixedRo() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO); + return getValue(Flags.FLAG_ENABLED_FIXED_RO, + FeatureFlags::enabledFixedRo); } @Override @UnsupportedAppUsage public boolean enabledRo() { - return getValue(Flags.FLAG_ENABLED_RO); + return getValue(Flags.FLAG_ENABLED_RO, + FeatureFlags::enabledRo); } @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); - } + return getValue(Flags.FLAG_ENABLED_RW, + FeatureFlags::enabledRw); } + public boolean isFlagReadOnlyOptimized(String flagName) { if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) { @@ -1082,27 +1151,27 @@ mod tests { } return false; } + @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() { return false; } - 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) - ) - ); + + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) { + return mGetValueImpl.test(flagName, getter); + } + + public List<String> getFlagNames() { + return Arrays.asList( + Flags.FLAG_DISABLED_RO, + Flags.FLAG_DISABLED_RW, + Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + Flags.FLAG_ENABLED_FIXED_RO, + Flags.FLAG_ENABLED_RO, + Flags.FLAG_ENABLED_RW + ); + } + private Set<String> mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( Flags.FLAG_DISABLED_RO, @@ -1116,11 +1185,16 @@ mod tests { ); } "#; + 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), + ("com/android/aconfig/test/CustomFeatureFlags.java", expect_customfeatureflags_content), + ( + "com/android/aconfig/test/FakeFeatureFlagsImpl.java", + EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, + ), ]); for file in generated_files { diff --git a/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template new file mode 100644 index 0000000000..b82b9cb827 --- /dev/null +++ b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template @@ -0,0 +1,70 @@ +package {package_name}; + +{{ if not library_exported- }} +// TODO(b/303773055): Remove the annotation after access issue is resolved. +import android.compat.annotation.UnsupportedAppUsage; +{{ -endif }} +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +/** @hide */ +public class CustomFeatureFlags implements FeatureFlags \{ + + private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) \{ + mGetValueImpl = getValueImpl; + } + +{{ -for item in flag_elements}} + @Override +{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }} + public boolean {item.method_name}() \{ + return getValue(Flags.FLAG_{item.flag_name_constant_suffix}, + FeatureFlags::{item.method_name}); + } +{{ endfor }} + +{{ -if not library_exported }} + public boolean isFlagReadOnlyOptimized(String flagName) \{ + if (mReadOnlyFlagsSet.contains(flagName) && + isOptimizationEnabled()) \{ + return true; + } + return false; + } + + @com.android.aconfig.annotations.AssumeTrueForR8 + private boolean isOptimizationEnabled() \{ + return false; + } +{{ -endif }} + + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{ + return mGetValueImpl.test(flagName, getter); + } + + public List<String> getFlagNames() \{ + return Arrays.asList( + {{ -for item in flag_elements }} + Flags.FLAG_{item.flag_name_constant_suffix} + {{ -if not @last }},{{ endif }} + {{ -endfor }} + ); + } + + private Set<String> mReadOnlyFlagsSet = new HashSet<>( + Arrays.asList( + {{ -for item in flag_elements }} + {{ -if not item.is_read_write }} + Flags.FLAG_{item.flag_name_constant_suffix}, + {{ -endif }} + {{ -endfor }} + ""{# The empty string here is to resolve the ending comma #} + ) + ); +} diff --git a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template index 177e711e77..c20d3c5061 100644 --- a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template +++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template @@ -1,27 +1,30 @@ package {package_name}; -{{ if not library_exported- }} -// TODO(b/303773055): Remove the annotation after access issue is resolved. -import android.compat.annotation.UnsupportedAppUsage; -{{ -endif }} -import java.util.Arrays; + import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; +import java.util.function.Predicate; /** @hide */ -public class FakeFeatureFlagsImpl implements FeatureFlags \{ +public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{ + private Map<String, Boolean> mFlagMap = new HashMap<>(); + public FakeFeatureFlagsImpl() \{ - resetAll(); + super(null); + // Initialize the map with null values + for (String flagName : getFlagNames()) \{ + mFlagMap.put(flagName, null); + } } -{{ for item in flag_elements}} @Override -{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }} - public boolean {item.method_name}() \{ - return getValue(Flags.FLAG_{item.flag_name_constant_suffix}); + protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{ + Boolean value = this.mFlagMap.get(flagName); + if (value == null) \{ + throw new IllegalArgumentException(flagName + " is not set"); + } + return value; } -{{ endfor}} + public void setFlag(String flagName, boolean value) \{ if (!this.mFlagMap.containsKey(flagName)) \{ throw new IllegalArgumentException("no such flag " + flagName); @@ -34,46 +37,4 @@ public class FakeFeatureFlagsImpl implements FeatureFlags \{ entry.setValue(null); } } -{{ if not library_exported }} - public boolean isFlagReadOnlyOptimized(String flagName) \{ - if (mReadOnlyFlagsSet.contains(flagName) && - isOptimizationEnabled()) \{ - return true; - } - return false; - } - - @com.android.aconfig.annotations.AssumeTrueForR8 - private boolean isOptimizationEnabled() \{ - return false; - } -{{ -endif }} - 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( - {{ -for item in flag_elements }} - Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false) - {{ -if not @last }},{{ endif }} - {{ -endfor }} - ) - ); - - private Set<String> mReadOnlyFlagsSet = new HashSet<>( - Arrays.asList( - {{ -for item in flag_elements }} - {{ -if not item.is_read_write }} - Flags.FLAG_{item.flag_name_constant_suffix}, - {{ -endif }} - {{ -endfor }} - ""{# The empty string here is to resolve the ending comma #} - ) - ); } |