diff options
author | pommicket <pommicket@gmail.com> | 2025-09-08 22:01:30 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-08 22:01:30 -0400 |
commit | e3a4ef53a75bc10129010cf18fb7ce68a6acbf2a (patch) | |
tree | 6d7aeabe48ca02f0f83ac6a59178f9b7ca6aadac | |
parent | af57af207e5106a47e50ae1ca6de7f746cfe8da6 (diff) |
Add iter(), more tests
-rwxr-xr-x | pre-commit.sh | 1 | ||||
-rw-r--r-- | src/lib.rs | 32 | ||||
-rw-r--r-- | src/tests/locations.rs | 24 | ||||
-rw-r--r-- | src/tests/mod.rs | 72 | ||||
-rw-r--r-- | src/tests/parsing.rs | 63 |
5 files changed, 118 insertions, 74 deletions
diff --git a/pre-commit.sh b/pre-commit.sh index 603bc0f..82f7404 100755 --- a/pre-commit.sh +++ b/pre-commit.sh @@ -4,4 +4,5 @@ cargo check || exit 1 cargo clippy -- -D warnings || exit 1 # Check that there are no errors with no_std cargo check --target=wasm32-unknown-unknown --no-default-features || exit 1 +cargo test || exit 1 git add -u || exit 1 @@ -689,7 +689,7 @@ impl Configuration { } } - /// Get the list of all “direct keys” in this configuration. + /// Get all “direct keys” in this configuration. /// /// More specifically, this returns an iterator of all unique /// first components of keys in `self`. @@ -712,6 +712,15 @@ impl Configuration { }) } + /// Get all defined keys in this configuration, including nested ones, + /// and their values. + /// + /// The order of items returned is arbitrary and may change + /// in future versions without notice. + pub fn iter(&self) -> ConfigurationIter { + self.into_iter() + } + /// Get the list of all “direct children” of `key` in this configuration. /// /// More specifically, this returns an iterator of all unique @@ -893,3 +902,24 @@ impl Configuration { todo!() } } + +/// Opaque type returned by [`<&Configuration>::into_iter`]. +#[derive(Clone, Debug)] +pub struct ConfigurationIter<'a>(core::slice::Iter<'a, (Box<str>, Value)>); + +impl<'a> Iterator for ConfigurationIter<'a> { + type Item = (&'a str, &'a str); + fn next(&mut self) -> Option<Self::Item> { + let (key, val) = self.0.next()?; + Some((key, val.value.as_ref())) + } +} + +impl<'a> IntoIterator for &'a Configuration { + type IntoIter = ConfigurationIter<'a>; + type Item = (&'a str, &'a str); + /// See [`Configuration::iter`]. + fn into_iter(self) -> Self::IntoIter { + ConfigurationIter(self.items.iter()) + } +} diff --git a/src/tests/locations.rs b/src/tests/locations.rs new file mode 100644 index 0000000..2310101 --- /dev/null +++ b/src/tests/locations.rs @@ -0,0 +1,24 @@ +use crate::Configuration; + +#[test] +fn parsing() { + super::do_tests_in_dir("location", ".locations.pom", |filename| { + let locations = Configuration::load_path(filename)?; + let config = Configuration::load_path(&filename.replace(".locations.pom", ".pom"))?; + for (key, val) in &locations { + let location = config.location(key).ok_or_else(|| { + format!("key {key} not found in main config but it exists in the location's config") + })?; + let got = location.line(); + let expected = val + .parse() + .map_err(|_| format!("key {key} in location POM should be an integer"))?; + if got != expected { + Err(format!( + "key {key} reported as being at line {got}, but it should be at line {expected}" + ))?; + } + } + Ok(()) + }) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 58cd2b0..83873dc 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,56 +1,32 @@ -use crate::Configuration; -use std::collections::HashSet; - +mod locations; 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() +fn do_tests_in_dir( + dir: &str, + ext: &str, + test: impl Fn(&str) -> Result<(), Box<dyn std::error::Error>>, +) { + let result = (|| -> Result<(), Box<dyn std::error::Error>> { + let test_dir = format!("../tests/{dir}"); + 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?" ) - .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()); + })?; + 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(ext) { + println!("Running test {dir}/{filename}..."); + test(&format!("{test_dir}/{filename}"))? } - check_configs_equal_at(cfg1, cfg2, &format!("{full_key}."))?; } Ok(()) + })(); + if let Err(e) = result { + panic!("Error: {e}"); } - check_configs_equal_at(cfg1, cfg2, "") } diff --git a/src/tests/parsing.rs b/src/tests/parsing.rs index eb98a04..97cf711 100644 --- a/src/tests/parsing.rs +++ b/src/tests/parsing.rs @@ -1,28 +1,39 @@ use crate::Configuration; +use std::collections::HashSet; -static TEST_DIR: &str = "../tests/parsing"; - -fn single_test(flat_name: &str, complex_name: &str) -> Result<(), Box<dyn std::error::Error>> { - println!("Running test {complex_name}..."); - 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?" +fn check_configs_equal( + cfg1: &Configuration, + cfg2: &Configuration, +) -> Result<(), Box<dyn std::error::Error>> { + let keys1: HashSet<&str> = cfg1.iter().map(|(k, _)| k).collect(); + let keys2: HashSet<&str> = cfg2.iter().map(|(k, _)| k).collect(); + for key in keys1.difference(&keys2) { + return Err(format!( + "Key {key} appears at {}, but not in other configuration", + cfg1.location(&key).unwrap() ) - })?; - 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())?; + .into()); + } + for key in keys2.difference(&keys1) { + return Err(format!( + "Key {key} appears at {}, but not in other configuration", + cfg2.location(&key).unwrap() + ) + .into()); + } + for (key, val1) in cfg1.iter() { + let val2 = cfg2.get(&key).unwrap(); + if val1 != val2 { + return Err(format!( + "Mismatch between values for key {key} +defined as {val1:?} at {}, +but defined as {val2:?} at {}", + cfg1.location(&key) + .map_or("<nowhere>".into(), |x| format!("{x}")), + cfg2.location(&key) + .map_or("<nowhere>".into(), |x| format!("{x}")), + ) + .into()); } } Ok(()) @@ -30,7 +41,9 @@ fn try_parsing() -> Result<(), Box<dyn std::error::Error>> { #[test] fn parsing() { - if let Err(e) = try_parsing() { - panic!("Error: {e}"); - } + super::do_tests_in_dir("parsing", ".flat.pom", |filename| { + let config1 = Configuration::load_path(filename)?; + let config2 = Configuration::load_path(&filename.replace(".flat.pom", ".pom"))?; + check_configs_equal(&config1, &config2) + }) } |