1#![doc = include_str!("../README.md")]
2
3pub use edtest_macros::rstest;
7
8pub use rstest::fixture;
11
12pub use serial_test::serial;
13
14pub use static_assertions::*;
15
16#[macro_export]
26macro_rules! set_snapshot_suffix {
27 ($($expr:expr),*) => {
28 let mut settings = insta::Settings::clone_current();
29 let raw_suffix = format!($($expr,)*);
30 let cleaned = $crate::internal::clean_snapshot_suffix(&raw_suffix);
31 settings.set_snapshot_suffix(cleaned);
32 let _guard = settings.bind_to_scope();
33 }
34}
35
36#[doc(hidden)]
37pub mod internal {
38 use core::hint::black_box;
39 use core::sync::atomic::{AtomicUsize, Ordering};
40 use std::collections::hash_map::DefaultHasher;
41 use std::hash::{Hash, Hasher};
42
43 static COUNTER: AtomicUsize = AtomicUsize::new(0);
44
45 #[doc(hidden)]
52 pub fn clean_snapshot_suffix(input: &str) -> String {
53 let mut hasher = DefaultHasher::new();
54 input.hash(&mut hasher);
55 let h: u64 = hasher.finish();
56 format!("{:016x}", h)
57 }
58
59 pub fn on_test_enter(name: &str) {
61 black_box(name.len());
62 COUNTER.fetch_add(1, Ordering::Relaxed);
63 }
64
65 pub fn on_test_exit() {
66 COUNTER.fetch_add(1, Ordering::Relaxed);
67 }
68}
69
70#[doc(hidden)]
73pub struct TestGuard;
74
75#[allow(clippy::new_without_default)]
76impl TestGuard {
77 #[inline(always)]
78 pub fn new() -> Self {
79 Self
80 }
81}
82
83impl Drop for TestGuard {
84 fn drop(&mut self) {
85 internal::on_test_exit();
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 #[test]
92 fn internal_hooks_execute() {
93 crate::internal::on_test_enter("sample");
94 let _g = crate::TestGuard::new();
95 crate::internal::on_test_exit();
96 }
97
98 #[test]
99 fn hashes_suffix_general() {
100 use crate::internal::clean_snapshot_suffix as clean;
101 let s = "a/b\\c:d*e?f|g<h>i\"j k.";
102 let h = clean(s);
103 assert_eq!(h.len(), 16);
104 assert!(h
105 .chars()
106 .all(|c| c.is_ascii_hexdigit() && (c.is_ascii_lowercase() || c.is_ascii_digit())));
107 assert_eq!(h, clean(s));
109 assert_ne!(h, clean("different"));
111 }
112
113 #[test]
114 fn hash_properties_reserved_and_empty() {
115 use crate::internal::clean_snapshot_suffix as clean;
116 assert_ne!(clean("CON"), clean("con"));
118 let e = clean("");
120 assert_eq!(e.len(), 16);
121 assert!(e
122 .chars()
123 .all(|c| c.is_ascii_hexdigit() && (c.is_ascii_lowercase() || c.is_ascii_digit())));
124 }
125}