Skip to main content

hegel/
lib.rs

1//! Hegel is a property-based testing library for Rust. Hegel is based on [Hypothesis](https://github.com/hypothesisworks/hypothesis), using the [Hegel](https://hegel.dev/) protocol.
2//!
3//! # Getting started
4//!
5//! This guide walks you through the basics of installing Hegel and writing your first tests.
6//!
7//! ## Install Hegel
8//!
9//! Add `hegel-rust` to your `Cargo.toml` as a dev dependency using cargo:
10//!
11//! ```bash
12//! cargo add --dev hegeltest
13//! ```
14//!
15//! ## Write your first test
16//!
17//! You're now ready to write your first test. We'll use Cargo as a test runner for the
18//! purposes of this guide. Create a new test in the project's `tests/` directory:
19//!
20//! ```no_run
21//! use hegel::TestCase;
22//! use hegel::generators as gs;
23//!
24//! #[hegel::test]
25//! fn test_integer_self_equality(tc: TestCase) {
26//!     let n = tc.draw(gs::integers::<i32>());
27//!     assert_eq!(n, n); // integers should always be equal to themselves
28//! }
29//! ```
30//!
31//! Now run the test using `cargo test --test <filename>`. You should see that this test passes.
32//!
33//! Let's look at what's happening in more detail. The `#[hegel::test]` attribute runs your test
34//! many times (100, by default). The test function (in this case `test_integer_self_equality`)
35//! takes a [`TestCase`] parameter, which provides a [`draw`](TestCase::draw) method for drawing
36//! different values. This test draws a random integer and checks that it should be equal to itself.
37//!
38//! Next, try a test that fails:
39//!
40//! ```no_run
41//! # use hegel::TestCase;
42//! # use hegel::generators as gs;
43//! #[hegel::test]
44//! fn test_integers_always_below_50(tc: TestCase) {
45//!     let n = tc.draw(gs::integers::<i32>());
46//!     assert!(n < 50); // this will fail!
47//! }
48//! ```
49//!
50//! This test asserts that any integer is less than 50, which is obviously incorrect. Hegel will
51//! find a test case that makes this assertion fail, and then shrink it to find the smallest
52//! counterexample — in this case, `n = 50`.
53//!
54//! To fix this test, you can constrain the integers you generate with the `min_value` and
55//! `max_value` functions:
56//!
57//! ```no_run
58//! # use hegel::TestCase;
59//! # use hegel::generators as gs;
60//! #[hegel::test]
61//! fn test_bounded_integers_always_below_50(tc: TestCase) {
62//!     let n = tc.draw(gs::integers::<i32>()
63//!         .min_value(0)
64//!         .max_value(49));
65//!     assert!(n < 50);
66//! }
67//! ```
68//!
69//! Run the test again. It should now pass.
70//!
71//! ## Use generators
72//!
73//! Hegel provides a rich library of generators that you can use out of the box. There are
74//! primitive generators, such as [`integers`](generators::integers),
75//! [`floats`](generators::floats), and [`text`](generators::text), and combinators that allow
76//! you to make generators out of other generators, such as [`vecs`](generators::vecs) and
77//! [`tuples`].
78//!
79//! For example, you can use [`vecs`](generators::vecs) to generate a vector of integers:
80//!
81//! ```no_run
82//! # use hegel::TestCase;
83//! use hegel::generators as gs;
84//!
85//! #[hegel::test]
86//! fn test_append_increases_length(tc: TestCase) {
87//!     let mut vector = tc.draw(gs::vecs(gs::integers::<i32>()));
88//!     let initial_length = vector.len();
89//!     vector.push(tc.draw(gs::integers::<i32>()));
90//!     assert!(vector.len() > initial_length);
91//! }
92//! ```
93//!
94//! This test checks that appending an element to a random vector of integers should always
95//! increase its length.
96//!
97//! You can also define custom generators. For example, say you have a `Person` struct that
98//! we want to generate:
99//!
100//! ```no_run
101//! # use hegel::TestCase;
102//! # use hegel::generators as gs;
103//! #[derive(Debug)]
104//! struct Person {
105//!     age: i32,
106//!     name: String,
107//! }
108//!
109//! #[hegel::composite]
110//! fn generate_person(tc: &TestCase) -> Person {
111//!     let age = tc.draw(gs::integers::<i32>());
112//!     let name = tc.draw(gs::text());
113//!     Person { age, name }
114//! }
115//! ```
116//!
117//! Note that you can feed the results of a `draw` to subsequent calls. For example, say that
118//! you extend the `Person` struct to include a `driving_license` boolean field:
119//!
120//! ```no_run
121//! # use hegel::TestCase;
122//! # use hegel::generators as gs;
123//! #[derive(Debug)]
124//! struct Person {
125//!     age: i32,
126//!     name: String,
127//!     driving_license: bool,
128//! }
129//!
130//! #[hegel::composite]
131//! fn generate_person(tc: &TestCase) -> Person {
132//!     let age = tc.draw(gs::integers::<i32>());
133//!     let name = tc.draw(gs::text());
134//!     let driving_license = if age >= 18 {
135//!         tc.draw(gs::booleans())
136//!     } else {
137//!          false
138//!     };
139//!     Person { age, name, driving_license }
140//! }
141//! ```
142//!
143//! ## Debug your failing test cases
144//!
145//! Use the [`note`](TestCase::note) method to attach debug information:
146//!
147//! ```no_run
148//! # use hegel::TestCase;
149//! # use hegel::generators as gs;
150//! #[hegel::test]
151//! fn test_with_notes(tc: TestCase) {
152//!     let x = tc.draw(gs::integers::<i32>());
153//!     let y = tc.draw(gs::integers::<i32>());
154//!     tc.note(&format!("x + y = {}, y + x = {}", x + y, y + x));
155//!     assert_eq!(x + y, y + x);
156//! }
157//! ```
158//!
159//! Notes only appear when Hegel replays the minimal failing example.
160//!
161//! ## Change the number of test cases
162//!
163//! By default Hegel runs 100 test cases. To override this, pass the `test_cases` argument
164//! to the `test` attribute:
165//!
166//! ```no_run
167//! # use hegel::TestCase;
168//! # use hegel::generators as gs;
169//! #[hegel::test(test_cases = 500)]
170//! fn test_integers_many(tc: TestCase) {
171//!     let n = tc.draw(gs::integers::<i32>());
172//!     assert_eq!(n, n);
173//! }
174//! ```
175//!
176//! To override the number of test cases at runtime — for the whole suite,
177//! without editing source — set the `HEGEL_TEST_CASES` environment variable:
178//!
179//! ```bash
180//! HEGEL_TEST_CASES=10000 cargo test
181//! ```
182//!
183//! When set and non-empty, it takes precedence over any value configured in
184//! source, including explicit `test_cases` attributes.
185//!
186//! To see what a test actually generates, record events with
187//! [`TestCase::event`] and [`TestCase::event_value`] and enable the
188//! end-of-run statistics report with the `HEGEL_STATISTICS` environment
189//! variable (or [`Settings::show_statistics`]):
190//!
191//! ```bash
192//! HEGEL_STATISTICS=1 cargo test my_test -- --nocapture
193//! ```
194//!
195//! ## Threading
196//!
197//! [`TestCase`] is `Send` but not `Sync`: you can clone it and move the clone
198//! to another thread to drive generation from there.
199//!
200//! ```no_run
201//! use hegel::TestCase;
202//! use hegel::generators as gs;
203//!
204//! #[hegel::test]
205//! fn test_with_worker_thread(tc: TestCase) {
206//!     let tc_worker = tc.clone();
207//!     let handle = std::thread::spawn(move || {
208//!         tc_worker.draw(gs::vecs(gs::integers::<i32>()).max_size(10))
209//!     });
210//!     let xs = handle.join().unwrap();
211//!     let more: bool = tc.draw(gs::booleans());
212//!     let _ = (xs, more);
213//! }
214//! ```
215//!
216//! Clones share the test case's *outcome* — the whole family passes, fails,
217//! or is rejected as one test case — but each clone draws from its own
218//! independent, deterministic stream of choices, so several threads can
219//! generate concurrently without perturbing each other's values and the
220//! same seed replays the same values on every stream.
221//!
222//! In a failing example's report, a clone's drawn values appear together,
223//! at the point where the clone was created — not interleaved by wall-clock
224//! timing — so the report is deterministic no matter how the threads were
225//! scheduled.
226//!
227//! Determinism extends only as far as your own code's determinism: if your
228//! threads race on shared state, Hegel replays each stream faithfully but
229//! the test may still behave differently run to run — see [`TestCase`]'s
230//! documentation for the full contract and the patterns that are safe to
231//! rely on.
232//!
233//! ## Learning more
234//!
235//! - Browse the [`generators`] module for the full list of available generators.
236//! - See [`Settings`] for more configuration settings to customise how your test runs.
237
238#![forbid(future_incompatible)]
239#![cfg_attr(docsrs, feature(doc_cfg))]
240
241pub(crate) mod antithesis;
242#[doc(hidden)]
243pub mod backend;
244pub(crate) mod cli;
245pub(crate) mod control;
246#[doc(hidden)]
247pub mod explicit_test_case;
248pub mod extras;
249pub(crate) mod ffi;
250pub mod generators;
251pub mod pretty;
252#[doc(hidden)]
253pub mod run_lifecycle;
254pub(crate) mod runner;
255pub mod stateful;
256mod test_case;
257#[doc(hidden)]
258pub use control::currently_in_test_context;
259pub use explicit_test_case::ExplicitTestCase;
260pub use generators::Generator;
261pub use generators::PrintableGenerator;
262pub use pretty::{Document, PrettyPrintable, PrettyPrinter};
263pub use test_case::TestCase;
264
265#[doc(hidden)]
266pub use test_case::{__IsTestCase, __assert_is_test_case, with_output_override};
267
268#[doc(hidden)]
269pub use antithesis::TestLocation;
270
271#[doc(hidden)]
272#[cfg(feature = "__bench")]
273pub use hegel_c::__bench;
274
275/// Derive a generator for a struct or enum.
276///
277/// This implements [`DefaultGenerator`](generators::DefaultGenerator) for the type,
278/// allowing it to be used with [`default`](generators::default) via `default::<T>()`.
279///
280/// Deriving only works on type definitions you own; for a struct defined in
281/// another crate, see [`derive_generator!`](crate::derive_generator) instead.
282///
283/// The derived generator prints values field by field as it draws them, in
284/// the same Rust-expression format `#[derive(PrettyPrintable)]` produces,
285/// so the type itself needs no [`PrettyPrintable`] implementation. It is
286/// generic over its field generators — mirroring `one_of!` and tuples — and
287/// is a [`PrintableGenerator`] exactly when every field generator is one:
288/// the builder methods accept any [`Generator`] of the field's type, and a
289/// non-printable field generator simply makes the result silent-only (or
290/// printable again via [`print_as_value`](generators::Generator::print_as_value),
291/// [`print_as_debug`](generators::Generator::print_as_debug), or
292/// [`print_with`](generators::Generator::print_with)). Because the derived
293/// generator prints compositionally, a hand-written [`PrettyPrintable`]
294/// implementation on the type is **not consulted** for its failing-example
295/// output; a type that wants a different printed representation implements
296/// [`DefaultGenerator`] by hand.
297///
298/// For structs, the generated generator has:
299/// - `<field>(generator)` - builder method to customize each field's generator
300/// - for tuple structs, the builder methods are positional: `._0(generator)`,
301///   `._1(generator)`, etc.
302///
303/// For enums, the generated generator draws one of the variants at random.
304/// Unit variants need no configuration; every data-carrying variant gets
305/// builder methods named after the variant (snake_cased):
306/// - for a struct variant like `Active { since: String }`, a method
307///   `.active(|g| ...)` whose closure receives that variant's generator
308///   (with a `<field>(generator)` builder per field, like a struct) and
309///   returns the generator to use for the variant;
310/// - for a tuple variant like `Error(i32, String)`, a method
311///   `.error(g0, g1)` taking one generator per field positionally, plus a
312///   closure form `.error_with(|g| ...)` mirroring the struct-variant
313///   method, where the variant generator's fields are configured with
314///   `._0(...)`, `._1(...)`, etc.
315///
316/// # Struct Example
317///
318/// ```no_run
319/// use hegel::DefaultGenerator;
320/// use hegel::generators as gs;
321///
322/// #[derive(Debug, DefaultGenerator)]
323/// struct Person {
324///     name: String,
325///     age: u32,
326/// }
327///
328/// #[derive(Debug, DefaultGenerator)]
329/// struct Meters(f64);
330///
331/// #[hegel::test]
332/// fn generates_people(tc: hegel::TestCase) {
333///     let generator = gs::default::<Person>()
334///         .age(gs::integers::<u32>().min_value(0).max_value(120));
335///     let person: Person = tc.draw(generator);
336///     let height: Meters = tc.draw(
337///         gs::default::<Meters>()._0(gs::floats().min_value(0.0).max_value(3.0)),
338///     );
339/// }
340/// ```
341///
342/// # Enum Example
343///
344/// ```no_run
345/// use hegel::DefaultGenerator;
346/// use hegel::generators as gs;
347///
348/// #[derive(Debug, DefaultGenerator)]
349/// enum Status {
350///     Pending,
351///     Active { since: String },
352///     Error(i32, String),
353/// }
354///
355/// #[hegel::test]
356/// fn generates_statuses(tc: hegel::TestCase) {
357///     let generator = gs::default::<Status>()
358///         // Struct variant: configure through a closure over the
359///         // variant's own generator.
360///         .active(|g| g.since(gs::text().max_size(20)))
361///         // Tuple variant: pass one generator per field...
362///         .error(gs::integers::<i32>().min_value(400).max_value(599), gs::text())
363///         // ...or use the closure form with positional field builders.
364///         .error_with(|g| g._0(gs::just(500)));
365///     let status: Status = tc.draw(generator);
366/// }
367/// ```
368pub use hegel_macros::DefaultGenerator;
369
370/// Derive [`PrettyPrintable`] for a struct or enum.
371///
372/// The generated implementation prints the value in Rust-expression syntax —
373/// `Name { field: value, … }`, `Name(value, …)`, and `Name::Variant …` for
374/// enums — using the printer's group machinery so values that do not fit on
375/// one line wrap with each field on its own line. Every generic type
376/// parameter is given a [`PrettyPrintable`] bound, mirroring how
377/// `derive(Debug)` bounds `Debug`.
378///
379/// For a type whose `Debug` output is already the representation you want
380/// (or one you cannot add a derive to), use
381/// [`pretty_print_as_debug!`](crate::pretty_print_as_debug) instead.
382///
383/// A field whose type cannot implement [`PrettyPrintable`] — a foreign type
384/// the orphan rule keeps out, say — can opt out with `#[pretty(debug)]`:
385/// that field prints its `Debug` representation (re-laid-out through the
386/// printer, like [`print_as_debug`](generators::Generator::print_as_debug)),
387/// and its type must implement `Debug` instead.
388///
389/// ```
390/// use hegel::{Document, PrettyPrintable};
391///
392/// #[derive(PrettyPrintable)]
393/// struct Person {
394///     name: String,
395///     age: u32,
396///     #[pretty(debug)]
397///     home: std::path::PathBuf,
398/// }
399///
400/// let person = Person {
401///     name: "Ada".to_string(),
402///     age: 36,
403///     home: "/home/ada".into(),
404/// };
405/// let mut doc = Document::new();
406/// person.pretty_print(doc.printer());
407/// assert_eq!(
408///     doc.finish(),
409///     "Person { name: \"Ada\".to_string(), age: 36, home: \"/home/ada\" }"
410/// );
411/// ```
412pub use hegel_macros::PrettyPrintable;
413
414/// Define a composite generator from a function.
415///
416/// The first parameter must be a `&`[`TestCase`] and is passed automatically
417/// when the generator is drawn. Any additional parameters become parameters
418/// of the generator's constructor function and must implement [`Clone`]:
419/// they are stored on the generator and cloned into each draw (pass a
420/// non-`Clone` generator argument through
421/// [`boxed()`](generators::Generator::boxed)). The function must have an
422/// explicit return type.
423///
424/// ```no_run
425/// use hegel::generators as gs;
426///
427/// #[hegel::composite]
428/// fn sorted_vec(tc: &hegel::TestCase, min_len: usize) -> Vec<i32> {
429///     let mut v: Vec<i32> = tc.draw(gs::vecs(gs::integers()).min_size(min_len));
430///     v.sort();
431///     v
432/// }
433///
434/// #[hegel::test]
435/// fn test_sorted(tc: hegel::TestCase) {
436///     let v = tc.draw(sorted_vec(3));
437///     assert!(v.len() >= 3);
438///     assert!(v.windows(2).all(|w| w[0] <= w[1]));
439/// }
440/// ```
441///
442/// The attribute expands to a struct named after the function
443/// (`sorted_vec` above becomes `SortedVecCompositeGenerator`) plus a
444/// constructor function with the original name, so the generator has a
445/// nameable type that can be stored, cloned, and passed to other
446/// composites. Because the constructor is an ordinary function returning
447/// that struct, composite generators can also call themselves recursively:
448///
449/// ```no_run
450/// use hegel::generators as gs;
451///
452/// #[derive(Debug, Clone, hegel::PrettyPrintable)]
453/// enum Tree {
454///     Leaf,
455///     Branch(Box<Tree>, Box<Tree>),
456/// }
457///
458/// #[hegel::composite]
459/// fn tree(tc: &hegel::TestCase) -> Tree {
460///     tc.draw(hegel::one_of!(
461///         gs::just(Tree::Leaf),
462///         hegel::compose!(|tc| {
463///             Tree::Branch(Box::new(tc.draw(tree())), Box::new(tc.draw(tree())))
464///         }),
465///     ))
466/// }
467/// ```
468pub use hegel_macros::composite;
469pub use hegel_macros::explicit_test_case;
470
471/// Replay a single failing example from a base64 *failure blob*.
472///
473/// When a test fails on the native backend and the
474/// [`print_blob`](Settings::print_blob) setting is enabled, Hegel prints a
475/// reproducer line of the form:
476///
477/// ```text
478/// To reproduce this failure, add the attribute below #[hegel::test]:
479///     #[hegel::reproduce_failure("AAEC…")]
480/// ```
481///
482/// Paste that attribute **below** `#[hegel::test]` and the next run will
483/// decode the blob's choice sequence and run *only* that example.
484///
485/// ```no_run
486/// #[hegel::test]
487/// #[hegel::reproduce_failure("AAEC…")]
488/// fn my_test(tc: hegel::TestCase) {
489///     let x: i32 = tc.draw(hegel::generators::integers());
490///     assert!(x < 100);
491/// }
492/// ```
493///
494/// The argument is any expression that resolves to a base64 blob — a string
495/// literal, or a `const`/`static`/variable holding one:
496///
497/// ```no_run
498/// const REGRESSION: &str = "AAEC…";
499///
500/// #[hegel::test]
501/// #[hegel::reproduce_failure(REGRESSION)]
502/// fn my_test(tc: hegel::TestCase) { /* ... */ }
503/// ```
504///
505/// The attribute may be stacked to keep track of several failures, but only
506/// the **first** one replays — the rest are bookkeeping. Delete them one by
507/// one as the failures are fixed:
508///
509/// ```no_run
510/// #[hegel::test]
511/// #[hegel::reproduce_failure("AAEC…")] // replayed
512/// #[hegel::reproduce_failure("AAED…")] // kept for later
513/// fn my_test(tc: hegel::TestCase) { /* ... */ }
514/// ```
515///
516/// The blob encodes Hegel's internal choice sequence, so it is only
517/// guaranteed to reproduce a failure within a specific version of Hegel.
518/// A blob that can't be decoded (corrupt or from an incompatible version),
519/// or that no longer reproduces a failure, panics with an explanatory
520/// message.
521pub use hegel_macros::reproduce_failure;
522
523#[doc(hidden)]
524pub use hegel_macros::rewrite_draws;
525
526/// Derive a [`StateMachine`](crate::stateful::StateMachine) implementation from an `impl` block.
527///
528/// See the [`stateful`] module docs for more information.
529pub use hegel_macros::state_machine;
530
531/// Derive a [`ConcurrentStateMachine`](crate::stateful::ConcurrentStateMachine)
532/// implementation from an `impl` block, for concurrent stateful testing via
533/// [`stateful::run_concurrent`].
534///
535/// Methods annotated `#[rule(group = "name")]` become rules assigned to the
536/// named concurrency group; methods annotated `#[invariant]` become
537/// invariants, checked in full on the machine's initial and final state and
538/// sampled at the join points between rounds. Rules in the same
539/// group may run concurrently with each other; rules in different groups
540/// never overlap.
541///
542/// A bare `#[rule]` with no `group = "..."` argument is assigned to a
543/// single shared anonymous group, so a machine with no group annotations
544/// is maximally concurrent: any rule may overlap with any other, and naming
545/// groups is how overlap gets restricted.
546///
547/// The model is shared by reference across worker threads, so rules and
548/// invariants must take `&self` (mutable state needs interior mutability),
549/// and the model type must be `Sync`. See
550/// [`run_concurrent`](crate::stateful::run_concurrent) for the full
551/// execution model.
552///
553/// ```no_run
554/// use std::sync::Mutex;
555/// use hegel::TestCase;
556/// use hegel::generators as gs;
557///
558/// struct KvTest {
559///     store: Mutex<std::collections::HashMap<u8, i64>>,
560/// }
561///
562/// #[hegel::concurrent_state_machine]
563/// impl KvTest {
564///     #[rule(group = "rw")]
565///     fn put(&self, tc: TestCase) {
566///         let key: u8 = tc.draw(gs::integers());
567///         let value: i64 = tc.draw(gs::integers());
568///         self.store.lock().unwrap_or_else(|e| e.into_inner()).insert(key, value);
569///     }
570///
571///     #[rule(group = "rw")]
572///     fn get(&self, tc: TestCase) {
573///         let key: u8 = tc.draw(gs::integers());
574///         let _ = self.store.lock().unwrap_or_else(|e| e.into_inner()).get(&key).copied();
575///     }
576///
577///     #[rule(group = "dump")]
578///     fn dump(&self, _: TestCase) {
579///         let _ = self.store.lock().unwrap_or_else(|e| e.into_inner()).clone();
580///     }
581///
582///     #[invariant]
583///     fn small_enough(&self, _: TestCase) {
584///         assert!(self.store.lock().unwrap_or_else(|e| e.into_inner()).len() <= 256);
585///     }
586/// }
587///
588/// #[hegel::test]
589/// fn test_kv(tc: TestCase) {
590///     let m = KvTest { store: Mutex::new(std::collections::HashMap::new()) };
591///     hegel::stateful::run_concurrent(m, tc, 1, 3);
592/// }
593/// ```
594pub use hegel_macros::concurrent_state_machine;
595
596/// The main entrypoint into Hegel.
597///
598/// The function must take exactly one parameter of type [`TestCase`]. The test case can be
599/// used to generate values via [`TestCase::draw`].
600///
601/// The `#[test]` attribute is added automatically and must not be present on the function.
602///
603/// ```no_run
604/// use hegel::TestCase;
605/// use hegel::generators::integers;
606///
607/// #[hegel::test]
608/// fn my_test(tc: TestCase) {
609///     let x: i32 = tc.draw(integers());
610///     assert!(x + 0 == x);
611/// }
612/// ```
613///
614/// You can set settings using attributes on [`test`], corresponding to methods on [`Settings`]:
615///
616/// ```no_run
617/// use hegel::TestCase;
618/// use hegel::generators::integers;
619///
620/// #[hegel::test(test_cases = 500)]
621/// fn test_runs_many_more_times(tc: TestCase) {
622///     let x: i32 = tc.draw(integers());
623///     assert!(x + 0 == x);
624/// }
625/// ```
626///
627/// You can use other test attribute macros, like `tokio::test`, by putting them *before* `hegel::test`:
628///
629/// ```no_run
630/// #[tokio::test]
631/// #[hegel::test]
632/// async fn my_async_test(tc: hegel::TestCase) {
633///     let x: bool = tc.draw(hegel::generators::booleans());
634///     let handle = tokio::spawn(async move { x });
635///     assert_eq!(handle.await.unwrap(), x);
636/// }
637/// ```
638pub use hegel_macros::test;
639
640/// Turn a function into a standalone Hegel binary entry point.
641///
642/// The function must take exactly one parameter of type [`TestCase`]. Behaves
643/// like [`test`] — draws are rewritten to record variable names, and any
644/// `#[hegel::explicit_test_case]` attributes are run first — but instead of
645/// producing a `#[test]` it produces a plain function body that parses CLI
646/// arguments and runs a [`Hegel`] driver.
647///
648/// Each invocation of the binary runs exactly one test case: invalid cases
649/// (a failed [`assume`](TestCase::assume)) are retried until one valid case
650/// has run, and a failure is shrunk, reported, and persisted to the failure
651/// database as usual, so a failure found by one invocation is replayed by
652/// the next. The test-case count is the one thing that cannot be changed:
653/// `test_cases` is rejected as an attribute arg, `--test-cases` is not
654/// accepted on the command line, and `HEGEL_TEST_CASES` has no effect.
655///
656/// Supported CLI flags (with defaults taken from the attribute args):
657/// `--seed`, `--verbosity`, `--derandomize`, `--database`,
658/// `--suppress-health-check`, `--backend`, `-h` / `--help`.
659///
660/// ```no_run
661/// use hegel::TestCase;
662/// use hegel::generators as gs;
663///
664/// #[hegel::main]
665/// fn main(tc: TestCase) {
666///     let n: i32 = tc.draw(gs::integers());
667///     assert_eq!(n + 0, n);
668/// }
669/// ```
670pub use hegel_macros::main;
671
672/// Rewrite a function taking a [`TestCase`] plus additional arguments into
673/// one that takes just those arguments and internally runs Hegel.
674///
675/// Behaves like [`test`] for name rewriting, explicit test cases, and
676/// settings parsing. The generated function has the original signature
677/// with the `TestCase` parameter removed, and its body is run as an
678/// [`FnMut`] closure inside [`Hegel::run`].
679///
680/// ```no_run
681/// use hegel::TestCase;
682/// use hegel::generators as gs;
683///
684/// #[hegel::standalone_function(test_cases = 10)]
685/// fn check_addition_commutative(tc: TestCase, increment: i32) {
686///     let n: i32 = tc.draw(gs::integers());
687///     assert_eq!(n + increment, increment + n);
688/// }
689///
690/// // callers invoke it as a normal function:
691/// # fn _example() {
692/// check_addition_commutative(5);
693/// # }
694/// ```
695pub use hegel_macros::standalone_function;
696
697#[doc(hidden)]
698pub use cli::CliOutcome;
699#[doc(hidden)]
700pub use cli::apply_cli_args as __apply_cli_args;
701#[doc(hidden)]
702pub use runner::hegel;
703pub use runner::{Backend, HealthCheck, Hegel, Phase, Settings, Verbosity};