aconfig: add flag info read rust api

Bug: b/321077378
Test: atest aconfig_storage_read_api.test; atest
aconfig_storage_read_api.test.rust

Change-Id: I382ac0145c5d91827952b3ddb01cabefd1539854
This commit is contained in:
Dennis Shen 2024-04-07 19:23:09 +00:00
parent eff5363e2d
commit 2ac7a4c2bd
10 changed files with 219 additions and 45 deletions

View file

@ -91,9 +91,9 @@ impl FlagInfoHeader {
/// bit field for flag info
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlagInfoBit {
IsSticky = 0,
IsReadWrite = 1,
HasOverride = 2,
IsSticky = 1 << 0,
IsReadWrite = 1 << 1,
HasOverride = 1 << 2,
}
/// Flag info node struct
@ -108,9 +108,9 @@ impl fmt::Debug for FlagInfoNode {
writeln!(
f,
"sticky: {}, readwrite: {}, override: {}",
self.attributes & (FlagInfoBit::IsSticky as u8),
self.attributes & (FlagInfoBit::IsReadWrite as u8),
self.attributes & (FlagInfoBit::HasOverride as u8),
self.attributes & (FlagInfoBit::IsSticky as u8) != 0,
self.attributes & (FlagInfoBit::IsReadWrite as u8) != 0,
self.attributes & (FlagInfoBit::HasOverride as u8) != 0,
)?;
Ok(())
}

View file

@ -46,7 +46,7 @@ use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::Read;
pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagInfoBit};
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};

View file

@ -38,6 +38,7 @@ rust_test_host {
"tests/package.map",
"tests/flag.map",
"tests/flag.val",
"tests/flag.info",
],
}

View file

@ -0,0 +1,115 @@
/*
* 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.
*/
//! flag value query module defines the flag value file read from mapped bytes
use crate::{AconfigStorageError, FILE_VERSION};
use aconfig_storage_file::{flag_info::FlagInfoHeader, read_u8_from_bytes};
use anyhow::anyhow;
/// Get flag attribute bitfield
pub fn find_boolean_flag_attribute(
buf: &[u8],
flag_offset: u32,
) -> Result<u8, AconfigStorageError> {
let interpreted_header = FlagInfoHeader::from_bytes(buf)?;
if interpreted_header.version > crate::FILE_VERSION {
return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
"Cannot read storage file with a higher version of {} with lib version {}",
interpreted_header.version,
FILE_VERSION
)));
}
let mut head = (interpreted_header.boolean_flag_offset + flag_offset) as usize;
if head >= interpreted_header.file_size as usize {
return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
"Flag info offset goes beyond the end of the file."
)));
}
let val = read_u8_from_bytes(buf, &mut head)?;
Ok(val)
}
#[cfg(test)]
mod tests {
use super::*;
use aconfig_storage_file::{test_utils::create_test_flag_info_list, FlagInfoBit};
#[test]
// this test point locks down query if flag is sticky
fn test_is_flag_sticky() {
let flag_info_list = create_test_flag_info_list().into_bytes();
for offset in 0..8 {
let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap();
assert_eq!((attribute & FlagInfoBit::IsSticky as u8) != 0u8, false);
}
}
#[test]
// this test point locks down query if flag is readwrite
fn test_is_flag_readwrite() {
let flag_info_list = create_test_flag_info_list().into_bytes();
let baseline: Vec<bool> = vec![true, false, true, false, false, false, false, false];
for offset in 0..8 {
let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap();
assert_eq!(
(attribute & FlagInfoBit::IsReadWrite as u8) != 0u8,
baseline[offset as usize]
);
}
}
#[test]
// this test point locks down query if flag has override
fn test_flag_has_override() {
let flag_info_list = create_test_flag_info_list().into_bytes();
for offset in 0..8 {
let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap();
assert_eq!((attribute & FlagInfoBit::HasOverride as u8) != 0u8, false);
}
}
#[test]
// this test point locks down query beyond the end of boolean section
fn test_boolean_out_of_range() {
let flag_info_list = create_test_flag_info_list().into_bytes();
let error = find_boolean_flag_attribute(&flag_info_list[..], 8).unwrap_err();
assert_eq!(
format!("{:?}", error),
"InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)"
);
}
#[test]
// this test point locks down query error when file has a higher version
fn test_higher_version_storage_file() {
let mut info_list = create_test_flag_info_list();
info_list.header.version = crate::FILE_VERSION + 1;
let flag_info = info_list.into_bytes();
let error = find_boolean_flag_attribute(&flag_info[..], 4).unwrap_err();
assert_eq!(
format!("{:?}", error),
format!(
"HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
crate::FILE_VERSION + 1,
crate::FILE_VERSION
)
);
}
}

