diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp index a4ea7f4552..93fd6f73fc 100644 --- a/tools/aconfig/Android.bp +++ b/tools/aconfig/Android.bp @@ -101,7 +101,7 @@ java_aconfig_library { android_test { name: "aconfig.test.java", srcs: [ - "tests/**/*.java", + "tests/AconfigTest.java", ], manifest: "tests/AndroidManifest.xml", certificate: "platform", @@ -113,6 +113,25 @@ android_test { test_suites: ["device-tests"], } +java_aconfig_library { + name: "aconfig_host_test_java_library", + aconfig_declarations: "aconfig.test.flags", + host_supported: true, + test: true, +} + +java_test_host { + name: "AconfigJavaHostTest", + srcs: [ + "tests/AconfigHostTest.java", + ], + static_libs: [ + "aconfig_host_test_java_library", + "junit", + ], + test_suites: ["general-tests"], +} + // integration tests: C++ cc_aconfig_library { diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs index 8ab6ffa8f7..2c9dcf5a48 100644 --- a/tools/aconfig/src/codegen_java.rs +++ b/tools/aconfig/src/codegen_java.rs @@ -47,9 +47,13 @@ where "FeatureFlags.java", include_str!("../templates/FeatureFlags.java.template"), )?; + template.add_template( + "FakeFeatureFlagsImpl.java", + include_str!("../templates/FakeFeatureFlagsImpl.java.template"), + )?; let path: PathBuf = package.split('.').collect(); - ["Flags.java", "FeatureFlagsImpl.java", "FeatureFlags.java"] + ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"] .iter() .map(|file| { Ok(OutputFile { @@ -112,14 +116,14 @@ mod tests { use super::*; use std::collections::HashMap; - const EXPECTED_FEATUREFLAGS_CONTENT: &str = r#" + const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#" package com.android.aconfig.test; public interface FeatureFlags { boolean disabledRo(); boolean disabledRw(); boolean enabledRo(); boolean enabledRw(); - }"#; + "#; const EXPECTED_FLAG_COMMON_CONTENT: &str = r#" package com.android.aconfig.test; @@ -143,6 +147,29 @@ mod tests { } "#; + const EXPECTED_METHOD_NOT_IMPL_COMMON_CONTENT: &str = r#" + @Override + public boolean disabledRo() { + throw new UnsupportedOperationException( + "Method is not implemented."); + } + @Override + public boolean disabledRw() { + throw new UnsupportedOperationException( + "Method is not implemented."); + } + @Override + public boolean enabledRo() { + throw new UnsupportedOperationException( + "Method is not implemented."); + } + @Override + public boolean enabledRw() { + throw new UnsupportedOperationException( + "Method is not implemented."); + } + "#; + #[test] fn test_generate_java_code_production() { let parsed_flags = crate::test::parse_test_flags(); @@ -152,11 +179,22 @@ mod tests { CodegenMode::Production, ) .unwrap(); + let expect_featureflags_content = EXPECTED_FEATUREFLAGS_COMMON_CONTENT.to_string() + + r#" + }"#; let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); }"#; - let expected_featureflagsimpl_content = r#" + let expect_fakefeatureflagsimpl_content = r#" + package com.android.aconfig.test; + public class FakeFeatureFlagsImpl implements FeatureFlags {"# + .to_owned() + + EXPECTED_METHOD_NOT_IMPL_COMMON_CONTENT + + r#" + } + "#; + let expect_featureflagsimpl_content = r#" package com.android.aconfig.test; import android.provider.DeviceConfig; public final class FeatureFlagsImpl implements FeatureFlags { @@ -188,8 +226,12 @@ mod tests { "#; let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()), - ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content), - ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT), + ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), + ("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content.as_str()), + ( + "com/android/aconfig/test/FakeFeatureFlagsImpl.java", + expect_fakefeatureflagsimpl_content.as_str(), + ), ]); for file in generated_files { @@ -219,24 +261,47 @@ mod tests { CodegenMode::Test, ) .unwrap(); + let expect_featureflags_content = EXPECTED_FEATUREFLAGS_COMMON_CONTENT.to_string() + + r#" + public void setFlag(String flagName, boolean value); + public void resetAll(); + }"#; let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" - public static void setFeatureFlagsImpl(FeatureFlags featureFlags) { + public static void setFeatureFlags(FeatureFlags featureFlags) { Flags.FEATURE_FLAGS = featureFlags; } - public static void unsetFeatureFlagsImpl() { + public static void unsetFeatureFlags() { Flags.FEATURE_FLAGS = null; } private static FeatureFlags FEATURE_FLAGS; } "#; - let expected_featureflagsimpl_content = r#" + let expect_featureflagsimpl_content = r#" + package com.android.aconfig.test; + public final class FeatureFlagsImpl implements FeatureFlags {"# + .to_owned() + + EXPECTED_METHOD_NOT_IMPL_COMMON_CONTENT + + r#" + @Override + public void setFlag(String flagName, boolean value) { + throw new UnsupportedOperationException( + "Method is not implemented."); + } + @Override + public void resetAll() { + throw new UnsupportedOperationException( + "Method is not implemented."); + } + } + "#; + let expect_fakefeatureflagsimpl_content = r#" package com.android.aconfig.test; import static java.util.stream.Collectors.toMap; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; - public final class FeatureFlagsImpl implements FeatureFlags { + public class FakeFeatureFlagsImpl implements FeatureFlags { @Override public boolean disabledRo() { return getFlag(Flags.FLAG_DISABLED_RO); @@ -253,12 +318,14 @@ mod tests { public boolean enabledRw() { return getFlag(Flags.FLAG_ENABLED_RW); } + @Override public void setFlag(String flagName, boolean value) { if (!this.mFlagMap.containsKey(flagName)) { throw new IllegalArgumentException("no such flag" + flagName); } this.mFlagMap.put(flagName, value); } + @Override public void resetAll() { for (Map.Entry entry : mFlagMap.entrySet()) { entry.setValue(null); @@ -284,10 +351,18 @@ mod tests { ); } "#; + let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()), - ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content), - ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT), + ("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content.as_str()), + ( + "com/android/aconfig/test/FeatureFlagsImpl.java", + expect_featureflagsimpl_content.as_str(), + ), + ( + "com/android/aconfig/test/FakeFeatureFlagsImpl.java", + expect_fakefeatureflagsimpl_content, + ), ]); for file in generated_files { diff --git a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template new file mode 100644 index 0000000000..dba82ef6fa --- /dev/null +++ b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template @@ -0,0 +1,61 @@ +package {package_name}; +{{ if is_test_mode }} +import static java.util.stream.Collectors.toMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +public class FakeFeatureFlagsImpl implements FeatureFlags \{ +{{ for item in class_elements}} + @Override + public boolean {item.method_name}() \{ + return getFlag(Flags.FLAG_{item.flag_name_constant_suffix}); + } +{{ endfor}} + @Override + public void setFlag(String flagName, boolean value) \{ + if (!this.mFlagMap.containsKey(flagName)) \{ + throw new IllegalArgumentException("no such flag" + flagName); + } + this.mFlagMap.put(flagName, value); + } + + @Override + public void resetAll() \{ + for (Map.Entry entry : mFlagMap.entrySet()) \{ + entry.setValue(null); + } + } + + private boolean getFlag(String flagName) \{ + Boolean value = this.mFlagMap.get(flagName); + if (value == null) \{ + throw new IllegalArgumentException(flagName + " is not set"); + } + return value; + } + + private HashMap mFlagMap = Stream.of( + {{-for item in class_elements}} + Flags.FLAG_{item.flag_name_constant_suffix}{{ if not @last }},{{ endif }} + {{ -endfor }} + ) + .collect( + HashMap::new, + (map, elem) -> map.put(elem, null), + HashMap::putAll + ); +} +{{ else }} +{#- Generate only stub if in prod mode #} +public class FakeFeatureFlagsImpl implements FeatureFlags \{ +{{ for item in class_elements}} + @Override + public boolean {item.method_name}() \{ + throw new UnsupportedOperationException( + "Method is not implemented."); + } +{{ endfor}} +} +{{ endif }} diff --git a/tools/aconfig/templates/FeatureFlags.java.template b/tools/aconfig/templates/FeatureFlags.java.template index e0f201fc88..c99ccbb97a 100644 --- a/tools/aconfig/templates/FeatureFlags.java.template +++ b/tools/aconfig/templates/FeatureFlags.java.template @@ -4,4 +4,10 @@ public interface FeatureFlags \{ {{ for item in class_elements}} boolean {item.method_name}(); {{ endfor }} + +{{ -if is_test_mode }} + public void setFlag(String flagName, boolean value); + + public void resetAll(); +{{ -endif }} } diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/templates/FeatureFlagsImpl.java.template index 082d476de5..7e1eb151f8 100644 --- a/tools/aconfig/templates/FeatureFlagsImpl.java.template +++ b/tools/aconfig/templates/FeatureFlagsImpl.java.template @@ -1,65 +1,44 @@ package {package_name}; -{{ -if is_test_mode }} -import static java.util.stream.Collectors.toMap; - -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Stream; -{{ else}} +{{ if not is_test_mode }} {{ if is_read_write- }} import android.provider.DeviceConfig; -{{ -endif- }} {{ endif }} public final class FeatureFlagsImpl implements FeatureFlags \{ {{ for item in class_elements}} @Override public boolean {item.method_name}() \{ - {{ -if not is_test_mode- }} - {{ if item.is_read_write }} + {{ -if item.is_read_write }} return DeviceConfig.getBoolean( "{item.device_config_namespace}", "{item.device_config_flag}", {item.default_value} ); - {{ -else }} + {{ else }} return {item.default_value}; - {{ -endif- }} - {{ else }} - return getFlag(Flags.FLAG_{item.flag_name_constant_suffix}); - {{ -endif }} + {{ endif- }} } {{ endfor- }} -{{ if is_test_mode }} - 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 getFlag(String flagName) \{ - Boolean value = this.mFlagMap.get(flagName); - if (value == null) \{ - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; - } - - private HashMap mFlagMap = Stream.of( - {{-for item in class_elements}} - Flags.FLAG_{item.flag_name_constant_suffix}{{ if not @last }},{{ endif }} - {{ -endfor }} - ) - .collect( - HashMap::new, - (map, elem) -> map.put(elem, null), - HashMap::putAll - ); -{{ -endif }} } +{{ else }} +{#- Generate only stub if in test mode #} +public final class FeatureFlagsImpl implements FeatureFlags \{ +{{ for item in class_elements}} + @Override + public boolean {item.method_name}() \{ + throw new UnsupportedOperationException( + "Method is not implemented."); + } +{{ endfor- }} + @Override + public void setFlag(String flagName, boolean value) \{ + throw new UnsupportedOperationException( + "Method is not implemented."); + } + + @Override + public void resetAll() \{ + throw new UnsupportedOperationException( + "Method is not implemented."); + } +} +{{ endif }} diff --git a/tools/aconfig/templates/Flags.java.template b/tools/aconfig/templates/Flags.java.template index c244b15156..012eba6660 100644 --- a/tools/aconfig/templates/Flags.java.template +++ b/tools/aconfig/templates/Flags.java.template @@ -10,11 +10,11 @@ public final class Flags \{ } {{ endfor }} {{ -if is_test_mode }} - public static void setFeatureFlagsImpl(FeatureFlags featureFlags) \{ + public static void setFeatureFlags(FeatureFlags featureFlags) \{ Flags.FEATURE_FLAGS = featureFlags; } - public static void unsetFeatureFlagsImpl() \{ + public static void unsetFeatureFlags() \{ Flags.FEATURE_FLAGS = null; } {{ endif}} diff --git a/tools/aconfig/tests/AconfigHostTest.java b/tools/aconfig/tests/AconfigHostTest.java new file mode 100644 index 0000000000..29a01e30c8 --- /dev/null +++ b/tools/aconfig/tests/AconfigHostTest.java @@ -0,0 +1,88 @@ +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +import com.android.aconfig.test.FakeFeatureFlagsImpl; +import com.android.aconfig.test.FeatureFlags; +import com.android.aconfig.test.FeatureFlagsImpl; +import com.android.aconfig.test.Flags; + +@RunWith(JUnit4.class) +public final class AconfigHostTest { + @Test + public void testThrowsExceptionIfFlagNotSet() { + assertThrows(NullPointerException.class, () -> Flags.disabledRo()); + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + assertThrows(IllegalArgumentException.class, () -> featureFlags.disabledRo()); + } + + @Test + public void testSetFlagInFakeFeatureFlagsImpl() { + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); + assertTrue(featureFlags.enabledRw()); + featureFlags.setFlag(Flags.FLAG_ENABLED_RW, false); + assertFalse(featureFlags.enabledRw()); + + //Set Flags + assertThrows(NullPointerException.class, () -> Flags.enabledRw()); + Flags.setFeatureFlags(featureFlags); + featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); + assertTrue(Flags.enabledRw()); + Flags.unsetFeatureFlags(); + } + + @Test + public void testSetFlagWithRandomName() { + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + assertThrows(IllegalArgumentException.class, + () -> featureFlags.setFlag("Randome_name", true)); + } + + @Test + public void testResetFlagsInFakeFeatureFlagsImpl() { + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + featureFlags.setFlag(Flags.FLAG_ENABLED_RO, true); + assertTrue(featureFlags.enabledRo()); + featureFlags.resetAll(); + assertThrows(IllegalArgumentException.class, () -> featureFlags.enabledRo()); + + // Set value after reset + featureFlags.setFlag(Flags.FLAG_ENABLED_RO, false); + assertFalse(featureFlags.enabledRo()); + } + + @Test + public void testFlagsSetFeatureFlags() { + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); + assertThrows(NullPointerException.class, () -> Flags.enabledRw()); + Flags.setFeatureFlags(featureFlags); + assertTrue(Flags.enabledRw()); + Flags.unsetFeatureFlags(); + } + + @Test + public void testFlagsUnsetFeatureFlags() { + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + featureFlags.setFlag(Flags.FLAG_ENABLED_RW, true); + assertThrows(NullPointerException.class, () -> Flags.enabledRw()); + Flags.setFeatureFlags(featureFlags); + assertTrue(Flags.enabledRw()); + + Flags.unsetFeatureFlags(); + assertThrows(NullPointerException.class, () -> Flags.enabledRw()); + } + + @Test + public void testFeatureFlagsImplNotImpl() { + FeatureFlags featureFlags = new FeatureFlagsImpl(); + assertThrows(UnsupportedOperationException.class, + () -> featureFlags.enabledRw()); + } +} diff --git a/tools/aconfig/tests/AconfigTest.java b/tools/aconfig/tests/AconfigTest.java index 6681f320c1..317289de3d 100644 --- a/tools/aconfig/tests/AconfigTest.java +++ b/tools/aconfig/tests/AconfigTest.java @@ -8,12 +8,16 @@ import static com.android.aconfig.test.Flags.enabledRo; import static com.android.aconfig.test.Flags.enabledRw; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import com.android.aconfig.test.FakeFeatureFlagsImpl; +import com.android.aconfig.test.FeatureFlags; + @RunWith(JUnit4.class) public final class AconfigTest { @Test @@ -43,4 +47,10 @@ public final class AconfigTest { // (currently all flags are assigned the default READ_ONLY + DISABLED) assertFalse(enabledRw()); } + + @Test + public void testFakeFeatureFlagsImplNotImpl() { + FeatureFlags featureFlags = new FakeFeatureFlagsImpl(); + assertThrows(UnsupportedOperationException.class, () -> featureFlags.enabledRw()); + } }