1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Imperative property-based testing library with no dependencies, no
//! macros, no unsafe, and no implicit I/O.
//!
//! A property is a plain Rust closure that samples values with
//! `noprop::sample_*` and asserts on the results. Ordinary control
//! flow — `if` / `match` / `for` — expresses any generator directly,
//! so a test reads like the code it exercises. Every `sample_*` call
//! records the produced value at its source location, so a failure
//! surfaces the actual generated input without extra plumbing.
//!
//! # Quick start
//!
//! ```
//! # fn body() -> noprop::TestResult {
//! noprop::Runner::new(0xDEAD_BEEF).run(1024, |ctx| {
//! let a = noprop::sample_u32(ctx);
//! let b = noprop::sample_u32(ctx);
//! assert_eq!(a.wrapping_add(b), b.wrapping_add(a));
//! Ok(())
//! })?;
//! # Ok(())
//! # }
//! # body().unwrap();
//! ```
//!
//! The seed is always caller-supplied; noprop never reads the system
//! clock or environment on its own. For a `#[test]` that stays
//! reproducible from a failure report, read the seed from an
//! environment variable and fall back to the clock:
//!
//! ```
//! # fn body() -> noprop::TestResult {
//! let seed = noprop::seed_from_env_or_time("MYAPP_SEED")?;
//! noprop::Runner::new(seed).run(256, |_ctx| Ok(()))?;
//! # Ok(())
//! # }
//! # body().unwrap();
//! ```
//!
//! Pick a project-specific variable name — the hex seed printed by a
//! failure report can be pasted into it verbatim.
//!
//! # Choosing a runner
//!
//! - [`Runner::run`] samples inputs uniformly. This is the default;
//! use it unless a property has a rare region that uniform sampling
//! would only reach by luck.
//! - [`Runner::run_feedback_guided`] steers the search toward inputs
//! that report new semantic features via
//! [`TestCaseContext::event`], [`bucket`](TestCaseContext::bucket),
//! and [`transition`](TestCaseContext::transition). The property
//! closure has the same shape as `run`, and the feedback methods are
//! allocation-free no-ops under `Runner::run`, so the same property
//! can be exercised under both entry points. The design is
//! documented in [`docs::feedback_guided_search`].
//!
//! # Reproducing a failure
//!
//! A property failure (panic, returned `Err`, or too-many-rejections
//! exit) becomes a [`RunError`] carrying the seed, the case index, the
//! recorded value trace, and observability [`Stats`]. Both `Debug` and
//! `Display` include a reproduce hint that reuses the original case
//! budget:
//!
//! ```text
//! reproduce with: noprop::Runner::new(0x...).run(N, |ctx| ...)
//! ```
//!
//! Copy the seed into the runner (or into the env variable read by
//! `seed_from_env_or_time`) and rerun with the same case budget and
//! the same property closure — the run reproduces the identical
//! failure case index. The [`examples/reproduce.rs`][reproduce] file
//! walks through the full workflow with a deliberately failing
//! property.
//!
//! [reproduce]: https://github.com/sile/noprop/blob/main/examples/reproduce.rs
//!
//! # Requirements and constraints
//!
//! - **`panic=unwind`.** noprop catches property panics and uses
//! panic-based unwinding for
//! [`TestCaseContext::reject_case`], so the crate does not work
//! under `panic=abort`.
//! - **No automatic shrinking.** Failures are reproduced from
//! the seed and the case budget; to freeze a specific case as a
//! regression test, simplify it by hand from the value trace and
//! inline the witness as a regular `#[test]`. See
//! [`docs::recipes`] ("Turn a trace into a hand-written regression
//! test") for the pattern.
//! - **No file-based failure persistence.** The caller manages the
//! seed and case budget; there is no on-disk seed corpus.
//!
//! noprop is imperative-first and does not replace a general PBT
//! library where automatic shrinking or seed persistence is the
//! priority. It fits well when the property is naturally sequential
//! (dependent generation, stateful commands, protocol traces) and
//! reads more clearly as plain Rust than as a combinator DSL.
//!
//! # Where to look next
//!
//! - [`docs::recipes`] — task-oriented recipes for common property
//! shapes (sampling, collections, rejection scopes, stateful,
//! feedback-guided, reproduction).
//! - [`docs::generator_design`] — the small design decisions every
//! `sample_*` generator has to make (support, distribution,
//! termination, rejection scope, valid-by-construction).
//! - [`docs::generator_authoring`] — how-to guide for writing
//! `sample_*` helpers: composing primitives, bounded rejection,
//! `NonZero` recipes, and the finite-by-default float samplers.
//! - [`docs::feedback_guided_search`] — the design of
//! [`Runner::run_feedback_guided`], the corpus admission and
//! eviction rules, and how the feature registry is bounded.
//! - The [`examples/`][examples] directory ships runnable end-to-end
//! demos: `basics.rs`, `stateful.rs`, `feedback_guided.rs`,
//! `rejection.rs`, `reproduce.rs`. Each runs with
//! `cargo run --example <name>`.
//!
//! [examples]: https://github.com/sile/noprop/tree/main/examples
pub use ;
pub use ;
pub use ;
pub use ;
pub use seed_from_env_or_time;