antithesis_sdk/assert/macros.rs
1#[cfg(feature = "full")]
2#[doc(hidden)]
3#[macro_export]
4macro_rules! function {
5 ($static:ident) => {
6 // Define a do-nothing function `'_f()'` within the context of
7 // the function invoking an assertion. Then the ``type_name`` of
8 // this do-nothing will be something like:
9 //
10 // bincrate::binmod::do_stuff::_f
11 //
12 // After trimming off the last three chars ``::_f`` what remains is
13 // the full path to the name of the function invoking the assertion
14 //
15 // The result will be stored as a lazily initialized statics in
16 // `$static`, so that it can be available at
17 // assertion catalog registration time.
18 use $crate::once_cell::sync::Lazy;
19 fn _f() {}
20 static $static: $crate::once_cell::sync::Lazy<&'static str> =
21 $crate::once_cell::sync::Lazy::new(|| {
22 fn type_name_of<T>(_: T) -> &'static str {
23 ::std::any::type_name::<T>()
24 }
25 let name = type_name_of(_f);
26 &name[..name.len() - 4]
27 });
28 };
29}
30
31/// Common handling used by all the assertion-related macros
32#[cfg(feature = "full")]
33#[doc(hidden)]
34#[macro_export]
35macro_rules! assert_helper {
36 // The handling of this pattern-arm of assert_helper
37 // is wrapped in a block {} to avoid name collisions
38 (condition = $condition:expr, $message:expr, $(details = $details:expr)?, $assert_type:path, $display_type:literal, must_hit = $must_hit:literal) => {{
39 // `$message` must be const evaluable
40 const _: &str = $message;
41
42 // Force evaluation of expressions.
43 let condition = $condition;
44 let details = &$crate::serde_json::json!({});
45 $(let details = $details;)?
46
47 $crate::function!(FUN_NAME);
48
49 use $crate::assert::AssertionCatalogInfo;
50 #[$crate::linkme::distributed_slice($crate::assert::ANTITHESIS_CATALOG)]
51 #[linkme(crate = $crate::linkme)] // Refer to our re-exported linkme.
52 static ALWAYS_CATALOG_ITEM: AssertionCatalogInfo = AssertionCatalogInfo {
53 assert_type: $assert_type,
54 display_type: $display_type,
55 condition: false,
56 message: $message,
57 class: ::std::module_path!(),
58 function: &FUN_NAME, /* function: &Lazy<&str> */
59 file: ::std::file!(),
60 begin_line: ::std::line!(),
61 begin_column: ::std::column!(),
62 must_hit: $must_hit,
63 id: $message,
64 };
65
66 let ptr_function = Lazy::force(&FUN_NAME);
67
68 static TRACKER: $crate::assert::TrackingInfo = $crate::assert::TrackingInfo::new();
69
70 $crate::assert::assert_impl(
71 $assert_type, /* assert_type */
72 $display_type, /* display_type */
73 condition, /* condition */
74 $message, /* message */
75 ::std::module_path!(), /* class */
76 *ptr_function, /* function */
77 ::std::file!(), /* file */
78 ::std::line!(), /* line */
79 ::std::column!(), /* column */
80 true, /* hit */
81 $must_hit, /* must-hit */
82 $message, /* id */
83 details, /* details */
84 Some(&TRACKER), /* tracker */
85 )
86 }}; // end pattern-arm block
87}
88
89#[cfg(not(feature = "full"))]
90#[doc(hidden)]
91#[macro_export]
92macro_rules! assert_helper {
93 (condition = $condition:expr, $message:expr, $(details = $details:expr)?, $assert_type:path, $display_type:literal, must_hit = $must_hit:literal) => {{
94 // `$message` must be const evaluable
95 const _: &str = $message;
96
97 // Force evaluation of expressions, ensuring that
98 // any side effects of these expressions will always be
99 // evaluated at runtime - even if the assertion itself
100 // is supressed by the `no-antithesis-sdk` feature
101 let condition = $condition;
102 $(let details = $details;)?
103 }};
104}
105
106/// Assert that ``condition`` is true every time this function is called, **and** that it is
107/// called at least once. The corresponding test property will be viewable in the ``Antithesis SDK: Always`` group of your triage report.
108///
109/// # Example
110///
111/// ```
112/// use serde::Serialize;
113/// use antithesis_sdk::{assert_always, random};
114///
115/// // Details can be any Serialize type, and are only serialized when the
116/// // assertion actually emits — on a sparse sample of passing and failing
117/// // evaluations. A plain struct of values and references like this
118/// // therefore costs nothing on the hot path.
119/// #[derive(Serialize)]
120/// struct Details<'a> {
121/// max_allowed: u64,
122/// actual: u64,
123/// source: &'a str,
124/// }
125///
126/// const MAX_ALLOWED: u64 = 100;
127/// let actual = random::get_random() % 100u64;
128/// let details = Details { max_allowed: MAX_ALLOWED, actual, source: "demo" };
129/// antithesis_sdk::assert_always!(actual < MAX_ALLOWED, "Value in range", &details);
130/// ```
131///
132/// Ensure that non-const-evaluable messages are rejected.
133///
134/// ```
135/// use serde_json::json;
136/// const MESSAGE: &str = concat!("Value", " in range");
137/// antithesis_sdk::assert_always!(true, MESSAGE, &json!({}));
138/// ```
139///
140/// A message computed at runtime is rejected at compile time:
141///
142/// ```compile_fail
143/// use serde_json::json;
144/// let message = String::from("Value in range");
145/// antithesis_sdk::assert_always!(true, message, &json!({}));
146/// ```
147///
148/// ```compile_fail
149/// use serde_json::json;
150/// antithesis_sdk::assert_always!(true, format!("{}", "Value in range"), &json!({}));
151/// ```
152#[macro_export]
153macro_rules! assert_always {
154 ($condition:expr, $message:expr$(, $details:expr)?) => {
155 $crate::assert_helper!(
156 condition = $condition,
157 $message,
158 $(details = $details)?,
159 $crate::assert::AssertType::Always,
160 "Always",
161 must_hit = true
162 )
163 };
164 ($($rest:tt)*) => {
165 ::std::compile_error!(
166r#"Invalid syntax when calling macro `assert_always`.
167Example usage:
168 `assert_always!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
169"#
170 );
171 };
172}
173
174/// Assert that ``condition`` is true every time this function is called. The corresponding test property will pass even if the assertion is never encountered.
175/// This test property will be viewable in the ``Antithesis SDK: Always`` group of your triage report.
176///
177/// # Example
178///
179/// ```
180/// use serde::Serialize;
181/// use antithesis_sdk::{assert_always_or_unreachable, random};
182///
183/// // Only serialized when the assertion emits; see assert_always.
184/// #[derive(Serialize)]
185/// struct Details { max_allowed: u64, actual: u64 }
186///
187/// const MAX_ALLOWED: u64 = 100;
188/// let actual = random::get_random() % 100u64;
189/// let details = Details { max_allowed: MAX_ALLOWED, actual };
190/// antithesis_sdk::assert_always_or_unreachable!(actual < MAX_ALLOWED, "Value in range", &details);
191/// ```
192#[macro_export]
193macro_rules! assert_always_or_unreachable {
194 ($condition:expr, $message:expr$(, $details:expr)?) => {
195 $crate::assert_helper!(
196 condition = $condition,
197 $message,
198 $(details = $details)?,
199 $crate::assert::AssertType::Always,
200 "AlwaysOrUnreachable",
201 must_hit = false
202 )
203 };
204 ($($rest:tt)*) => {
205 ::std::compile_error!(
206r#"Invalid syntax when calling macro `assert_always_or_unreachable`.
207Example usage:
208 `assert_always_or_unreachable!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
209"#
210 );
211 };
212}
213
214/// Assert that ``condition`` is true at least one time that this function was called.
215/// (If the assertion is never encountered, the test property will therefore fail.)
216/// This test property will be viewable in the ``Antithesis SDK: Sometimes`` group.
217///
218/// # Example
219///
220/// ```
221/// use serde::Serialize;
222/// use antithesis_sdk::{assert_sometimes, random};
223///
224/// // Only serialized when the assertion emits; see assert_always.
225/// #[derive(Serialize)]
226/// struct Details { max_allowed: u64, actual: u64 }
227///
228/// const MAX_ALLOWED: u64 = 100;
229/// let actual = random::get_random() % 120u64;
230/// let details = Details { max_allowed: MAX_ALLOWED, actual };
231/// antithesis_sdk::assert_sometimes!(actual > MAX_ALLOWED, "Value in range", &details);
232/// ```
233#[macro_export]
234macro_rules! assert_sometimes {
235 ($condition:expr, $message:expr$(, $details:expr)?) => {
236 $crate::assert_helper!(
237 condition = $condition,
238 $message,
239 $(details = $details)?,
240 $crate::assert::AssertType::Sometimes,
241 "Sometimes",
242 must_hit = true
243 )
244 };
245 ($($rest:tt)*) => {
246 ::std::compile_error!(
247r#"Invalid syntax when calling macro `assert_sometimes`.
248Example usage:
249 `assert_sometimes!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
250"#
251 );
252 };
253}
254
255/// Assert that a line of code is reached at least once.
256/// The corresponding test property will pass if this macro is ever called. (If it is never called the test property will therefore fail.)
257/// This test property will be viewable in the ``Antithesis SDK: Reachablity assertions`` group.
258///
259/// # Example
260///
261/// ```
262/// use serde::Serialize;
263/// use antithesis_sdk::{assert_reachable, random};
264///
265/// // Only serialized when the assertion emits; see assert_always.
266/// #[derive(Serialize)]
267/// struct Details { max_allowed: u64, actual: u64 }
268///
269/// const MAX_ALLOWED: u64 = 100;
270/// let actual = random::get_random() % 120u64;
271/// let details = Details { max_allowed: MAX_ALLOWED, actual };
272/// if (actual > MAX_ALLOWED) {
273/// antithesis_sdk::assert_reachable!("Value in range", &details);
274/// }
275/// ```
276#[macro_export]
277macro_rules! assert_reachable {
278 ($message:expr$(, $details:expr)?) => {
279 $crate::assert_helper!(
280 condition = true,
281 $message,
282 $(details = $details)?,
283 $crate::assert::AssertType::Reachability,
284 "Reachable",
285 must_hit = true
286 )
287 };
288 ($($rest:tt)*) => {
289 ::std::compile_error!(
290r#"Invalid syntax when calling macro `assert_reachable`.
291Example usage:
292 `assert_reachable!("assertion message (const &'static str)", &details_json_value_expr)`
293"#
294 );
295 };
296}
297
298/// Assert that a line of code is never reached.
299/// The corresponding test property will fail if this macro is ever called.
300/// (If it is never called the test property will therefore pass.)
301/// This test property will be viewable in the ``Antithesis SDK: Reachablity assertions`` group.
302///
303/// # Example
304///
305/// ```
306/// use serde::Serialize;
307/// use antithesis_sdk::{assert_unreachable, random};
308///
309/// // Only serialized when the assertion emits; see assert_always.
310/// #[derive(Serialize)]
311/// struct Details { max_allowed: u64, actual: u64 }
312///
313/// const MAX_ALLOWED: u64 = 100;
314/// let actual = random::get_random() % 120u64;
315/// let details = Details { max_allowed: MAX_ALLOWED, actual };
316/// if (actual > 120u64) {
317/// antithesis_sdk::assert_unreachable!("Value is above range", &details);
318/// }
319/// ```
320#[macro_export]
321macro_rules! assert_unreachable {
322 ($message:expr$(, $details:expr)?) => {
323 $crate::assert_helper!(
324 condition = false,
325 $message,
326 $(details = $details)?,
327 $crate::assert::AssertType::Reachability,
328 "Unreachable",
329 must_hit = false
330 )
331 };
332 ($($rest:tt)*) => {
333 ::std::compile_error!(
334r#"Invalid syntax when calling macro `assert_unreachable`.
335Example usage:
336 `assert_unreachable!("assertion message (const &'static str)", &details_json_value_expr)`
337"#
338 );
339 };
340}
341
342#[cfg(feature = "full")]
343#[doc(hidden)]
344#[macro_export]
345macro_rules! guidance_helper {
346 ($guidance_type:expr, $message:expr, $maximize:literal, $guidance_data:expr) => {
347 // `$message` must be const evaluable
348 const _: &str = $message;
349
350 $crate::function!(FUN_NAME);
351
352 use $crate::assert::guidance::{GuidanceCatalogInfo, GuidanceType};
353 #[$crate::linkme::distributed_slice($crate::assert::ANTITHESIS_GUIDANCE_CATALOG)]
354 #[linkme(crate = $crate::linkme)] // Refer to our re-exported linkme.
355 static GUIDANCE_CATALOG_ITEM: GuidanceCatalogInfo = GuidanceCatalogInfo {
356 guidance_type: $guidance_type,
357 message: $message,
358 id: $message,
359 class: ::std::module_path!(),
360 function: &FUN_NAME,
361 file: ::std::file!(),
362 begin_line: ::std::line!(),
363 begin_column: ::std::column!(),
364 maximize: $maximize,
365 };
366
367 $crate::assert::guidance::guidance_impl(
368 $guidance_type,
369 $message,
370 $message,
371 ::std::module_path!(),
372 *Lazy::force(&FUN_NAME),
373 ::std::file!(),
374 ::std::line!(),
375 ::std::column!(),
376 $maximize,
377 $guidance_data,
378 true,
379 )
380 };
381}
382
383#[cfg(feature = "full")]
384#[doc(hidden)]
385#[macro_export]
386macro_rules! numeric_guidance_helper {
387 ($assert:path, $op:tt, $maximize:literal, $left:expr, $right:expr, $message:expr$(, $details:expr)?) => {{
388 let left = $left;
389 let right = $right;
390 let details = &$crate::serde_json::json!({});
391 $(let details = $details;)?
392 let mut details = details.clone();
393 details["left"] = left.into();
394 details["right"] = right.into();
395 $assert!(left $op right, $message, &details);
396
397 // TODO: Right now it seems to be impossible for this macro to use the returned
398 // type of `diff` to instanciate the `T` in `Guard<T>`, which has to be
399 // explicitly provided for the static variable `GUARD`.
400 // Instead, we currently fix `T` to be `f64`, and ensure all implementations of `Diff` returns `f64`.
401 // Here are some related language limitations:
402 // - Although `typeof` is a reserved keyword in Rust, it is never implemented. See <https://stackoverflow.com/questions/64890774>.
403 // - Rust does not, and explicitly would not (see https://doc.rust-lang.org/reference/items/static-items.html#statics--generics), support generic static variable.
404 // - Type inference is not performed for static variable, i.e. `Guard<_>` is not allowed.
405 // - Some form of existential type can help, but that's only available in nightly Rust under feature `type_alias_impl_trait`.
406 //
407 // Other approaches I can think of either requires dynamic type tagging that has
408 // runtime overhead, or requires the user of the macro to explicitly provide the type,
409 // which is really not ergonomic and deviate from the APIs from other SDKs.
410 let diff = $crate::assert::guidance::Diff::diff(&left, right);
411 type Guard<T> = $crate::assert::guidance::Guard<$maximize, T>;
412 // TODO: Waiting for [type_alias_impl_trait](https://github.com/rust-lang/rust/issues/63063) to stabilize...
413 // type Distance = impl Minimal;
414 type Distance = f64;
415 static GUARD: Guard<Distance> = Guard::init();
416 if GUARD.should_emit(diff) {
417 let guidance_data = $crate::serde_json::json!({
418 "left": left,
419 "right": right,
420 });
421 $crate::guidance_helper!($crate::assert::guidance::GuidanceType::Numeric, $message, $maximize, guidance_data);
422 }
423 }};
424}
425
426#[cfg(not(feature = "full"))]
427#[doc(hidden)]
428#[macro_export]
429macro_rules! numeric_guidance_helper {
430 ($assert:path, $op:tt, $maximize:literal, $left:expr, $right:expr, $message:expr$(, $details:expr)?) => {{
431 // `$message` must be const evaluable
432 const _: &str = $message;
433 $assert!($left $op $right, $message$(, $details)?);
434 }};
435}
436
437#[cfg(feature = "full")]
438#[doc(hidden)]
439#[macro_export]
440macro_rules! boolean_guidance_helper {
441 ($assert:path, $all:literal, {$($name:ident: $cond:expr),*}, $message:expr$(, $details:expr)?) => {{
442 let details = &$crate::serde_json::json!({});
443 $(let details = $details;)?
444 let mut details = details.clone();
445 let (cond, guidance_data) = {
446 $(let $name = $cond;)*
447 $(details[::std::stringify!($name)] = $name.into();)*
448 (
449 if $all { true $(&& $name)* } else { false $(|| $name)* },
450 $crate::serde_json::json!({$(::std::stringify!($name): $name),*})
451 )
452 };
453 $assert!(cond, $message, &details);
454 $crate::guidance_helper!($crate::assert::guidance::GuidanceType::Boolean, $message, $all, guidance_data);
455 }};
456}
457
458#[cfg(not(feature = "full"))]
459#[doc(hidden)]
460#[macro_export]
461macro_rules! boolean_guidance_helper {
462 ($assert:path, $all:literal, {$($name:ident: $cond:expr),*}, $message:expr$(, $details:expr)?) => {{
463 let cond = {
464 $(let $name = $cond;)*
465 if $all { true $(&& $name)* } else { false $(|| $name)* }
466 };
467 $assert!(cond, $message$(, $details)?);
468 }};
469}
470
471/// `assert_always_greater_than(x, y, ...)` is mostly equivalent to `assert_always!(x > y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
472///
473/// Ensure that non-const-evaluable messages are rejected.
474///
475/// ```
476/// use serde_json::json;
477/// const MESSAGE: &str = concat!("x", " over y");
478/// antithesis_sdk::assert_always_greater_than!(2, 1, MESSAGE, &json!({}));
479/// ```
480///
481/// ```compile_fail
482/// use serde_json::json;
483/// antithesis_sdk::assert_always_greater_than!(2, 1, format!("{}", "x over y"), &json!({}));
484/// ```
485#[macro_export]
486macro_rules! assert_always_greater_than {
487 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
488 $crate::numeric_guidance_helper!($crate::assert_always, >, false, $left, $right, $message$(, $details)?)
489 };
490 ($($rest:tt)*) => {
491 ::std::compile_error!(
492r#"Invalid syntax when calling macro `assert_always_greater_than`.
493Example usage:
494 `assert_always_greater_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
495"#
496 );
497 };
498}
499
500/// `assert_always_greater_than_or_equal_to(x, y, ...)` is mostly equivalent to `assert_always!(x >= y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
501#[macro_export]
502macro_rules! assert_always_greater_than_or_equal_to {
503 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
504 $crate::numeric_guidance_helper!($crate::assert_always, >=, false, $left, $right, $message$(, $details)?)
505 };
506 ($($rest:tt)*) => {
507 ::std::compile_error!(
508r#"Invalid syntax when calling macro `assert_always_greater_than_or_equal_to`.
509Example usage:
510 `assert_always_greater_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
511"#
512 );
513 };
514}
515
516/// `assert_always_less_than(x, y, ...)` is mostly equivalent to `assert_always!(x < y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
517#[macro_export]
518macro_rules! assert_always_less_than {
519 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
520 $crate::numeric_guidance_helper!($crate::assert_always, <, true, $left, $right, $message$(, $details)?)
521 };
522 ($($rest:tt)*) => {
523 ::std::compile_error!(
524r#"Invalid syntax when calling macro `assert_always_less_than`.
525Example usage:
526 `assert_always_less_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
527"#
528 );
529 };
530}
531
532/// `assert_always_less_than_or_equal_to(x, y, ...)` is mostly equivalent to `assert_always!(x <= y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
533#[macro_export]
534macro_rules! assert_always_less_than_or_equal_to {
535 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
536 $crate::numeric_guidance_helper!($crate::assert_always, <=, true, $left, $right, $message$(, $details)?)
537 };
538 ($($rest:tt)*) => {
539 ::std::compile_error!(
540r#"Invalid syntax when calling macro `assert_always_less_than_or_equal_to`.
541Example usage:
542 `assert_always_less_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
543"#
544 );
545 };
546}
547
548/// `assert_sometimes_greater_than(x, y, ...)` is mostly equivalent to `assert_sometimes!(x > y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
549#[macro_export]
550macro_rules! assert_sometimes_greater_than {
551 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
552 $crate::numeric_guidance_helper!($crate::assert_sometimes, >, true, $left, $right, $message$(, $details)?)
553 };
554 ($($rest:tt)*) => {
555 ::std::compile_error!(
556r#"Invalid syntax when calling macro `assert_sometimes_greater_than`.
557Example usage:
558 `assert_sometimes_greater_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
559"#
560 );
561 };
562}
563
564/// `assert_sometimes_greater_than_or_equal_to(x, y, ...)` is mostly equivalent to `assert_sometimes!(x >= y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
565#[macro_export]
566macro_rules! assert_sometimes_greater_than_or_equal_to {
567 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
568 $crate::numeric_guidance_helper!($crate::assert_sometimes, >=, true, $left, $right, $message$(, $details)?)
569 };
570 ($($rest:tt)*) => {
571 ::std::compile_error!(
572r#"Invalid syntax when calling macro `assert_sometimes_greater_than_or_equal_to`.
573Example usage:
574 `assert_sometimes_greater_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
575"#
576 );
577 };
578}
579
580/// `assert_sometimes_less_than(x, y, ...)` is mostly equivalent to `assert_sometimes!(x < y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
581#[macro_export]
582macro_rules! assert_sometimes_less_than {
583 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
584 $crate::numeric_guidance_helper!($crate::assert_sometimes, <, false, $left, $right, $message$(, $details)?)
585 };
586 ($($rest:tt)*) => {
587 ::std::compile_error!(
588r#"Invalid syntax when calling macro `assert_sometimes_less_than`.
589Example usage:
590 `assert_sometimes_less_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
591"#
592 );
593 };
594}
595
596/// `assert_sometimes_less_than_or_equal_to(x, y, ...)` is mostly equivalent to `assert_sometimes!(x <= y, ...)`, except Antithesis has more visibility to the value of `x` and `y`, and the assertion details would be merged with `{"left": x, "right": y}`.
597#[macro_export]
598macro_rules! assert_sometimes_less_than_or_equal_to {
599 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
600 $crate::numeric_guidance_helper!($crate::assert_sometimes, <=, false, $left, $right, $message$(, $details)?)
601 };
602 ($($rest:tt)*) => {
603 ::std::compile_error!(
604r#"Invalid syntax when calling macro `assert_sometimes_less_than_or_equal_to`.
605Example usage:
606 `assert_sometimes_less_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
607"#
608 );
609 };
610}
611
612/// `assert_always_some({a: x, b: y, ...})` is similar to `assert_always(x || y || ...)`, except:
613/// - Antithesis has more visibility to the individual propositions.
614/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
615/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
616///
617/// Ensure that non-const-evaluable messages are rejected.
618///
619/// ```
620/// use serde_json::json;
621/// const MESSAGE: &str = concat!("at least ", "one");
622/// antithesis_sdk::assert_always_some!({a: true, b: false}, MESSAGE, &json!({}));
623/// ```
624///
625/// ```compile_fail
626/// use serde_json::json;
627/// antithesis_sdk::assert_always_some!({a: true, b: false}, format!("{}", "at least one"), &json!({}));
628/// ```
629#[macro_export]
630macro_rules! assert_always_some {
631 ({$($($name:ident: $cond:expr),+ $(,)?)?}, $message:expr$(, $details:expr)?) => {
632 $crate::boolean_guidance_helper!($crate::assert_always, false, {$($($name: $cond),+)?}, $message$(, $details)?);
633 };
634 ($($rest:tt)*) => {
635 ::std::compile_error!(
636r#"Invalid syntax when calling macro `assert_always_some`.
637Example usage:
638 `assert_always_some!({field1: cond1, field2: cond2, ...}, "assertion message (const &'static str)", &details_json_value_expr)`
639"#
640 );
641 };
642}
643
644/// `assert_sometimes_all({a: x, b: y, ...})` is similar to `assert_sometimes(x && y && ...)`, except:
645/// - Antithesis has more visibility to the individual propositions.
646/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
647/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
648#[macro_export]
649macro_rules! assert_sometimes_all {
650 ({$($($name:ident: $cond:expr),+ $(,)?)?}, $message:expr$(, $details:expr)?) => {
651 $crate::boolean_guidance_helper!($crate::assert_sometimes, true, {$($($name: $cond),+)?}, $message$(, $details)?);
652 };
653 ($($rest:tt)*) => {
654 ::std::compile_error!(
655r#"Invalid syntax when calling macro `assert_sometimes_all`.
656Example usage:
657 `assert_sometimes_all!({field1: cond1, field2: cond2, ...}, "assertion message (const &'static str)", &details_json_value_expr)`
658"#
659 );
660 };
661}