diff --git a/Cargo.lock b/Cargo.lock index 5b3f3ec84..ef7b6b5e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,6 +389,8 @@ version = "0.10.0-rc.2" dependencies = [ "arbitrary", "hex-literal", + "proptest", + "regex", ] [[package]] diff --git a/const-oid/Cargo.toml b/const-oid/Cargo.toml index 50e7a878b..df6ea43c9 100644 --- a/const-oid/Cargo.toml +++ b/const-oid/Cargo.toml @@ -22,6 +22,8 @@ arbitrary = { version = "1.2", optional = true, features = ["derive"] } [dev-dependencies] hex-literal = "0.4" +proptest = "1" +regex = "1" [features] db = [] diff --git a/const-oid/tests/proptests.proptest-regressions b/const-oid/tests/proptests.proptest-regressions new file mode 100644 index 000000000..805e18453 --- /dev/null +++ b/const-oid/tests/proptests.proptest-regressions @@ -0,0 +1,13 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 1663923d2fb0c804c5b850d10dd0ded1cbfc06dddf3f88faa4abf149b8430831 # shrinks to s = "" +cc 829ba8833ee42816bc33d308b7a186452e36617f0fa0e771edea08fd07d78718 # shrinks to s = "0.40" +cc bc88f232a7e2e45d2b1325d4e02c09742aca7ad31903326fedb36c047533696c # shrinks to s = "0" +cc d90305406041ea5e4cf4d9e7849cad03391db1869d0b1329f60ccbf1fabaee91 # shrinks to s = "0..0" +cc 8ed8dde35d12a2c8e10cdde6d591a8f17f0cd6d6fdf90f1582536401364623bf # shrinks to s = "0.00" +cc ba5e3e3dc1a64870477e82054bbf6d8272f8b0d0c9094115bf7e8b5ff59f3c63 # shrinks to s = "00.1.1" +cc d211e943da9a0e3d0ee5097899b2435f784ca2b3d2f8d4790aae3744823a268a # shrinks to s = "1.1.1.60817410.1" diff --git a/const-oid/tests/proptests.rs b/const-oid/tests/proptests.rs new file mode 100644 index 000000000..5f398fbb5 --- /dev/null +++ b/const-oid/tests/proptests.rs @@ -0,0 +1,56 @@ +//! `proptest`-powered property-based tests. + +use const_oid::{Error, ObjectIdentifier}; +use proptest::prelude::*; +use regex::Regex; + +prop_compose! { + /// Produce a string of digits and dots, i.e. the component parts of OIDs. + /// + /// Note that this can be any permutation of digits-and-dots and does not necessarily + /// represent a valid OID. + fn oid_like_string()(bytes in any::>()) -> String { + // Create a digit or dot from a byte input + fn byte_to_char(byte: u8) -> char { + match byte % 11 { + n @ 0..=9 => (b'0' + n) as char, + 10 => '.', + _ => unreachable!() + } + } + + + let mut ret = String::with_capacity(bytes.len()); + for byte in bytes { + ret.push(byte_to_char(byte)); + } + ret + } +} + +proptest! { + #[test] + fn round_trip(s in oid_like_string()) { + match ObjectIdentifier::new(&s) { + Ok(oid) => { + // Leading zeros won't round trip, so ignore that case + // TODO(tarcieri): disallow leading zeros? + if !s.starts_with("0") && !s.contains(".0") { + let oid_string = oid.to_string(); + prop_assert_eq!(s, oid_string); + } + }, + Err(Error::ArcInvalid { .. }) | Err(Error::ArcTooBig) => (), + Err(e) => { + let re = Regex::new("^([0-2])\\.([0-3]?[0-9])((\\.0)|(\\.[1-9][0-9]*))+$").unwrap(); + + prop_assert!( + re.find(&s).is_none(), + "regex asserts OID `{}` is valid, but `const-oid`failed: {}", + &s, + &e + ); + } + } + } +}