* 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
1262 lines
48 KiB
Rust
1262 lines
48 KiB
Rust
/*
|
|
* 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.
|
|
*/
|
|
|
|
use anyhow::Result;
|
|
use serde::Serialize;
|
|
use std::collections::{BTreeMap, BTreeSet};
|
|
use std::path::PathBuf;
|
|
use tinytemplate::TinyTemplate;
|
|
|
|
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
|
|
|
|
use crate::codegen;
|
|
use crate::codegen::CodegenMode;
|
|
use crate::commands::OutputFile;
|
|
|
|
pub fn generate_java_code<I>(
|
|
package: &str,
|
|
parsed_flags_iter: I,
|
|
codegen_mode: CodegenMode,
|
|
) -> Result<Vec<OutputFile>>
|
|
where
|
|
I: Iterator<Item = ProtoParsedFlag>,
|
|
{
|
|
let flag_elements: Vec<FlagElement> =
|
|
parsed_flags_iter.map(|pf| create_flag_element(package, &pf)).collect();
|
|
let namespace_flags = gen_flags_by_namespace(&flag_elements);
|
|
let properties_set: BTreeSet<String> =
|
|
flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect();
|
|
let is_test_mode = codegen_mode == CodegenMode::Test;
|
|
let library_exported = codegen_mode == CodegenMode::Exported;
|
|
let runtime_lookup_required =
|
|
flag_elements.iter().any(|elem| elem.is_read_write) || library_exported;
|
|
|
|
let context = Context {
|
|
flag_elements,
|
|
namespace_flags,
|
|
is_test_mode,
|
|
runtime_lookup_required,
|
|
properties_set,
|
|
package_name: package.to_string(),
|
|
library_exported,
|
|
};
|
|
let mut template = TinyTemplate::new();
|
|
template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?;
|
|
template.add_template(
|
|
"FeatureFlagsImpl.java",
|
|
include_str!("../../templates/FeatureFlagsImpl.java.template"),
|
|
)?;
|
|
template.add_template(
|
|
"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",
|
|
"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> {
|
|
let mut namespace_to_flag: BTreeMap<String, Vec<FlagElement>> = BTreeMap::new();
|
|
|
|
for flag in flags {
|
|
match namespace_to_flag.get_mut(&flag.device_config_namespace) {
|
|
Some(flag_list) => flag_list.push(flag.clone()),
|
|
None => {
|
|
namespace_to_flag.insert(flag.device_config_namespace.clone(), vec![flag.clone()]);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace_to_flag
|
|
.iter()
|
|
.map(|(namespace, flags)| NamespaceFlags {
|
|
namespace: namespace.to_string(),
|
|
flags: flags.clone(),
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct Context {
|
|
pub flag_elements: Vec<FlagElement>,
|
|
pub namespace_flags: Vec<NamespaceFlags>,
|
|
pub is_test_mode: bool,
|
|
pub runtime_lookup_required: bool,
|
|
pub properties_set: BTreeSet<String>,
|
|
pub package_name: String,
|
|
pub library_exported: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Debug)]
|
|
struct NamespaceFlags {
|
|
pub namespace: String,
|
|
pub flags: Vec<FlagElement>,
|
|
}
|
|
|
|
#[derive(Serialize, Clone, Debug)]
|
|
struct FlagElement {
|
|
pub default_value: bool,
|
|
pub device_config_namespace: String,
|
|
pub device_config_flag: String,
|
|
pub flag_name_constant_suffix: String,
|
|
pub is_read_write: bool,
|
|
pub method_name: String,
|
|
pub properties: String,
|
|
}
|
|
|
|
fn create_flag_element(package: &str, pf: &ProtoParsedFlag) -> FlagElement {
|
|
let device_config_flag = codegen::create_device_config_ident(package, pf.name())
|
|
.expect("values checked at flag parse time");
|
|
FlagElement {
|
|
default_value: pf.state() == ProtoFlagState::ENABLED,
|
|
device_config_namespace: pf.namespace().to_string(),
|
|
device_config_flag,
|
|
flag_name_constant_suffix: pf.name().to_ascii_uppercase(),
|
|
is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,
|
|
method_name: format_java_method_name(pf.name()),
|
|
properties: format_property_name(pf.namespace()),
|
|
}
|
|
}
|
|
|
|
fn format_java_method_name(flag_name: &str) -> String {
|
|
let splits: Vec<&str> = flag_name.split('_').filter(|&word| !word.is_empty()).collect();
|
|
if splits.len() == 1 {
|
|
let name = splits[0];
|
|
name[0..1].to_ascii_lowercase() + &name[1..]
|
|
} else {
|
|
splits
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, word)| {
|
|
if index == 0 {
|
|
word.to_ascii_lowercase()
|
|
} else {
|
|
word[0..1].to_ascii_uppercase() + &word[1..].to_ascii_lowercase()
|
|
}
|
|
})
|
|
.collect::<Vec<String>>()
|
|
.join("")
|
|
}
|
|
}
|
|
|
|
fn format_property_name(property_name: &str) -> String {
|
|
let name = format_java_method_name(property_name);
|
|
format!("mProperties{}{}", &name[0..1].to_ascii_uppercase(), &name[1..])
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::collections::HashMap;
|
|
|
|
const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = 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
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRo();
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRw();
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRwExported();
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRwInOtherNamespace();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledFixedRo();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledFixedRoExported();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledRo();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledRoExported();
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledRw();
|
|
}
|
|
"#;
|
|
|
|
const EXPECTED_FLAG_COMMON_CONTENT: &str = 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_EXPORTED = "com.android.aconfig.test.disabled_rw_exported";
|
|
/** @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_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported";
|
|
/** @hide */
|
|
public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
|
|
/** @hide */
|
|
public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
|
|
/** @hide */
|
|
public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
|
|
|
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRo() {
|
|
return FEATURE_FLAGS.disabledRo();
|
|
}
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRw() {
|
|
return FEATURE_FLAGS.disabledRw();
|
|
}
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRwExported() {
|
|
return FEATURE_FLAGS.disabledRwExported();
|
|
}
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRwInOtherNamespace() {
|
|
return FEATURE_FLAGS.disabledRwInOtherNamespace();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledFixedRo() {
|
|
return FEATURE_FLAGS.enabledFixedRo();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledFixedRoExported() {
|
|
return FEATURE_FLAGS.enabledFixedRoExported();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledRo() {
|
|
return FEATURE_FLAGS.enabledRo();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledRoExported() {
|
|
return FEATURE_FLAGS.enabledRoExported();
|
|
}
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledRw() {
|
|
return FEATURE_FLAGS.enabledRw();
|
|
}
|
|
"#;
|
|
|
|
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.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;
|
|
}
|
|
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRo() {
|
|
return getValue(Flags.FLAG_DISABLED_RO,
|
|
FeatureFlags::disabledRo);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRw() {
|
|
return getValue(Flags.FLAG_DISABLED_RW,
|
|
FeatureFlags::disabledRw);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwExported() {
|
|
return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
|
|
FeatureFlags::disabledRwExported);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwInOtherNamespace() {
|
|
return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
|
|
FeatureFlags::disabledRwInOtherNamespace);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRo() {
|
|
return getValue(Flags.FLAG_ENABLED_FIXED_RO,
|
|
FeatureFlags::enabledFixedRo);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRoExported() {
|
|
return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
|
|
FeatureFlags::enabledFixedRoExported);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRo() {
|
|
return getValue(Flags.FLAG_ENABLED_RO,
|
|
FeatureFlags::enabledRo);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRoExported() {
|
|
return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
|
|
FeatureFlags::enabledRoExported);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRw() {
|
|
return getValue(Flags.FLAG_ENABLED_RW,
|
|
FeatureFlags::enabledRw);
|
|
}
|
|
|
|
public boolean isFlagReadOnlyOptimized(String flagName) {
|
|
if (mReadOnlyFlagsSet.contains(flagName) &&
|
|
isOptimizationEnabled()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
private boolean isOptimizationEnabled() {
|
|
return 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_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,
|
|
Flags.FLAG_ENABLED_FIXED_RO,
|
|
Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
|
|
Flags.FLAG_ENABLED_RO,
|
|
Flags.FLAG_ENABLED_RO_EXPORTED,
|
|
""
|
|
)
|
|
);
|
|
}
|
|
"#;
|
|
|
|
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();
|
|
let mode = CodegenMode::Production;
|
|
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_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
|
|
+ r#"
|
|
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
|
}"#;
|
|
|
|
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;
|
|
import android.provider.DeviceConfig;
|
|
import android.provider.DeviceConfig.Properties;
|
|
/** @hide */
|
|
public final class FeatureFlagsImpl implements FeatureFlags {
|
|
private static boolean aconfig_test_is_cached = false;
|
|
private static boolean other_namespace_is_cached = false;
|
|
private static boolean disabledRw = false;
|
|
private static boolean disabledRwExported = false;
|
|
private static boolean disabledRwInOtherNamespace = false;
|
|
private static boolean enabledRw = true;
|
|
|
|
|
|
private void load_overrides_aconfig_test() {
|
|
try {
|
|
Properties properties = DeviceConfig.getProperties("aconfig_test");
|
|
disabledRw =
|
|
properties.getBoolean("com.android.aconfig.test.disabled_rw", false);
|
|
disabledRwExported =
|
|
properties.getBoolean("com.android.aconfig.test.disabled_rw_exported", false);
|
|
enabledRw =
|
|
properties.getBoolean("com.android.aconfig.test.enabled_rw", true);
|
|
} catch (NullPointerException e) {
|
|
throw new RuntimeException(
|
|
"Cannot read value from namespace aconfig_test "
|
|
+ "from DeviceConfig. It could be that the code using flag "
|
|
+ "executed before SettingsProvider initialization. Please use "
|
|
+ "fixed read-only flag by adding is_fixed_read_only: true in "
|
|
+ "flag declaration.",
|
|
e
|
|
);
|
|
}
|
|
aconfig_test_is_cached = true;
|
|
}
|
|
|
|
private void load_overrides_other_namespace() {
|
|
try {
|
|
Properties properties = DeviceConfig.getProperties("other_namespace");
|
|
disabledRwInOtherNamespace =
|
|
properties.getBoolean("com.android.aconfig.test.disabled_rw_in_other_namespace", false);
|
|
} catch (NullPointerException e) {
|
|
throw new RuntimeException(
|
|
"Cannot read value from namespace other_namespace "
|
|
+ "from DeviceConfig. It could be that the code using flag "
|
|
+ "executed before SettingsProvider initialization. Please use "
|
|
+ "fixed read-only flag by adding is_fixed_read_only: true in "
|
|
+ "flag declaration.",
|
|
e
|
|
);
|
|
}
|
|
other_namespace_is_cached = true;
|
|
}
|
|
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRo() {
|
|
return false;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRw() {
|
|
if (!aconfig_test_is_cached) {
|
|
load_overrides_aconfig_test();
|
|
}
|
|
return disabledRw;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwExported() {
|
|
if (!aconfig_test_is_cached) {
|
|
load_overrides_aconfig_test();
|
|
}
|
|
return disabledRwExported;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwInOtherNamespace() {
|
|
if (!other_namespace_is_cached) {
|
|
load_overrides_other_namespace();
|
|
}
|
|
return disabledRwInOtherNamespace;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRo() {
|
|
return true;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRoExported() {
|
|
return true;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRo() {
|
|
return true;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRoExported() {
|
|
return true;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRw() {
|
|
if (!aconfig_test_is_cached) {
|
|
load_overrides_aconfig_test();
|
|
}
|
|
return enabledRw;
|
|
}
|
|
}
|
|
"#;
|
|
let mut file_set = HashMap::from([
|
|
("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
|
|
("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,
|
|
),
|
|
]);
|
|
|
|
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]
|
|
fn test_generate_java_code_exported() {
|
|
let parsed_flags = crate::test::parse_test_flags();
|
|
let mode = CodegenMode::Exported;
|
|
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_flags_content = r#"
|
|
package com.android.aconfig.test;
|
|
/** @hide */
|
|
public final class Flags {
|
|
/** @hide */
|
|
public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported";
|
|
/** @hide */
|
|
public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported";
|
|
/** @hide */
|
|
public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
|
|
public static boolean disabledRwExported() {
|
|
return FEATURE_FLAGS.disabledRwExported();
|
|
}
|
|
public static boolean enabledFixedRoExported() {
|
|
return FEATURE_FLAGS.enabledFixedRoExported();
|
|
}
|
|
public static boolean enabledRoExported() {
|
|
return FEATURE_FLAGS.enabledRoExported();
|
|
}
|
|
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
|
}
|
|
"#;
|
|
|
|
let expect_feature_flags_content = r#"
|
|
package com.android.aconfig.test;
|
|
/** @hide */
|
|
public interface FeatureFlags {
|
|
boolean disabledRwExported();
|
|
boolean enabledFixedRoExported();
|
|
boolean enabledRoExported();
|
|
}
|
|
"#;
|
|
|
|
let expect_feature_flags_impl_content = r#"
|
|
package com.android.aconfig.test;
|
|
import android.provider.DeviceConfig;
|
|
import android.provider.DeviceConfig.Properties;
|
|
/** @hide */
|
|
public final class FeatureFlagsImpl implements FeatureFlags {
|
|
private static boolean aconfig_test_is_cached = false;
|
|
private static boolean disabledRwExported = false;
|
|
private static boolean enabledFixedRoExported = false;
|
|
private static boolean enabledRoExported = false;
|
|
|
|
|
|
private void load_overrides_aconfig_test() {
|
|
try {
|
|
Properties properties = DeviceConfig.getProperties("aconfig_test");
|
|
disabledRwExported =
|
|
properties.getBoolean("com.android.aconfig.test.disabled_rw_exported", false);
|
|
enabledFixedRoExported =
|
|
properties.getBoolean("com.android.aconfig.test.enabled_fixed_ro_exported", false);
|
|
enabledRoExported =
|
|
properties.getBoolean("com.android.aconfig.test.enabled_ro_exported", false);
|
|
} catch (NullPointerException e) {
|
|
throw new RuntimeException(
|
|
"Cannot read value from namespace aconfig_test "
|
|
+ "from DeviceConfig. It could be that the code using flag "
|
|
+ "executed before SettingsProvider initialization. Please use "
|
|
+ "fixed read-only flag by adding is_fixed_read_only: true in "
|
|
+ "flag declaration.",
|
|
e
|
|
);
|
|
}
|
|
aconfig_test_is_cached = true;
|
|
}
|
|
@Override
|
|
public boolean disabledRwExported() {
|
|
if (!aconfig_test_is_cached) {
|
|
load_overrides_aconfig_test();
|
|
}
|
|
return disabledRwExported;
|
|
}
|
|
@Override
|
|
public boolean enabledFixedRoExported() {
|
|
if (!aconfig_test_is_cached) {
|
|
load_overrides_aconfig_test();
|
|
}
|
|
return enabledFixedRoExported;
|
|
}
|
|
@Override
|
|
public boolean enabledRoExported() {
|
|
if (!aconfig_test_is_cached) {
|
|
load_overrides_aconfig_test();
|
|
}
|
|
return enabledRoExported;
|
|
}
|
|
}"#;
|
|
|
|
let expect_custom_feature_flags_content = r#"
|
|
package com.android.aconfig.test;
|
|
|
|
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;
|
|
}
|
|
|
|
@Override
|
|
public boolean disabledRwExported() {
|
|
return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
|
|
FeatureFlags::disabledRwExported);
|
|
}
|
|
@Override
|
|
public boolean enabledFixedRoExported() {
|
|
return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
|
|
FeatureFlags::enabledFixedRoExported);
|
|
}
|
|
@Override
|
|
public boolean enabledRoExported() {
|
|
return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
|
|
FeatureFlags::enabledRoExported);
|
|
}
|
|
|
|
protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
|
|
return mGetValueImpl.test(flagName, getter);
|
|
}
|
|
|
|
public List<String> getFlagNames() {
|
|
return Arrays.asList(
|
|
Flags.FLAG_DISABLED_RW_EXPORTED,
|
|
Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
|
|
Flags.FLAG_ENABLED_RO_EXPORTED
|
|
);
|
|
}
|
|
|
|
private Set<String> mReadOnlyFlagsSet = new HashSet<>(
|
|
Arrays.asList(
|
|
""
|
|
)
|
|
);
|
|
}
|
|
"#;
|
|
|
|
let mut file_set = HashMap::from([
|
|
("com/android/aconfig/test/Flags.java", expect_flags_content),
|
|
("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",
|
|
EXPECTED_FAKEFEATUREFLAGSIMPL_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]
|
|
fn test_generate_java_code_test() {
|
|
let parsed_flags = crate::test::parse_test_flags();
|
|
let mode = CodegenMode::Test;
|
|
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_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
|
|
+ r#"
|
|
public static void setFeatureFlags(FeatureFlags featureFlags) {
|
|
Flags.FEATURE_FLAGS = featureFlags;
|
|
}
|
|
public static void unsetFeatureFlags() {
|
|
Flags.FEATURE_FLAGS = null;
|
|
}
|
|
private static FeatureFlags FEATURE_FLAGS;
|
|
}
|
|
"#;
|
|
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
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRo() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRw() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwExported() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwInOtherNamespace() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRo() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRoExported() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRo() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRoExported() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRw() {
|
|
throw new UnsupportedOperationException(
|
|
"Method is not implemented.");
|
|
}
|
|
}
|
|
"#;
|
|
|
|
let mut file_set = HashMap::from([
|
|
("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
|
|
("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,
|
|
),
|
|
]);
|
|
|
|
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]
|
|
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
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRo();
|
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRw();
|
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean disabledRwInOtherNamespace();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledFixedRo();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
boolean enabledRo();
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@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
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRo() {
|
|
return false;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRw() {
|
|
return false;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwInOtherNamespace() {
|
|
return false;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRo() {
|
|
return true;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRo() {
|
|
return true;
|
|
}
|
|
@Override
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@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
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRo() {
|
|
return FEATURE_FLAGS.disabledRo();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRw() {
|
|
return FEATURE_FLAGS.disabledRw();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeFalseForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean disabledRwInOtherNamespace() {
|
|
return FEATURE_FLAGS.disabledRwInOtherNamespace();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledFixedRo() {
|
|
return FEATURE_FLAGS.enabledFixedRo();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledRo() {
|
|
return FEATURE_FLAGS.enabledRo();
|
|
}
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
@com.android.aconfig.annotations.AconfigFlagAccessor
|
|
@UnsupportedAppUsage
|
|
public static boolean enabledRw() {
|
|
return FEATURE_FLAGS.enabledRw();
|
|
}
|
|
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
|
}"#;
|
|
|
|
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.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;
|
|
}
|
|
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRo() {
|
|
return getValue(Flags.FLAG_DISABLED_RO,
|
|
FeatureFlags::disabledRo);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRw() {
|
|
return getValue(Flags.FLAG_DISABLED_RW,
|
|
FeatureFlags::disabledRw);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean disabledRwInOtherNamespace() {
|
|
return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
|
|
FeatureFlags::disabledRwInOtherNamespace);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledFixedRo() {
|
|
return getValue(Flags.FLAG_ENABLED_FIXED_RO,
|
|
FeatureFlags::enabledFixedRo);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRo() {
|
|
return getValue(Flags.FLAG_ENABLED_RO,
|
|
FeatureFlags::enabledRo);
|
|
}
|
|
@Override
|
|
@UnsupportedAppUsage
|
|
public boolean enabledRw() {
|
|
return getValue(Flags.FLAG_ENABLED_RW,
|
|
FeatureFlags::enabledRw);
|
|
}
|
|
|
|
public boolean isFlagReadOnlyOptimized(String flagName) {
|
|
if (mReadOnlyFlagsSet.contains(flagName) &&
|
|
isOptimizationEnabled()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@com.android.aconfig.annotations.AssumeTrueForR8
|
|
private boolean isOptimizationEnabled() {
|
|
return 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,
|
|
Flags.FLAG_DISABLED_RW,
|
|
Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
|
|
Flags.FLAG_ENABLED_FIXED_RO,
|
|
Flags.FLAG_ENABLED_RO,
|
|
Flags.FLAG_ENABLED_RW,
|
|
""
|
|
)
|
|
);
|
|
}
|
|
"#;
|
|
|
|
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/CustomFeatureFlags.java", expect_customfeatureflags_content),
|
|
(
|
|
"com/android/aconfig/test/FakeFeatureFlagsImpl.java",
|
|
EXPECTED_FAKEFEATUREFLAGSIMPL_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]
|
|
fn test_format_java_method_name() {
|
|
let expected = "someSnakeName";
|
|
let input = "____some_snake___name____";
|
|
let formatted_name = format_java_method_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "someSnakeName";
|
|
let formatted_name = format_java_method_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "SomeSnakeName";
|
|
let formatted_name = format_java_method_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "SomeSnakeName_";
|
|
let formatted_name = format_java_method_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "_SomeSnakeName";
|
|
let formatted_name = format_java_method_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_property_name() {
|
|
let expected = "mPropertiesSomeSnakeName";
|
|
let input = "____some_snake___name____";
|
|
let formatted_name = format_property_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "someSnakeName";
|
|
let formatted_name = format_property_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "SomeSnakeName";
|
|
let formatted_name = format_property_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
|
|
let input = "SomeSnakeName_";
|
|
let formatted_name = format_property_name(input);
|
|
assert_eq!(expected, formatted_name);
|
|
}
|
|
}
|