musli_tests/
lib.rs

1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/musli-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/musli)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/musli-tests.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/musli-tests)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-musli--tests-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/musli-tests)
4//!
5//! Helper utilities for ensuring that musli is safe to use and behaves as expected.
6//!
7//! This does include a fairly sophisticated benchmarking suite.
8//!
9//! <br>
10//!
11//! ## Benchmarking
12//!
13//! Now this includes the `extra` category, which requires `model_minimal`. This
14//! is not exactly a fair comparison, because it compares serialization formats
15//! which were designed to model data that others are not capable off.
16//!
17//! The exact tradeoff can be seen by comparing the feature `model_full` with
18//! `model_minimal`.
19//!
20//! ```sh
21//! cargo bench --no-default-features --features musli,full,extra,model_minimal,simdutf8
22//! ```
23//!
24//! To run benchmarks for serialization suites which *do* support all features, do:
25//!
26//! ```sh
27//! cargo bench --features full
28//! ```
29//!
30//! <br>
31//!
32//! ## Fuzz testing
33//!
34//! This comes with the `fuzz` binary which can do the following things.
35//!
36//! Run serialization for a long time against a lot of random data. Both with
37//! and without miri. When run with miri, the size of the datasets is
38//! drastically reduced.
39//!
40//! ```sh
41//! cargo run --bin fuzz
42//! cargo +nightly miri run --bin fuzz
43//! ```
44//!
45//! Run deserialization against randomly generated bytes. Both with and without
46//! miri. When run with miri, the size of the datasets is drastically reduced.
47//!
48//! ```sh
49//! cargo run --bin fuzz -- --random
50//! cargo +nightly miri run --bin fuzz -- --random
51//! ```
52//!
53//! > **Note** you can add the running of optional crates by enabling its
54//! > corresponding feature, such as `--features bincode`.
55//!
56//! <br>
57//!
58//! ## Potential security issues discovered by this crate
59//!
60//! * `dlhn`: Allocating and initializing large arrays based on untrusted input
61//!   (DoS): [dlhn#11](https://github.com/otake84/dlhn/issues/11).
62//!
63//! <br>
64//!
65//! ## Size comparison
66//!
67//! To perform a one-off size comparison:
68//!
69//! ```sh
70//! cargo run --bin fuzz -- --size
71//! ```
72//!
73//! This will *for now* just take the first generated field of a given type,
74//! serialize it, and print out its size. In the future it will perform an
75//! average over the entire set with more statistics.
76
77#![no_std]
78
79extern crate alloc;
80
81#[cfg(feature = "std")]
82extern crate std;
83
84const RNG_SEED: u64 = 2718281828459045235;
85
86#[macro_export]
87macro_rules! miri {
88    ($($(#[$($meta:meta)*])* const $ident:ident: $value_ty:ty = $range:expr, $miri:expr;)*) => {
89        $(
90            $(#[$($meta)*])*
91            #[cfg(miri)]
92            const $ident: $value_ty = $miri;
93            $(#[$($meta)*])*
94            #[cfg(not(miri))]
95            const $ident: $value_ty = $range;
96        )*
97    }
98}
99
100// Defines denies feature combinations.
101//
102// * Negative features are not supported in cargo, and feature blocking
103//   everything is too complex. model_map for example also depends on std.
104// * Benchmarks for these must be explicitly run, because they only include a
105//   subset of available data, we wouldn't be doing an apples-to-apples
106//   comparison if we allowed only a model subset to be compared against a
107//   serialization which supports a superset. If you do want to make this
108//   comparison, you can enable `model_minimal`.
109macro_rules! deny {
110    ($base:literal $(, $feature:literal)*) => {
111        $(
112            #[cfg(all(feature = $base, feature = $feature))]
113            compile_error!(concat!($base, ": does not support feature: ", $feature));
114        )*
115    }
116}
117
118deny!("rkyv", "model_tuple", "model_map_string_key", "model_usize");
119deny!("dlhn", "model_map", "model_128");
120deny!("bitcode", "model_128");
121
122mod mode;
123pub mod models;
124pub mod utils;
125
126#[cfg(feature = "musli-wire")]
127pub mod wire {
128    pub use musli_wire::*;
129}
130
131#[cfg(feature = "musli-storage")]
132pub mod storage {
133    pub use musli_storage::*;
134}
135
136#[cfg(feature = "musli-descriptive")]
137pub mod s {
138    pub use musli_descriptive::*;
139}
140
141/// Roundtrip the given expression through all supported formats.
142#[cfg(all(
143    feature = "musli-wire",
144    feature = "musli-storage",
145    feature = "musli-descriptive"
146))]
147#[macro_export]
148macro_rules! rt {
149    ($($tt:tt)*) => {{
150        let a = ::musli_wire::rt!($($tt)*);
151        let b = ::musli_storage::rt!($($tt)*);
152        let c = ::musli_descriptive::rt!($($tt)*);
153        assert_eq!(a, b);
154        assert_eq!(a, c);
155        a
156    }};
157}
158
159#[macro_export]
160macro_rules! musli_zerocopy_call {
161    ($call:path) => {
162    };
163
164    ($call:path, prim, $ty:ty, $size_hint:expr $(, $tt:tt)*) => {
165        $call!(musli_value, musli_value_buf, prim, $ty, $size_hint);
166        $crate::musli_zerocopy_call!($call $(, $tt)*);
167    };
168
169    // Ignore others.
170    ($call:path, $name:ident, $ty:ty, $size_hint:expr $(, $tt:tt)*) => {
171        $crate::musli_zerocopy_call!($call $(, $tt)*);
172    };
173}
174
175/// Call the given macro with the existing feature matrix.
176#[macro_export]
177macro_rules! feature_matrix {
178    ($call:path $(, $($tt:tt)*)?) => {
179        #[cfg(feature = "serde_json")]
180        $call!(serde_json, serde_json_buf $(, $($tt)*)*);
181        #[cfg(feature = "bincode")]
182        $call!(serde_bincode, serde_bincode_buf $(, $($tt)*)*);
183        #[cfg(feature = "rmp-serde")]
184        $call!(serde_rmp, serde_rmp_buf $(, $($tt)*)*);
185        #[cfg(feature = "musli-json")]
186        $call!(musli_json, musli_json_buf $(, $($tt)*)*);
187        #[cfg(feature = "musli-wire")]
188        $call!(musli_wire, musli_wire_buf $(, $($tt)*)*);
189        #[cfg(feature = "musli-descriptive")]
190        $call!(musli_descriptive, musli_descriptive_buf $(, $($tt)*)*);
191        #[cfg(feature = "musli-storage")]
192        $call!(musli_storage, musli_storage_buf $(, $($tt)*)*);
193        #[cfg(feature = "musli-storage")]
194        $call!(musli_storage_packed, musli_storage_packed_buf $(, $($tt)*)*);
195        #[cfg(feature = "musli-value")]
196        $call!(musli_value, musli_value_buf $(, $($tt)*)*);
197        #[cfg(feature = "musli-zerocopy")]
198        $call!(musli_zerocopy, musli_zerocopy_buf $(, $($tt)*)*);
199        #[cfg(all(feature = "dlhn", not(any(model_128, model_all))))]
200        $call!(serde_dlhn, serde_dlhn_buf $(, $($tt)*)*);
201        #[cfg(feature = "serde_cbor")]
202        $call!(serde_cbor, serde_cbor_buf $(, $($tt)*)*);
203        #[cfg(feature = "bitcode")]
204        $call!(serde_bitcode, serde_bitcode_buf $(, $($tt)*)*);
205        #[cfg(feature = "bitcode")]
206        $call!(derive_bitcode, derive_bitcode_buf $(, $($tt)*)*);
207        #[cfg(feature = "rkyv")]
208        $call!(rkyv, rkyv_buf $(, $($tt)*)*);
209        #[cfg(feature = "postcard")]
210        $call!(postcard, postcard_buf $(, $($tt)*)*);
211    };
212}
213
214/// Only expand `$block` if the given test is supported by this framework.
215#[macro_export]
216macro_rules! if_supported {
217    (musli_zerocopy, lg, $block:block) => {};
218    (musli_zerocopy, allocated, $block:block) => {};
219    (musli_zerocopy, medium_enum, $block:block) => {};
220    ($framework:ident, $test:ident, $block:block) => {
221        $block
222    };
223}
224
225#[macro_export]
226macro_rules! types {
227    ($call:path $(, $($tt:tt)*)?) => {
228        $call! {
229            $($($tt)*,)?
230            prim, Primitives, PRIMITIVES, 1000,
231            lg, LargeStruct, LARGE_STRUCTS, 10000,
232            allocated, Allocated, ALLOCATED, 5000,
233            medium_enum, MediumEnum, MEDIUM_ENUMS, 1000
234        };
235    }
236}
237
238/// Build common RNG.
239pub fn rng() -> rand::prelude::StdRng {
240    use rand::prelude::*;
241    rand::prelude::StdRng::seed_from_u64(RNG_SEED)
242}