Merge "aconfig: add java codegen test mode" am: 514f99b205
Original change: https://android-review.googlesource.com/c/platform/build/+/2638410 Change-Id: I164c75e0c0ab5e8ebdff7c9a746d4d08e4d9d3ae Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -20,17 +20,23 @@ use std::path::PathBuf;
|
|||||||
use tinytemplate::TinyTemplate;
|
use tinytemplate::TinyTemplate;
|
||||||
|
|
||||||
use crate::codegen;
|
use crate::codegen;
|
||||||
use crate::commands::OutputFile;
|
use crate::commands::{CodegenMode, OutputFile};
|
||||||
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
|
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
|
||||||
|
|
||||||
pub fn generate_java_code<'a, I>(package: &str, parsed_flags_iter: I) -> Result<Vec<OutputFile>>
|
pub fn generate_java_code<'a, I>(
|
||||||
|
package: &str,
|
||||||
|
parsed_flags_iter: I,
|
||||||
|
codegen_mode: CodegenMode,
|
||||||
|
) -> Result<Vec<OutputFile>>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a ProtoParsedFlag>,
|
I: Iterator<Item = &'a ProtoParsedFlag>,
|
||||||
{
|
{
|
||||||
let class_elements: Vec<ClassElement> =
|
let class_elements: Vec<ClassElement> =
|
||||||
parsed_flags_iter.map(|pf| create_class_element(package, pf)).collect();
|
parsed_flags_iter.map(|pf| create_class_element(package, pf)).collect();
|
||||||
let is_read_write = class_elements.iter().any(|elem| elem.is_read_write);
|
let is_read_write = class_elements.iter().any(|elem| elem.is_read_write);
|
||||||
let context = Context { package_name: package.to_string(), is_read_write, class_elements };
|
let is_test_mode = codegen_mode == CodegenMode::Test;
|
||||||
|
let context =
|
||||||
|
Context { class_elements, is_test_mode, is_read_write, package_name: package.to_string() };
|
||||||
let mut template = TinyTemplate::new();
|
let mut template = TinyTemplate::new();
|
||||||
template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
|
template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
|
||||||
template.add_template(
|
template.add_template(
|
||||||
@@ -56,14 +62,15 @@ where
|
|||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Context {
|
struct Context {
|
||||||
pub package_name: String,
|
|
||||||
pub is_read_write: bool,
|
|
||||||
pub class_elements: Vec<ClassElement>,
|
pub class_elements: Vec<ClassElement>,
|
||||||
|
pub is_test_mode: bool,
|
||||||
|
pub is_read_write: bool,
|
||||||
|
pub package_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ClassElement {
|
struct ClassElement {
|
||||||
pub default_value: String,
|
pub default_value: bool,
|
||||||
pub device_config_namespace: String,
|
pub device_config_namespace: String,
|
||||||
pub device_config_flag: String,
|
pub device_config_flag: String,
|
||||||
pub flag_name_constant_suffix: String,
|
pub flag_name_constant_suffix: String,
|
||||||
@@ -75,11 +82,7 @@ fn create_class_element(package: &str, pf: &ProtoParsedFlag) -> ClassElement {
|
|||||||
let device_config_flag = codegen::create_device_config_ident(package, pf.name())
|
let device_config_flag = codegen::create_device_config_ident(package, pf.name())
|
||||||
.expect("values checked at flag parse time");
|
.expect("values checked at flag parse time");
|
||||||
ClassElement {
|
ClassElement {
|
||||||
default_value: if pf.state() == ProtoFlagState::ENABLED {
|
default_value: pf.state() == ProtoFlagState::ENABLED,
|
||||||
"true".to_string()
|
|
||||||
} else {
|
|
||||||
"false".to_string()
|
|
||||||
},
|
|
||||||
device_config_namespace: pf.namespace().to_string(),
|
device_config_namespace: pf.namespace().to_string(),
|
||||||
device_config_flag,
|
device_config_flag,
|
||||||
flag_name_constant_suffix: pf.name().to_ascii_uppercase(),
|
flag_name_constant_suffix: pf.name().to_ascii_uppercase(),
|
||||||
@@ -109,34 +112,50 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[test]
|
const EXPECTED_FEATUREFLAGS_CONTENT: &str = r#"
|
||||||
fn test_generate_java_code() {
|
package com.android.aconfig.test;
|
||||||
let parsed_flags = crate::test::parse_test_flags();
|
public interface FeatureFlags {
|
||||||
let generated_files =
|
boolean disabledRo();
|
||||||
generate_java_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter()).unwrap();
|
boolean disabledRw();
|
||||||
let expect_flags_content = r#"
|
boolean enabledRo();
|
||||||
package com.android.aconfig.test;
|
boolean enabledRw();
|
||||||
public final class Flags {
|
}"#;
|
||||||
public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
|
|
||||||
public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
|
|
||||||
public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
|
|
||||||
public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
|
|
||||||
|
|
||||||
public static boolean disabledRo() {
|
const EXPECTED_FLAG_COMMON_CONTENT: &str = r#"
|
||||||
return FEATURE_FLAGS.disabledRo();
|
package com.android.aconfig.test;
|
||||||
}
|
public final class Flags {
|
||||||
public static boolean disabledRw() {
|
public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
|
||||||
return FEATURE_FLAGS.disabledRw();
|
public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
|
||||||
}
|
public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
|
||||||
public static boolean enabledRo() {
|
public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
|
||||||
return FEATURE_FLAGS.enabledRo();
|
|
||||||
}
|
public static boolean disabledRo() {
|
||||||
public static boolean enabledRw() {
|
return FEATURE_FLAGS.disabledRo();
|
||||||
return FEATURE_FLAGS.enabledRw();
|
|
||||||
}
|
|
||||||
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
|
||||||
}
|
}
|
||||||
"#;
|
public static boolean disabledRw() {
|
||||||
|
return FEATURE_FLAGS.disabledRw();
|
||||||
|
}
|
||||||
|
public static boolean enabledRo() {
|
||||||
|
return FEATURE_FLAGS.enabledRo();
|
||||||
|
}
|
||||||
|
public static boolean enabledRw() {
|
||||||
|
return FEATURE_FLAGS.enabledRw();
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generate_java_code_production() {
|
||||||
|
let parsed_flags = crate::test::parse_test_flags();
|
||||||
|
let generated_files = generate_java_code(
|
||||||
|
crate::test::TEST_PACKAGE,
|
||||||
|
parsed_flags.parsed_flag.iter(),
|
||||||
|
CodegenMode::Production,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
|
||||||
|
+ r#"
|
||||||
|
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
||||||
|
}"#;
|
||||||
let expected_featureflagsimpl_content = r#"
|
let expected_featureflagsimpl_content = r#"
|
||||||
package com.android.aconfig.test;
|
package com.android.aconfig.test;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
@@ -167,19 +186,102 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let expected_featureflags_content = r#"
|
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),
|
||||||
|
]);
|
||||||
|
|
||||||
|
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.clone()).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 generated_files = generate_java_code(
|
||||||
|
crate::test::TEST_PACKAGE,
|
||||||
|
parsed_flags.parsed_flag.iter(),
|
||||||
|
CodegenMode::Test,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
|
||||||
|
+ r#"
|
||||||
|
public static void setFeatureFlagsImpl(FeatureFlags featureFlags) {
|
||||||
|
Flags.FEATURE_FLAGS = featureFlags;
|
||||||
|
}
|
||||||
|
public static void unsetFeatureFlagsImpl() {
|
||||||
|
Flags.FEATURE_FLAGS = null;
|
||||||
|
}
|
||||||
|
private static FeatureFlags FEATURE_FLAGS;
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let expected_featureflagsimpl_content = r#"
|
||||||
package com.android.aconfig.test;
|
package com.android.aconfig.test;
|
||||||
public interface FeatureFlags {
|
import static java.util.stream.Collectors.toMap;
|
||||||
boolean disabledRo();
|
import java.util.stream.Stream;
|
||||||
boolean disabledRw();
|
import java.util.HashMap;
|
||||||
boolean enabledRo();
|
public final class FeatureFlagsImpl implements FeatureFlags {
|
||||||
boolean enabledRw();
|
@Override
|
||||||
|
public boolean disabledRo() {
|
||||||
|
return getFlag(Flags.FLAG_DISABLED_RO);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean disabledRw() {
|
||||||
|
return getFlag(Flags.FLAG_DISABLED_RW);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean enabledRo() {
|
||||||
|
return getFlag(Flags.FLAG_ENABLED_RO);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean enabledRw() {
|
||||||
|
return getFlag(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);
|
||||||
|
}
|
||||||
|
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<String, Boolean> mFlagMap = Stream.of(
|
||||||
|
Flags.FLAG_DISABLED_RO,
|
||||||
|
Flags.FLAG_DISABLED_RW,
|
||||||
|
Flags.FLAG_ENABLED_RO,
|
||||||
|
Flags.FLAG_ENABLED_RW
|
||||||
|
)
|
||||||
|
.collect(
|
||||||
|
HashMap::new,
|
||||||
|
(map, elem) -> map.put(elem, null),
|
||||||
|
HashMap::putAll
|
||||||
|
);
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let mut file_set = HashMap::from([
|
let mut file_set = HashMap::from([
|
||||||
("com/android/aconfig/test/Flags.java", expect_flags_content),
|
("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
|
||||||
("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
|
("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
|
||||||
("com/android/aconfig/test/FeatureFlags.java", expected_featureflags_content),
|
("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for file in generated_files {
|
for file in generated_files {
|
||||||
|
@@ -129,12 +129,18 @@ pub fn parse_flags(package: &str, declarations: Vec<Input>, values: Vec<Input>)
|
|||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_java_lib(mut input: Input) -> Result<Vec<OutputFile>> {
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
|
||||||
|
pub enum CodegenMode {
|
||||||
|
Production,
|
||||||
|
Test,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
|
||||||
let parsed_flags = input.try_parse_flags()?;
|
let parsed_flags = input.try_parse_flags()?;
|
||||||
let Some(package) = find_unique_package(&parsed_flags) else {
|
let Some(package) = find_unique_package(&parsed_flags) else {
|
||||||
bail!("no parsed flags, or the parsed flags use different packages");
|
bail!("no parsed flags, or the parsed flags use different packages");
|
||||||
};
|
};
|
||||||
generate_java_code(package, parsed_flags.parsed_flag.iter())
|
generate_java_code(package, parsed_flags.parsed_flag.iter(), codegen_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_cpp_lib(mut input: Input) -> Result<OutputFile> {
|
pub fn create_cpp_lib(mut input: Input) -> Result<OutputFile> {
|
||||||
|
@@ -34,7 +34,7 @@ mod protos;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
use commands::{DumpFormat, Input, OutputFile};
|
use commands::{CodegenMode, DumpFormat, Input, OutputFile};
|
||||||
|
|
||||||
fn cli() -> Command {
|
fn cli() -> Command {
|
||||||
Command::new("aconfig")
|
Command::new("aconfig")
|
||||||
@@ -49,7 +49,13 @@ fn cli() -> Command {
|
|||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("create-java-lib")
|
Command::new("create-java-lib")
|
||||||
.arg(Arg::new("cache").long("cache").required(true))
|
.arg(Arg::new("cache").long("cache").required(true))
|
||||||
.arg(Arg::new("out").long("out").required(true)),
|
.arg(Arg::new("out").long("out").required(true))
|
||||||
|
.arg(
|
||||||
|
Arg::new("mode")
|
||||||
|
.long("mode")
|
||||||
|
.value_parser(EnumValueParser::<commands::CodegenMode>::new())
|
||||||
|
.default_value("production"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("create-cpp-lib")
|
Command::new("create-cpp-lib")
|
||||||
@@ -148,7 +154,8 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
Some(("create-java-lib", sub_matches)) => {
|
Some(("create-java-lib", sub_matches)) => {
|
||||||
let cache = open_single_file(sub_matches, "cache")?;
|
let cache = open_single_file(sub_matches, "cache")?;
|
||||||
let generated_files = commands::create_java_lib(cache)?;
|
let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
|
||||||
|
let generated_files = commands::create_java_lib(cache, *mode)?;
|
||||||
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
|
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
|
||||||
generated_files
|
generated_files
|
||||||
.iter()
|
.iter()
|
||||||
|
@@ -1,20 +1,61 @@
|
|||||||
package {package_name};
|
package {package_name};
|
||||||
{{ if is_read_write }}
|
{{ -if is_test_mode }}
|
||||||
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
{{ else}}
|
||||||
|
{{ if is_read_write- }}
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
{{ -endif- }}
|
||||||
{{ endif }}
|
{{ endif }}
|
||||||
public final class FeatureFlagsImpl implements FeatureFlags \{
|
public final class FeatureFlagsImpl implements FeatureFlags \{
|
||||||
{{ for item in class_elements}}
|
{{ for item in class_elements}}
|
||||||
@Override
|
@Override
|
||||||
public boolean {item.method_name}() \{
|
public boolean {item.method_name}() \{
|
||||||
{{ if item.is_read_write- }}
|
{{ -if not is_test_mode- }}
|
||||||
|
{{ if item.is_read_write }}
|
||||||
return DeviceConfig.getBoolean(
|
return DeviceConfig.getBoolean(
|
||||||
"{item.device_config_namespace}",
|
"{item.device_config_namespace}",
|
||||||
"{item.device_config_flag}",
|
"{item.device_config_flag}",
|
||||||
{item.default_value}
|
{item.default_value}
|
||||||
);
|
);
|
||||||
{{ -else- }}
|
{{ else }}
|
||||||
return {item.default_value};
|
return {item.default_value};
|
||||||
|
{{ -endif- }}
|
||||||
|
{{ else }}
|
||||||
|
return getFlag(Flags.FLAG_{item.flag_name_constant_suffix});
|
||||||
{{ -endif }}
|
{{ -endif }}
|
||||||
}
|
}
|
||||||
{{ endfor }}
|
{{ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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<String, Boolean> 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 }}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -9,6 +9,16 @@ public final class Flags \{
|
|||||||
return FEATURE_FLAGS.{item.method_name}();
|
return FEATURE_FLAGS.{item.method_name}();
|
||||||
}
|
}
|
||||||
{{ endfor }}
|
{{ endfor }}
|
||||||
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
{{ if is_test_mode }}
|
||||||
|
public static void setFeatureFlagsImpl(FeatureFlags featureFlags) \{
|
||||||
|
Flags.FEATURE_FLAGS = featureFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unsetFeatureFlagsImpl() \{
|
||||||
|
Flags.FEATURE_FLAGS = null;
|
||||||
|
}
|
||||||
|
{{ -endif}}
|
||||||
|
|
||||||
|
private static FeatureFlags FEATURE_FLAGS{{ -if not is_test_mode }} = new FeatureFlagsImpl(){{ -endif- }};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user