Skip to content

Commit 7a740eb

Browse files
Add comments for rich assertions.
1 parent d2030ad commit 7a740eb

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

lib/src/assert/guidance.rs

+18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ use crate::internal;
88

99
use super::AntithesisLocationInfo;
1010

11+
// Types and traits that model the SDK filtering of numerical guidance reporting.
12+
// For assertions like "always (x < y)", we would like to only report the most extreme
13+
// violations seen so far, which is implemented by having a `Guard` that keep a maximizing
14+
// watermark on the difference (x - y).
15+
// The `AtomicMinMax` trait requirement allows multiple concurrent update to the watermark.
16+
17+
// NOTE: The structures setup in this modules allow `Guard` to be generic over the numeric
18+
// type (or even any partially ordered type).
19+
// But due to some limitation of stable Rust, we are only instanciating `Guard<f64>` by
20+
// converting the result of all `x - y` into `f64`.
21+
// See the impl `numeric_guidance_helper` for more details on the limitation.
22+
// Once that is lifted, some implementations of `Diff` can be changed to truly take advantage
23+
// of the zero-cost polymorphism that `Guard` provides.
24+
1125
pub struct Guard<const MAX: bool, T: AtomicMinMax> {
1226
mark: T::Atomic,
1327
}
@@ -71,6 +85,8 @@ macro_rules! impl_extremal_atomic {
7185

7286
impl_extremal_atomic! { (AtomicUsize, usize) (AtomicU8, u8) (AtomicU16, u16) (AtomicU32, u32) (AtomicU64, u64) (AtomicIsize, isize) (AtomicI8, i8) (AtomicI16, i16) (AtomicI32, i32) (AtomicI64, i64) }
7387

88+
// For atomic floats, their minimal/maximal elements are `-inf` and `+inf` respectively.
89+
7490
impl Extremal for AtomicF32 {
7591
const MIN: Self = AtomicF32(AtomicU32::new(0xff800000));
7692
const MAX: Self = AtomicF32(AtomicU32::new(0x7f800000));
@@ -106,6 +122,8 @@ macro_rules! impl_atomic_min_max_float {
106122
impl AtomicMinMax for $t {
107123
type Atomic = $atomic_t;
108124

125+
// TODO: Check the atomic orderings are used properly in general.
126+
// Right now we are always passing SeqCst, which should be fine.
109127
fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self {
110128
<$t>::from_bits(current.0.fetch_update(ordering, ordering, |x| Some(<$t>::from_bits(x).min(other).to_bits())).unwrap())
111129
}

lib/src/assert/macros.rs

+29
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,19 @@ macro_rules! numeric_guidance_helper {
295295
"left": left,
296296
"right": right,
297297
});
298+
// TODO: Right now it seems to be impossible for this macro to use the returned
299+
// type of `diff` to instanciate the `T` in `Guard<T>`, which has to be
300+
// explicitly provided for the static variable `GUARD`.
301+
// Instead, we currently fix `T` to be `f64`, and ensure all implementations of `Diff` returns `f64`.
302+
// Here are some related language limitations:
303+
// - Although `typeof` is a reserved keyword in Rust, it is never implemented. See <https://stackoverflow.com/questions/64890774>.
304+
// - Rust does not, and explicitly would not (see https://doc.rust-lang.org/reference/items/static-items.html#statics--generics), support generic static variable.
305+
// - Type inference is not performed for static variable, i.e. `Guard<_>` is not allowed.
306+
// - Some form of existential type can help, but that's only available in nightly Rust under feature `type_alias_impl_trait`.
307+
//
308+
// Other approaches I can think of either requires dynamic type tagging that has
309+
// runtime overhead, or requires the user of the macro to explicitly provide the type,
310+
// which is really not ergonomic and deviate from the APIs from other SDKs.
298311
let diff = $crate::assert::guidance::Diff::diff(&left, right);
299312
type Guard<T> = $crate::assert::guidance::Guard<$maximize, T>;
300313
// TODO: Waiting for [type_alias_impl_trait](https://github.com/rust-lang/rust/issues/63063) to stabilize...
@@ -345,69 +358,85 @@ macro_rules! boolean_guidance_helper {
345358
}};
346359
}
347360

361+
/// `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}`.
348362
#[macro_export]
349363
macro_rules! assert_always_greater_than {
350364
($left:expr, $right:expr, $message:literal, $details:expr) => {
351365
$crate::numeric_guidance_helper!($crate::assert_always, >, false, $left, $right, $message, $details)
352366
};
353367
}
354368

369+
/// `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}`.
355370
#[macro_export]
356371
macro_rules! assert_always_greater_than_or_equal_to {
357372
($left:expr, $right:expr, $message:literal, $details:expr) => {
358373
$crate::numeric_guidance_helper!($crate::assert_always, >=, false, $left, $right, $message, $details)
359374
};
360375
}
361376

377+
/// `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}`.
362378
#[macro_export]
363379
macro_rules! assert_always_less_than {
364380
($left:expr, $right:expr, $message:literal, $details:expr) => {
365381
$crate::numeric_guidance_helper!($crate::assert_always, <, true, $left, $right, $message, $details)
366382
};
367383
}
368384

385+
/// `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}`.
369386
#[macro_export]
370387
macro_rules! assert_always_less_than_or_equal_to {
371388
($left:expr, $right:expr, $message:literal, $details:expr) => {
372389
$crate::numeric_guidance_helper!($crate::assert_always, <=, true, $left, $right, $message, $details)
373390
};
374391
}
375392

393+
/// `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}`.
376394
#[macro_export]
377395
macro_rules! assert_sometimes_greater_than {
378396
($left:expr, $right:expr, $message:literal, $details:expr) => {
379397
$crate::numeric_guidance_helper!($crate::assert_sometimes, >, true, $left, $right, $message, $details)
380398
};
381399
}
382400

401+
/// `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}`.
383402
#[macro_export]
384403
macro_rules! assert_sometimes_greater_than_or_equal_to {
385404
($left:expr, $right:expr, $message:literal, $details:expr) => {
386405
$crate::numeric_guidance_helper!($crate::assert_sometimes, >=, true, $left, $right, $message, $details)
387406
};
388407
}
389408

409+
/// `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}`.
390410
#[macro_export]
391411
macro_rules! assert_sometimes_less_than {
392412
($left:expr, $right:expr, $message:literal, $details:expr) => {
393413
$crate::numeric_guidance_helper!($crate::assert_sometimes, <, false, $left, $right, $message, $details)
394414
};
395415
}
396416

417+
/// `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}`.
397418
#[macro_export]
398419
macro_rules! assert_sometimes_less_than_or_equal_to {
399420
($left:expr, $right:expr, $message:literal, $details:expr) => {
400421
$crate::numeric_guidance_helper!($crate::assert_sometimes, <=, false, $left, $right, $message, $details)
401422
};
402423
}
403424

425+
/// `assert_always_some({a: x, b: y, ...})` is similar to `assert_always(x || y || ...)`, except:
426+
/// - Antithesis has more visibility to the individual propositions.
427+
/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
428+
/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
404429
#[macro_export]
405430
macro_rules! assert_always_some {
406431
({$($name:ident: $cond:expr),*}, $message:literal, $details:expr) => {
407432
$crate::boolean_guidance_helper!($crate::assert_always, false, {$($name: $cond),*}, $message, $details);
408433
}
409434
}
410435

436+
/// `assert_sometimes_all({a: x, b: y, ...})` is similar to `assert_sometimes(x && y && ...)`, except:
437+
/// - Antithesis has more visibility to the individual propositions.
438+
/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
439+
/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
411440
#[macro_export]
412441
macro_rules! assert_sometimes_all {
413442
({$($name:ident: $cond:expr),*}, $message:literal, $details:expr) => {

0 commit comments

Comments
 (0)