Skip to main content

supercov_engine/
rust_test_context.rs

1//! Frozen Rust test-attempt context identity and supervisor collision preflight.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5const DOMAIN: &[u8] = b"supercov-rust-test-v1\0";
6const OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
7const PRIME: u64 = 0x0000_0100_0000_01b3;
8const RESERVED_REMAP: u64 = 0xa5a5_a5a5_a5a5_a5a5;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum RustTestContextError {
12    InvalidTestName,
13    DuplicateTest(String),
14    Collision {
15        context_id: u64,
16        first: String,
17        second: String,
18    },
19}
20
21impl std::fmt::Display for RustTestContextError {
22    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        match self {
24            Self::InvalidTestName => write!(formatter, "Rust test name is empty or contains NUL"),
25            Self::DuplicateTest(test) => write!(formatter, "duplicate Rust test identity {test}"),
26            Self::Collision {
27                context_id,
28                first,
29                second,
30            } => write!(
31                formatter,
32                "Rust test context {context_id:016x} collides between {first} and {second}"
33            ),
34        }
35    }
36}
37
38impl std::error::Error for RustTestContextError {}
39
40pub fn rust_test_context_id(test: &str) -> Result<u64, RustTestContextError> {
41    if test.is_empty() || test.contains('\0') {
42        return Err(RustTestContextError::InvalidTestName);
43    }
44    let mut value = OFFSET_BASIS;
45    for byte in DOMAIN.iter().copied().chain(test.bytes()) {
46        value ^= u64::from(byte);
47        value = value.wrapping_mul(PRIME);
48    }
49    Ok(if matches!(value, 0 | u64::MAX) {
50        value ^ RESERVED_REMAP
51    } else {
52        value
53    })
54}
55
56fn preflight_with(
57    tests: impl IntoIterator<Item = String>,
58    derive: impl Fn(&str) -> Result<u64, RustTestContextError>,
59) -> Result<BTreeMap<String, u64>, RustTestContextError> {
60    let mut names = BTreeSet::new();
61    let mut owners = BTreeMap::<u64, String>::new();
62    let mut contexts = BTreeMap::new();
63    for test in tests {
64        if !names.insert(test.clone()) {
65            return Err(RustTestContextError::DuplicateTest(test));
66        }
67        let context_id = derive(&test)?;
68        if let Some(first) = owners.insert(context_id, test.clone()) {
69            return Err(RustTestContextError::Collision {
70                context_id,
71                first,
72                second: test,
73            });
74        }
75        contexts.insert(test, context_id);
76    }
77    Ok(contexts)
78}
79
80pub fn preflight_rust_test_contexts(
81    tests: impl IntoIterator<Item = String>,
82) -> Result<BTreeMap<String, u64>, RustTestContextError> {
83    preflight_with(tests, rust_test_context_id)
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn derivation_matches_the_compiler_contract_and_handles_unicode() {
92        assert_eq!(
93            rust_test_context_id("tests::child_context").unwrap(),
94            0x4a50_d37c_b3b7_f06f
95        );
96        assert_ne!(
97            rust_test_context_id("tests::δ").unwrap(),
98            rust_test_context_id("tests::d").unwrap()
99        );
100        assert!(matches!(
101            rust_test_context_id(""),
102            Err(RustTestContextError::InvalidTestName)
103        ));
104    }
105
106    #[test]
107    fn supervisor_rejects_duplicates_and_hash_collisions_before_launch() {
108        assert!(matches!(
109            preflight_rust_test_contexts(["same".into(), "same".into()]),
110            Err(RustTestContextError::DuplicateTest(_))
111        ));
112        assert!(matches!(
113            preflight_with(["first".into(), "second".into()], |_| Ok(7)),
114            Err(RustTestContextError::Collision { context_id: 7, .. })
115        ));
116        let contexts = preflight_rust_test_contexts(["second".into(), "first".into()]).unwrap();
117        assert_eq!(
118            contexts.keys().cloned().collect::<Vec<_>>(),
119            ["first", "second"]
120        );
121    }
122}