View file

@ -48,26 +48,13 @@ 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, StorageFileType};
pub fn create_test_flag_value_list() -> FlagValueList {
let header = FlagValueHeader {
version: crate::FILE_VERSION,
container: String::from("system"),
file_type: StorageFileType::FlagVal as u8,
file_size: 35,
num_flags: 8,
boolean_value_offset: 27,
};
let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
FlagValueList { header, booleans }
}
use aconfig_storage_file::test_utils::create_test_flag_value_list;
#[test]
// this test point locks down flag value query
fn test_flag_value_query() {
let flag_value_list = create_test_flag_value_list().into_bytes();
let baseline: Vec<bool> = vec![false, true, false, false, true, true, false, true];
let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];
for (offset, expected_value) in baseline.into_iter().enumerate() {
let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
assert_eq!(flag_value, expected_value);

View file

@ -34,6 +34,7 @@
//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
//! please refer to the g3doc go/android-flags
pub mod flag_info_query;
pub mod flag_table_query;
pub mod flag_value_query;
pub mod mapped_file;
@ -47,6 +48,7 @@ pub use flag_table_query::FlagOffset;
pub use package_table_query::PackageOffset;
use aconfig_storage_file::{read_u32_from_bytes, FILE_VERSION};
use flag_info_query::find_boolean_flag_attribute;
use flag_table_query::find_flag_offset;
use flag_value_query::find_boolean_flag_value;
use package_table_query::find_package_offset;
@ -145,6 +147,18 @@ pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageEr
read_u32_from_bytes(&buffer, &mut head)
}
/// Get the boolean flag attribute.
///
/// \input file: mapped flag info file
/// \input offset: boolean flag offset
///
/// \return
/// If the provide offset is valid, it returns the boolean flag attribute bitfiled, otherwise it
/// returns the error message.
pub fn get_boolean_flag_attribute(file: &Mmap, offset: u32) -> Result<u8, AconfigStorageError> {
find_boolean_flag_attribute(file, offset)
}
// *************************************** //
// CC INTERLOP
// *************************************** //
@ -315,12 +329,14 @@ mod tests {
use crate::mapped_file::get_mapped_file;
use crate::test_utils::copy_to_temp_file;
use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
use aconfig_storage_file::FlagInfoBit;
use tempfile::NamedTempFile;
fn create_test_storage_files() -> [NamedTempFile; 4] {
fn create_test_storage_files() -> [NamedTempFile; 5] {
let package_map = copy_to_temp_file("./tests/package.map").unwrap();
let flag_map = copy_to_temp_file("./tests/flag.map").unwrap();
let flag_val = copy_to_temp_file("./tests/flag.val").unwrap();
let flag_info = copy_to_temp_file("./tests/flag.info").unwrap();
let text_proto = format!(
r#"
@ -330,21 +346,23 @@ files {{
package_map: "{}"
flag_map: "{}"
flag_val: "{}"
flag_info: "{}"
timestamp: 12345
}}
"#,
package_map.path().display(),
flag_map.path().display(),
flag_val.path().display()
flag_val.path().display(),
flag_info.path().display()
);
let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
[package_map, flag_map, flag_val, pb_file]
[package_map, flag_map, flag_val, flag_info, pb_file]
}
#[test]
// this test point locks down flag package offset query
fn test_package_offset_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
let package_mapped_file = unsafe {
get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
@ -375,7 +393,7 @@ files {{
#[test]
// this test point locks down flag offset query
fn test_flag_offset_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
let flag_mapped_file =
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
@ -398,9 +416,9 @@ files {{
}
#[test]
// this test point locks down flag offset query
// this test point locks down flag value query
fn test_flag_value_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
let flag_value_file =
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
@ -411,11 +429,28 @@ files {{
}
}
#[test]
// this test point locks donw flag info query
fn test_flag_info_query() {
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_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];
for (offset, expected_value) in is_rw.into_iter().enumerate() {
let attribute = get_boolean_flag_attribute(&flag_info_file, offset as u32).unwrap();
assert!((attribute & FlagInfoBit::IsSticky as u8) == 0u8);
assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value);
assert!((attribute & FlagInfoBit::HasOverride as u8) == 0u8);
}
}
#[test]
// this test point locks down flag storage file version number query api
fn test_storage_version_query() {
assert_eq!(get_storage_file_version("./tests/package.map").unwrap(), 1);
assert_eq!(get_storage_file_version("./tests/flag.map").unwrap(), 1);
assert_eq!(get_storage_file_version("./tests/flag.val").unwrap(), 1);
assert_eq!(get_storage_file_version("./tests/flag.info").unwrap(), 1);
}
}

View file

@ -91,9 +91,7 @@ pub unsafe fn get_mapped_file(
StorageFileType::PackageMap => unsafe { map_file(files_location.package_map()) },
StorageFileType::FlagMap => unsafe { map_file(files_location.flag_map()) },
StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
StorageFileType::FlagInfo => {
Err(MapFileFail(anyhow!("TODO: add support for flag info file")))
}
StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) },
}
}

