diff options
author | pommicket <pommicket@gmail.com> | 2025-09-09 11:42:23 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-09 11:42:23 -0400 |
commit | baf62a2566aa87eed42081e0fd881e89cb721415 (patch) | |
tree | 7e74434bb0def05d9f188b105d6dc771c4c11375 | |
parent | 9c72ccfe61df5cc2ef05f2982b748d73ff28b406 (diff) |
Start interpretation tests
-rw-r--r-- | src/lib.rs | 40 | ||||
-rw-r--r-- | src/tests/interpretation.rs | 61 | ||||
-rw-r--r-- | src/tests/mod.rs | 1 |
3 files changed, 79 insertions, 23 deletions
@@ -195,27 +195,17 @@ pub type Result<T> = core::result::Result<T, Error>; fn parse_int(location: &Location, string: &str) -> Result<i64> { let bad_int = || Error::BadInt(location.clone(), string.into()); - if !string - .bytes() - .all(|c| c.is_ascii_hexdigit() || c == b'x' || c == b'X' || c == b'-' || c == b'+') - { - return Err(bad_int()); + let mut sign = "+"; + let mut signless = string; + if let Some(s) = string.strip_prefix(['-', '+']) { + sign = &string[..1]; + signless = s; } - let signless = string.strip_prefix(['-', '+']).unwrap_or(string); - let mut base = 10; - let baseless = signless - .strip_prefix("0x") - .or_else(|| signless.strip_prefix("0X")) - .unwrap_or_else(|| { - base = 16; - signless - }); - for digit in baseless.bytes() { - if base == 10 && !digit.is_ascii_digit() { - return Err(bad_int()); - } + if signless.starts_with('+') { + return Err(bad_int()); } - string.parse().map_err(|_| bad_int()) + let uint = parse_uint(location, signless).map_err(|_| bad_int())? as i64; + if sign == "-" { Ok(-uint) } else { Ok(uint) } } fn parse_uint(location: &Location, string: &str) -> Result<u64> { @@ -227,20 +217,24 @@ fn parse_uint(location: &Location, string: &str) -> Result<u64> { return Err(bad_uint()); } let signless = string.strip_prefix('+').unwrap_or(string); - let mut base = 10; + let mut base = 16; let baseless = signless .strip_prefix("0x") .or_else(|| signless.strip_prefix("0X")) .unwrap_or_else(|| { - base = 16; + base = 10; signless }); + if baseless.len() > 1 && baseless.starts_with('0') && base == 10 { + // decimal leading zeroes are not allowed + return Err(bad_uint()); + } for digit in baseless.bytes() { if base == 10 && !digit.is_ascii_digit() { return Err(bad_uint()); } } - let val = signless.parse().map_err(|_| bad_uint())?; + let val = u64::from_str_radix(baseless, base).map_err(|_| bad_uint())?; if val > i64::MAX as u64 { return Err(bad_uint()); } @@ -648,7 +642,7 @@ impl Parser { impl Configuration { /// Load a configuration. /// - /// `reader` can be `&str`, `&[u8]`, or anything that implements [`std::io::BufRead`] + /// `reader` can be `&[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. diff --git a/src/tests/interpretation.rs b/src/tests/interpretation.rs new file mode 100644 index 0000000..64106af --- /dev/null +++ b/src/tests/interpretation.rs @@ -0,0 +1,61 @@ +use crate::Configuration; +use std::error::Error; +use std::fmt::Display; + +fn do_list_test(_cfg: &Configuration) -> Result<(), Box<dyn Error>> { + todo!() +} + +fn do_goodbad_test<I: PartialEq + Display>( + cfg: &Configuration, + name: &str, + parser: impl Fn(&Configuration, &str) -> Option<crate::Result<I>>, +) -> Result<(), Box<dyn Error>> { + let good = cfg.section("good"); + for key in good.keys() { + let [a_value, b_value] = ["a", "b"].map(|c| { + parser(&good, &format!("{key}.{c}")) + .ok_or_else(|| format!("expected to find key good.{key}.{c}"))? + .map_err(|e| format!("parsing good.{key}.{c} failed: {e}")) + }); + let a_value = a_value?; + let b_value = b_value?; + if a_value != b_value { + Err(format!( + "{name} incorrectly parsed: good.{key}.a = {a_value}, but good.{key}.b = {b_value}" + ))?; + } + } + let bad = cfg.section("bad"); + for (key, val) in bad.iter() { + if parser(&bad, key) + .ok_or_else(|| format!("parsing bad.{key} failed: {key} not found"))? + .is_ok() + { + Err(format!( + "bad.{key} value {val:?} should be rejected as a(n) {name} but it wasn't" + ))?; + } + } + Ok(()) +} + +#[test] +fn interpretation() { + super::do_tests_in_dir("interpretation", ".pom", |filename| { + let cfg = Configuration::load_path(filename)?; + if filename.ends_with("list.pom") { + do_list_test(&cfg) + } else if filename.ends_with("uint.pom") { + do_goodbad_test(&cfg, "uint", Configuration::get_uint) + } else if filename.ends_with("int.pom") { + do_goodbad_test(&cfg, "int", Configuration::get_int) + } else if filename.ends_with("float.pom") { + do_goodbad_test(&cfg, "float", Configuration::get_float) + } else if filename.ends_with("bool.pom") { + do_goodbad_test(&cfg, "bool", Configuration::get_bool) + } else { + panic!("unrecognized test: {filename}") + } + }) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 644c131..6c40f6f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,4 +1,5 @@ mod errors; +mod interpretation; mod locations; mod parsing; |