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::Value::Null;
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,
107/// **and** that it is called at least once. The corresponding test property
108/// will be viewable in the ``Antithesis SDK: Always`` group of your triage
109/// report.
110///
111/// # Example
112///
113/// ```
114/// use serde::Serialize;
115/// use antithesis_sdk::{assert_always, random};
116///
117/// // Details can be any Serialize type; non-null scalars and arrays are
118/// // wrapped under "value".
119/// // A borrowed struct avoids serialization on evaluations that are not emitted.
120/// #[derive(Serialize)]
121/// struct Details<'a> {
122/// max_allowed: u64,
123/// actual: u64,
124/// source: &'a str,
125/// }
126///
127/// const MAX_ALLOWED: u64 = 100;
128/// let actual = random::get_random() % 100u64;
129/// let details = Details { max_allowed: MAX_ALLOWED, actual, source: "demo" };
130/// antithesis_sdk::assert_always!(actual < MAX_ALLOWED, "Value in range", &details);
131/// ```
132///
133/// Ensure that non-const-evaluable messages are rejected.
134///
135/// ```
136/// use serde_json::json;
137/// const MESSAGE: &str = concat!("Value", " in range");
138/// antithesis_sdk::assert_always!(true, MESSAGE, &json!({}));
139/// ```
140///
141/// A message computed at runtime is rejected at compile time:
142///
143/// ```compile_fail
144/// use serde_json::json;
145/// let message = String::from("Value in range");
146/// antithesis_sdk::assert_always!(true, message, &json!({}));
147/// ```
148///
149/// ```compile_fail
150/// use serde_json::json;
151/// antithesis_sdk::assert_always!(true, format!("{}", "Value in range"), &json!({}));
152/// ```
153#[macro_export]
154macro_rules! assert_always {
155 ($condition:expr, $message:expr$(, $details:expr)?) => {
156 $crate::assert_helper!(
157 condition = $condition,
158 $message,
159 $(details = $details)?,
160 $crate::assert::AssertType::Always,
161 "Always",
162 must_hit = true
163 )
164 };
165 ($($rest:tt)*) => {
166 ::std::compile_error!(
167r#"Invalid syntax when calling macro `assert_always`.
168Example usage:
169 `assert_always!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
170"#
171 );
172 };
173}
174
175/// Assert that ``condition`` is true every time this function is called. The
176/// corresponding test property will pass even if the assertion is never
177/// encountered. This test property will be viewable in the
178/// ``Antithesis SDK: Always`` group of your triage report.
179///
180/// # Example
181///
182/// ```
183/// use serde::Serialize;
184/// use antithesis_sdk::{assert_always_or_unreachable, random};
185///
186/// // Only serialized when the assertion emits; see assert_always.
187/// #[derive(Serialize)]
188/// struct Details { max_allowed: u64, actual: u64 }
189///
190/// const MAX_ALLOWED: u64 = 100;
191/// let actual = random::get_random() % 100u64;
192/// let details = Details { max_allowed: MAX_ALLOWED, actual };
193/// antithesis_sdk::assert_always_or_unreachable!(actual < MAX_ALLOWED, "Value in range", &details);
194/// ```
195#[macro_export]
196macro_rules! assert_always_or_unreachable {
197 ($condition:expr, $message:expr$(, $details:expr)?) => {
198 $crate::assert_helper!(
199 condition = $condition,
200 $message,
201 $(details = $details)?,
202 $crate::assert::AssertType::Always,
203 "AlwaysOrUnreachable",
204 must_hit = false
205 )
206 };
207 ($($rest:tt)*) => {
208 ::std::compile_error!(
209r#"Invalid syntax when calling macro `assert_always_or_unreachable`.
210Example usage:
211 `assert_always_or_unreachable!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
212"#
213 );
214 };
215}
216
217/// Assert that ``condition`` is true at least one time that this function was
218/// called. (If the assertion is never encountered, the test property will
219/// therefore fail.) This test property will be viewable in the
220/// ``Antithesis SDK: Sometimes`` group.
221///
222/// # Example
223///
224/// ```
225/// use serde::Serialize;
226/// use antithesis_sdk::{assert_sometimes, random};
227///
228/// // Only serialized when the assertion emits; see assert_always.
229/// #[derive(Serialize)]
230/// struct Details { max_allowed: u64, actual: u64 }
231///
232/// const MAX_ALLOWED: u64 = 100;
233/// let actual = random::get_random() % 120u64;
234/// let details = Details { max_allowed: MAX_ALLOWED, actual };
235/// antithesis_sdk::assert_sometimes!(actual > MAX_ALLOWED, "Value in range", &details);
236/// ```
237#[macro_export]
238macro_rules! assert_sometimes {
239 ($condition:expr, $message:expr$(, $details:expr)?) => {
240 $crate::assert_helper!(
241 condition = $condition,
242 $message,
243 $(details = $details)?,
244 $crate::assert::AssertType::Sometimes,
245 "Sometimes",
246 must_hit = true
247 )
248 };
249 ($($rest:tt)*) => {
250 ::std::compile_error!(
251r#"Invalid syntax when calling macro `assert_sometimes`.
252Example usage:
253 `assert_sometimes!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
254"#
255 );
256 };
257}
258
259/// Assert that a line of code is reached at least once. The corresponding test
260/// property will pass if this macro is ever called. (If it is never called the
261/// test property will therefore fail.) This test property will be viewable in
262/// the ``Antithesis SDK: Reachablity assertions`` group.
263///
264/// # Example
265///
266/// ```
267/// use serde::Serialize;
268/// use antithesis_sdk::{assert_reachable, random};
269///
270/// // Only serialized when the assertion emits; see assert_always.
271/// #[derive(Serialize)]
272/// struct Details { max_allowed: u64, actual: u64 }
273///
274/// const MAX_ALLOWED: u64 = 100;
275/// let actual = random::get_random() % 120u64;
276/// let details = Details { max_allowed: MAX_ALLOWED, actual };
277/// if (actual > MAX_ALLOWED) {
278/// antithesis_sdk::assert_reachable!("Value in range", &details);
279/// }
280/// ```
281#[macro_export]
282macro_rules! assert_reachable {
283 ($message:expr$(, $details:expr)?) => {
284 $crate::assert_helper!(
285 condition = true,
286 $message,
287 $(details = $details)?,
288 $crate::assert::AssertType::Reachability,
289 "Reachable",
290 must_hit = true
291 )
292 };
293 ($($rest:tt)*) => {
294 ::std::compile_error!(
295r#"Invalid syntax when calling macro `assert_reachable`.
296Example usage:
297 `assert_reachable!("assertion message (const &'static str)", &details_json_value_expr)`
298"#
299 );
300 };
301}
302
303/// Assert that a line of code is never reached. The corresponding test property
304/// will fail if this macro is ever called. (If it is never called the test
305/// property will therefore pass.) This test property will be viewable in the
306/// ``Antithesis SDK: Reachablity assertions`` group.
307///
308/// # Example
309///
310/// ```
311/// use serde::Serialize;
312/// use antithesis_sdk::{assert_unreachable, random};
313///
314/// // Only serialized when the assertion emits; see assert_always.
315/// #[derive(Serialize)]
316/// struct Details { max_allowed: u64, actual: u64 }
317///
318/// const MAX_ALLOWED: u64 = 100;
319/// let actual = random::get_random() % 120u64;
320/// let details = Details { max_allowed: MAX_ALLOWED, actual };
321/// if (actual > 120u64) {
322/// antithesis_sdk::assert_unreachable!("Value is above range", &details);
323/// }
324/// ```
325#[macro_export]
326macro_rules! assert_unreachable {
327 ($message:expr$(, $details:expr)?) => {
328 $crate::assert_helper!(
329 condition = false,
330 $message,
331 $(details = $details)?,
332 $crate::assert::AssertType::Reachability,
333 "Unreachable",
334 must_hit = false
335 )
336 };
337 ($($rest:tt)*) => {
338 ::std::compile_error!(
339r#"Invalid syntax when calling macro `assert_unreachable`.
340Example usage:
341 `assert_unreachable!("assertion message (const &'static str)", &details_json_value_expr)`
342"#
343 );
344 };
345}
346
347#[cfg(feature = "full")]
348#[doc(hidden)]
349#[macro_export]
350macro_rules! guidance_helper {
351 ($guidance_type:expr, $message:expr, $maximize:literal, $guidance_data:expr) => {
352 // `$message` must be const evaluable
353 const _: &str = $message;
354
355 $crate::function!(FUN_NAME);
356
357 use $crate::assert::guidance::{GuidanceCatalogInfo, GuidanceType};
358 #[$crate::linkme::distributed_slice($crate::assert::ANTITHESIS_GUIDANCE_CATALOG)]
359 #[linkme(crate = $crate::linkme)] // Refer to our re-exported linkme.
360 static GUIDANCE_CATALOG_ITEM: GuidanceCatalogInfo = GuidanceCatalogInfo {
361 guidance_type: $guidance_type,
362 message: $message,
363 id: $message,
364 class: ::std::module_path!(),
365 function: &FUN_NAME,
366 file: ::std::file!(),
367 begin_line: ::std::line!(),
368 begin_column: ::std::column!(),
369 maximize: $maximize,
370 };
371
372 $crate::assert::guidance::guidance_impl(
373 $guidance_type,
374 $message,
375 $message,
376 ::std::module_path!(),
377 *Lazy::force(&FUN_NAME),
378 ::std::file!(),
379 ::std::line!(),
380 ::std::column!(),
381 $maximize,
382 $guidance_data,
383 true,
384 )
385 };
386}
387
388#[cfg(feature = "full")]
389#[doc(hidden)]
390#[macro_export]
391macro_rules! numeric_guidance_helper {
392 ($assert:path, $op:tt, $maximize:literal, $left:expr, $right:expr, $message:expr$(, $details:expr)?) => {{
393 let left = $left;
394 let right = $right;
395 let details = &$crate::serde_json::Value::Null;
396 $(let details = $details;)?
397 let details = $crate::details::WithGuidance {
398 details,
399 guidance: || $crate::serde_json::json!({ "left": left, "right": right }),
400 context: $message,
401 };
402 $assert!(left $op right, $message, &details);
403
404 // TODO: Right now it seems to be impossible for this macro to use the returned
405 // type of `diff` to instanciate the `T` in `Guard<T>`, which has to be
406 // explicitly provided for the static variable `GUARD`.
407 // Instead, we currently fix `T` to be `f64`, and ensure all implementations of `Diff` returns `f64`.
408 // Here are some related language limitations:
409 // - Although `typeof` is a reserved keyword in Rust, it is never implemented. See <https://stackoverflow.com/questions/64890774>.
410 // - Rust does not, and explicitly would not (see https://doc.rust-lang.org/reference/items/static-items.html#statics--generics), support generic static variable.
411 // - Type inference is not performed for static variable, i.e. `Guard<_>` is not allowed.
412 // - Some form of existential type can help, but that's only available in nightly Rust under feature `type_alias_impl_trait`.
413 //
414 // Other approaches I can think of either requires dynamic type tagging that has
415 // runtime overhead, or requires the user of the macro to explicitly provide the type,
416 // which is really not ergonomic and deviate from the APIs from other SDKs.
417 let diff = $crate::assert::guidance::Diff::diff(&left, right);
418 type Guard<T> = $crate::assert::guidance::Guard<$maximize, T>;
419 // TODO: Waiting for [type_alias_impl_trait](https://github.com/rust-lang/rust/issues/63063) to stabilize...
420 // type Distance = impl Minimal;
421 type Distance = f64;
422 static GUARD: Guard<Distance> = Guard::init();
423 if GUARD.should_emit(diff) {
424 let guidance_data = $crate::serde_json::json!({ "left": left, "right": right });
425 $crate::guidance_helper!($crate::assert::guidance::GuidanceType::Numeric, $message, $maximize, guidance_data);
426 }
427 }};
428}
429
430#[cfg(not(feature = "full"))]
431#[doc(hidden)]
432#[macro_export]
433macro_rules! numeric_guidance_helper {
434 ($assert:path, $op:tt, $maximize:literal, $left:expr, $right:expr, $message:expr$(, $details:expr)?) => {{
435 // `$message` must be const evaluable
436 const _: &str = $message;
437 $assert!($left $op $right, $message$(, $details)?);
438 }};
439}
440
441#[cfg(feature = "full")]
442#[doc(hidden)]
443#[macro_export]
444macro_rules! boolean_guidance_helper {
445 ($assert:path, $all:literal, {$($name:ident: $cond:expr),*}, $message:expr$(, $details:expr)?) => {{
446 let details = &$crate::serde_json::Value::Null;
447 $(let details = $details;)?
448 let (cond, guidance_data) = {
449 $(let $name = $cond;)*
450 (
451 if $all { true $(&& $name)* } else { false $(|| $name)* },
452 $crate::serde_json::json!({$(::std::stringify!($name): $name),*})
453 )
454 };
455 let details = $crate::details::WithGuidance {
456 details,
457 guidance: || guidance_data.clone(),
458 context: $message,
459 };
460 $assert!(cond, $message, &details);
461 $crate::guidance_helper!($crate::assert::guidance::GuidanceType::Boolean, $message, $all, guidance_data);
462 }};
463}
464
465#[cfg(not(feature = "full"))]
466#[doc(hidden)]
467#[macro_export]
468macro_rules! boolean_guidance_helper {
469 ($assert:path, $all:literal, {$($name:ident: $cond:expr),*}, $message:expr$(, $details:expr)?) => {{
470 let cond = {
471 $(let $name = $cond;)*
472 if $all { true $(&& $name)* } else { false $(|| $name)* }
473 };
474 $assert!(cond, $message$(, $details)?);
475 }};
476}
477
478/// `assert_always_greater_than(x, y, ...)` is mostly equivalent to
479/// `assert_always!(x > y, ...)`, except Antithesis has more visibility to the
480/// value of `x` and `y`, and the assertion details would be merged with
481/// `{"left": x, "right": y}`.
482///
483/// Ensure that non-const-evaluable messages are rejected.
484///
485/// ```
486/// use serde_json::json;
487/// const MESSAGE: &str = concat!("x", " over y");
488/// antithesis_sdk::assert_always_greater_than!(2, 1, MESSAGE, &json!({}));
489/// ```
490///
491/// ```compile_fail
492/// use serde_json::json;
493/// antithesis_sdk::assert_always_greater_than!(2, 1, format!("{}", "x over y"), &json!({}));
494/// ```
495#[macro_export]
496macro_rules! assert_always_greater_than {
497 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
498 $crate::numeric_guidance_helper!($crate::assert_always, >, false, $left, $right, $message$(, $details)?)
499 };
500 ($($rest:tt)*) => {
501 ::std::compile_error!(
502r#"Invalid syntax when calling macro `assert_always_greater_than`.
503Example usage:
504 `assert_always_greater_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
505"#
506 );
507 };
508}
509
510/// `assert_always_greater_than_or_equal_to(x, y, ...)` is mostly equivalent to
511/// `assert_always!(x >= y, ...)`, except Antithesis has more visibility to the
512/// value of `x` and `y`, and the assertion details would be merged with
513/// `{"left": x, "right": y}`.
514#[macro_export]
515macro_rules! assert_always_greater_than_or_equal_to {
516 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
517 $crate::numeric_guidance_helper!($crate::assert_always, >=, false, $left, $right, $message$(, $details)?)
518 };
519 ($($rest:tt)*) => {
520 ::std::compile_error!(
521r#"Invalid syntax when calling macro `assert_always_greater_than_or_equal_to`.
522Example usage:
523 `assert_always_greater_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
524"#
525 );
526 };
527}
528
529/// `assert_always_less_than(x, y, ...)` is mostly equivalent to
530/// `assert_always!(x < y, ...)`, except Antithesis has more visibility to the
531/// value of `x` and `y`, and the assertion details would be merged with
532/// `{"left": x, "right": y}`.
533#[macro_export]
534macro_rules! assert_always_less_than {
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`.
541Example usage:
542 `assert_always_less_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
543"#
544 );
545 };
546}
547
548/// `assert_always_less_than_or_equal_to(x, y, ...)` is mostly equivalent to
549/// `assert_always!(x <= y, ...)`, except Antithesis has more visibility to the
550/// value of `x` and `y`, and the assertion details would be merged with
551/// `{"left": x, "right": y}`.
552#[macro_export]
553macro_rules! assert_always_less_than_or_equal_to {
554 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
555 $crate::numeric_guidance_helper!($crate::assert_always, <=, true, $left, $right, $message$(, $details)?)
556 };
557 ($($rest:tt)*) => {
558 ::std::compile_error!(
559r#"Invalid syntax when calling macro `assert_always_less_than_or_equal_to`.
560Example usage:
561 `assert_always_less_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
562"#
563 );
564 };
565}
566
567/// `assert_sometimes_greater_than(x, y, ...)` is mostly equivalent to
568/// `assert_sometimes!(x > y, ...)`, except Antithesis has more visibility to
569/// the value of `x` and `y`, and the assertion details would be merged with
570/// `{"left": x, "right": y}`.
571#[macro_export]
572macro_rules! assert_sometimes_greater_than {
573 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
574 $crate::numeric_guidance_helper!($crate::assert_sometimes, >, true, $left, $right, $message$(, $details)?)
575 };
576 ($($rest:tt)*) => {
577 ::std::compile_error!(
578r#"Invalid syntax when calling macro `assert_sometimes_greater_than`.
579Example usage:
580 `assert_sometimes_greater_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
581"#
582 );
583 };
584}
585
586/// `assert_sometimes_greater_than_or_equal_to(x, y, ...)` is mostly equivalent
587/// to `assert_sometimes!(x >= y, ...)`, except Antithesis has more visibility
588/// to the value of `x` and `y`, and the assertion details would be merged with
589/// `{"left": x, "right": y}`.
590#[macro_export]
591macro_rules! assert_sometimes_greater_than_or_equal_to {
592 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
593 $crate::numeric_guidance_helper!($crate::assert_sometimes, >=, true, $left, $right, $message$(, $details)?)
594 };
595 ($($rest:tt)*) => {
596 ::std::compile_error!(
597r#"Invalid syntax when calling macro `assert_sometimes_greater_than_or_equal_to`.
598Example usage:
599 `assert_sometimes_greater_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
600"#
601 );
602 };
603}
604
605/// `assert_sometimes_less_than(x, y, ...)` is mostly equivalent to
606/// `assert_sometimes!(x < y, ...)`, except Antithesis has more visibility to
607/// the value of `x` and `y`, and the assertion details would be merged with
608/// `{"left": x, "right": y}`.
609#[macro_export]
610macro_rules! assert_sometimes_less_than {
611 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
612 $crate::numeric_guidance_helper!($crate::assert_sometimes, <, false, $left, $right, $message$(, $details)?)
613 };
614 ($($rest:tt)*) => {
615 ::std::compile_error!(
616r#"Invalid syntax when calling macro `assert_sometimes_less_than`.
617Example usage:
618 `assert_sometimes_less_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
619"#
620 );
621 };
622}
623
624/// `assert_sometimes_less_than_or_equal_to(x, y, ...)` is mostly equivalent to
625/// `assert_sometimes!(x <= y, ...)`, except Antithesis has more visibility to
626/// the value of `x` and `y`, and the assertion details would be merged with
627/// `{"left": x, "right": y}`.
628#[macro_export]
629macro_rules! assert_sometimes_less_than_or_equal_to {
630 ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
631 $crate::numeric_guidance_helper!($crate::assert_sometimes, <=, false, $left, $right, $message$(, $details)?)
632 };
633 ($($rest:tt)*) => {
634 ::std::compile_error!(
635r#"Invalid syntax when calling macro `assert_sometimes_less_than_or_equal_to`.
636Example usage:
637 `assert_sometimes_less_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
638"#
639 );
640 };
641}
642
643/// `assert_always_some({a: x, b: y, ...})` is similar to `assert_always(x || y
644/// || ...)`, 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///
649/// Ensure that non-const-evaluable messages are rejected.
650///
651/// ```
652/// use serde_json::json;
653/// const MESSAGE: &str = concat!("at least ", "one");
654/// antithesis_sdk::assert_always_some!({a: true, b: false}, MESSAGE, &json!({}));
655/// ```
656///
657/// ```compile_fail
658/// use serde_json::json;
659/// antithesis_sdk::assert_always_some!({a: true, b: false}, format!("{}", "at least one"), &json!({}));
660/// ```
661#[macro_export]
662macro_rules! assert_always_some {
663 ({$($($name:ident: $cond:expr),+ $(,)?)?}, $message:expr$(, $details:expr)?) => {
664 $crate::boolean_guidance_helper!($crate::assert_always, false, {$($($name: $cond),+)?}, $message$(, $details)?);
665 };
666 ($($rest:tt)*) => {
667 ::std::compile_error!(
668r#"Invalid syntax when calling macro `assert_always_some`.
669Example usage:
670 `assert_always_some!({field1: cond1, field2: cond2, ...}, "assertion message (const &'static str)", &details_json_value_expr)`
671"#
672 );
673 };
674}
675
676/// `assert_sometimes_all({a: x, b: y, ...})` is similar to
677/// `assert_sometimes(x && y && ...)`, except:
678/// - Antithesis has more visibility to the individual propositions.
679/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
680/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
681#[macro_export]
682macro_rules! assert_sometimes_all {
683 ({$($($name:ident: $cond:expr),+ $(,)?)?}, $message:expr$(, $details:expr)?) => {
684 $crate::boolean_guidance_helper!($crate::assert_sometimes, true, {$($($name: $cond),+)?}, $message$(, $details)?);
685 };
686 ($($rest:tt)*) => {
687 ::std::compile_error!(
688r#"Invalid syntax when calling macro `assert_sometimes_all`.
689Example usage:
690 `assert_sometimes_all!({field1: cond1, field2: cond2, ...}, "assertion message (const &'static str)", &details_json_value_expr)`
691"#
692 );
693 };
694}