diff options
author | pommicket <pommicket@gmail.com> | 2025-09-08 17:46:54 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-08 17:46:54 -0400 |
commit | e588e013bed4e5c30aa5f605cc0973976e542258 (patch) | |
tree | bcad849a69e3b373f2554f494b110f034e08fdb4 /src | |
parent | 1632f44e99d8819b0fd5a279ec93f5601c8a7b06 (diff) |
Fix subkeys, write comparison test
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 7 | ||||
-rw-r--r-- | src/tests/mod.rs | 56 | ||||
-rw-r--r-- | src/tests/parsing.rs | 35 |
3 files changed, 97 insertions, 1 deletions
@@ -14,6 +14,9 @@ use alloc::{format, vec}; use core::fmt; use core::mem::take; +#[cfg(test)] +mod tests; + /// File and line information #[derive(Clone, Debug)] pub struct Location { @@ -635,6 +638,8 @@ impl Configuration { /// /// `reader` can be `&str`, `&[u8]`, or anything that implements [`std::io::BufRead`] /// (if the `std` feature is enabled) such as `std::io::BufReader<std::fs::File>`. + /// + /// `filename` is used in error messages. pub fn load<R: Read>(filename: &str, mut reader: R) -> Result<Self> { // avoid big code size by using dyn reference. // the impact on performance is not really important. @@ -717,7 +722,7 @@ impl Configuration { pub fn subkeys(&self, key: &str) -> impl '_ + Iterator<Item = &str> { let key_dot = format!("{key}."); let start_idx = self.first_subkey_index(key); - (start_idx..).map_while(move |i| { + (start_idx..self.items.len()).map_while(move |i| { let this_key = &self.items[i].0; let suffix = this_key.strip_prefix(&key_dot)?; Some(suffix.split_once('.').map_or(suffix, |(x, _)| x)) diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..58cd2b0 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,56 @@ +use crate::Configuration; +use std::collections::HashSet; + +mod parsing; + +fn check_configs_equal( + cfg1: &Configuration, + cfg2: &Configuration, +) -> Result<(), Box<dyn std::error::Error>> { + fn check_configs_equal_at( + cfg1: &Configuration, + cfg2: &Configuration, + prefix: &str, + ) -> Result<(), Box<dyn std::error::Error>> { + let (keys1, keys2): (HashSet<&str>, HashSet<&str>) = + if let Some(k) = prefix.strip_suffix('.') { + (cfg1.subkeys(k).collect(), cfg2.subkeys(k).collect()) + } else { + (cfg1.keys().collect(), cfg2.keys().collect()) + }; + for key in keys1.difference(&keys2) { + return Err(format!( + "Key {prefix}{key} appears at {}, but not in other configuration", + cfg1.location(&format!("{prefix}{key}")).unwrap() + ) + .into()); + } + for key in keys2.difference(&keys1) { + return Err(format!( + "Key {prefix}{key} appears at {}, but not in other configuration", + cfg2.location(&format!("{prefix}{key}")).unwrap() + ) + .into()); + } + for key in &keys1 { + let full_key = format!("{prefix}{key}"); + let val1 = cfg1.get(&full_key); + let val2 = cfg2.get(&full_key); + if val1 != val2 { + return Err(format!( + "Mismatch between values for key {full_key} + defined as {val1:?} at {}, +but defined as {val2:?} at {}", + cfg1.location(&full_key) + .map_or("<nowhere>".into(), |x| format!("{x}")), + cfg2.location(&full_key) + .map_or("<nowhere>".into(), |x| format!("{x}")), + ) + .into()); + } + check_configs_equal_at(cfg1, cfg2, &format!("{full_key}."))?; + } + Ok(()) + } + check_configs_equal_at(cfg1, cfg2, "") +} diff --git a/src/tests/parsing.rs b/src/tests/parsing.rs new file mode 100644 index 0000000..2e701ed --- /dev/null +++ b/src/tests/parsing.rs @@ -0,0 +1,35 @@ +use crate::Configuration; + +static TEST_DIR: &str = "../tests/parsing"; + +fn single_test(flat_name: &str, complex_name: &str) -> Result<(), Box<dyn std::error::Error>> { + let config1 = Configuration::load_path(format!("{TEST_DIR}/{flat_name}"))?; + let config2 = Configuration::load_path(format!("{TEST_DIR}/{complex_name}"))?; + super::check_configs_equal(&config1, &config2) +} + +fn try_parsing() -> Result<(), Box<dyn std::error::Error>> { + let tests = std::fs::read_dir(TEST_DIR).map_err(|e| { + format!( + "error reading {TEST_DIR}: {e} — try moving pom-rs to inside the main pom repository?" + ) + })?; + for test_entry in tests { + let filename = test_entry + .map_err(|e| format!("error reading tests directory {TEST_DIR}: {e}"))? + .file_name() + .into_string() + .expect("bad UTF-8 in test name (file in {TEST_DIR})"); + if filename.ends_with(".flat.pom") { + single_test(&filename, filename.replace(".flat.pom", ".pom").as_ref())?; + } + } + Ok(()) +} + +#[test] +fn parsing() { + if let Err(e) = try_parsing() { + panic!("Error: {e}"); + } +} |