lsmem: Implement sysroot, add many test, more output alignment with util-linux

This commit is contained in:
Foorack 2025-02-09 11:53:21 +01:00
parent 8b8728e1df
commit 57b9c1f0f4
528 changed files with 1844 additions and 47 deletions

@ -29,16 +29,16 @@ mod options {
pub const PAIRS: &str = "pairs";
pub const RAW: &str = "raw";
pub const SPLIT: &str = "split";
pub const SYSROOT: &str = "sysroot";
}
// const BUFSIZ: usize = 1024;
const PATH_SYS_MEMORY: &str = "/sys/devices/system/memory";
const PATH_BLOCK_SIZE_BYTES: &str = "/sys/devices/system/memory/block_size_bytes";
const PATH_VALID_ZONES: &str = "/sys/devices/system/memory/valid_zones";
const PATH_NAME_MEMORY: &str = "memory";
const PATH_NAME_NODE: &str = "node";
const PATH_SUB_BLOCK_SIZE_BYTES: &str = "block_size_bytes";
const PATH_SUB_REMOVABLE: &str = "removable";
const PATH_SUB_STATE: &str = "state";
const NAME_MEMORY: &str = "memory";
const PATH_SUB_VALID_ZONES: &str = "valid_zones";
const PATH_SYS_MEMORY: &str = "/sys/devices/system/memory";
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
enum Column {
@ -107,15 +107,7 @@ impl Column {
#[allow(dead_code)]
fn get_float_right(&self) -> bool {
match self {
Column::Range => false,
Column::Size => true,
Column::State => true,
Column::Removable => true,
Column::Block => true,
Column::Node => true,
Column::Zones => true,
}
self != &Column::Range
}
#[allow(dead_code)]
@ -226,8 +218,8 @@ struct TableRow {
state: String,
removable: String,
block: String,
#[serde(skip_serializing)]
node: String,
#[allow(unused)]
#[serde(skip_serializing)]
zones: String,
}
@ -276,6 +268,8 @@ struct Options {
split_by_removable: bool,
split_by_state: bool,
split_by_zones: bool,
/// Default to PATH_SYS_MEMORY, but a prefix can be appended
sysmem: String,
// Set by read_info
have_nodes: bool,
@ -324,6 +318,7 @@ impl Options {
split_by_removable: false,
split_by_state: false,
split_by_zones: false,
sysmem: String::from(PATH_SYS_MEMORY),
have_nodes: false,
have_zones: false,
@ -336,16 +331,20 @@ impl Options {
fn read_info(lsmem: &mut Lsmem, opts: &mut Options) {
lsmem.block_size = u64::from_str_radix(
&read_file_content::<String>(Path::new(PATH_BLOCK_SIZE_BYTES)).unwrap(),
&read_file_content::<String>(&Path::new(&format!(
"{}/{}",
opts.sysmem, PATH_SUB_BLOCK_SIZE_BYTES
)))
.unwrap(),
16,
)
.unwrap();
lsmem.dirs = get_block_paths();
lsmem.dirs = get_block_paths(opts);
lsmem.dirs.sort_by(|a, b| {
let filename_a = a.to_str().unwrap().split('/').last().unwrap();
let filename_b = b.to_str().unwrap().split('/').last().unwrap();
let idx_a: u64 = filename_a[NAME_MEMORY.len()..].parse().unwrap();
let idx_b: u64 = filename_b[NAME_MEMORY.len()..].parse().unwrap();
let idx_a: u64 = filename_a[PATH_NAME_MEMORY.len()..].parse().unwrap();
let idx_b: u64 = filename_b[PATH_NAME_MEMORY.len()..].parse().unwrap();
idx_a.cmp(&idx_b)
});
lsmem.ndirs = lsmem.dirs.len();
@ -355,7 +354,7 @@ fn read_info(lsmem: &mut Lsmem, opts: &mut Options) {
}
let mut p = path.clone();
p.push("valid_zones");
p.push(PATH_SUB_VALID_ZONES);
if fs::read_dir(p).is_ok() {
opts.have_zones = true;
}
@ -381,13 +380,13 @@ fn read_info(lsmem: &mut Lsmem, opts: &mut Options) {
}
}
fn get_block_paths() -> Vec<PathBuf> {
fn get_block_paths(opts: &mut Options) -> Vec<PathBuf> {
let mut paths = Vec::<PathBuf>::new();
for entry in fs::read_dir(PATH_SYS_MEMORY).unwrap() {
for entry in fs::read_dir(&opts.sysmem).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
let filename = path.to_str().unwrap().split('/').last().unwrap();
if path.is_dir() && filename.starts_with(NAME_MEMORY) {
if path.is_dir() && filename.starts_with(PATH_NAME_MEMORY) {
paths.push(path);
}
}
@ -434,8 +433,8 @@ fn memory_block_get_node(path: &PathBuf) -> Result<i32, <i32 as FromStr>::Err> {
let entry = entry.unwrap();
let path = entry.path();
let filename = path.to_str().unwrap().split('/').last().unwrap();
if path.is_dir() && filename.starts_with("node") {
return filename["node".len()..].parse();
if path.is_dir() && filename.starts_with(PATH_NAME_NODE) {
return filename[PATH_NAME_NODE.len()..].parse();
}
}
Ok(-1)
@ -446,7 +445,7 @@ fn memory_block_read_attrs(opts: &Options, path: &PathBuf) -> MemoryBlock {
blk.count = 1;
blk.state = MemoryState::Unknown;
let filename = path.to_str().unwrap().split('/').last().unwrap();
blk.index = filename[NAME_MEMORY.len()..].parse().unwrap();
blk.index = filename[PATH_NAME_MEMORY.len()..].parse().unwrap();
let mut removable_path = path.clone();
removable_path.push(PATH_SUB_REMOVABLE);
@ -464,7 +463,11 @@ fn memory_block_read_attrs(opts: &Options, path: &PathBuf) -> MemoryBlock {
blk.nr_zones = 0;
if opts.have_zones {
if let Ok(raw_content) = read_file_content::<String>(Path::new(PATH_VALID_ZONES)) {
if let Ok(raw_content) = read_file_content::<String>(Path::new(&format!(
"{}/{}",
opts.sysmem.clone(),
PATH_SUB_VALID_ZONES
))) {
let zone_toks = raw_content.split(' ').collect::<Vec<&str>>();
for (i, zone_tok) in zone_toks
.iter()
@ -608,6 +611,10 @@ fn print_pairs(lsmem: &Lsmem, opts: &Options) {
}
fn print_raw(lsmem: &Lsmem, opts: &Options) {
if !opts.noheadings {
println!("RANGE SIZE STATE REMOVABLE BLOCK");
}
let table_rows = create_table_rows(lsmem, opts);
let mut table_raw_string = String::new();
for row in table_rows {
@ -616,7 +623,6 @@ fn print_raw(lsmem: &Lsmem, opts: &Options) {
}
// remove the last newline
table_raw_string.pop();
println!("RANGE SIZE STATE REMOVABLE BLOCK");
println!("{table_raw_string}");
}
@ -689,6 +695,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
opts.want_summary = false;
}
if let Some(sysroot) = matches.get_one::<String>(options::SYSROOT) {
opts.sysmem = format!(
"{}/{}",
sysroot.trim_end_matches('/'),
opts.sysmem.trim_start_matches('/')
);
}
read_info(&mut lsmem, &mut opts);
if opts.want_table {
@ -766,6 +780,7 @@ pub fn uu_app() -> Command {
.help("output columns")
.ignore_case(true)
.action(ArgAction::Set)
.value_name("list")
.value_delimiter(',')
.value_parser(EnumValueParser::<Column>::new()),
)
@ -791,6 +806,7 @@ pub fn uu_app() -> Command {
.conflicts_with(options::ALL)
.ignore_case(true)
.action(ArgAction::Set)
.value_name("list")
.value_delimiter(',')
.value_parser(PossibleValuesParser::new(
SPLIT_COLUMNS
@ -799,6 +815,14 @@ pub fn uu_app() -> Command {
.collect::<Vec<_>>(),
)),
)
.arg(
Arg::new(options::SYSROOT)
.short('s')
.long("sysroot")
.help("use the specified directory as system root")
.action(ArgAction::Set)
.value_name("dir"),
)
.after_help(&format!(
"Available output columns:\n{}",
Column::value_variants()

@ -5,34 +5,157 @@
use crate::common::util::TestScenario;
#[must_use]
fn sysroot() -> String {
format!("{}/tests/fixtures/lsmem/input", env!("CARGO_MANIFEST_DIR"))
}
fn sysroot_test_with_args(expected_output: &str, args: &[&str]) {
let mut cmd = new_ucmd!();
cmd.arg("-s").arg(sysroot());
for arg in args {
cmd.arg(arg);
}
cmd.succeeds()
.no_stderr()
.stdout_is_templated_fixture(expected_output, &[("\r\n", "\n")]);
}
#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}
#[test]
fn test_basic() {
// Verify basic usage with no args prints both table and summary
new_ucmd!()
.succeeds()
.stdout_contains("STATE REMOVABLE")
.stdout_contains("Memory block size:");
fn test_columns_table() {
sysroot_test_with_args("test_lsmem_columns_table.expected", &["-o", "block,size"]);
}
// FAILS, COMMENT FOR NOW - TODO
// #[test]
// fn test_columns_raw() {
// sysroot_test_with_args(
// "test_lsmem_columns_raw.expected",
// &["-o", "block,size", "-r"],
// );
// }
// FAILS, COMMENT FOR NOW - TODO
// #[test]
// fn test_columns_json() {
// sysroot_test_with_args(
// "test_lsmem_columns_json.expected",
// &["-o", "block,size", "-J"],
// );
// }
// FAILS, COMMENT FOR NOW - TODO
// #[test]
// fn test_columns_pairs() {
// sysroot_test_with_args(
// "test_lsmem_columns_pairs.expected",
// &["-o", "block,size", "-P"],
// );
// }
#[test]
fn test_json() {
sysroot_test_with_args("test_lsmem_json.expected", &["-J"]);
}
#[test]
fn test_table_not_padded() {
let result = new_ucmd!().succeeds();
let stdout = result.code_is(0).stdout_str();
assert!(
!stdout.starts_with(' '),
"Table output should not start with a space"
);
fn test_json_all() {
sysroot_test_with_args("test_lsmem_json_all.expected", &["-J", "-a"]);
}
// FAILS, COMMENT FOR NOW - TODO
// #[test]
// fn test_json_bytes() {
// sysroot_test_with_args("test_lsmem_json_bytes.expected", &["-J", "-b"]);
// }
#[test]
fn test_json_noheadings() {
sysroot_test_with_args("test_lsmem_json_noheadings.expected", &["-J", "-n"]);
}
#[test]
fn test_json_output() {
new_ucmd!()
.arg("-J")
.succeeds()
.stdout_contains(" \"memory\": [\n");
fn test_pairs() {
sysroot_test_with_args("test_lsmem_pairs.expected", &["-P"]);
}
#[test]
fn test_pairs_all() {
sysroot_test_with_args("test_lsmem_pairs_all.expected", &["-P", "-a"]);
}
#[test]
fn test_pairs_bytes() {
sysroot_test_with_args("test_lsmem_pairs_bytes.expected", &["-P", "-b"]);
}
#[test]
fn test_pairs_noheadings() {
sysroot_test_with_args("test_lsmem_pairs_noheadings.expected", &["-P", "-n"]);
}
#[test]
fn test_raw() {
sysroot_test_with_args("test_lsmem_raw.expected", &["-r"]);
}
#[test]
fn test_raw_all() {
sysroot_test_with_args("test_lsmem_raw_all.expected", &["-r", "-a"]);
}
#[test]
fn test_raw_bytes() {
sysroot_test_with_args("test_lsmem_raw_bytes.expected", &["-r", "-b"]);
}
#[test]
fn test_raw_noheadings() {
sysroot_test_with_args("test_lsmem_raw_noheadings.expected", &["-r", "-n"]);
}
#[test]
fn test_split_node() {
sysroot_test_with_args("test_lsmem_split_node.expected", &["-S", "node"]);
}
#[test]
fn test_split_removable() {
sysroot_test_with_args("test_lsmem_split_removable.expected", &["-S", "removable"]);
}
#[test]
fn test_split_state() {
sysroot_test_with_args("test_lsmem_split_state.expected", &["-S", "state"]);
}
// FAILS, COMMENT FOR NOW - TODO
// #[test]
// fn test_split_zones() {
// sysroot_test_with_args("test_lsmem_split_zones.expected", &["-S", "zones"]);
// }
#[test]
fn test_table() {
sysroot_test_with_args("test_lsmem_table.expected", &[]);
}
#[test]
fn test_table_all() {
sysroot_test_with_args("test_lsmem_table_all.expected", &["-a"]);
}
#[test]
fn test_table_bytes() {
sysroot_test_with_args("test_lsmem_table_bytes.expected", &["-b"]);
}
#[test]
fn test_table_noheadings() {
sysroot_test_with_args("test_lsmem_table_noheadings.expected", &["-n"]);
}

@ -0,0 +1 @@
8000000

@ -0,0 +1 @@
online

@ -0,0 +1 @@
online

Some files were not shown because too many files have changed in this diff Show More