aconfig: add storage file type into storage file serialization
1, add storage file type into storage files serialization 2, update aconfig_storage_read_api integration tests to not rely on soong to produce read only storage files. Instead, have the test process create temp read only storage file copies. Bug: b/312444587 Test: atest aconfig.test; atest.aconfig_storage_file.test Change-Id: I1607fb000b48e8acffc966b16a5136578911ab3e
This commit is contained in:
@@ -31,7 +31,7 @@ use aconfig_protos::{
|
||||
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
|
||||
ProtoParsedFlags, ProtoTracepoint,
|
||||
};
|
||||
use aconfig_storage_file::StorageFileSelection;
|
||||
use aconfig_storage_file::StorageFileType;
|
||||
|
||||
pub struct Input {
|
||||
pub source: String,
|
||||
@@ -237,7 +237,7 @@ pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Ou
|
||||
pub fn create_storage(
|
||||
caches: Vec<Input>,
|
||||
container: &str,
|
||||
file: &StorageFileSelection,
|
||||
file: &StorageFileType,
|
||||
) -> Result<Vec<u8>> {
|
||||
let parsed_flags_vec: Vec<ProtoParsedFlags> = caches
|
||||
.into_iter()
|
||||
|
@@ -29,7 +29,7 @@ mod commands;
|
||||
mod dump;
|
||||
mod storage;
|
||||
|
||||
use aconfig_storage_file::StorageFileSelection;
|
||||
use aconfig_storage_file::StorageFileType;
|
||||
use codegen::CodegenMode;
|
||||
use dump::DumpFormat;
|
||||
|
||||
@@ -138,7 +138,7 @@ fn cli() -> Command {
|
||||
.arg(
|
||||
Arg::new("file")
|
||||
.long("file")
|
||||
.value_parser(|s: &str| StorageFileSelection::try_from(s)),
|
||||
.value_parser(|s: &str| StorageFileType::try_from(s)),
|
||||
)
|
||||
.arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
|
||||
.arg(Arg::new("out").long("out").required(true)),
|
||||
@@ -285,7 +285,7 @@ fn main() -> Result<()> {
|
||||
write_output_to_file_or_stdout(path, &output)?;
|
||||
}
|
||||
Some(("create-storage", sub_matches)) => {
|
||||
let file = get_required_arg::<StorageFileSelection>(sub_matches, "file")
|
||||
let file = get_required_arg::<StorageFileType>(sub_matches, "file")
|
||||
.context("Invalid storage file selection")?;
|
||||
let cache = open_zero_or_more_files(sub_matches, "cache")?;
|
||||
let container = get_required_arg::<String>(sub_matches, "container")?;
|
||||
|
@@ -17,7 +17,7 @@
|
||||
use crate::commands::assign_flag_ids;
|
||||
use crate::storage::FlagPackage;
|
||||
use aconfig_storage_file::{
|
||||
get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION,
|
||||
get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION, StorageFileType
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
@@ -25,6 +25,7 @@ fn new_header(container: &str, num_flags: u32) -> FlagTableHeader {
|
||||
FlagTableHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from(container),
|
||||
file_type: StorageFileType::FlagMap as u8,
|
||||
file_size: 0,
|
||||
num_flags,
|
||||
bucket_offset: 0,
|
||||
@@ -168,31 +169,32 @@ mod tests {
|
||||
let expected_header = FlagTableHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 320,
|
||||
file_type: StorageFileType::FlagMap as u8,
|
||||
file_size: 321,
|
||||
num_flags: 8,
|
||||
bucket_offset: 30,
|
||||
node_offset: 98,
|
||||
bucket_offset: 31,
|
||||
node_offset: 99,
|
||||
};
|
||||
assert_eq!(header, &expected_header);
|
||||
|
||||
let buckets: &Vec<Option<u32>> = &flag_table.as_ref().unwrap().buckets;
|
||||
let expected_bucket: Vec<Option<u32>> = vec![
|
||||
Some(98),
|
||||
Some(124),
|
||||
Some(99),
|
||||
Some(125),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(177),
|
||||
Some(178),
|
||||
None,
|
||||
Some(203),
|
||||
Some(204),
|
||||
None,
|
||||
Some(261),
|
||||
Some(262),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(293),
|
||||
Some(294),
|
||||
None,
|
||||
];
|
||||
assert_eq!(buckets, &expected_bucket);
|
||||
@@ -201,10 +203,10 @@ mod tests {
|
||||
assert_eq!(nodes.len(), 8);
|
||||
|
||||
assert_eq!(nodes[0], new_expected_node(0, "enabled_ro", 1, 1, None));
|
||||
assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(150)));
|
||||
assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(151)));
|
||||
assert_eq!(nodes[2], new_expected_node(1, "disabled_ro", 1, 0, None));
|
||||
assert_eq!(nodes[3], new_expected_node(2, "enabled_ro", 1, 1, None));
|
||||
assert_eq!(nodes[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235)));
|
||||
assert_eq!(nodes[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(236)));
|
||||
assert_eq!(nodes[5], new_expected_node(1, "enabled_ro", 1, 2, None));
|
||||
assert_eq!(nodes[6], new_expected_node(2, "enabled_fixed_ro", 1, 0, None));
|
||||
assert_eq!(nodes[7], new_expected_node(0, "disabled_rw", 1, 0, None));
|
||||
|
@@ -17,13 +17,14 @@
|
||||
use crate::commands::assign_flag_ids;
|
||||
use crate::storage::FlagPackage;
|
||||
use aconfig_protos::ProtoFlagState;
|
||||
use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION};
|
||||
use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION, StorageFileType};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
fn new_header(container: &str, num_flags: u32) -> FlagValueHeader {
|
||||
FlagValueHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from(container),
|
||||
file_type: StorageFileType::FlagVal as u8,
|
||||
file_size: 0,
|
||||
num_flags,
|
||||
boolean_value_offset: 0,
|
||||
@@ -79,9 +80,10 @@ mod tests {
|
||||
let expected_header = FlagValueHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 34,
|
||||
file_type: StorageFileType::FlagVal as u8,
|
||||
file_size: 35,
|
||||
num_flags: 8,
|
||||
boolean_value_offset: 26,
|
||||
boolean_value_offset: 27,
|
||||
};
|
||||
assert_eq!(header, &expected_header);
|
||||
|
||||
|
@@ -26,7 +26,7 @@ use crate::storage::{
|
||||
package_table::create_package_table,
|
||||
};
|
||||
use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
|
||||
use aconfig_storage_file::StorageFileSelection;
|
||||
use aconfig_storage_file::StorageFileType;
|
||||
|
||||
pub struct FlagPackage<'a> {
|
||||
pub package_name: &'a str,
|
||||
@@ -87,7 +87,7 @@ where
|
||||
pub fn generate_storage_file<'a, I>(
|
||||
container: &str,
|
||||
parsed_flags_vec_iter: I,
|
||||
file: &StorageFileSelection,
|
||||
file: &StorageFileType,
|
||||
) -> Result<Vec<u8>>
|
||||
where
|
||||
I: Iterator<Item = &'a ProtoParsedFlags>,
|
||||
@@ -95,15 +95,15 @@ where
|
||||
let packages = group_flags_by_package(parsed_flags_vec_iter);
|
||||
|
||||
match file {
|
||||
StorageFileSelection::PackageMap => {
|
||||
StorageFileType::PackageMap => {
|
||||
let package_table = create_package_table(container, &packages)?;
|
||||
Ok(package_table.as_bytes())
|
||||
}
|
||||
StorageFileSelection::FlagMap => {
|
||||
StorageFileType::FlagMap => {
|
||||
let flag_table = create_flag_table(container, &packages)?;
|
||||
Ok(flag_table.as_bytes())
|
||||
}
|
||||
StorageFileSelection::FlagVal => {
|
||||
StorageFileType::FlagVal => {
|
||||
let flag_value = create_flag_value(container, &packages)?;
|
||||
Ok(flag_value.as_bytes())
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use aconfig_storage_file::{
|
||||
get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION,
|
||||
get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION, StorageFileType
|
||||
};
|
||||
|
||||
use crate::storage::FlagPackage;
|
||||
@@ -26,6 +26,7 @@ fn new_header(container: &str, num_packages: u32) -> PackageTableHeader {
|
||||
PackageTableHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from(container),
|
||||
file_type: StorageFileType::PackageMap as u8,
|
||||
file_size: 0,
|
||||
num_packages,
|
||||
bucket_offset: 0,
|
||||
@@ -123,15 +124,16 @@ mod tests {
|
||||
let expected_header = PackageTableHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 208,
|
||||
file_type: StorageFileType::PackageMap as u8,
|
||||
file_size: 209,
|
||||
num_packages: 3,
|
||||
bucket_offset: 30,
|
||||
node_offset: 58,
|
||||
bucket_offset: 31,
|
||||
node_offset: 59,
|
||||
};
|
||||
assert_eq!(header, &expected_header);
|
||||
|
||||
let buckets: &Vec<Option<u32>> = &package_table.as_ref().unwrap().buckets;
|
||||
let expected: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
|
||||
let expected: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None];
|
||||
assert_eq!(buckets, &expected);
|
||||
|
||||
let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
|
||||
@@ -147,7 +149,7 @@ mod tests {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
package_id: 0,
|
||||
boolean_offset: 0,
|
||||
next_offset: Some(158),
|
||||
next_offset: Some(159),
|
||||
};
|
||||
assert_eq!(nodes[1], second_node_expected);
|
||||
let third_node_expected = PackageTableNode {
|
||||
|
@@ -17,8 +17,11 @@
|
||||
//! flag table module defines the flag table file format and methods for serialization
|
||||
//! and deserialization
|
||||
|
||||
use crate::AconfigStorageError::{self, BytesParseFail};
|
||||
use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
|
||||
use crate::{
|
||||
get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes,
|
||||
read_u8_from_bytes,
|
||||
};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use std::fmt;
|
||||
|
||||
@@ -27,6 +30,7 @@ use std::fmt;
|
||||
pub struct FlagTableHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
pub file_type: u8,
|
||||
pub file_size: u32,
|
||||
pub num_flags: u32,
|
||||
pub bucket_offset: u32,
|
||||
@@ -38,8 +42,11 @@ impl fmt::Debug for FlagTableHeader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"Version: {}, Container: {}, File Size: {}",
|
||||
self.version, self.container, self.file_size
|
||||
"Version: {}, Container: {}, File Type: {:?}, File Size: {}",
|
||||
self.version,
|
||||
self.container,
|
||||
StorageFileType::try_from(self.file_type),
|
||||
self.file_size
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
@@ -58,6 +65,7 @@ impl FlagTableHeader {
|
||||
let container_bytes = self.container.as_bytes();
|
||||
result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
|
||||
result.extend_from_slice(container_bytes);
|
||||
result.extend_from_slice(&self.file_type.to_le_bytes());
|
||||
result.extend_from_slice(&self.file_size.to_le_bytes());
|
||||
result.extend_from_slice(&self.num_flags.to_le_bytes());
|
||||
result.extend_from_slice(&self.bucket_offset.to_le_bytes());
|
||||
@@ -68,14 +76,21 @@ impl FlagTableHeader {
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
|
||||
let mut head = 0;
|
||||
Ok(Self {
|
||||
let table = Self {
|
||||
version: read_u32_from_bytes(bytes, &mut head)?,
|
||||
container: read_str_from_bytes(bytes, &mut head)?,
|
||||
file_type: read_u8_from_bytes(bytes, &mut head)?,
|
||||
file_size: read_u32_from_bytes(bytes, &mut head)?,
|
||||
num_flags: read_u32_from_bytes(bytes, &mut head)?,
|
||||
bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||
node_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||
})
|
||||
};
|
||||
if table.file_type != StorageFileType::FlagMap as u8 {
|
||||
return Err(AconfigStorageError::BytesParseFail(anyhow!(
|
||||
"binary file is not a flag map"
|
||||
)));
|
||||
}
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +206,9 @@ impl FlagTable {
|
||||
Ok(node)
|
||||
})
|
||||
.collect::<Result<Vec<_>, AconfigStorageError>>()
|
||||
.map_err(|errmsg| BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg)))?;
|
||||
.map_err(|errmsg| {
|
||||
AconfigStorageError::BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg))
|
||||
})?;
|
||||
|
||||
let table = Self { header, buckets, nodes };
|
||||
Ok(table)
|
||||
@@ -234,4 +251,16 @@ mod tests {
|
||||
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
|
||||
assert_eq!(version, 1234)
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down file type check
|
||||
fn test_file_type_check() {
|
||||
let mut flag_table = create_test_flag_table();
|
||||
flag_table.header.file_type = 123u8;
|
||||
let error = FlagTable::from_bytes(&flag_table.as_bytes()).unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!("BytesParseFail(binary file is not a flag map)")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -17,8 +17,9 @@
|
||||
//! flag value module defines the flag value file format and methods for serialization
|
||||
//! and deserialization
|
||||
|
||||
use crate::AconfigStorageError;
|
||||
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use std::fmt;
|
||||
|
||||
/// Flag value header struct
|
||||
@@ -26,6 +27,7 @@ use std::fmt;
|
||||
pub struct FlagValueHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
pub file_type: u8,
|
||||
pub file_size: u32,
|
||||
pub num_flags: u32,
|
||||
pub boolean_value_offset: u32,
|
||||
@@ -36,8 +38,11 @@ impl fmt::Debug for FlagValueHeader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"Version: {}, Container: {}, File Size: {}",
|
||||
self.version, self.container, self.file_size
|
||||
"Version: {}, Container: {}, File Type: {:?}, File Size: {}",
|
||||
self.version,
|
||||
self.container,
|
||||
StorageFileType::try_from(self.file_type),
|
||||
self.file_size
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
@@ -56,6 +61,7 @@ impl FlagValueHeader {
|
||||
let container_bytes = self.container.as_bytes();
|
||||
result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
|
||||
result.extend_from_slice(container_bytes);
|
||||
result.extend_from_slice(&self.file_type.to_le_bytes());
|
||||
result.extend_from_slice(&self.file_size.to_le_bytes());
|
||||
result.extend_from_slice(&self.num_flags.to_le_bytes());
|
||||
result.extend_from_slice(&self.boolean_value_offset.to_le_bytes());
|
||||
@@ -65,13 +71,20 @@ impl FlagValueHeader {
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
|
||||
let mut head = 0;
|
||||
Ok(Self {
|
||||
let list = Self {
|
||||
version: read_u32_from_bytes(bytes, &mut head)?,
|
||||
container: read_str_from_bytes(bytes, &mut head)?,
|
||||
file_type: read_u8_from_bytes(bytes, &mut head)?,
|
||||
file_size: read_u32_from_bytes(bytes, &mut head)?,
|
||||
num_flags: read_u32_from_bytes(bytes, &mut head)?,
|
||||
boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||
})
|
||||
};
|
||||
if list.file_type != StorageFileType::FlagVal as u8 {
|
||||
return Err(AconfigStorageError::BytesParseFail(anyhow!(
|
||||
"binary file is not a flag value file"
|
||||
)));
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,4 +158,16 @@ mod tests {
|
||||
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
|
||||
assert_eq!(version, 1234)
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down file type check
|
||||
fn test_file_type_check() {
|
||||
let mut flag_value_list = create_test_flag_value_list();
|
||||
flag_value_list.header.file_type = 123u8;
|
||||
let error = FlagValueList::from_bytes(&flag_value_list.as_bytes()).unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!("BytesParseFail(binary file is not a flag value file)")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -64,13 +64,13 @@ pub(crate) const HASH_PRIMES: [u32; 29] = [
|
||||
|
||||
/// Storage file type enum
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum StorageFileSelection {
|
||||
PackageMap,
|
||||
FlagMap,
|
||||
FlagVal,
|
||||
pub enum StorageFileType {
|
||||
PackageMap = 0,
|
||||
FlagMap = 1,
|
||||
FlagVal = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for StorageFileSelection {
|
||||
impl TryFrom<&str> for StorageFileType {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
|
||||
@@ -79,12 +79,25 @@ impl TryFrom<&str> for StorageFileSelection {
|
||||
"flag_map" => Ok(Self::FlagMap),
|
||||
"flag_val" => Ok(Self::FlagVal),
|
||||
_ => Err(anyhow!(
|
||||
"Invalid storage file type, valid types are package_map|flag_map|flag_val]"
|
||||
"Invalid storage file type, valid types are package_map|flag_map|flag_val"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for StorageFileType {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == Self::PackageMap as u8 => Ok(Self::PackageMap),
|
||||
x if x == Self::FlagMap as u8 => Ok(Self::FlagMap),
|
||||
x if x == Self::FlagVal as u8 => Ok(Self::FlagVal),
|
||||
_ => Err(anyhow!("Invalid storage file type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the right hash table size given number of entries in the table. Use a
|
||||
/// load factor of 0.5 for performance.
|
||||
pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
use aconfig_storage_file::{
|
||||
list_flags, read_file_to_bytes, AconfigStorageError, FlagTable, FlagValueList, PackageTable,
|
||||
StorageFileSelection,
|
||||
StorageFileType,
|
||||
};
|
||||
|
||||
use clap::{builder::ArgAction, Arg, Command};
|
||||
@@ -33,7 +33,7 @@ fn cli() -> Command {
|
||||
Arg::new("type")
|
||||
.long("type")
|
||||
.required(true)
|
||||
.value_parser(|s: &str| StorageFileSelection::try_from(s)),
|
||||
.value_parser(|s: &str| StorageFileType::try_from(s)),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -51,19 +51,19 @@ fn cli() -> Command {
|
||||
|
||||
fn print_storage_file(
|
||||
file_path: &str,
|
||||
file_type: &StorageFileSelection,
|
||||
file_type: &StorageFileType,
|
||||
) -> Result<(), AconfigStorageError> {
|
||||
let bytes = read_file_to_bytes(file_path)?;
|
||||
match file_type {
|
||||
StorageFileSelection::PackageMap => {
|
||||
StorageFileType::PackageMap => {
|
||||
let package_table = PackageTable::from_bytes(&bytes)?;
|
||||
println!("{:?}", package_table);
|
||||
}
|
||||
StorageFileSelection::FlagMap => {
|
||||
StorageFileType::FlagMap => {
|
||||
let flag_table = FlagTable::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_table);
|
||||
}
|
||||
StorageFileSelection::FlagVal => {
|
||||
StorageFileType::FlagVal => {
|
||||
let flag_value = FlagValueList::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_value);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ fn main() -> Result<(), AconfigStorageError> {
|
||||
match matches.subcommand() {
|
||||
Some(("print", sub_matches)) => {
|
||||
let file_path = sub_matches.get_one::<String>("file").unwrap();
|
||||
let file_type = sub_matches.get_one::<StorageFileSelection>("type").unwrap();
|
||||
let file_type = sub_matches.get_one::<StorageFileType>("type").unwrap();
|
||||
print_storage_file(file_path, file_type)?
|
||||
}
|
||||
Some(("list", sub_matches)) => {
|
||||
|
@@ -17,8 +17,8 @@
|
||||
//! package table module defines the package table file format and methods for serialization
|
||||
//! and deserialization
|
||||
|
||||
use crate::AconfigStorageError::{self, BytesParseFail};
|
||||
use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes};
|
||||
use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use std::fmt;
|
||||
|
||||
@@ -27,6 +27,7 @@ use std::fmt;
|
||||
pub struct PackageTableHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
pub file_type: u8,
|
||||
pub file_size: u32,
|
||||
pub num_packages: u32,
|
||||
pub bucket_offset: u32,
|
||||
@@ -38,8 +39,11 @@ impl fmt::Debug for PackageTableHeader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"Version: {}, Container: {}, File Size: {}",
|
||||
self.version, self.container, self.file_size
|
||||
"Version: {}, Container: {}, File Type: {:?}, File Size: {}",
|
||||
self.version,
|
||||
self.container,
|
||||
StorageFileType::try_from(self.file_type),
|
||||
self.file_size
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
@@ -58,6 +62,7 @@ impl PackageTableHeader {
|
||||
let container_bytes = self.container.as_bytes();
|
||||
result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
|
||||
result.extend_from_slice(container_bytes);
|
||||
result.extend_from_slice(&self.file_type.to_le_bytes());
|
||||
result.extend_from_slice(&self.file_size.to_le_bytes());
|
||||
result.extend_from_slice(&self.num_packages.to_le_bytes());
|
||||
result.extend_from_slice(&self.bucket_offset.to_le_bytes());
|
||||
@@ -68,14 +73,21 @@ impl PackageTableHeader {
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
|
||||
let mut head = 0;
|
||||
Ok(Self {
|
||||
let table = Self {
|
||||
version: read_u32_from_bytes(bytes, &mut head)?,
|
||||
container: read_str_from_bytes(bytes, &mut head)?,
|
||||
file_type: read_u8_from_bytes(bytes, &mut head)?,
|
||||
file_size: read_u32_from_bytes(bytes, &mut head)?,
|
||||
num_packages: read_u32_from_bytes(bytes, &mut head)?,
|
||||
bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||
node_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||
})
|
||||
};
|
||||
if table.file_type != StorageFileType::PackageMap as u8 {
|
||||
return Err(AconfigStorageError::BytesParseFail(anyhow!(
|
||||
"binary file is not a package map"
|
||||
)));
|
||||
}
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +203,12 @@ impl PackageTable {
|
||||
Ok(node)
|
||||
})
|
||||
.collect::<Result<Vec<_>, AconfigStorageError>>()
|
||||
.map_err(|errmsg| BytesParseFail(anyhow!("fail to parse package table: {}", errmsg)))?;
|
||||
.map_err(|errmsg| {
|
||||
AconfigStorageError::BytesParseFail(anyhow!(
|
||||
"fail to parse package table: {}",
|
||||
errmsg
|
||||
))
|
||||
})?;
|
||||
|
||||
let table = Self { header, buckets, nodes };
|
||||
Ok(table)
|
||||
@@ -233,4 +250,16 @@ mod tests {
|
||||
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
|
||||
assert_eq!(version, 1234)
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down file type check
|
||||
fn test_file_type_check() {
|
||||
let mut package_table = create_test_package_table();
|
||||
package_table.header.file_type = 123u8;
|
||||
let error = PackageTable::from_bytes(&package_table.as_bytes()).unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!("BytesParseFail(binary file is not a package map)")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ pub(crate) fn create_test_package_table() -> PackageTable {
|
||||
let header = PackageTableHeader {
|
||||
version: 1234,
|
||||
container: String::from("system"),
|
||||
file_type: 0,
|
||||
file_size: 208,
|
||||
num_packages: 3,
|
||||
bucket_offset: 30,
|
||||
@@ -72,6 +73,7 @@ pub(crate) fn create_test_flag_table() -> FlagTable {
|
||||
let header = FlagTableHeader {
|
||||
version: 1234,
|
||||
container: String::from("system"),
|
||||
file_type: 1,
|
||||
file_size: 320,
|
||||
num_flags: 8,
|
||||
bucket_offset: 30,
|
||||
@@ -113,6 +115,7 @@ pub(crate) fn create_test_flag_value_list() -> FlagValueList {
|
||||
let header = FlagValueHeader {
|
||||
version: 1234,
|
||||
container: String::from("system"),
|
||||
file_type: 2,
|
||||
file_size: 34,
|
||||
num_flags: 8,
|
||||
boolean_value_offset: 26,
|
||||
|
@@ -74,24 +74,3 @@ cc_library_static {
|
||||
whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
|
||||
export_include_dirs: ["include"],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "ro.package.map",
|
||||
out: ["tests/tmp.ro.package.map"],
|
||||
srcs: ["tests/package.map"],
|
||||
cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "ro.flag.map",
|
||||
out: ["tests/tmp.ro.flag.map"],
|
||||
srcs: ["tests/flag.map"],
|
||||
cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "ro.flag.val",
|
||||
out: ["tests/tmp.ro.flag.val"],
|
||||
srcs: ["tests/flag.val"],
|
||||
cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ pub fn find_flag_offset(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aconfig_storage_file::FlagTable;
|
||||
use aconfig_storage_file::{StorageFileType, FlagTable};
|
||||
|
||||
// create test baseline, syntactic sugar
|
||||
fn new_expected_node(
|
||||
@@ -88,36 +88,37 @@ mod tests {
|
||||
let header = FlagTableHeader {
|
||||
version: crate::FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 320,
|
||||
file_type: StorageFileType::FlagMap as u8,
|
||||
file_size: 321,
|
||||
num_flags: 8,
|
||||
bucket_offset: 30,
|
||||
node_offset: 98,
|
||||
bucket_offset: 31,
|
||||
node_offset: 99,
|
||||
};
|
||||
let buckets: Vec<Option<u32>> = vec![
|
||||
Some(98),
|
||||
Some(124),
|
||||
Some(99),
|
||||
Some(125),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(177),
|
||||
Some(178),
|
||||
None,
|
||||
Some(203),
|
||||
Some(204),
|
||||
None,
|
||||
Some(261),
|
||||
Some(262),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(293),
|
||||
Some(294),
|
||||
None,
|
||||
];
|
||||
let nodes = vec![
|
||||
new_expected_node(0, "enabled_ro", 1, 1, None),
|
||||
new_expected_node(0, "enabled_rw", 1, 2, Some(150)),
|
||||
new_expected_node(0, "enabled_rw", 1, 2, Some(151)),
|
||||
new_expected_node(1, "disabled_ro", 1, 0, None),
|
||||
new_expected_node(2, "enabled_ro", 1, 1, None),
|
||||
new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235)),
|
||||
new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(236)),
|
||||
new_expected_node(1, "enabled_ro", 1, 2, None),
|
||||
new_expected_node(2, "enabled_fixed_ro", 1, 0, None),
|
||||
new_expected_node(0, "disabled_rw", 1, 0, None),
|
||||
|
@@ -48,15 +48,16 @@ pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, Aco
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aconfig_storage_file::FlagValueList;
|
||||
use aconfig_storage_file::{StorageFileType, FlagValueList};
|
||||
|
||||
pub fn create_test_flag_value_list() -> FlagValueList {
|
||||
let header = FlagValueHeader {
|
||||
version: crate::FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 34,
|
||||
file_type: StorageFileType::FlagVal as u8,
|
||||
file_size: 35,
|
||||
num_flags: 8,
|
||||
boolean_value_offset: 26,
|
||||
boolean_value_offset: 27,
|
||||
};
|
||||
let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
|
||||
FlagValueList { header, booleans }
|
||||
|
@@ -43,7 +43,7 @@ pub mod package_table_query;
|
||||
mod test_utils;
|
||||
|
||||
pub use aconfig_storage_file::{
|
||||
protos::ProtoStorageFiles, read_u32_from_bytes, AconfigStorageError, StorageFileSelection,
|
||||
protos::ProtoStorageFiles, read_u32_from_bytes, AconfigStorageError, StorageFileType,
|
||||
FILE_VERSION,
|
||||
};
|
||||
pub use flag_table_query::FlagOffset;
|
||||
@@ -67,7 +67,7 @@ pub fn get_package_offset_impl(
|
||||
container: &str,
|
||||
package: &str,
|
||||
) -> Result<Option<PackageOffset>, AconfigStorageError> {
|
||||
let mapped_file = get_mapped_file(pb_file, container, StorageFileSelection::PackageMap)?;
|
||||
let mapped_file = get_mapped_file(pb_file, container, StorageFileType::PackageMap)?;
|
||||
find_package_offset(&mapped_file, package)
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn get_flag_offset_impl(
|
||||
package_id: u32,
|
||||
flag: &str,
|
||||
) -> Result<Option<FlagOffset>, AconfigStorageError> {
|
||||
let mapped_file = get_mapped_file(pb_file, container, StorageFileSelection::FlagMap)?;
|
||||
let mapped_file = get_mapped_file(pb_file, container, StorageFileType::FlagMap)?;
|
||||
find_flag_offset(&mapped_file, package_id, flag)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ pub fn get_boolean_flag_value_impl(
|
||||
container: &str,
|
||||
offset: u32,
|
||||
) -> Result<bool, AconfigStorageError> {
|
||||
let mapped_file = get_mapped_file(pb_file, container, StorageFileSelection::FlagVal)?;
|
||||
let mapped_file = get_mapped_file(pb_file, container, StorageFileType::FlagVal)?;
|
||||
find_boolean_flag_value(&mapped_file, offset)
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{BufReader, Read};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -26,7 +26,7 @@ use once_cell::sync::Lazy;
|
||||
use crate::AconfigStorageError::{
|
||||
self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
|
||||
};
|
||||
use crate::StorageFileSelection;
|
||||
use crate::StorageFileType;
|
||||
use aconfig_storage_file::protos::{
|
||||
storage_record_pb::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
|
||||
};
|
||||
@@ -75,17 +75,15 @@ fn find_container_storage_location(
|
||||
|
||||
/// Verify the file is read only and then map it
|
||||
fn verify_read_only_and_map(file_path: &str) -> Result<Mmap, AconfigStorageError> {
|
||||
let file = File::open(file_path)
|
||||
.map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
|
||||
let metadata = file.metadata().map_err(|errmsg| {
|
||||
FileReadFail(anyhow!("Failed to find metadata for {}: {}", file_path, errmsg))
|
||||
})?;
|
||||
|
||||
// ensure storage file is read only
|
||||
if !metadata.permissions().readonly() {
|
||||
// ensure file has read only permission
|
||||
let perms = fs::metadata(file_path).unwrap().permissions();
|
||||
if !perms.readonly() {
|
||||
return Err(MapFileFail(anyhow!("fail to map non read only storage file {}", file_path)));
|
||||
}
|
||||
|
||||
let file = File::open(file_path)
|
||||
.map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
|
||||
|
||||
// SAFETY:
|
||||
//
|
||||
// Mmap constructors are unsafe as it would have undefined behaviors if the file
|
||||
@@ -95,10 +93,6 @@ fn verify_read_only_and_map(file_path: &str) -> Result<Mmap, AconfigStorageError
|
||||
// which means it is read only. Here in the code, we check explicitly that the file
|
||||
// being mapped must only have read permission, otherwise, error out, thus making sure
|
||||
// it is safe.
|
||||
//
|
||||
// We should remove this restriction if we need to support mmap non read only file in
|
||||
// the future (by making this api unsafe). But for now, all flags are boot stable, so
|
||||
// the boot flag file copy should be readonly.
|
||||
unsafe {
|
||||
let mapped_file = Mmap::map(&file).map_err(|errmsg| {
|
||||
MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
|
||||
@@ -123,21 +117,21 @@ fn map_container_storage_files(
|
||||
pub(crate) fn get_mapped_file(
|
||||
location_pb_file: &str,
|
||||
container: &str,
|
||||
file_selection: StorageFileSelection,
|
||||
file_selection: StorageFileType,
|
||||
) -> Result<Arc<Mmap>, AconfigStorageError> {
|
||||
let mut all_mapped_files = ALL_MAPPED_FILES.lock().unwrap();
|
||||
match all_mapped_files.get(container) {
|
||||
Some(mapped_files) => Ok(match file_selection {
|
||||
StorageFileSelection::PackageMap => Arc::clone(&mapped_files.package_map),
|
||||
StorageFileSelection::FlagMap => Arc::clone(&mapped_files.flag_map),
|
||||
StorageFileSelection::FlagVal => Arc::clone(&mapped_files.flag_val),
|
||||
StorageFileType::PackageMap => Arc::clone(&mapped_files.package_map),
|
||||
StorageFileType::FlagMap => Arc::clone(&mapped_files.flag_map),
|
||||
StorageFileType::FlagVal => Arc::clone(&mapped_files.flag_val),
|
||||
}),
|
||||
None => {
|
||||
let mapped_files = map_container_storage_files(location_pb_file, container)?;
|
||||
let file_ptr = match file_selection {
|
||||
StorageFileSelection::PackageMap => Arc::clone(&mapped_files.package_map),
|
||||
StorageFileSelection::FlagMap => Arc::clone(&mapped_files.flag_map),
|
||||
StorageFileSelection::FlagVal => Arc::clone(&mapped_files.flag_val),
|
||||
StorageFileType::PackageMap => Arc::clone(&mapped_files.package_map),
|
||||
StorageFileType::FlagMap => Arc::clone(&mapped_files.flag_map),
|
||||
StorageFileType::FlagVal => Arc::clone(&mapped_files.flag_val),
|
||||
};
|
||||
all_mapped_files.insert(container.to_string(), mapped_files);
|
||||
Ok(file_ptr)
|
||||
@@ -196,11 +190,7 @@ files {
|
||||
);
|
||||
}
|
||||
|
||||
fn map_and_verify(
|
||||
location_pb_file: &str,
|
||||
file_selection: StorageFileSelection,
|
||||
actual_file: &str,
|
||||
) {
|
||||
fn map_and_verify(location_pb_file: &str, file_selection: StorageFileType, actual_file: &str) {
|
||||
let mut opened_file = File::open(actual_file).unwrap();
|
||||
let mut content = Vec::new();
|
||||
opened_file.read_to_end(&mut content).unwrap();
|
||||
@@ -238,13 +228,9 @@ files {{
|
||||
|
||||
let file = write_proto_to_temp_file(&text_proto).unwrap();
|
||||
let file_full_path = file.path().display().to_string();
|
||||
map_and_verify(
|
||||
&file_full_path,
|
||||
StorageFileSelection::PackageMap,
|
||||
&ro_files.package_map.name,
|
||||
);
|
||||
map_and_verify(&file_full_path, StorageFileSelection::FlagMap, &ro_files.flag_map.name);
|
||||
map_and_verify(&file_full_path, StorageFileSelection::FlagVal, &ro_files.flag_val.name);
|
||||
map_and_verify(&file_full_path, StorageFileType::PackageMap, &ro_files.package_map.name);
|
||||
map_and_verify(&file_full_path, StorageFileType::FlagMap, &ro_files.flag_map.name);
|
||||
map_and_verify(&file_full_path, StorageFileType::FlagVal, &ro_files.flag_val.name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@@ -72,18 +72,19 @@ pub fn find_package_offset(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aconfig_storage_file::PackageTable;
|
||||
use aconfig_storage_file::{StorageFileType, PackageTable};
|
||||
|
||||
pub fn create_test_package_table() -> PackageTable {
|
||||
let header = PackageTableHeader {
|
||||
version: crate::FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 208,
|
||||
file_type: StorageFileType::PackageMap as u8,
|
||||
file_size: 209,
|
||||
num_packages: 3,
|
||||
bucket_offset: 30,
|
||||
node_offset: 58,
|
||||
bucket_offset: 31,
|
||||
node_offset: 59,
|
||||
};
|
||||
let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
|
||||
let buckets: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None];
|
||||
let first_node = PackageTableNode {
|
||||
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||
package_id: 1,
|
||||
@@ -94,7 +95,7 @@ mod tests {
|
||||
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||
package_id: 0,
|
||||
boolean_offset: 0,
|
||||
next_offset: Some(158),
|
||||
next_offset: Some(159),
|
||||
};
|
||||
let third_node = PackageTableNode {
|
||||
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||
|
@@ -26,14 +26,6 @@ fn set_file_read_only(file: &NamedTempFile) {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_file_read_write(file: &NamedTempFile) {
|
||||
let mut perms = fs::metadata(file.path()).unwrap().permissions();
|
||||
if perms.readonly() {
|
||||
perms.set_readonly(false);
|
||||
fs::set_permissions(file.path(), perms).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct TestStorageFile {
|
||||
pub file: NamedTempFile,
|
||||
@@ -46,8 +38,6 @@ impl TestStorageFile {
|
||||
fs::copy(source_file, file.path())?;
|
||||
if read_only {
|
||||
set_file_read_only(&file);
|
||||
} else {
|
||||
set_file_read_write(&file);
|
||||
}
|
||||
let name = file.path().display().to_string();
|
||||
Ok(Self { file, name })
|
||||
|
@@ -10,9 +10,9 @@ rust_test {
|
||||
"libtempfile",
|
||||
],
|
||||
data: [
|
||||
":ro.package.map",
|
||||
":ro.flag.map",
|
||||
":ro.flag.val",
|
||||
"package.map",
|
||||
"flag.map",
|
||||
"flag.val",
|
||||
],
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
@@ -31,9 +31,9 @@ cc_test {
|
||||
"liblog",
|
||||
],
|
||||
data: [
|
||||
":ro.package.map",
|
||||
":ro.flag.map",
|
||||
":ro.flag.val",
|
||||
"package.map",
|
||||
"flag.map",
|
||||
"flag.val",
|
||||
],
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -16,58 +16,104 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "aconfig_storage/aconfig_storage_read_api.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <protos/aconfig_storage_metadata.pb.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/result.h>
|
||||
|
||||
using android::aconfig_storage_metadata::storage_files;
|
||||
using ::android::base::ReadFileToString;
|
||||
using ::android::base::WriteStringToFile;
|
||||
using ::android::base::Result;
|
||||
using ::android::base::Error;
|
||||
using ::aconfig_storage::test_only_api::get_package_offset_impl;
|
||||
using ::aconfig_storage::test_only_api::get_flag_offset_impl;
|
||||
using ::aconfig_storage::test_only_api::get_boolean_flag_value_impl;
|
||||
using ::aconfig_storage::get_storage_file_version;
|
||||
|
||||
void write_storage_location_pb_to_file(std::string const& file_path) {
|
||||
auto const test_dir = android::base::GetExecutableDirectory();
|
||||
auto proto = storage_files();
|
||||
auto* info = proto.add_files();
|
||||
info->set_version(0);
|
||||
info->set_container("system");
|
||||
info->set_package_map(test_dir + "/tests/tmp.ro.package.map");
|
||||
info->set_flag_map(test_dir + "/tests/tmp.ro.flag.map");
|
||||
info->set_flag_val(test_dir + "/tests/tmp.ro.flag.val");
|
||||
info->set_timestamp(12345);
|
||||
class AconfigStorageTest : public ::testing::Test {
|
||||
protected:
|
||||
Result<std::string> copy_to_ro_temp_file(std::string const& source_file) {
|
||||
auto temp_file = std::string(std::tmpnam(nullptr));
|
||||
auto content = std::string();
|
||||
if (!ReadFileToString(source_file, &content)) {
|
||||
return Error() << "failed to read file: " << source_file;
|
||||
}
|
||||
if (!WriteStringToFile(content, temp_file)) {
|
||||
return Error() << "failed to copy file: " << source_file;
|
||||
}
|
||||
if (chmod(temp_file.c_str(), S_IRUSR | S_IRGRP) == -1) {
|
||||
return Error() << "failed to make file read only";
|
||||
}
|
||||
return temp_file;
|
||||
}
|
||||
|
||||
auto content = std::string();
|
||||
proto.SerializeToString(&content);
|
||||
ASSERT_TRUE(WriteStringToFile(content, file_path))
|
||||
<< "Failed to write a file: " << file_path;
|
||||
}
|
||||
Result<std::string> write_storage_location_pb_file(std::string const& package_map,
|
||||
std::string const& flag_map,
|
||||
std::string const& flag_val) {
|
||||
auto temp_file = std::tmpnam(nullptr);
|
||||
auto proto = storage_files();
|
||||
auto* info = proto.add_files();
|
||||
info->set_version(0);
|
||||
info->set_container("system");
|
||||
info->set_package_map(package_map);
|
||||
info->set_flag_map(flag_map);
|
||||
info->set_flag_val(flag_val);
|
||||
info->set_timestamp(12345);
|
||||
|
||||
TEST(AconfigStorageTest, test_storage_version_query) {
|
||||
auto const test_dir = android::base::GetExecutableDirectory();
|
||||
auto query = get_storage_file_version(test_dir + "/tests/tmp.ro.package.map");
|
||||
auto content = std::string();
|
||||
proto.SerializeToString(&content);
|
||||
if (!WriteStringToFile(content, temp_file)) {
|
||||
return Error() << "failed to write storage records pb file";
|
||||
}
|
||||
return temp_file;
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
auto const test_dir = android::base::GetExecutableDirectory();
|
||||
package_map = *copy_to_ro_temp_file(test_dir + "/package.map");
|
||||
flag_map = *copy_to_ro_temp_file(test_dir + "/flag.map");
|
||||
flag_val = *copy_to_ro_temp_file(test_dir + "/flag.val");
|
||||
storage_record_pb = *write_storage_location_pb_file(
|
||||
package_map, flag_map, flag_val);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
std::remove(package_map.c_str());
|
||||
std::remove(flag_map.c_str());
|
||||
std::remove(flag_val.c_str());
|
||||
std::remove(storage_record_pb.c_str());
|
||||
}
|
||||
|
||||
std::string package_map;
|
||||
std::string flag_map;
|
||||
std::string flag_val;
|
||||
std::string storage_record_pb;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(AconfigStorageTest, test_storage_version_query) {
|
||||
auto query = get_storage_file_version(package_map);
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_EQ(query.version_number, 1);
|
||||
query = get_storage_file_version(test_dir + "/tests/tmp.ro.flag.map");
|
||||
query = get_storage_file_version(flag_map);
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_EQ(query.version_number, 1);
|
||||
query = get_storage_file_version(test_dir + "/tests/tmp.ro.flag.val");
|
||||
query = get_storage_file_version(flag_val);
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_EQ(query.version_number, 1);
|
||||
}
|
||||
|
||||
TEST(AconfigStorageTest, test_package_offset_query) {
|
||||
auto pb_file = std::string("/tmp/test_package_offset_query.pb");
|
||||
write_storage_location_pb_to_file(pb_file);
|
||||
|
||||
TEST_F(AconfigStorageTest, test_package_offset_query) {
|
||||
auto query = get_package_offset_impl(
|
||||
pb_file, "system", "com.android.aconfig.storage.test_1");
|
||||
storage_record_pb, "system", "com.android.aconfig.storage.test_1");
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_TRUE(query.package_exists);
|
||||
@@ -75,7 +121,7 @@ TEST(AconfigStorageTest, test_package_offset_query) {
|
||||
ASSERT_EQ(query.boolean_offset, 0);
|
||||
|
||||
query = get_package_offset_impl(
|
||||
pb_file, "system", "com.android.aconfig.storage.test_2");
|
||||
storage_record_pb, "system", "com.android.aconfig.storage.test_2");
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_TRUE(query.package_exists);
|
||||
@@ -83,7 +129,7 @@ TEST(AconfigStorageTest, test_package_offset_query) {
|
||||
ASSERT_EQ(query.boolean_offset, 3);
|
||||
|
||||
query = get_package_offset_impl(
|
||||
pb_file, "system", "com.android.aconfig.storage.test_4");
|
||||
storage_record_pb, "system", "com.android.aconfig.storage.test_4");
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_TRUE(query.package_exists);
|
||||
@@ -91,27 +137,21 @@ TEST(AconfigStorageTest, test_package_offset_query) {
|
||||
ASSERT_EQ(query.boolean_offset, 6);
|
||||
}
|
||||
|
||||
TEST(AconfigStorageTest, test_invalid_package_offset_query) {
|
||||
auto pb_file = std::string("/tmp/test_package_offset_query.pb");
|
||||
write_storage_location_pb_to_file(pb_file);
|
||||
|
||||
TEST_F(AconfigStorageTest, test_invalid_package_offset_query) {
|
||||
auto query = get_package_offset_impl(
|
||||
pb_file, "system", "com.android.aconfig.storage.test_3");
|
||||
storage_record_pb, "system", "com.android.aconfig.storage.test_3");
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_FALSE(query.package_exists);
|
||||
|
||||
query = get_package_offset_impl(
|
||||
pb_file, "vendor", "com.android.aconfig.storage.test_1");
|
||||
storage_record_pb, "vendor", "com.android.aconfig.storage.test_1");
|
||||
ASSERT_EQ(query.error_message,
|
||||
std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
|
||||
ASSERT_FALSE(query.query_success);
|
||||
}
|
||||
|
||||
TEST(AconfigStorageTest, test_flag_offset_query) {
|
||||
auto pb_file = std::string("/tmp/test_package_offset_query.pb");
|
||||
write_storage_location_pb_to_file(pb_file);
|
||||
|
||||
TEST_F(AconfigStorageTest, test_flag_offset_query) {
|
||||
auto baseline = std::vector<std::tuple<int, std::string, int>>{
|
||||
{0, "enabled_ro", 1},
|
||||
{0, "enabled_rw", 2},
|
||||
@@ -123,7 +163,7 @@ TEST(AconfigStorageTest, test_flag_offset_query) {
|
||||
{0, "disabled_rw", 0},
|
||||
};
|
||||
for (auto const&[package_id, flag_name, expected_offset] : baseline) {
|
||||
auto query = get_flag_offset_impl(pb_file, "system", package_id, flag_name);
|
||||
auto query = get_flag_offset_impl(storage_record_pb, "system", package_id, flag_name);
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_TRUE(query.flag_exists);
|
||||
@@ -131,47 +171,39 @@ TEST(AconfigStorageTest, test_flag_offset_query) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AconfigStorageTest, test_invalid_flag_offset_query) {
|
||||
auto pb_file = std::string("/tmp/test_invalid_package_offset_query.pb");
|
||||
write_storage_location_pb_to_file(pb_file);
|
||||
|
||||
auto query = get_flag_offset_impl(pb_file, "system", 0, "none_exist");
|
||||
TEST_F(AconfigStorageTest, test_invalid_flag_offset_query) {
|
||||
auto query = get_flag_offset_impl(storage_record_pb, "system", 0, "none_exist");
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_FALSE(query.flag_exists);
|
||||
|
||||
query = get_flag_offset_impl(pb_file, "system", 3, "enabled_ro");
|
||||
query = get_flag_offset_impl(storage_record_pb, "system", 3, "enabled_ro");
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_FALSE(query.flag_exists);
|
||||
|
||||
query = get_flag_offset_impl(pb_file, "vendor", 0, "enabled_ro");
|
||||
query = get_flag_offset_impl(storage_record_pb, "vendor", 0, "enabled_ro");
|
||||
ASSERT_EQ(query.error_message,
|
||||
std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
|
||||
ASSERT_FALSE(query.query_success);
|
||||
}
|
||||
|
||||
TEST(AconfigStorageTest, test_boolean_flag_value_query) {
|
||||
auto pb_file = std::string("/tmp/test_boolean_flag_value_query.pb");
|
||||
write_storage_location_pb_to_file(pb_file);
|
||||
TEST_F(AconfigStorageTest, test_boolean_flag_value_query) {
|
||||
for (int offset = 0; offset < 8; ++offset) {
|
||||
auto query = get_boolean_flag_value_impl(pb_file, "system", offset);
|
||||
auto query = get_boolean_flag_value_impl(storage_record_pb, "system", offset);
|
||||
ASSERT_EQ(query.error_message, std::string());
|
||||
ASSERT_TRUE(query.query_success);
|
||||
ASSERT_FALSE(query.flag_value);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
|
||||
auto pb_file = std::string("/tmp/test_invalid_boolean_flag_value_query.pb");
|
||||
write_storage_location_pb_to_file(pb_file);
|
||||
|
||||
auto query = get_boolean_flag_value_impl(pb_file, "vendor", 0);
|
||||
TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
|
||||
auto query = get_boolean_flag_value_impl(storage_record_pb, "vendor", 0);
|
||||
ASSERT_EQ(query.error_message,
|
||||
std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
|
||||
ASSERT_FALSE(query.query_success);
|
||||
|
||||
query = get_boolean_flag_value_impl(pb_file, "system", 8);
|
||||
query = get_boolean_flag_value_impl(storage_record_pb, "system", 8);
|
||||
ASSERT_EQ(query.error_message,
|
||||
std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
|
||||
ASSERT_FALSE(query.query_success);
|
||||
|
@@ -5,22 +5,43 @@ mod aconfig_storage_rust_test {
|
||||
get_storage_file_version, PackageOffset, ProtoStorageFiles,
|
||||
};
|
||||
use protobuf::Message;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
fn write_storage_location_file() -> NamedTempFile {
|
||||
let text_proto = r#"
|
||||
files {
|
||||
pub fn copy_to_ro_temp_file(source_file: &str) -> NamedTempFile {
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
fs::copy(source_file, file.path()).unwrap();
|
||||
let file_name = file.path().display().to_string();
|
||||
let mut perms = fs::metadata(file_name).unwrap().permissions();
|
||||
perms.set_readonly(true);
|
||||
fs::set_permissions(file.path(), perms.clone()).unwrap();
|
||||
file
|
||||
}
|
||||
|
||||
fn write_storage_location_file(
|
||||
package_table: &NamedTempFile,
|
||||
flag_table: &NamedTempFile,
|
||||
flag_value: &NamedTempFile,
|
||||
) -> NamedTempFile {
|
||||
let text_proto = format!(
|
||||
r#"
|
||||
files {{
|
||||
version: 0
|
||||
container: "system"
|
||||
package_map: "./tests/tmp.ro.package.map"
|
||||
flag_map: "./tests/tmp.ro.flag.map"
|
||||
flag_val: "./tests/tmp.ro.flag.val"
|
||||
package_map: "{}"
|
||||
flag_map: "{}"
|
||||
flag_val: "{}"
|
||||
timestamp: 12345
|
||||
}
|
||||
"#;
|
||||
}}
|
||||
"#,
|
||||
package_table.path().display(),
|
||||
flag_table.path().display(),
|
||||
flag_value.path().display(),
|
||||
);
|
||||
|
||||
let storage_files: ProtoStorageFiles =
|
||||
protobuf::text_format::parse_from_str(text_proto).unwrap();
|
||||
protobuf::text_format::parse_from_str(&text_proto).unwrap();
|
||||
let mut binary_proto_bytes = Vec::new();
|
||||
storage_files.write_to_vec(&mut binary_proto_bytes).unwrap();
|
||||
let mut file = NamedTempFile::new().unwrap();
|
||||
@@ -30,7 +51,11 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_package_offset_query() {
|
||||
let file = write_storage_location_file();
|
||||
let package_table = copy_to_ro_temp_file("./package.map");
|
||||
let flag_table = copy_to_ro_temp_file("./flag.map");
|
||||
let flag_value = copy_to_ro_temp_file("./flag.val");
|
||||
|
||||
let file = write_storage_location_file(&package_table, &flag_table, &flag_value);
|
||||
let file_full_path = file.path().display().to_string();
|
||||
|
||||
let package_offset = get_package_offset_impl(
|
||||
@@ -74,7 +99,11 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_package_offset_query() {
|
||||
let file = write_storage_location_file();
|
||||
let package_table = copy_to_ro_temp_file("./package.map");
|
||||
let flag_table = copy_to_ro_temp_file("./flag.map");
|
||||
let flag_value = copy_to_ro_temp_file("./flag.val");
|
||||
|
||||
let file = write_storage_location_file(&package_table, &flag_table, &flag_value);
|
||||
let file_full_path = file.path().display().to_string();
|
||||
|
||||
let package_offset_option = get_package_offset_impl(
|
||||
@@ -99,7 +128,11 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_flag_offset_query() {
|
||||
let file = write_storage_location_file();
|
||||
let package_table = copy_to_ro_temp_file("./package.map");
|
||||
let flag_table = copy_to_ro_temp_file("./flag.map");
|
||||
let flag_value = copy_to_ro_temp_file("./flag.val");
|
||||
|
||||
let file = write_storage_location_file(&package_table, &flag_table, &flag_value);
|
||||
let file_full_path = file.path().display().to_string();
|
||||
|
||||
let baseline = vec![
|
||||
@@ -123,7 +156,11 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_flag_offset_query() {
|
||||
let file = write_storage_location_file();
|
||||
let package_table = copy_to_ro_temp_file("./package.map");
|
||||
let flag_table = copy_to_ro_temp_file("./flag.map");
|
||||
let flag_value = copy_to_ro_temp_file("./flag.val");
|
||||
|
||||
let file = write_storage_location_file(&package_table, &flag_table, &flag_value);
|
||||
let file_full_path = file.path().display().to_string();
|
||||
|
||||
let flag_offset_option =
|
||||
@@ -143,7 +180,11 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_boolean_flag_value_query() {
|
||||
let file = write_storage_location_file();
|
||||
let package_table = copy_to_ro_temp_file("./package.map");
|
||||
let flag_table = copy_to_ro_temp_file("./flag.map");
|
||||
let flag_value = copy_to_ro_temp_file("./flag.val");
|
||||
|
||||
let file = write_storage_location_file(&package_table, &flag_table, &flag_value);
|
||||
let file_full_path = file.path().display().to_string();
|
||||
|
||||
let baseline: Vec<bool> = vec![false; 8];
|
||||
@@ -156,7 +197,11 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_boolean_flag_value_query() {
|
||||
let file = write_storage_location_file();
|
||||
let package_table = copy_to_ro_temp_file("./package.map");
|
||||
let flag_table = copy_to_ro_temp_file("./flag.map");
|
||||
let flag_value = copy_to_ro_temp_file("./flag.val");
|
||||
|
||||
let file = write_storage_location_file(&package_table, &flag_table, &flag_value);
|
||||
let file_full_path = file.path().display().to_string();
|
||||
|
||||
let err = get_boolean_flag_value_impl(&file_full_path, "vendor", 0u32).unwrap_err();
|
||||
@@ -174,8 +219,8 @@ files {
|
||||
|
||||
#[test]
|
||||
fn test_storage_version_query() {
|
||||
assert_eq!(get_storage_file_version("./tests/tmp.ro.package.map").unwrap(), 1);
|
||||
assert_eq!(get_storage_file_version("./tests/tmp.ro.flag.map").unwrap(), 1);
|
||||
assert_eq!(get_storage_file_version("./tests/tmp.ro.flag.val").unwrap(), 1);
|
||||
assert_eq!(get_storage_file_version("./package.map").unwrap(), 1);
|
||||
assert_eq!(get_storage_file_version("./flag.map").unwrap(), 1);
|
||||
assert_eq!(get_storage_file_version("./flag.val").unwrap(), 1);
|
||||
}
|
||||
}
|
||||
|
@@ -51,15 +51,16 @@ pub fn update_boolean_flag_value(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aconfig_storage_file::FlagValueList;
|
||||
use aconfig_storage_file::{FlagValueList, StorageFileType};
|
||||
|
||||
pub fn create_test_flag_value_list() -> FlagValueList {
|
||||
let header = FlagValueHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_size: 34,
|
||||
file_type: StorageFileType::FlagVal as u8,
|
||||
file_size: 35,
|
||||
num_flags: 8,
|
||||
boolean_value_offset: 26,
|
||||
boolean_value_offset: 27,
|
||||
};
|
||||
let booleans: Vec<bool> = vec![false; 8];
|
||||
FlagValueList { header, booleans }
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user