test_fuzz_runtime/
lib.rs

1use internal::{
2    dirs::{
3        corpus_directory_from_args_type, generic_args_directory_from_args_type,
4        impl_generic_args_directory_from_args_type,
5    },
6    serde_format,
7};
8use serde::{de::DeserializeOwned, Serialize};
9use sha1::{Digest, Sha1};
10use std::{
11    any::type_name,
12    env,
13    fmt::{self, Debug, Formatter},
14    fs::{create_dir_all, write},
15    io::{self, Read},
16    marker::PhantomData,
17    path::Path,
18};
19
20pub use num_traits;
21
22pub mod traits;
23
24// smoelius: TryDebug, etc. use Nikolai Vazquez's trick from `impls`.
25// https://github.com/nvzqz/impls#how-it-works
26
27struct DebugUnimplemented<T>(PhantomData<T>);
28
29impl<T> Debug for DebugUnimplemented<T> {
30    // smoelius: The third party-tests check for the absence of "unknown of type..." messages. So if
31    // the messages change, the third-party tests must be updated.
32    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
33        const PAT: &str = "TryDebug<";
34        let type_name = type_name::<T>();
35        let pos = type_name.find(PAT).unwrap() + PAT.len();
36        write!(
37            f,
38            "<unknown of type {}>",
39            type_name[pos..].strip_suffix('>').unwrap()
40        )
41    }
42}
43
44pub trait TryDebugFallback {
45    fn apply<U>(&self, f: &mut dyn FnMut(&dyn Debug) -> U) -> U;
46}
47
48impl<T> TryDebugFallback for T {
49    fn apply<U>(&self, f: &mut dyn FnMut(&dyn Debug) -> U) -> U {
50        f(&DebugUnimplemented::<Self>(PhantomData))
51    }
52}
53
54pub struct TryDebug<'a, T>(pub &'a T);
55
56impl<T: Debug> TryDebug<'_, T> {
57    pub fn apply<U>(&self, f: &mut dyn FnMut(&dyn Debug) -> U) -> U {
58        f(self.0)
59    }
60}
61
62/// * `ty` - Type. When `auto_impl!` is used in the `auto!` macro, this should be just `$ty`.
63/// * `trait` - Trait that `ty` may or may not implement.
64/// * `expr` - Vector of values using `trait`, should `ty` implement it. In `expr`, `T` should be
65///   used as the name of the type, not `$ty`.
66#[macro_export]
67macro_rules! auto_impl {
68    ($ty:ty, $trait:path, $expr:expr) => {{
69        trait AutoFallback<U> {
70            fn auto() -> Vec<U>;
71        }
72
73        impl<T, U> AutoFallback<U> for T {
74            #[must_use]
75            fn auto() -> Vec<U> {
76                vec![]
77            }
78        }
79
80        struct Auto<T>(std::marker::PhantomData<T>);
81
82        impl<T: $trait> Auto<T> {
83            #[must_use]
84            pub fn auto() -> Vec<T> {
85                $expr
86            }
87        }
88
89        Auto::<$ty>::auto() as Vec<$ty>
90    }};
91}
92
93#[macro_export]
94macro_rules! auto {
95    ($ty:ty) => {{
96        let xss = [
97            $crate::auto_impl!(
98                $ty,
99                $crate::num_traits::bounds::Bounded,
100                vec![T::min_value(), T::max_value()]
101            ),
102            $crate::auto_impl!($ty, Default, vec![T::default()]),
103            $crate::auto_impl!(
104                $ty,
105                $crate::traits::MaxValueSubOne,
106                vec![T::max_value_sub_one()]
107            ),
108            $crate::auto_impl!($ty, $crate::traits::Middle, vec![T::low(), T::high()]),
109            $crate::auto_impl!(
110                $ty,
111                $crate::traits::MinValueAddOne,
112                vec![T::min_value_add_one()]
113            ),
114        ];
115        IntoIterator::into_iter(xss).flatten()
116    }};
117}
118
119#[must_use]
120pub fn test_fuzz_enabled() -> bool {
121    enabled("")
122}
123
124#[must_use]
125pub fn display_enabled() -> bool {
126    enabled("DISPLAY")
127}
128
129#[must_use]
130pub fn pretty_print_enabled() -> bool {
131    enabled("PRETTY_PRINT")
132}
133
134#[must_use]
135pub fn replay_enabled() -> bool {
136    enabled("REPLAY")
137}
138
139#[must_use]
140pub fn write_enabled() -> bool {
141    enabled("WRITE")
142}
143
144#[must_use]
145fn enabled(opt: &str) -> bool {
146    let key = "TEST_FUZZ".to_owned() + if opt.is_empty() { "" } else { "_" } + opt;
147    env::var(key).is_ok_and(|value| value != "0")
148}
149
150pub fn write_impl_generic_args<T>(args: &[&str]) {
151    let impl_generic_args = impl_generic_args_directory_from_args_type::<T>();
152    let data = args.join(", ");
153    write_data(&impl_generic_args, data.as_bytes()).unwrap();
154}
155
156pub fn write_generic_args<T>(args: &[&str]) {
157    let generic_args = generic_args_directory_from_args_type::<T>();
158    let data = args.join(", ");
159    write_data(&generic_args, data.as_bytes()).unwrap();
160}
161
162pub fn write_args<T: Serialize>(args: &T) {
163    let corpus = corpus_directory_from_args_type::<T>();
164    let data = serde_format::serialize(args);
165    write_data(&corpus, &data).unwrap();
166}
167
168pub fn write_data(dir: &Path, data: &[u8]) -> io::Result<()> {
169    create_dir_all(dir).unwrap_or_default();
170    let hex = {
171        let digest = Sha1::digest(data);
172        hex::encode(digest)
173    };
174    let path_buf = dir.join(hex);
175    write(path_buf, data)
176}
177
178pub fn read_args<T: DeserializeOwned, R: Read>(reader: R) -> Option<T> {
179    serde_format::deserialize(reader)
180}