Merge "aconfig: create aconfig_storage_file binary to print the storage files" into main
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 6be3c19..1d74f69 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -10,6 +10,7 @@
rustlibs: [
"libanyhow",
"libthiserror",
+ "libtempfile",
],
}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index c4e2670..6a9483e 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -15,6 +15,7 @@
tempfile = "3.9.0"
cxx = "1.0"
thiserror = "1.0.56"
+clap = { version = "4.1.8", features = ["derive"] }
[build-dependencies]
protobuf-codegen = "3.2.0"
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index 61cf371..d6e5c62 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -20,9 +20,10 @@
use crate::AconfigStorageError::{self, BytesParseFail};
use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
use anyhow::anyhow;
+use std::fmt;
/// Flag table header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagTableHeader {
pub version: u32,
pub container: String,
@@ -32,6 +33,23 @@
pub node_offset: u32,
}
+/// Implement debug print trait for header
+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
+ )?;
+ writeln!(
+ f,
+ "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}",
+ self.num_flags, self.bucket_offset, self.node_offset
+ )?;
+ Ok(())
+ }
+}
+
impl FlagTableHeader {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -62,7 +80,7 @@
}
/// Flag table node struct
-#[derive(PartialEq, Debug, Clone)]
+#[derive(PartialEq, Clone)]
pub struct FlagTableNode {
pub package_id: u32,
pub flag_name: String,
@@ -71,6 +89,18 @@
pub next_offset: Option<u32>,
}
+/// Implement debug print trait for node
+impl fmt::Debug for FlagTableNode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Package Id: {}, Flag: {}, Type: {}, Offset: {}, Next: {:?}",
+ self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset
+ )?;
+ Ok(())
+ }
+}
+
impl FlagTableNode {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -108,13 +138,28 @@
}
}
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagTable {
pub header: FlagTableHeader,
pub buckets: Vec<Option<u32>>,
pub nodes: Vec<FlagTableNode>,
}
+/// Implement debug print trait for flag table
+impl fmt::Debug for FlagTable {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Header:")?;
+ write!(f, "{:?}", self.header)?;
+ writeln!(f, "Buckets:")?;
+ writeln!(f, "{:?}", self.buckets)?;
+ writeln!(f, "Nodes:")?;
+ for node in self.nodes.iter() {
+ write!(f, "{:?}", node)?;
+ }
+ Ok(())
+ }
+}
+
/// Flag table struct
impl FlagTable {
/// Serialize to bytes
@@ -156,60 +201,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- impl FlagTableNode {
- // create test baseline, syntactic sugar
- fn new_expected(
- package_id: u32,
- flag_name: &str,
- flag_type: u16,
- flag_id: u16,
- next_offset: Option<u32>,
- ) -> Self {
- Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
- }
- }
-
- pub fn create_test_flag_table() -> FlagTable {
- let header = FlagTableHeader {
- version: 1234,
- container: String::from("system"),
- file_size: 320,
- num_flags: 8,
- bucket_offset: 30,
- node_offset: 98,
- };
- let buckets: Vec<Option<u32>> = vec![
- Some(98),
- Some(124),
- None,
- None,
- None,
- Some(177),
- None,
- Some(203),
- None,
- Some(261),
- None,
- None,
- None,
- None,
- None,
- Some(293),
- None,
- ];
- let nodes = vec![
- FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
- FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
- FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
- FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
- FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
- FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
- FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
- FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
- ];
- FlagTable { header, buckets, nodes }
- }
+ use crate::test_utils::create_test_flag_table;
#[test]
// this test point locks down the table serialization
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
index 62e94ef..021546c 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -19,9 +19,10 @@
use crate::AconfigStorageError;
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use std::fmt;
/// Flag value header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagValueHeader {
pub version: u32,
pub container: String,
@@ -30,6 +31,23 @@
pub boolean_value_offset: u32,
}
+/// Implement debug print trait for header
+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
+ )?;
+ writeln!(
+ f,
+ "Num of Flags: {}, Value Offset:{}",
+ self.num_flags, self.boolean_value_offset
+ )?;
+ Ok(())
+ }
+}
+
impl FlagValueHeader {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -58,12 +76,23 @@
}
/// Flag value list struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagValueList {
pub header: FlagValueHeader,
pub booleans: Vec<bool>,
}
+/// Implement debug print trait for flag value
+impl fmt::Debug for FlagValueList {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Header:")?;
+ write!(f, "{:?}", self.header)?;
+ writeln!(f, "Values:")?;
+ writeln!(f, "{:?}", self.booleans)?;
+ Ok(())
+ }
+}
+
impl FlagValueList {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -89,18 +118,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- pub fn create_test_flag_value_list() -> FlagValueList {
- let header = FlagValueHeader {
- version: 1234,
- container: String::from("system"),
- file_size: 34,
- num_flags: 8,
- boolean_value_offset: 26,
- };
- let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
- FlagValueList { header, booleans }
- }
+ use crate::test_utils::create_test_flag_value_list;
#[test]
// this test point locks down the value list serialization
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index e06e149..7544838 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -36,9 +36,14 @@
pub mod flag_value;
pub mod package_table;
+#[cfg(test)]
+mod test_utils;
+
use anyhow::anyhow;
use std::collections::hash_map::DefaultHasher;
+use std::fs::File;
use std::hash::{Hash, Hasher};
+use std::io::Read;
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
@@ -166,4 +171,88 @@
#[error("invalid storage file byte offset")]
InvalidStorageFileOffset(#[source] anyhow::Error),
+
+ #[error("failed to create file")]
+ FileCreationFail(#[source] anyhow::Error),
+}
+
+/// Read in storage file as bytes
+pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
+ let mut file = File::open(file_path).map_err(|errmsg| {
+ AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+ })?;
+ let mut buffer = Vec::new();
+ file.read_to_end(&mut buffer).map_err(|errmsg| {
+ AconfigStorageError::FileReadFail(anyhow!(
+ "Failed to read 4 bytes from file {}: {}",
+ file_path,
+ errmsg
+ ))
+ })?;
+ Ok(buffer)
+}
+
+/// List flag values from storage files
+pub fn list_flags(
+ package_map: &str,
+ flag_map: &str,
+ flag_val: &str,
+) -> Result<Vec<(String, bool)>, 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 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_offset);
+ }
+
+ 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 full_flag_name = String::from(package_name) + "/" + &node.flag_name;
+ let flag_offset = package_offset + node.flag_id as u32;
+ let flag_value = flag_value_list.booleans[flag_offset as usize];
+ flags.push((full_flag_name, flag_value));
+ }
+
+ flags.sort_by(|v1, v2| v1.0.cmp(&v2.0));
+ Ok(flags)
+}
+
+#[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,
+ };
+
+ #[test]
+ // this test point locks down the flag list api
+ fn test_list_flag() {
+ let package_table =
+ write_bytes_to_temp_file(&create_test_package_table().as_bytes()).unwrap();
+ let flag_table = write_bytes_to_temp_file(&create_test_flag_table().as_bytes()).unwrap();
+ let flag_value_list =
+ write_bytes_to_temp_file(&create_test_flag_value_list().as_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 flags =
+ list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
+ let expected = [
+ (String::from("com.android.aconfig.storage.test_1/disabled_rw"), false),
+ (String::from("com.android.aconfig.storage.test_1/enabled_ro"), true),
+ (String::from("com.android.aconfig.storage.test_1/enabled_rw"), false),
+ (String::from("com.android.aconfig.storage.test_2/disabled_ro"), false),
+ (String::from("com.android.aconfig.storage.test_2/enabled_fixed_ro"), true),
+ (String::from("com.android.aconfig.storage.test_2/enabled_ro"), true),
+ (String::from("com.android.aconfig.storage.test_4/enabled_fixed_ro"), false),
+ (String::from("com.android.aconfig.storage.test_4/enabled_ro"), true),
+ ];
+ assert_eq!(flags, expected);
+ }
}
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
new file mode 100644
index 0000000..2c7b87c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+//! `aconfig_storage` is a debugging tool to parse storage files
+
+use aconfig_storage_file::{
+ list_flags, read_file_to_bytes, AconfigStorageError, FlagTable, FlagValueList, PackageTable,
+ StorageFileSelection,
+};
+
+use clap::{builder::ArgAction, Arg, Command};
+
+fn cli() -> Command {
+ Command::new("aconfig_storage_file")
+ .subcommand_required(true)
+ .subcommand(
+ Command::new("print")
+ .arg(Arg::new("file").long("file").required(true).action(ArgAction::Set))
+ .arg(
+ Arg::new("type")
+ .long("type")
+ .required(true)
+ .value_parser(|s: &str| StorageFileSelection::try_from(s)),
+ ),
+ )
+ .subcommand(
+ Command::new("list")
+ .arg(
+ Arg::new("package_map")
+ .long("package_map")
+ .required(true)
+ .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)),
+ )
+}
+
+fn print_storage_file(
+ file_path: &str,
+ file_type: &StorageFileSelection,
+) -> Result<(), AconfigStorageError> {
+ let bytes = read_file_to_bytes(file_path)?;
+ match file_type {
+ StorageFileSelection::PackageMap => {
+ let package_table = PackageTable::from_bytes(&bytes)?;
+ println!("{:?}", package_table);
+ }
+ StorageFileSelection::FlagMap => {
+ let flag_table = FlagTable::from_bytes(&bytes)?;
+ println!("{:?}", flag_table);
+ }
+ StorageFileSelection::FlagVal => {
+ let flag_value = FlagValueList::from_bytes(&bytes)?;
+ println!("{:?}", flag_value);
+ }
+ }
+ Ok(())
+}
+
+fn main() -> Result<(), AconfigStorageError> {
+ let matches = cli().get_matches();
+ 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();
+ print_storage_file(file_path, file_type)?
+ }
+ Some(("list", sub_matches)) => {
+ 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 flag in flags.iter() {
+ println!("{}: {}", flag.0, flag.1);
+ }
+ }
+ _ => unreachable!(),
+ }
+ Ok(())
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index 1c21179..f7435b0 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -20,9 +20,10 @@
use crate::AconfigStorageError::{self, BytesParseFail};
use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes};
use anyhow::anyhow;
+use std::fmt;
/// Package table header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct PackageTableHeader {
pub version: u32,
pub container: String,
@@ -32,6 +33,23 @@
pub node_offset: u32,
}
+/// Implement debug print trait for header
+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
+ )?;
+ writeln!(
+ f,
+ "Num of Packages: {}, Bucket Offset:{}, Node Offset: {}",
+ self.num_packages, self.bucket_offset, self.node_offset
+ )?;
+ Ok(())
+ }
+}
+
impl PackageTableHeader {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -62,7 +80,7 @@
}
/// Package table node struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct PackageTableNode {
pub package_name: String,
pub package_id: u32,
@@ -72,6 +90,18 @@
pub next_offset: Option<u32>,
}
+/// Implement debug print trait for node
+impl fmt::Debug for PackageTableNode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Package: {}, Id: {}, Offset: {}, Next: {:?}",
+ self.package_name, self.package_id, self.boolean_offset, self.next_offset
+ )?;
+ Ok(())
+ }
+}
+
impl PackageTableNode {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -109,13 +139,28 @@
}
/// Package table struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct PackageTable {
pub header: PackageTableHeader,
pub buckets: Vec<Option<u32>>,
pub nodes: Vec<PackageTableNode>,
}
+/// Implement debug print trait for package table
+impl fmt::Debug for PackageTable {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Header:")?;
+ write!(f, "{:?}", self.header)?;
+ writeln!(f, "Buckets:")?;
+ writeln!(f, "{:?}", self.buckets)?;
+ writeln!(f, "Nodes:")?;
+ for node in self.nodes.iter() {
+ write!(f, "{:?}", node)?;
+ }
+ Ok(())
+ }
+}
+
impl PackageTable {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -156,38 +201,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- pub fn create_test_package_table() -> PackageTable {
- let header = PackageTableHeader {
- version: 1234,
- container: String::from("system"),
- file_size: 208,
- num_packages: 3,
- bucket_offset: 30,
- node_offset: 58,
- };
- let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
- let first_node = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_2"),
- package_id: 1,
- boolean_offset: 3,
- next_offset: None,
- };
- let second_node = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_1"),
- package_id: 0,
- boolean_offset: 0,
- next_offset: Some(158),
- };
- let third_node = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_4"),
- package_id: 2,
- boolean_offset: 6,
- next_offset: None,
- };
- let nodes = vec![first_node, second_node, third_node];
- PackageTable { header, buckets, nodes }
- }
+ use crate::test_utils::create_test_package_table;
#[test]
// this test point locks down the table serialization
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
new file mode 100644
index 0000000..7780044
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
+use crate::flag_value::{FlagValueHeader, FlagValueList};
+use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
+use crate::AconfigStorageError;
+
+use anyhow::anyhow;
+use std::io::Write;
+use tempfile::NamedTempFile;
+
+pub(crate) fn create_test_package_table() -> PackageTable {
+ let header = PackageTableHeader {
+ version: 1234,
+ container: String::from("system"),
+ file_size: 208,
+ num_packages: 3,
+ bucket_offset: 30,
+ node_offset: 58,
+ };
+ let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
+ let first_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_2"),
+ package_id: 1,
+ boolean_offset: 3,
+ next_offset: None,
+ };
+ let second_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_1"),
+ package_id: 0,
+ boolean_offset: 0,
+ next_offset: Some(158),
+ };
+ let third_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_4"),
+ package_id: 2,
+ boolean_offset: 6,
+ next_offset: None,
+ };
+ let nodes = vec![first_node, second_node, third_node];
+ PackageTable { header, buckets, nodes }
+}
+
+impl FlagTableNode {
+ // create test baseline, syntactic sugar
+ fn new_expected(
+ package_id: u32,
+ flag_name: &str,
+ flag_type: u16,
+ flag_id: u16,
+ next_offset: Option<u32>,
+ ) -> Self {
+ Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
+ }
+}
+
+pub(crate) fn create_test_flag_table() -> FlagTable {
+ let header = FlagTableHeader {
+ version: 1234,
+ container: String::from("system"),
+ file_size: 320,
+ num_flags: 8,
+ bucket_offset: 30,
+ node_offset: 98,
+ };
+ let buckets: Vec<Option<u32>> = vec![
+ Some(98),
+ Some(124),
+ None,
+ None,
+ None,
+ Some(177),
+ None,
+ Some(203),
+ None,
+ Some(261),
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(293),
+ None,
+ ];
+ let nodes = vec![
+ FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
+ FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
+ FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
+ FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
+ FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
+ FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
+ ];
+ FlagTable { header, buckets, nodes }
+}
+
+pub(crate) fn create_test_flag_value_list() -> FlagValueList {
+ let header = FlagValueHeader {
+ version: 1234,
+ container: String::from("system"),
+ file_size: 34,
+ num_flags: 8,
+ boolean_value_offset: 26,
+ };
+ let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+ FlagValueList { header, booleans }
+}
+
+pub(crate) fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {
+ let mut file = NamedTempFile::new().map_err(|_| {
+ AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
+ })?;
+ let _ = file.write_all(&bytes);
+ Ok(file)
+}