antithesis_sdk/lib.rs
1/// The assert module enables defining
2/// [test properties](https://antithesis.com/docs/properties_assertions/properties/)
3/// about your program or
4/// [workload](https://antithesis.com/docs/test_templates/first_test/).
5///
6/// The constant [const@LOCAL_OUTPUT] is associated with local logging, which is
7/// one of the
8/// [local execution](https://antithesis.com/docs/using_antithesis/sdk/rust/#sdk-runtime-behavior)
9/// modes.
10///
11/// Each macro/function in this module takes a parameter called ``message``,
12/// which is a string literal identifier used to aggregate assertions.
13/// Antithesis generates one test property per unique ``message`` This test
14/// property will be named ``message`` in the
15/// [triage report](https://antithesis.com/reports/example-triage-report).
16///
17/// Each macro/function also takes a parameter called ``details``, which is a
18/// key-value map of optional additional information provided by the user to add
19/// context for assertion failures. The information that is logged will appear
20/// in the ``logs`` section of a
21/// [triage report](https://antithesis.com/reports/example-triage-report).
22/// Normally the values in ``details`` are evaluated at runtime. Details may be
23/// borrowed structs or maps implementing [`serde::Serialize`]; serialization
24/// happens only when an assertion evaluation is emitted. Constructing the
25/// details argument still happens on every call, so `json!` at the call site is
26/// not deferred. Omitted details and values that serialize to null are omitted
27/// from the event; explicit empty objects are preserved. Other JSON values are
28/// wrapped in an object under `"value"`, for example `3` becomes
29/// `{"value": 3}`. Serialization errors emit an `antithesis_error` message and
30/// omit details without suppressing the assertion. Declarations never include
31/// details.
32pub mod assert;
33
34#[doc(hidden)]
35pub mod details;
36
37// External crates used in assertion macros
38#[doc(hidden)]
39#[cfg(feature = "full")]
40pub use linkme;
41#[doc(hidden)]
42#[cfg(feature = "full")]
43pub use once_cell;
44#[doc(hidden)]
45#[cfg(feature = "full")]
46pub use serde_json;
47
48/// The catalog module gives read access to the assertion catalog compiled
49/// into this binary: every assertion macro call site linked into the
50/// program, whether or not that code ever runs.
51///
52/// It exists for tooling rather than for workloads: comparing the assertions
53/// a binary declares against the ones a local test suite encounters, failing
54/// CI when a change silently drops an assertion, or checking an inventory of
55/// test properties into the repository. Workloads use [`assert`](mod@crate::assert)
56/// and [`random`] instead and never need this module.
57///
58/// [`catalog::assertions()`] lists the assertions as values.
59/// [`catalog::write()`] writes them as the same JSON lines, in the same
60/// order, that [`antithesis_init()`] emits for them, without init having
61/// run. A binary can expose its own catalog with a few lines:
62///
63/// ```no_run
64/// use std::io::stdout;
65///
66/// fn main() -> std::io::Result<()> {
67/// if std::env::args().any(|a| a == "--antithesis-catalog") {
68/// return antithesis_sdk::catalog::write(stdout());
69/// }
70/// // ... the actual program ...
71/// Ok(())
72/// }
73/// ```
74pub mod catalog;
75
76/// The lifecycle module contains functions which inform the Antithesis
77/// environment that particular test phases or milestones have been reached.
78///
79/// The constant [const@LOCAL_OUTPUT] is associated with local logging, which is
80/// one of the
81/// [local execution](https://antithesis.com/docs/using_antithesis/sdk/rust/#sdk-runtime-behavior)
82/// modes.
83pub mod lifecycle;
84
85/// The random module provides functions that request both structured and
86/// unstructured randomness from the Antithesis environment.
87///
88/// These functions should not be used to seed a conventional PRNG, and should
89/// not have their return values stored and used to make a decision at a later
90/// time. Doing either of these things makes it much harder for the Antithesis
91/// platform to control the history of your program's execution, and also makes
92/// it harder for Antithesis to learn which inputs provided at which times are
93/// most fruitful. Instead, you should call a function from the random package
94/// every time your program or
95/// [workload](https://antithesis.com/docs/test_templates/first_test/) needs to
96/// make a decision, at the moment that you need to make the decision.
97///
98/// These functions are also safe to call outside the Antithesis environment,
99/// where they will fall back on the rust std library implementation.
100///
101/// # `rand` Integration
102///
103/// [`AntithesisRng`](crate::random::AntithesisRng) plugs the same
104/// Antithesis-controlled randomness into the `rand` ecosystem. Enable the
105/// feature flag that matches the version of `rand` your project already uses:
106///
107/// | Your `rand` version | Feature flag | Trait implemented |
108/// |----------------------|----------------------------|------------------------------|
109/// | 0.8 | `rand_v0_8` **(default)** | [`rand_core::RngCore`](https://docs.rs/rand_core/0.6/rand_core/trait.RngCore.html) |
110/// | 0.9 | `rand_v0_9` | [`rand_core::RngCore`](https://docs.rs/rand_core/0.9/rand_core/trait.RngCore.html) |
111/// | 0.10 | `rand_v0_10` | [`rand_core::TryRng`](https://docs.rs/rand_core/0.10/rand_core/trait.TryRng.html) |
112///
113/// ## Setup
114///
115/// Pick the flag matching your `rand` version. For example, with `rand 0.9`:
116///
117/// ```toml
118/// [dependencies]
119/// antithesis_sdk = { version = "0.3", features = ["rand_v0_9"] }
120/// rand = "0.9"
121/// ```
122///
123/// Multiple flags can coexist if your dependency tree includes more than one
124/// `rand` version.
125pub mod random;
126
127mod internal;
128
129/// Convenience to import all macros and functions
130pub mod prelude;
131
132/// Global initialization logic. Performs registration of the Antithesis
133/// assertion catalog. This should be invoked as early as possible during
134/// program execution. It is recommended to call it immediately in ``main``.
135///
136/// If called more than once, only the first call will result in the assertion
137/// catalog being registered. If never called, the assertion catalog will be
138/// registered when it encounters the first assertion at runtime.
139///
140/// Example:
141///
142/// ```
143/// use std::env;
144/// use serde_json::{json};
145/// use antithesis_sdk::{antithesis_init, assert_unreachable};
146///
147/// fn main() {
148/// if (env::args_os().len() == 1888999778899) {
149/// assert_unreachable!("Unable to provide trillions of arguments", &json!({}));
150/// }
151///
152/// // if antithesis_init() is omitted, the above unreachable will
153/// // not be reported
154/// antithesis_init();
155/// }
156/// ```
157#[allow(clippy::needless_doctest_main)]
158pub fn antithesis_init() {
159 init();
160}
161
162#[cfg(feature = "full")]
163fn init() {
164 Lazy::force(&internal::LIB_HANDLER);
165 Lazy::force(&assert::INIT_CATALOG);
166}
167
168#[cfg(not(feature = "full"))]
169fn init() {}
170
171#[cfg(feature = "full")]
172use once_cell::sync::Lazy;
173
174/// A constant provided by the SDK to report the location of logged output when
175/// run locally. This constant is the name of an environment variable
176/// ``ANTITHESIS_SDK_LOCAL_OUTPUT``. ``ANTITHESIS_SDK_LOCAL_OUTPUT`` is a path
177/// to a file that can be created and written to when running locally. If this
178/// environment variable is not present at runtime, then no assertion and
179/// lifecycle output will be attempted.
180///
181/// This allows you to make use of the Antithesis assertions module in your
182/// regular testing, or even in production. In particular, very few assertions
183/// frameworks offer a convenient way to define
184/// [Sometimes assertions](https://antithesis.com/docs/best_practices/sometimes_assertions/),
185/// but they can be quite useful even outside Antithesis.
186///
187/// See also the documentation for
188/// [local execution](https://antithesis.com/docs/using_antithesis/sdk/rust/#sdk-runtime-behavior).
189pub use crate::internal::LOCAL_OUTPUT;