Merge "aconfig: add a new aconfig storage file flag listing api" into main
This commit is contained in:
@@ -97,5 +97,9 @@
|
||||
}
|
||||
],
|
||||
"postsubmit": [
|
||||
{
|
||||
// aconfig_storage file cpp integration tests
|
||||
"name": "aconfig_storage_file.test.cpp"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ rust_defaults {
|
||||
"libtempfile",
|
||||
"libprotobuf",
|
||||
"libclap",
|
||||
"libcxx",
|
||||
"libaconfig_storage_protos",
|
||||
],
|
||||
}
|
||||
@@ -77,3 +78,62 @@ cc_library {
|
||||
product_available: true,
|
||||
double_loadable: true,
|
||||
}
|
||||
|
||||
// cxx source codegen from rust api
|
||||
genrule {
|
||||
name: "libcxx_aconfig_storage_file_bridge_code",
|
||||
tools: ["cxxbridge"],
|
||||
cmd: "$(location cxxbridge) $(in) > $(out)",
|
||||
srcs: ["src/lib.rs"],
|
||||
out: ["aconfig_storage/lib.rs.cc"],
|
||||
}
|
||||
|
||||
// cxx header codegen from rust api
|
||||
genrule {
|
||||
name: "libcxx_aconfig_storage_file_bridge_header",
|
||||
tools: ["cxxbridge"],
|
||||
cmd: "$(location cxxbridge) $(in) --header > $(out)",
|
||||
srcs: ["src/lib.rs"],
|
||||
out: ["aconfig_storage/lib.rs.h"],
|
||||
}
|
||||
|
||||
// a static cc lib based on generated code
|
||||
rust_ffi_static {
|
||||
name: "libaconfig_storage_file_cxx_bridge",
|
||||
crate_name: "aconfig_storage_file_cxx_bridge",
|
||||
host_supported: true,
|
||||
vendor_available: true,
|
||||
product_available: true,
|
||||
srcs: ["src/lib.rs"],
|
||||
defaults: ["aconfig_storage_file.defaults"],
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"//apex_available:anyapex",
|
||||
],
|
||||
min_sdk_version: "29",
|
||||
}
|
||||
|
||||
// storage file parse api cc interface
|
||||
cc_library {
|
||||
name: "libaconfig_storage_file_cc",
|
||||
srcs: ["aconfig_storage_file.cpp"],
|
||||
generated_headers: [
|
||||
"cxx-bridge-header",
|
||||
"libcxx_aconfig_storage_file_bridge_header",
|
||||
],
|
||||
generated_sources: ["libcxx_aconfig_storage_file_bridge_code"],
|
||||
whole_static_libs: ["libaconfig_storage_file_cxx_bridge"],
|
||||
export_include_dirs: ["include"],
|
||||
host_supported: true,
|
||||
vendor_available: true,
|
||||
product_available: true,
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
],
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"//apex_available:anyapex",
|
||||
],
|
||||
min_sdk_version: "29",
|
||||
double_loadable: true,
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ protobuf = "3.2.0"
|
||||
tempfile = "3.9.0"
|
||||
thiserror = "1.0.56"
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
cxx = "1.0"
|
||||
|
||||
[[bin]]
|
||||
name = "aconfig-storage"
|
||||
|
38
tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
Normal file
38
tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "rust/cxx.h"
|
||||
#include "aconfig_storage/lib.rs.h"
|
||||
|
||||
#include "aconfig_storage/aconfig_storage_file.hpp"
|
||||
|
||||
using namespace android::base;
|
||||
|
||||
namespace aconfig_storage {
|
||||
|
||||
Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
|
||||
const std::string& package_map,
|
||||
const std::string& flag_map,
|
||||
const std::string& flag_val,
|
||||
const std::string& flag_info) {
|
||||
auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()),
|
||||
rust::Str(flag_map.c_str()),
|
||||
rust::Str(flag_val.c_str()),
|
||||
rust::Str(flag_info.c_str()));
|
||||
if (flag_list_cxx.query_success) {
|
||||
auto flag_list = std::vector<FlagValueAndInfoSummary>();
|
||||
for (const auto& flag_cxx : flag_list_cxx.flags) {
|
||||
auto flag = FlagValueAndInfoSummary();
|
||||
flag.package_name = std::string(flag_cxx.package_name);
|
||||
flag.flag_name = std::string(flag_cxx.flag_name);
|
||||
flag.flag_value = std::string(flag_cxx.flag_value);
|
||||
flag.value_type = std::string(flag_cxx.value_type);
|
||||
flag.is_readwrite = flag_cxx.is_readwrite;
|
||||
flag.has_server_override = flag_cxx.has_server_override;
|
||||
flag.has_local_override = flag_cxx.has_local_override;
|
||||
flag_list.push_back(flag);
|
||||
}
|
||||
return flag_list;
|
||||
} else {
|
||||
return Error() << flag_list_cxx.error_message;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aconfig_storage
|
@@ -14,4 +14,6 @@ fn main() {
|
||||
.inputs(proto_files)
|
||||
.cargo_out_dir("aconfig_storage_protos")
|
||||
.run_from_script();
|
||||
|
||||
let _ = cxx_build::bridge("src/lib.rs");
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <android-base/result.h>
|
||||
|
||||
namespace aconfig_storage {
|
||||
|
||||
/// Flag value and info summary for a flag
|
||||
struct FlagValueAndInfoSummary {
|
||||
std::string package_name;
|
||||
std::string flag_name;
|
||||
std::string flag_value;
|
||||
std::string value_type;
|
||||
bool is_readwrite;
|
||||
bool has_server_override;
|
||||
bool has_local_override;
|
||||
};
|
||||
|
||||
/// List all flag values with their flag info
|
||||
/// \input package_map: package map file
|
||||
/// \input flag_map: flag map file
|
||||
/// \input flag_val: flag value file
|
||||
/// \input flag_info: flag info file
|
||||
android::base::Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
|
||||
const std::string& package_map,
|
||||
const std::string& flag_map,
|
||||
const std::string& flag_val,
|
||||
const std::string& flag_info);
|
||||
|
||||
}// namespace aconfig_storage
|
@@ -278,12 +278,21 @@ pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageErro
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/// Flag value summary
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct FlagValueSummary {
|
||||
pub package_name: String,
|
||||
pub flag_name: String,
|
||||
pub flag_value: String,
|
||||
pub value_type: StoredFlagType,
|
||||
}
|
||||
|
||||
/// List flag values from storage files
|
||||
pub fn list_flags(
|
||||
package_map: &str,
|
||||
flag_map: &str,
|
||||
flag_val: &str,
|
||||
) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> {
|
||||
) -> Result<Vec<FlagValueSummary>, AconfigStorageError> {
|
||||
let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
|
||||
let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
|
||||
let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
|
||||
@@ -295,30 +304,155 @@ pub fn list_flags(
|
||||
|
||||
let mut flags = Vec::new();
|
||||
for node in flag_table.nodes.iter() {
|
||||
let (package_name, package_offset) = package_info[node.package_id as usize];
|
||||
let flag_offset = package_offset + node.flag_index as u32;
|
||||
let flag_value = flag_value_list.booleans[flag_offset as usize];
|
||||
flags.push((
|
||||
String::from(package_name),
|
||||
node.flag_name.clone(),
|
||||
node.flag_type,
|
||||
flag_value,
|
||||
));
|
||||
let (package_name, boolean_start_index) = package_info[node.package_id as usize];
|
||||
let flag_index = boolean_start_index + node.flag_index as u32;
|
||||
let flag_value = flag_value_list.booleans[flag_index as usize];
|
||||
flags.push(FlagValueSummary {
|
||||
package_name: String::from(package_name),
|
||||
flag_name: node.flag_name.clone(),
|
||||
flag_value: flag_value.to_string(),
|
||||
value_type: node.flag_type,
|
||||
});
|
||||
}
|
||||
|
||||
flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
|
||||
Ordering::Equal => v1.1.cmp(&v2.1),
|
||||
flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
|
||||
Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
|
||||
other => other,
|
||||
});
|
||||
Ok(flags)
|
||||
}
|
||||
|
||||
/// Flag value and info summary
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct FlagValueAndInfoSummary {
|
||||
pub package_name: String,
|
||||
pub flag_name: String,
|
||||
pub flag_value: String,
|
||||
pub value_type: StoredFlagType,
|
||||
pub is_readwrite: bool,
|
||||
pub has_server_override: bool,
|
||||
pub has_local_override: bool,
|
||||
}
|
||||
|
||||
/// List flag values and info from storage files
|
||||
pub fn list_flags_with_info(
|
||||
package_map: &str,
|
||||
flag_map: &str,
|
||||
flag_val: &str,
|
||||
flag_info: &str,
|
||||
) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError> {
|
||||
let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
|
||||
let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
|
||||
let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
|
||||
let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?;
|
||||
|
||||
let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
|
||||
for node in package_table.nodes.iter() {
|
||||
package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
|
||||
}
|
||||
|
||||
let mut flags = Vec::new();
|
||||
for node in flag_table.nodes.iter() {
|
||||
let (package_name, boolean_start_index) = package_info[node.package_id as usize];
|
||||
let flag_index = boolean_start_index + node.flag_index as u32;
|
||||
let flag_value = flag_value_list.booleans[flag_index as usize];
|
||||
let flag_attribute = flag_info.nodes[flag_index as usize].attributes;
|
||||
flags.push(FlagValueAndInfoSummary {
|
||||
package_name: String::from(package_name),
|
||||
flag_name: node.flag_name.clone(),
|
||||
flag_value: flag_value.to_string(),
|
||||
value_type: node.flag_type,
|
||||
is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0,
|
||||
has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0,
|
||||
has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0,
|
||||
});
|
||||
}
|
||||
|
||||
flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
|
||||
Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
|
||||
other => other,
|
||||
});
|
||||
Ok(flags)
|
||||
}
|
||||
|
||||
// *************************************** //
|
||||
// CC INTERLOP
|
||||
// *************************************** //
|
||||
|
||||
// Exported rust data structure and methods, c++ code will be generated
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
/// flag value and info summary cxx return
|
||||
pub struct FlagValueAndInfoSummaryCXX {
|
||||
pub package_name: String,
|
||||
pub flag_name: String,
|
||||
pub flag_value: String,
|
||||
pub value_type: String,
|
||||
pub is_readwrite: bool,
|
||||
pub has_server_override: bool,
|
||||
pub has_local_override: bool,
|
||||
}
|
||||
|
||||
/// list flag result cxx return
|
||||
pub struct ListFlagValueAndInfoResultCXX {
|
||||
pub query_success: bool,
|
||||
pub error_message: String,
|
||||
pub flags: Vec<FlagValueAndInfoSummaryCXX>,
|
||||
}
|
||||
|
||||
// Rust export to c++
|
||||
extern "Rust" {
|
||||
pub fn list_flags_with_info_cxx(
|
||||
package_map: &str,
|
||||
flag_map: &str,
|
||||
flag_val: &str,
|
||||
flag_info: &str,
|
||||
) -> ListFlagValueAndInfoResultCXX;
|
||||
}
|
||||
}
|
||||
|
||||
/// implement flag value and info summary cxx return type
|
||||
impl ffi::FlagValueAndInfoSummaryCXX {
|
||||
pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self {
|
||||
Self {
|
||||
package_name: summary.package_name,
|
||||
flag_name: summary.flag_name,
|
||||
flag_value: summary.flag_value,
|
||||
value_type: format!("{:?}", summary.value_type),
|
||||
is_readwrite: summary.is_readwrite,
|
||||
has_server_override: summary.has_server_override,
|
||||
has_local_override: summary.has_local_override,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// implement list flag with info cxx interlop
|
||||
pub fn list_flags_with_info_cxx(
|
||||
package_map: &str,
|
||||
flag_map: &str,
|
||||
flag_val: &str,
|
||||
flag_info: &str,
|
||||
) -> ffi::ListFlagValueAndInfoResultCXX {
|
||||
match list_flags_with_info(package_map, flag_map, flag_val, flag_info) {
|
||||
Ok(summary) => ffi::ListFlagValueAndInfoResultCXX {
|
||||
query_success: true,
|
||||
error_message: String::new(),
|
||||
flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(),
|
||||
},
|
||||
Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX {
|
||||
query_success: false,
|
||||
error_message: format!("{:?}", errmsg),
|
||||
flags: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::{
|
||||
create_test_flag_table, create_test_flag_value_list, create_test_package_table,
|
||||
write_bytes_to_temp_file,
|
||||
create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
|
||||
create_test_package_table, write_bytes_to_temp_file,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -337,54 +471,154 @@ mod tests {
|
||||
let flags =
|
||||
list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
|
||||
let expected = [
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_1"),
|
||||
String::from("disabled_rw"),
|
||||
StoredFlagType::ReadWriteBoolean,
|
||||
false,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_1"),
|
||||
String::from("enabled_ro"),
|
||||
StoredFlagType::ReadOnlyBoolean,
|
||||
true,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_1"),
|
||||
String::from("enabled_rw"),
|
||||
StoredFlagType::ReadWriteBoolean,
|
||||
true,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_2"),
|
||||
String::from("disabled_rw"),
|
||||
StoredFlagType::ReadWriteBoolean,
|
||||
false,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_2"),
|
||||
String::from("enabled_fixed_ro"),
|
||||
StoredFlagType::FixedReadOnlyBoolean,
|
||||
true,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_2"),
|
||||
String::from("enabled_ro"),
|
||||
StoredFlagType::ReadOnlyBoolean,
|
||||
true,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_4"),
|
||||
String::from("enabled_fixed_ro"),
|
||||
StoredFlagType::FixedReadOnlyBoolean,
|
||||
true,
|
||||
),
|
||||
(
|
||||
String::from("com.android.aconfig.storage.test_4"),
|
||||
String::from("enabled_rw"),
|
||||
StoredFlagType::ReadWriteBoolean,
|
||||
true,
|
||||
),
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
flag_name: String::from("disabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("false"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
flag_name: String::from("enabled_ro"),
|
||||
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
flag_name: String::from("enabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("true"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
flag_name: String::from("disabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("false"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
flag_name: String::from("enabled_fixed_ro"),
|
||||
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
flag_name: String::from("enabled_ro"),
|
||||
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||
flag_name: String::from("enabled_fixed_ro"),
|
||||
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
},
|
||||
FlagValueSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||
flag_name: String::from("enabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("true"),
|
||||
},
|
||||
];
|
||||
assert_eq!(flags, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down the flag list with info api
|
||||
fn test_list_flag_with_info() {
|
||||
let package_table =
|
||||
write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
|
||||
let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
|
||||
let flag_value_list =
|
||||
write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
|
||||
let flag_info_list =
|
||||
write_bytes_to_temp_file(&create_test_flag_info_list().into_bytes()).unwrap();
|
||||
|
||||
let package_table_path = package_table.path().display().to_string();
|
||||
let flag_table_path = flag_table.path().display().to_string();
|
||||
let flag_value_list_path = flag_value_list.path().display().to_string();
|
||||
let flag_info_list_path = flag_info_list.path().display().to_string();
|
||||
|
||||
let flags = list_flags_with_info(
|
||||
&package_table_path,
|
||||
&flag_table_path,
|
||||
&flag_value_list_path,
|
||||
&flag_info_list_path,
|
||||
)
|
||||
.unwrap();
|
||||
let expected = [
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
flag_name: String::from("disabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("false"),
|
||||
is_readwrite: true,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
flag_name: String::from("enabled_ro"),
|
||||
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
is_readwrite: false,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
flag_name: String::from("enabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("true"),
|
||||
is_readwrite: true,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
flag_name: String::from("disabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("false"),
|
||||
is_readwrite: true,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
flag_name: String::from("enabled_fixed_ro"),
|
||||
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
is_readwrite: false,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
flag_name: String::from("enabled_ro"),
|
||||
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
is_readwrite: false,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||
flag_name: String::from("enabled_fixed_ro"),
|
||||
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||
flag_value: String::from("true"),
|
||||
is_readwrite: false,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
FlagValueAndInfoSummary {
|
||||
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||
flag_name: String::from("enabled_rw"),
|
||||
value_type: StoredFlagType::ReadWriteBoolean,
|
||||
flag_value: String::from("true"),
|
||||
is_readwrite: true,
|
||||
has_server_override: false,
|
||||
has_local_override: false,
|
||||
},
|
||||
];
|
||||
assert_eq!(flags, expected);
|
||||
}
|
||||
|
@@ -17,8 +17,8 @@
|
||||
//! `aconfig-storage` is a debugging tool to parse storage files
|
||||
|
||||
use aconfig_storage_file::{
|
||||
list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
|
||||
PackageTable, StorageFileType,
|
||||
list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList,
|
||||
FlagTable, FlagValueList, PackageTable, StorageFileType,
|
||||
};
|
||||
|
||||
use clap::{builder::ArgAction, Arg, Command};
|
||||
@@ -45,7 +45,10 @@ fn cli() -> Command {
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set))
|
||||
.arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)),
|
||||
.arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set))
|
||||
.arg(
|
||||
Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -87,9 +90,27 @@ fn main() -> Result<(), AconfigStorageError> {
|
||||
let package_map = sub_matches.get_one::<String>("package-map").unwrap();
|
||||
let flag_map = sub_matches.get_one::<String>("flag-map").unwrap();
|
||||
let flag_val = sub_matches.get_one::<String>("flag-val").unwrap();
|
||||
let flags = list_flags(package_map, flag_map, flag_val)?;
|
||||
for (package_name, flag_name, flag_type, flag_value) in flags.iter() {
|
||||
println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value);
|
||||
let flag_info = sub_matches.get_one::<String>("flag-info");
|
||||
match flag_info {
|
||||
Some(info_file) => {
|
||||
let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?;
|
||||
for flag in flags.iter() {
|
||||
println!(
|
||||
"{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}",
|
||||
flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
|
||||
flag.is_readwrite, flag.has_server_override, flag.has_local_override,
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let flags = list_flags(package_map, flag_map, flag_val)?;
|
||||
for flag in flags.iter() {
|
||||
println!(
|
||||
"{} {} {} {:?}",
|
||||
flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
23
tools/aconfig/aconfig_storage_file/tests/Android.bp
Normal file
23
tools/aconfig/aconfig_storage_file/tests/Android.bp
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
cc_test {
|
||||
name: "aconfig_storage_file.test.cpp",
|
||||
team: "trendy_team_android_core_experiments",
|
||||
srcs: [
|
||||
"storage_file_test.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
"libaconfig_storage_file_cc",
|
||||
"libbase",
|
||||
],
|
||||
data: [
|
||||
"package.map",
|
||||
"flag.map",
|
||||
"flag.val",
|
||||
"flag.info",
|
||||
],
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
"general-tests",
|
||||
],
|
||||
}
|
BIN
tools/aconfig/aconfig_storage_file/tests/flag.info
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/flag.info
Normal file
Binary file not shown.
BIN
tools/aconfig/aconfig_storage_file/tests/flag.map
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/flag.map
Normal file
Binary file not shown.
BIN
tools/aconfig/aconfig_storage_file/tests/flag.val
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/flag.val
Normal file
Binary file not shown.
BIN
tools/aconfig/aconfig_storage_file/tests/package.map
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/package.map
Normal file
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/result.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "aconfig_storage/aconfig_storage_file.hpp"
|
||||
|
||||
using namespace android::base;
|
||||
using namespace aconfig_storage;
|
||||
|
||||
|
||||
void verify_flag(const FlagValueAndInfoSummary& flag,
|
||||
const std::string& package_name,
|
||||
const std::string& flag_name,
|
||||
const std::string& flag_val,
|
||||
const std::string& value_type,
|
||||
bool is_readwrite,
|
||||
bool has_server_override,
|
||||
bool has_local_override) {
|
||||
ASSERT_EQ(flag.package_name, package_name);
|
||||
ASSERT_EQ(flag.flag_name, flag_name);
|
||||
ASSERT_EQ(flag.flag_value, flag_val);
|
||||
ASSERT_EQ(flag.value_type, value_type);
|
||||
ASSERT_EQ(flag.is_readwrite, is_readwrite);
|
||||
ASSERT_EQ(flag.has_server_override, has_server_override);
|
||||
ASSERT_EQ(flag.has_local_override, has_local_override);
|
||||
}
|
||||
|
||||
TEST(AconfigStorageFileTest, test_list_flag_with_info) {
|
||||
auto const test_dir = GetExecutableDirectory();
|
||||
auto const package_map = test_dir + "/package.map";
|
||||
auto const flag_map = test_dir + "/flag.map";
|
||||
auto const flag_val = test_dir + "/flag.val";
|
||||
auto const flag_info = test_dir + "/flag.info";
|
||||
auto flag_list_result = aconfig_storage::list_flags_with_info(
|
||||
package_map, flag_map, flag_val, flag_info);
|
||||
ASSERT_TRUE(flag_list_result.ok());
|
||||
|
||||
auto const& flag_list = *flag_list_result;
|
||||
ASSERT_EQ(flag_list.size(), 8);
|
||||
verify_flag(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw",
|
||||
"false", "ReadWriteBoolean", true, false, false);
|
||||
verify_flag(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
|
||||
"true", "ReadOnlyBoolean", false, false, false);
|
||||
verify_flag(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
|
||||
"true", "ReadWriteBoolean", true, false, false);
|
||||
verify_flag(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw",
|
||||
"false", "ReadWriteBoolean", true, false, false);
|
||||
verify_flag(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro",
|
||||
"true", "FixedReadOnlyBoolean", false, false, false);
|
||||
verify_flag(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
|
||||
"true", "ReadOnlyBoolean", false, false, false);
|
||||
verify_flag(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro",
|
||||
"true", "FixedReadOnlyBoolean", false, false, false);
|
||||
verify_flag(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
|
||||
"true", "ReadWriteBoolean", true, false, false);
|
||||
}
|
@@ -504,7 +504,7 @@ files {{
|
||||
let pb_file_path = pb_file.path().display().to_string();
|
||||
let flag_info_file =
|
||||
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
|
||||
let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
|
||||
let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
|
||||
for (offset, expected_value) in is_rw.into_iter().enumerate() {
|
||||
let attribute =
|
||||
get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
|
||||
|
Binary file not shown.
@@ -232,7 +232,7 @@ TEST_F(AconfigStorageTest, test_boolean_flag_info_query) {
|
||||
ASSERT_TRUE(mapped_file.ok());
|
||||
|
||||
auto expected_value = std::vector<bool>{
|
||||
true, false, true, false, false, false, false, false};
|
||||
true, false, true, true, false, false, false, true};
|
||||
for (int index = 0; index < 8; ++index) {
|
||||
auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);
|
||||
ASSERT_TRUE(attribute.ok());
|
||||
|
@@ -188,7 +188,7 @@ files {{
|
||||
// The safety here is ensured as the test process will not write to temp storage file
|
||||
let flag_info_file =
|
||||
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
|
||||
let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
|
||||
let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
|
||||
for (offset, expected_value) in is_rw.into_iter().enumerate() {
|
||||
let attribute =
|
||||
get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
|
||||
|
@@ -27,13 +27,13 @@ impl FlagSource for AconfigStorageSource {
|
||||
let container =
|
||||
file_info.container.ok_or(anyhow!("storage file is missing container"))?;
|
||||
|
||||
for (package, name, _flag_type, val) in
|
||||
for listed_flag in
|
||||
aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
|
||||
{
|
||||
result.push(Flag {
|
||||
name: name.to_string(),
|
||||
package: package.to_string(),
|
||||
value: FlagValue::try_from(val.to_string().as_str())?,
|
||||
name: listed_flag.flag_name,
|
||||
package: listed_flag.package_name,
|
||||
value: FlagValue::try_from(listed_flag.flag_value.as_str())?,
|
||||
container: container.to_string(),
|
||||
|
||||
// TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
|
||||
|
Reference in New Issue
Block a user