aconfig: allow dots in package fields

Allow package fields to include dots.

Update the generated code based on the package name: if the package name
is com.android.example:

  - java: package com.android.example; ...
  - C++: namespace com::android::example { ... }
  - Rust: mod com { mod android { mod example { ... } } }

Also, update examples to use dots in the package fields.

Also, remove unnecessary #include from the auto-generated C++ code: the
header should not include itself.

Bug: 285000854
Test: atest aconfig.test
Change-Id: I8a5352e25c64c34dee0725202a1b7c9957819de8
This commit is contained in:
Mårten Kongstad
2023-05-31 13:29:35 +02:00
parent 9fb58965af
commit fbd71e2773
14 changed files with 161 additions and 112 deletions

View File

@@ -244,7 +244,7 @@ mod tests {
#[test] #[test]
fn test_package_try_from_text_proto() { fn test_package_try_from_text_proto() {
let expected = FlagDeclarations { let expected = FlagDeclarations {
package: "ns".to_owned(), package: "com.example".to_owned(),
flags: vec![ flags: vec![
FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() }, FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() }, FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
@@ -252,7 +252,7 @@ mod tests {
}; };
let s = r#" let s = r#"
package: "ns" package: "com.example"
flag { flag {
name: "a" name: "a"
description: "A" description: "A"
@@ -270,14 +270,14 @@ mod tests {
#[test] #[test]
fn test_flag_declaration_try_from_text_proto_list() { fn test_flag_declaration_try_from_text_proto_list() {
let expected = FlagValue { let expected = FlagValue {
package: "ns".to_owned(), package: "com.example".to_owned(),
name: "1234".to_owned(), name: "1234".to_owned(),
state: FlagState::Enabled, state: FlagState::Enabled,
permission: Permission::ReadOnly, permission: Permission::ReadOnly,
}; };
let s = r#" let s = r#"
package: "ns" package: "com.example"
name: "1234" name: "1234"
state: ENABLED state: ENABLED
permission: READ_ONLY permission: READ_ONLY

View File

@@ -109,7 +109,7 @@ pub struct CacheBuilder {
impl CacheBuilder { impl CacheBuilder {
pub fn new(package: String) -> Result<CacheBuilder> { pub fn new(package: String) -> Result<CacheBuilder> {
ensure!(codegen::is_valid_identifier(&package), "bad package"); ensure!(codegen::is_valid_package_ident(&package), "bad package");
let cache = Cache { package, items: vec![] }; let cache = Cache { package, items: vec![] };
Ok(CacheBuilder { cache }) Ok(CacheBuilder { cache })
} }
@@ -119,7 +119,7 @@ impl CacheBuilder {
source: Source, source: Source,
declaration: FlagDeclaration, declaration: FlagDeclaration,
) -> Result<&mut CacheBuilder> { ) -> Result<&mut CacheBuilder> {
ensure!(codegen::is_valid_identifier(&declaration.name), "bad flag name"); ensure!(codegen::is_valid_name_ident(&declaration.name), "bad flag name");
ensure!(!declaration.description.is_empty(), "empty flag description"); ensure!(!declaration.description.is_empty(), "empty flag description");
ensure!( ensure!(
self.cache.items.iter().all(|item| item.name != declaration.name), self.cache.items.iter().all(|item| item.name != declaration.name),
@@ -147,8 +147,8 @@ impl CacheBuilder {
source: Source, source: Source,
value: FlagValue, value: FlagValue,
) -> Result<&mut CacheBuilder> { ) -> Result<&mut CacheBuilder> {
ensure!(codegen::is_valid_identifier(&value.package), "bad flag package"); ensure!(codegen::is_valid_package_ident(&value.package), "bad flag package");
ensure!(codegen::is_valid_identifier(&value.name), "bad flag name"); ensure!(codegen::is_valid_name_ident(&value.name), "bad flag name");
ensure!( ensure!(
value.package == self.cache.package, value.package == self.cache.package,
"failed to set values for flag {}/{} from {}: expected package {}", "failed to set values for flag {}/{} from {}: expected package {}",
@@ -182,7 +182,7 @@ mod tests {
#[test] #[test]
fn test_add_flag_declaration() { fn test_add_flag_declaration() {
let mut builder = CacheBuilder::new("ns".to_string()).unwrap(); let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
builder builder
.add_flag_declaration( .add_flag_declaration(
Source::File("first.txt".to_string()), Source::File("first.txt".to_string()),
@@ -217,12 +217,12 @@ mod tests {
#[test] #[test]
fn test_add_flag_value() { fn test_add_flag_value() {
let mut builder = CacheBuilder::new("ns".to_string()).unwrap(); let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
let error = builder let error = builder
.add_flag_value( .add_flag_value(
Source::Memory, Source::Memory,
FlagValue { FlagValue {
package: "ns".to_string(), package: "com.example".to_string(),
name: "foo".to_string(), name: "foo".to_string(),
state: FlagState::Enabled, state: FlagState::Enabled,
permission: Permission::ReadOnly, permission: Permission::ReadOnly,
@@ -231,7 +231,7 @@ mod tests {
.unwrap_err(); .unwrap_err();
assert_eq!( assert_eq!(
&format!("{:?}", error), &format!("{:?}", error),
"failed to set values for flag ns/foo from <memory>: flag not declared" "failed to set values for flag com.example/foo from <memory>: flag not declared"
); );
builder builder
@@ -245,7 +245,7 @@ mod tests {
.add_flag_value( .add_flag_value(
Source::Memory, Source::Memory,
FlagValue { FlagValue {
package: "ns".to_string(), package: "com.example".to_string(),
name: "foo".to_string(), name: "foo".to_string(),
state: FlagState::Disabled, state: FlagState::Disabled,
permission: Permission::ReadOnly, permission: Permission::ReadOnly,
@@ -257,7 +257,7 @@ mod tests {
.add_flag_value( .add_flag_value(
Source::Memory, Source::Memory,
FlagValue { FlagValue {
package: "ns".to_string(), package: "com.example".to_string(),
name: "foo".to_string(), name: "foo".to_string(),
state: FlagState::Enabled, state: FlagState::Enabled,
permission: Permission::ReadWrite, permission: Permission::ReadWrite,
@@ -277,7 +277,7 @@ mod tests {
}, },
) )
.unwrap_err(); .unwrap_err();
assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_package/foo from <memory>: expected package ns"); assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_package/foo from <memory>: expected package com.example");
let cache = builder.build(); let cache = builder.build();
let item = cache.iter().find(|&item| item.name == "foo").unwrap(); let item = cache.iter().find(|&item| item.name == "foo").unwrap();
@@ -292,7 +292,7 @@ mod tests {
#[test] #[test]
fn test_reject_empty_flag_declaration_fields() { fn test_reject_empty_flag_declaration_fields() {
let mut builder = CacheBuilder::new("ns".to_string()).unwrap(); let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
let error = builder let error = builder
.add_flag_declaration( .add_flag_declaration(
@@ -313,7 +313,7 @@ mod tests {
#[test] #[test]
fn test_reject_empty_flag_value_files() { fn test_reject_empty_flag_value_files() {
let mut builder = CacheBuilder::new("ns".to_string()).unwrap(); let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
builder builder
.add_flag_declaration( .add_flag_declaration(
Source::Memory, Source::Memory,
@@ -338,7 +338,7 @@ mod tests {
.add_flag_value( .add_flag_value(
Source::Memory, Source::Memory,
FlagValue { FlagValue {
package: "ns".to_string(), package: "com.example".to_string(),
name: "".to_string(), name: "".to_string(),
state: FlagState::Enabled, state: FlagState::Enabled,
permission: Permission::ReadOnly, permission: Permission::ReadOnly,

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
pub fn is_valid_identifier(s: &str) -> bool { pub fn is_valid_name_ident(s: &str) -> bool {
// Identifiers must match [a-z][a-z0-9_]* // Identifiers must match [a-z][a-z0-9_]*
let mut chars = s.chars(); let mut chars = s.chars();
let Some(first) = chars.next() else { let Some(first) = chars.next() else {
@@ -26,18 +26,40 @@ pub fn is_valid_identifier(s: &str) -> bool {
chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_') chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
} }
pub fn is_valid_package_ident(s: &str) -> bool {
s.split('.').all(is_valid_name_ident)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_is_valid_identifier() { fn test_is_valid_name_ident() {
assert!(is_valid_identifier("foo")); assert!(is_valid_name_ident("foo"));
assert!(is_valid_identifier("foo_bar_123")); assert!(is_valid_name_ident("foo_bar_123"));
assert!(!is_valid_identifier("")); assert!(!is_valid_name_ident(""));
assert!(!is_valid_identifier("123_foo")); assert!(!is_valid_name_ident("123_foo"));
assert!(!is_valid_identifier("foo-bar")); assert!(!is_valid_name_ident("foo-bar"));
assert!(!is_valid_identifier("foo-b\u{00e5}r")); assert!(!is_valid_name_ident("foo-b\u{00e5}r"));
}
#[test]
fn test_is_valid_package_ident() {
assert!(is_valid_package_ident("foo"));
assert!(is_valid_package_ident("foo_bar_123"));
assert!(is_valid_package_ident("foo.bar"));
assert!(is_valid_package_ident("foo.bar.a123"));
assert!(!is_valid_package_ident(""));
assert!(!is_valid_package_ident("123_foo"));
assert!(!is_valid_package_ident("foo-bar"));
assert!(!is_valid_package_ident("foo-b\u{00e5}r"));
assert!(!is_valid_package_ident("foo.bar.123"));
assert!(!is_valid_package_ident(".foo.bar"));
assert!(!is_valid_package_ident("foo.bar."));
assert!(!is_valid_package_ident("."));
assert!(!is_valid_package_ident("foo..bar"));
} }
} }

View File

@@ -14,28 +14,35 @@
* limitations under the License. * limitations under the License.
*/ */
use anyhow::Result; use anyhow::{ensure, Result};
use serde::Serialize; use serde::Serialize;
use tinytemplate::TinyTemplate; use tinytemplate::TinyTemplate;
use crate::aconfig::{FlagState, Permission}; use crate::aconfig::{FlagState, Permission};
use crate::cache::{Cache, Item}; use crate::cache::{Cache, Item};
use crate::codegen;
use crate::commands::OutputFile; use crate::commands::OutputFile;
pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> { pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect(); let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
let readwrite = class_elements.iter().any(|item| item.readwrite); let readwrite = class_elements.iter().any(|item| item.readwrite);
let package = cache.package().to_lowercase(); let package = cache.package().to_string();
let context = Context { package: package.clone(), readwrite, class_elements }; let header = package.replace('.', "_");
let cpp_namespace = package.replace('.', "::");
ensure!(codegen::is_valid_name_ident(&header));
let context =
Context { header: header.clone(), cpp_namespace, package, readwrite, class_elements };
let mut template = TinyTemplate::new(); let mut template = TinyTemplate::new();
template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?; template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
let contents = template.render("cpp_code_gen", &context)?; let contents = template.render("cpp_code_gen", &context)?;
let path = ["aconfig", &(package + ".h")].iter().collect(); let path = ["aconfig", &(header + ".h")].iter().collect();
Ok(OutputFile { contents: contents.into(), path }) Ok(OutputFile { contents: contents.into(), path })
} }
#[derive(Serialize)] #[derive(Serialize)]
struct Context { struct Context {
pub header: String,
pub cpp_namespace: String,
pub package: String, pub package: String,
pub readwrite: bool, pub readwrite: bool,
pub class_elements: Vec<ClassElement>, pub class_elements: Vec<ClassElement>,
@@ -69,7 +76,7 @@ mod tests {
#[test] #[test]
fn test_cpp_codegen_build_time_flag_only() { fn test_cpp_codegen_build_time_flag_only() {
let package = "my_package"; let package = "com.example";
let mut builder = CacheBuilder::new(package.to_string()).unwrap(); let mut builder = CacheBuilder::new(package.to_string()).unwrap();
builder builder
.add_flag_declaration( .add_flag_declaration(
@@ -109,11 +116,10 @@ mod tests {
) )
.unwrap(); .unwrap();
let cache = builder.build(); let cache = builder.build();
let expect_content = r#"#ifndef my_package_HEADER_H let expect_content = r#"#ifndef com_example_HEADER_H
#define my_package_HEADER_H #define com_example_HEADER_H
#include "my_package.h"
namespace my_package { namespace com::example {
class my_flag_one { class my_flag_one {
public: public:
@@ -133,7 +139,7 @@ mod tests {
#endif #endif
"#; "#;
let file = generate_cpp_code(&cache).unwrap(); let file = generate_cpp_code(&cache).unwrap();
assert_eq!("aconfig/my_package.h", file.path.to_str().unwrap()); assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
assert_eq!( assert_eq!(
expect_content.replace(' ', ""), expect_content.replace(' ', ""),
String::from_utf8(file.contents).unwrap().replace(' ', "") String::from_utf8(file.contents).unwrap().replace(' ', "")
@@ -142,7 +148,7 @@ mod tests {
#[test] #[test]
fn test_cpp_codegen_runtime_flag() { fn test_cpp_codegen_runtime_flag() {
let package = "my_package"; let package = "com.example";
let mut builder = CacheBuilder::new(package.to_string()).unwrap(); let mut builder = CacheBuilder::new(package.to_string()).unwrap();
builder builder
.add_flag_declaration( .add_flag_declaration(
@@ -172,20 +178,19 @@ mod tests {
) )
.unwrap(); .unwrap();
let cache = builder.build(); let cache = builder.build();
let expect_content = r#"#ifndef my_package_HEADER_H let expect_content = r#"#ifndef com_example_HEADER_H
#define my_package_HEADER_H #define com_example_HEADER_H
#include "my_package.h"
#include <server_configurable_flags/get_flags.h> #include <server_configurable_flags/get_flags.h>
using namespace server_configurable_flags; using namespace server_configurable_flags;
namespace my_package { namespace com::example {
class my_flag_one { class my_flag_one {
public: public:
virtual const bool value() { virtual const bool value() {
return GetServerConfigurableFlag( return GetServerConfigurableFlag(
"my_package", "com.example",
"my_flag_one", "my_flag_one",
"false") == "true"; "false") == "true";
} }
@@ -195,7 +200,7 @@ mod tests {
public: public:
virtual const bool value() { virtual const bool value() {
return GetServerConfigurableFlag( return GetServerConfigurableFlag(
"my_package", "com.example",
"my_flag_two", "my_flag_two",
"true") == "true"; "true") == "true";
} }
@@ -205,7 +210,7 @@ mod tests {
#endif #endif
"#; "#;
let file = generate_cpp_code(&cache).unwrap(); let file = generate_cpp_code(&cache).unwrap();
assert_eq!("aconfig/my_package.h", file.path.to_str().unwrap()); assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
assert_eq!( assert_eq!(
expect_content.replace(' ', ""), expect_content.replace(' ', ""),
String::from_utf8(file.contents).unwrap().replace(' ', "") String::from_utf8(file.contents).unwrap().replace(' ', "")

View File

@@ -31,7 +31,7 @@ pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
let mut template = TinyTemplate::new(); let mut template = TinyTemplate::new();
template.add_template("java_code_gen", include_str!("../templates/java.template"))?; template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
let contents = template.render("java_code_gen", &context)?; let contents = template.render("java_code_gen", &context)?;
let mut path: PathBuf = ["aconfig", package].iter().collect(); let mut path: PathBuf = package.split('.').collect();
// TODO: Allow customization of the java class name // TODO: Allow customization of the java class name
path.push("Flags.java"); path.push("Flags.java");
Ok(OutputFile { contents: contents.into(), path }) Ok(OutputFile { contents: contents.into(), path })
@@ -76,7 +76,7 @@ mod tests {
#[test] #[test]
fn test_generate_java_code() { fn test_generate_java_code() {
let package = "example"; let package = "com.example";
let mut builder = CacheBuilder::new(package.to_string()).unwrap(); let mut builder = CacheBuilder::new(package.to_string()).unwrap();
builder builder
.add_flag_declaration( .add_flag_declaration(
@@ -106,7 +106,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let cache = builder.build(); let cache = builder.build();
let expect_content = r#"package aconfig.example; let expect_content = r#"package com.example;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
@@ -118,7 +118,7 @@ mod tests {
public static boolean test2() { public static boolean test2() {
return DeviceConfig.getBoolean( return DeviceConfig.getBoolean(
"example", "com.example",
"test2__test2", "test2__test2",
false false
); );
@@ -127,7 +127,7 @@ mod tests {
} }
"#; "#;
let file = generate_java_code(&cache).unwrap(); let file = generate_java_code(&cache).unwrap();
assert_eq!("aconfig/example/Flags.java", file.path.to_str().unwrap()); assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
assert_eq!( assert_eq!(
expect_content.replace(' ', ""), expect_content.replace(' ', ""),
String::from_utf8(file.contents).unwrap().replace(' ', "") String::from_utf8(file.contents).unwrap().replace(' ', "")

View File

@@ -24,9 +24,12 @@ use crate::commands::OutputFile;
pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> { pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
let package = cache.package(); let package = cache.package();
let parsed_flags: Vec<TemplateParsedFlag> = let parsed_flags: Vec<TemplateParsedFlag> = cache.iter().map(|item| item.into()).collect();
cache.iter().map(|item| create_template_parsed_flag(package, item)).collect(); let context = TemplateContext {
let context = TemplateContext { package: package.to_string(), parsed_flags }; package: package.to_string(),
parsed_flags,
modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
};
let mut template = TinyTemplate::new(); let mut template = TinyTemplate::new();
template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?; template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
let contents = template.render("rust_code_gen", &context)?; let contents = template.render("rust_code_gen", &context)?;
@@ -38,12 +41,12 @@ pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
struct TemplateContext { struct TemplateContext {
pub package: String, pub package: String,
pub parsed_flags: Vec<TemplateParsedFlag>, pub parsed_flags: Vec<TemplateParsedFlag>,
pub modules: Vec<String>,
} }
#[derive(Serialize)] #[derive(Serialize)]
struct TemplateParsedFlag { struct TemplateParsedFlag {
pub name: String, pub name: String,
pub fn_name: String,
// TinyTemplate's conditionals are limited to single <bool> expressions; list all options here // TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
// Invariant: exactly one of these fields will be true // Invariant: exactly one of these fields will be true
@@ -52,28 +55,29 @@ struct TemplateParsedFlag {
pub is_read_write: bool, pub is_read_write: bool,
} }
#[allow(clippy::nonminimal_bool)] impl From<&Item> for TemplateParsedFlag {
fn create_template_parsed_flag(package: &str, item: &Item) -> TemplateParsedFlag { #[allow(clippy::nonminimal_bool)]
let template = TemplateParsedFlag { fn from(item: &Item) -> Self {
name: item.name.clone(), let template = TemplateParsedFlag {
fn_name: format!("{}_{}", package, &item.name), name: item.name.clone(),
is_read_only_enabled: item.permission == Permission::ReadOnly is_read_only_enabled: item.permission == Permission::ReadOnly
&& item.state == FlagState::Enabled, && item.state == FlagState::Enabled,
is_read_only_disabled: item.permission == Permission::ReadOnly is_read_only_disabled: item.permission == Permission::ReadOnly
&& item.state == FlagState::Disabled, && item.state == FlagState::Disabled,
is_read_write: item.permission == Permission::ReadWrite, is_read_write: item.permission == Permission::ReadWrite,
}; };
#[rustfmt::skip] #[rustfmt::skip]
debug_assert!( debug_assert!(
(template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) || (template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) ||
(!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) || (!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) ||
(!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write), (!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write),
"TemplateParsedFlag invariant failed: {} {} {}", "TemplateParsedFlag invariant failed: {} {} {}",
template.is_read_only_enabled, template.is_read_only_enabled,
template.is_read_only_disabled, template.is_read_only_disabled,
template.is_read_write, template.is_read_write,
); );
template template
}
} }
#[cfg(test)] #[cfg(test)]
@@ -86,24 +90,33 @@ mod tests {
let generated = generate_rust_code(&cache).unwrap(); let generated = generate_rust_code(&cache).unwrap();
assert_eq!("src/lib.rs", format!("{}", generated.path.display())); assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
let expected = r#" let expected = r#"
pub mod com {
pub mod android {
pub mod aconfig {
pub mod test {
#[inline(always)] #[inline(always)]
pub const fn r#test_disabled_ro() -> bool { pub const fn r#disabled_ro() -> bool {
false false
} }
#[inline(always)] #[inline(always)]
pub fn r#test_disabled_rw() -> bool { pub fn r#disabled_rw() -> bool {
flags_rust::GetServerConfigurableFlag("test", "disabled_rw", "false") == "true" flags_rust::GetServerConfigurableFlag("com.android.aconfig.test", "disabled_rw", "false") == "true"
} }
#[inline(always)] #[inline(always)]
pub const fn r#test_enabled_ro() -> bool { pub const fn r#enabled_ro() -> bool {
true true
} }
#[inline(always)] #[inline(always)]
pub fn r#test_enabled_rw() -> bool { pub fn r#enabled_rw() -> bool {
flags_rust::GetServerConfigurableFlag("test", "enabled_rw", "false") == "true" flags_rust::GetServerConfigurableFlag("com.android.aconfig.test", "enabled_rw", "false") == "true"
}
}
}
}
} }
"#; "#;
assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim()); assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());

View File

@@ -186,9 +186,9 @@ mod tests {
use super::*; use super::*;
use crate::aconfig::{FlagState, Permission}; use crate::aconfig::{FlagState, Permission};
fn create_test_cache_ns1() -> Cache { fn create_test_cache_com_example() -> Cache {
let s = r#" let s = r#"
package: "ns1" package: "com.example"
flag { flag {
name: "a" name: "a"
description: "Description of a" description: "Description of a"
@@ -201,19 +201,19 @@ mod tests {
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }]; let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
let o = r#" let o = r#"
flag_value { flag_value {
package: "ns1" package: "com.example"
name: "a" name: "a"
state: DISABLED state: DISABLED
permission: READ_ONLY permission: READ_ONLY
} }
"#; "#;
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }]; let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
create_cache("ns1", declarations, values).unwrap() create_cache("com.example", declarations, values).unwrap()
} }
fn create_test_cache_ns2() -> Cache { fn create_test_cache_com_other() -> Cache {
let s = r#" let s = r#"
package: "ns2" package: "com.other"
flag { flag {
name: "c" name: "c"
description: "Description of c" description: "Description of c"
@@ -222,19 +222,19 @@ mod tests {
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }]; let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
let o = r#" let o = r#"
flag_value { flag_value {
package: "ns2" package: "com.other"
name: "c" name: "c"
state: DISABLED state: DISABLED
permission: READ_ONLY permission: READ_ONLY
} }
"#; "#;
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }]; let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
create_cache("ns2", declarations, values).unwrap() create_cache("com.other", declarations, values).unwrap()
} }
#[test] #[test]
fn test_create_cache() { fn test_create_cache() {
let caches = create_test_cache_ns1(); // calls create_cache let caches = create_test_cache_com_example(); // calls create_cache
let item = caches.iter().find(|&item| item.name == "a").unwrap(); let item = caches.iter().find(|&item| item.name == "a").unwrap();
assert_eq!(FlagState::Disabled, item.state); assert_eq!(FlagState::Disabled, item.state);
assert_eq!(Permission::ReadOnly, item.permission); assert_eq!(Permission::ReadOnly, item.permission);
@@ -245,7 +245,7 @@ mod tests {
let caches = vec![crate::test::create_cache()]; let caches = vec![crate::test::create_cache()];
let bytes = create_device_config_defaults(caches).unwrap(); let bytes = create_device_config_defaults(caches).unwrap();
let text = std::str::from_utf8(&bytes).unwrap(); let text = std::str::from_utf8(&bytes).unwrap();
assert_eq!("test/disabled_rw:disabled\ntest/enabled_rw:enabled\n", text); assert_eq!("com.android.aconfig.test/disabled_rw:disabled\ncom.android.aconfig.test/enabled_rw:enabled\n", text);
} }
#[test] #[test]
@@ -253,12 +253,12 @@ mod tests {
let caches = vec![crate::test::create_cache()]; let caches = vec![crate::test::create_cache()];
let bytes = create_device_config_sysprops(caches).unwrap(); let bytes = create_device_config_sysprops(caches).unwrap();
let text = std::str::from_utf8(&bytes).unwrap(); let text = std::str::from_utf8(&bytes).unwrap();
assert_eq!("persist.device_config.test.disabled_rw=false\npersist.device_config.test.enabled_rw=true\n", text); assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
} }
#[test] #[test]
fn test_dump_text_format() { fn test_dump_text_format() {
let caches = vec![create_test_cache_ns1()]; let caches = vec![create_test_cache_com_example()];
let bytes = dump_cache(caches, DumpFormat::Text).unwrap(); let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
let text = std::str::from_utf8(&bytes).unwrap(); let text = std::str::from_utf8(&bytes).unwrap();
assert!(text.contains("a: Disabled")); assert!(text.contains("a: Disabled"));
@@ -269,7 +269,7 @@ mod tests {
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint}; use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
use protobuf::Message; use protobuf::Message;
let caches = vec![create_test_cache_ns1()]; let caches = vec![create_test_cache_com_example()];
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap(); let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap(); let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
@@ -280,7 +280,7 @@ mod tests {
let item = let item =
actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap(); actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
assert_eq!(item.package(), "ns1"); assert_eq!(item.package(), "com.example");
assert_eq!(item.name(), "b"); assert_eq!(item.name(), "b");
assert_eq!(item.description(), "Description of b"); assert_eq!(item.description(), "Description of b");
assert_eq!(item.state(), ProtoFlagState::DISABLED); assert_eq!(item.state(), ProtoFlagState::DISABLED);
@@ -294,7 +294,7 @@ mod tests {
#[test] #[test]
fn test_dump_multiple_caches() { fn test_dump_multiple_caches() {
let caches = vec![create_test_cache_ns1(), create_test_cache_ns2()]; let caches = vec![create_test_cache_com_example(), create_test_cache_com_other()];
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap(); let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap(); let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
assert_eq!( assert_eq!(
@@ -302,10 +302,14 @@ mod tests {
.iter() .iter()
.map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name())) .map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name()))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec!["ns1/a".to_string(), "ns1/b".to_string(), "ns2/c".to_string()] vec![
"com.example/a".to_string(),
"com.example/b".to_string(),
"com.other/c".to_string()
]
); );
let caches = vec![create_test_cache_ns2(), create_test_cache_ns1()]; let caches = vec![create_test_cache_com_other(), create_test_cache_com_example()];
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap(); let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap(); let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
assert_eq!(dump, dump_reversed_input); assert_eq!(dump, dump_reversed_input);

View File

@@ -21,7 +21,7 @@ pub mod test_utils {
pub fn create_cache() -> Cache { pub fn create_cache() -> Cache {
crate::commands::create_cache( crate::commands::create_cache(
"test", "com.android.aconfig.test",
vec![Input { vec![Input {
source: Source::File("testdata/test.aconfig".to_string()), source: Source::File("testdata/test.aconfig".to_string()),
reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()), reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),

View File

@@ -1,11 +1,10 @@
#ifndef {package}_HEADER_H #ifndef {header}_HEADER_H
#define {package}_HEADER_H #define {header}_HEADER_H
#include "{package}.h"
{{ if readwrite }} {{ if readwrite }}
#include <server_configurable_flags/get_flags.h> #include <server_configurable_flags/get_flags.h>
using namespace server_configurable_flags; using namespace server_configurable_flags;
{{ endif }} {{ endif }}
namespace {package} \{ namespace {cpp_namespace} \{
{{ for item in class_elements}} {{ for item in class_elements}}
class {item.flag_name} \{ class {item.flag_name} \{
public: public:

View File

@@ -1,4 +1,4 @@
package aconfig.{package}; package {package};
{{ if readwrite }} {{ if readwrite }}
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
{{ endif }} {{ endif }}

View File

@@ -1,23 +1,29 @@
{{- for mod in modules -}}
pub mod {mod} \{
{{ endfor -}}
{{- for parsed_flag in parsed_flags -}} {{- for parsed_flag in parsed_flags -}}
{{- if parsed_flag.is_read_only_disabled -}} {{- if parsed_flag.is_read_only_disabled -}}
#[inline(always)] #[inline(always)]
pub const fn r#{parsed_flag.fn_name}() -> bool \{ pub const fn r#{parsed_flag.name}() -> bool \{
false false
} }
{{ endif -}} {{ endif -}}
{{- if parsed_flag.is_read_only_enabled -}} {{- if parsed_flag.is_read_only_enabled -}}
#[inline(always)] #[inline(always)]
pub const fn r#{parsed_flag.fn_name}() -> bool \{ pub const fn r#{parsed_flag.name}() -> bool \{
true true
} }
{{ endif -}} {{ endif -}}
{{- if parsed_flag.is_read_write -}} {{- if parsed_flag.is_read_write -}}
#[inline(always)] #[inline(always)]
pub fn r#{parsed_flag.fn_name}() -> bool \{ pub fn r#{parsed_flag.name}() -> bool \{
flags_rust::GetServerConfigurableFlag("{package}", "{parsed_flag.name}", "false") == "true" flags_rust::GetServerConfigurableFlag("{package}", "{parsed_flag.name}", "false") == "true"
} }
{{ endif -}} {{ endif -}}
{{- endfor -}} {{- endfor -}}
{{- for mod in modules -}}
}
{{ endfor -}}

View File

@@ -1,17 +1,17 @@
flag_value { flag_value {
package: "test" package: "com.android.aconfig.test"
name: "disabled_ro" name: "disabled_ro"
state: DISABLED state: DISABLED
permission: READ_ONLY permission: READ_ONLY
} }
flag_value { flag_value {
package: "test" package: "com.android.aconfig.test"
name: "enabled_ro" name: "enabled_ro"
state: DISABLED state: DISABLED
permission: READ_WRITE permission: READ_WRITE
} }
flag_value { flag_value {
package: "test" package: "com.android.aconfig.test"
name: "enabled_rw" name: "enabled_rw"
state: ENABLED state: ENABLED
permission: READ_WRITE permission: READ_WRITE

View File

@@ -1,5 +1,5 @@
flag_value { flag_value {
package: "test" package: "com.android.aconfig.test"
name: "enabled_ro" name: "enabled_ro"
state: ENABLED state: ENABLED
permission: READ_ONLY permission: READ_ONLY

View File

@@ -1,4 +1,4 @@
package: "test" package: "com.android.aconfig.test"
# This flag's final value is calculated from: # This flag's final value is calculated from:
# - test.aconfig: DISABLED + READ_WRITE (default) # - test.aconfig: DISABLED + READ_WRITE (default)