Skip to content

Commit 3c95abd

Browse files
Support rich assertion macros.
1 parent 4a36473 commit 3c95abd

File tree

5 files changed

+469
-22
lines changed

5 files changed

+469
-22
lines changed

lib/src/assert/guidance.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
use std::sync::atomic::{self, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize};
2+
3+
use once_cell::sync::Lazy;
4+
use serde::Serialize;
5+
use serde_json::{json, Value};
6+
7+
use crate::internal;
8+
9+
use super::AntithesisLocationInfo;
10+
11+
pub struct Guard<const MAX: bool, T: AtomicMinMax> {
12+
mark: T::Atomic,
13+
}
14+
15+
trait Extremal {
16+
const MIN: Self;
17+
const MAX: Self;
18+
}
19+
20+
impl<const MAX: bool, T: AtomicMinMax> Guard<MAX, T>
21+
where T::Atomic: Extremal {
22+
pub const fn init() -> Self {
23+
let mark = if MAX { T::Atomic::MIN } else { T::Atomic::MAX };
24+
Self { mark }
25+
}
26+
}
27+
28+
trait AtomicMinMax {
29+
type Atomic;
30+
fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self;
31+
fn fetch_max(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self;
32+
}
33+
34+
impl<const MAX: bool, T: AtomicMinMax + PartialOrd + Copy> Guard<MAX, T> {
35+
pub fn should_emit(&self, new: T) -> bool {
36+
if MAX {
37+
let max = T::fetch_max(&self.mark, new, atomic::Ordering::SeqCst);
38+
!(max >= new)
39+
} else {
40+
let min = T::fetch_min(&self.mark, new, atomic::Ordering::SeqCst);
41+
!(min <= new)
42+
}
43+
}
44+
}
45+
46+
pub trait Diff {
47+
type Output;
48+
49+
fn diff(&self, other: Self) -> Self::Output;
50+
}
51+
52+
macro_rules! impl_extremal {
53+
($($t:ty)*) => {$(
54+
impl Extremal for $t {
55+
const MIN: $t = <$t>::MIN;
56+
const MAX: $t = <$t>::MAX;
57+
}
58+
)*}
59+
}
60+
61+
impl_extremal! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
62+
63+
macro_rules! impl_extremal_atomic {
64+
($(($t:ty, $raw_t:ty))*) => {$(
65+
impl Extremal for $t {
66+
const MIN: $t = <$t>::new(<$raw_t>::MIN);
67+
const MAX: $t = <$t>::new(<$raw_t>::MAX);
68+
}
69+
)*}
70+
}
71+
72+
impl_extremal_atomic! { (AtomicUsize, usize) (AtomicU8, u8) (AtomicU16, u16) (AtomicU32, u32) (AtomicU64, u64) (AtomicIsize, isize) (AtomicI8, i8) (AtomicI16, i16) (AtomicI32, i32) (AtomicI64, i64) }
73+
74+
impl Extremal for AtomicF32 {
75+
const MIN: Self = AtomicF32(AtomicU32::new(0xff800000));
76+
const MAX: Self = AtomicF32(AtomicU32::new(0x7f800000));
77+
}
78+
79+
impl Extremal for AtomicF64 {
80+
const MIN: Self = AtomicF64(AtomicU64::new(0xfff0000000000000));
81+
const MAX: Self = AtomicF64(AtomicU64::new(0x7ff0000000000000));
82+
}
83+
84+
macro_rules! impl_atomic_min_max {
85+
($(($t:ty, $atomic_t:ty))*) => {$(
86+
impl AtomicMinMax for $t {
87+
type Atomic = $atomic_t;
88+
89+
fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self {
90+
current.fetch_min(other, ordering)
91+
}
92+
93+
fn fetch_max(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self {
94+
current.fetch_max(other, ordering)
95+
}
96+
}
97+
)*};
98+
}
99+
100+
impl_atomic_min_max! { (usize, AtomicUsize) (u8, AtomicU8) (u16, AtomicU16) (u32, AtomicU32) (u64, AtomicU64) (isize, AtomicIsize) (i8, AtomicI8) (i16, AtomicI16) (i32, AtomicI32) (i64, AtomicI64) }
101+
102+
macro_rules! impl_atomic_min_max_float {
103+
($(($t:ty, $atomic_t:ident, $store_t:ty))*) => {$(
104+
struct $atomic_t($store_t);
105+
106+
impl AtomicMinMax for $t {
107+
type Atomic = $atomic_t;
108+
109+
fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self {
110+
<$t>::from_bits(current.0.fetch_update(ordering, ordering, |x| Some(<$t>::from_bits(x).min(other).to_bits())).unwrap())
111+
}
112+
113+
fn fetch_max(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self {
114+
<$t>::from_bits(current.0.fetch_update(ordering, ordering, |x| Some(<$t>::from_bits(x).max(other).to_bits())).unwrap())
115+
}
116+
}
117+
)*};
118+
}
119+
120+
impl_atomic_min_max_float! { (f32, AtomicF32, AtomicU32) (f64, AtomicF64, AtomicU64)}
121+
122+
macro_rules! impl_diff_unsigned {
123+
($($t:ty)*) => {$(
124+
impl Diff for $t {
125+
type Output = f64;
126+
127+
fn diff(&self, other: Self) -> Self::Output {
128+
if *self < other {
129+
-((other - self) as f64)
130+
} else {
131+
(self - other) as f64
132+
}
133+
}
134+
}
135+
)*};
136+
}
137+
138+
impl_diff_unsigned! { usize u8 u16 u32 u64 u128 }
139+
140+
macro_rules! impl_diff_signed {
141+
($(($t:ty, $unsigned_t:ty))*) => {$(
142+
impl Diff for $t {
143+
type Output = f64;
144+
145+
fn diff(&self, other: Self) -> Self::Output {
146+
if *self < other {
147+
-((other as $unsigned_t).wrapping_sub(*self as $unsigned_t) as f64)
148+
} else {
149+
(*self as $unsigned_t).wrapping_sub(other as $unsigned_t) as f64
150+
}
151+
}
152+
}
153+
)*};
154+
}
155+
156+
impl_diff_signed! { (isize, usize) (i8, u8) (i16, u16) (i32, u32) (i64, u64) (i128, u128) }
157+
158+
macro_rules! impl_diff_float {
159+
($($t:ty)*) => {$(
160+
impl Diff for $t {
161+
type Output = f64;
162+
163+
fn diff(&self, other: Self) -> Self::Output {
164+
(self - other) as f64
165+
}
166+
}
167+
)*};
168+
}
169+
170+
impl_diff_float! { f32 f64 }
171+
172+
#[derive(Copy, Clone, Serialize)]
173+
#[serde(rename_all(serialize = "lowercase"))]
174+
pub enum GuidanceType {
175+
Numeric,
176+
Boolean,
177+
JSON,
178+
}
179+
180+
#[derive(Serialize)]
181+
struct GuidanceInfo {
182+
guidance_type: GuidanceType,
183+
message: String,
184+
id: String,
185+
location: AntithesisLocationInfo,
186+
maximize: bool,
187+
guidance_data: Value,
188+
hit: bool,
189+
}
190+
191+
pub struct GuidanceCatalogInfo {
192+
pub guidance_type: GuidanceType,
193+
pub message: &'static str,
194+
pub id: &'static str,
195+
pub class: &'static str,
196+
pub function: &'static Lazy<&'static str>,
197+
pub file: &'static str,
198+
pub begin_line: u32,
199+
pub begin_column: u32,
200+
pub maximize: bool,
201+
}
202+
203+
pub fn guidance_impl(
204+
guidance_type: GuidanceType,
205+
message: String,
206+
id: String,
207+
class: String,
208+
function: String,
209+
file: String,
210+
begin_line: u32,
211+
begin_column: u32,
212+
maximize: bool,
213+
guidance_data: Value,
214+
hit: bool,
215+
) {
216+
let location = AntithesisLocationInfo { class, function, file, begin_line, begin_column };
217+
let guidance = GuidanceInfo {
218+
guidance_type, message, id, location, maximize, guidance_data, hit
219+
};
220+
221+
internal::dispatch_output(&json!({ "antithesis_guidance": guidance }));
222+
}

0 commit comments

Comments
 (0)