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