View file

@ -14,6 +14,7 @@ rust_test {
"package.map",
"flag.map",
"flag.val",
"flag.info",
],
test_suites: ["general-tests"],
}
@ -35,6 +36,7 @@ cc_test {
"package.map",
"flag.map",
"flag.val",
"flag.info",
],
test_suites: [
"device-tests",

Binary file not shown.

View file

@ -1,10 +1,10 @@
#[cfg(not(feature = "cargo"))]
mod aconfig_storage_rust_test {
use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
use aconfig_storage_file::StorageFileType;
use aconfig_storage_file::{FlagInfoBit, StorageFileType};
use aconfig_storage_read_api::{
get_boolean_flag_value, get_flag_offset, get_package_offset, get_storage_file_version,
mapped_file::get_mapped_file, PackageOffset,
get_boolean_flag_attribute, get_boolean_flag_value, get_flag_offset, get_package_offset,
get_storage_file_version, mapped_file::get_mapped_file, PackageOffset,
};
use std::fs;
use tempfile::NamedTempFile;
@ -15,10 +15,11 @@ mod aconfig_storage_rust_test {
file
}
fn create_test_storage_files() -> [NamedTempFile; 4] {
fn create_test_storage_files() -> [NamedTempFile; 5] {
let package_map = copy_to_temp_file("./package.map");
let flag_map = copy_to_temp_file("./flag.map");
let flag_val = copy_to_temp_file("./flag.val");
let flag_info = copy_to_temp_file("./flag.info");
let text_proto = format!(
r#"
@ -28,20 +29,22 @@ files {{
package_map: "{}"
flag_map: "{}"
flag_val: "{}"
flag_info: "{}"
timestamp: 12345
}}
"#,
package_map.path().display(),
flag_map.path().display(),
flag_val.path().display()
flag_val.path().display(),
flag_info.path().display()
);
let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
[package_map, flag_map, flag_val, pb_file]
[package_map, flag_map, flag_val, flag_info, pb_file]
}
#[test]
fn test_unavailable_stoarge() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -56,7 +59,7 @@ files {{
#[test]
fn test_package_offset_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -88,7 +91,7 @@ files {{
#[test]
fn test_none_exist_package_offset_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -103,7 +106,7 @@ files {{
#[test]
fn test_flag_offset_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -129,7 +132,7 @@ files {{
#[test]
fn test_none_exist_flag_offset_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -144,7 +147,7 @@ files {{
#[test]
fn test_boolean_flag_value_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -159,7 +162,7 @@ files {{
#[test]
fn test_invalid_boolean_flag_value_query() {
let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as the test process will not write to temp storage file
@ -172,10 +175,43 @@ files {{
);
}
#[test]
fn test_flag_info_query() {
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// 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];
for (offset, expected_value) in is_rw.into_iter().enumerate() {
let attribute = get_boolean_flag_attribute(&flag_info_file, offset as u32).unwrap();
assert!((attribute & FlagInfoBit::IsSticky as u8) == 0u8);
assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value);
assert!((attribute & FlagInfoBit::HasOverride as u8) == 0u8);
}
}
#[test]
fn test_invalid_boolean_flag_info_query() {
let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
let pb_file_path = pb_file.path().display().to_string();
// SAFETY:
// 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 err = get_boolean_flag_attribute(&flag_info_file, 8u32).unwrap_err();
assert_eq!(
format!("{:?}", err),
"InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)"
);
}
#[test]
fn test_storage_version_query() {
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);
assert_eq!(get_storage_file_version("./flag.info").unwrap(), 1);
}
}