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}