Skip to main content

antithesis_sdk/assert/
mod.rs

1use std::sync::atomic::AtomicU64;
2#[cfg(feature = "full")]
3use std::{collections::HashMap, sync::{atomic::Ordering, Arc, RwLock}};
4use crate::details::Details;
5#[cfg(feature = "full")]
6use crate::internal;
7#[cfg(feature = "full")]
8use linkme::distributed_slice;
9#[cfg(feature = "full")]
10use once_cell::sync::Lazy;
11use serde::Serialize;
12use serde_json::Value;
13#[cfg(feature = "full")]
14use serde_json::json;
15
16mod macros;
17#[doc(hidden)]
18#[cfg(feature = "full")]
19pub mod guidance;
20
21/// Catalog of all antithesis assertions provided
22#[doc(hidden)]
23#[distributed_slice]
24#[cfg(feature = "full")]
25pub static ANTITHESIS_CATALOG: [AssertionCatalogInfo];
26
27/// Catalog of all antithesis guidances provided
28#[doc(hidden)]
29#[distributed_slice]
30#[cfg(feature = "full")]
31pub static ANTITHESIS_GUIDANCE_CATALOG: [self::guidance::GuidanceCatalogInfo];
32
33#[cfg(feature = "full")]
34pub(crate) static INIT_CATALOG: Lazy<()> = Lazy::new(|| {
35    for event in crate::catalog::registrations() {
36        internal::dispatch_output(&event);
37    }
38});
39
40/// The catalog-registration event for one assertion declaration, emitted by
41/// both `antithesis_init()` and `catalog::write`.
42#[cfg(feature = "full")]
43pub(crate) fn catalog_event(info: &AssertionCatalogInfo) -> Value {
44    let f_name: &str = info.function.as_ref();
45    let details = json!(null);
46    let assertion = AssertionInfo::new(
47        info.assert_type,
48        info.display_type,
49        info.condition,
50        info.message,
51        info.class,
52        f_name,
53        info.file,
54        info.begin_line,
55        info.begin_column,
56        false, /* hit */
57        info.must_hit,
58        info.id,
59        &details,
60    );
61    json!({ "antithesis_assert": &assertion })
62}
63
64pub struct TrackingInfo {
65    pub pass_count: AtomicU64,
66    pub fail_count: AtomicU64,
67}
68
69impl Default for TrackingInfo {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl TrackingInfo {
76    pub const fn new() -> Self {
77        TrackingInfo {
78            pass_count: AtomicU64::new(0),
79            fail_count: AtomicU64::new(0),
80        }
81    }
82}
83
84#[derive(Copy, Clone, PartialEq, Debug, Serialize)]
85#[serde(rename_all(serialize = "lowercase"))]
86pub enum AssertType {
87    Always,
88    Sometimes,
89    Reachability,
90}
91
92#[derive(Serialize, Debug)]
93struct AntithesisLocationInfo<'a> {
94    class: &'a str,
95    function: &'a str,
96    file: &'a str,
97    begin_line: u32,
98    begin_column: u32,
99}
100
101/// Internal representation for assertion catalog
102#[doc(hidden)]
103#[derive(Debug)]
104#[cfg(feature = "full")]
105pub struct AssertionCatalogInfo {
106    pub assert_type: AssertType,
107    pub display_type: &'static str,
108    pub condition: bool,
109    pub message: &'static str,
110    pub class: &'static str,
111    pub function: &'static Lazy<&'static str>,
112    pub file: &'static str,
113    pub begin_line: u32,
114    pub begin_column: u32,
115    pub must_hit: bool,
116    pub id: &'static str,
117}
118
119#[derive(Serialize)]
120struct AssertionInfo<'a> {
121    assert_type: AssertType,
122    display_type: &'a str,
123    condition: bool,
124    message: &'a str,
125    location: AntithesisLocationInfo<'a>,
126    hit: bool,
127    must_hit: bool,
128    id: &'a str,
129    // Serialized separately so declarations never evaluate the details serializer.
130    #[serde(skip)]
131    details: &'a dyn Details,
132}
133
134impl<'a> AssertionInfo<'a> {
135    #[allow(clippy::too_many_arguments)]
136    pub fn new(
137        assert_type: AssertType,
138        display_type: &'a str,
139        condition: bool,
140        message: &'a str,
141        class: &'a str,
142        function: &'a str,
143        file: &'a str,
144        begin_line: u32,
145        begin_column: u32,
146        hit: bool,
147        must_hit: bool,
148        id: &'a str,
149        details: &'a dyn Details,
150    ) -> Self {
151        let location = AntithesisLocationInfo {
152            class,
153            function,
154            file,
155            begin_line,
156            begin_column,
157        };
158
159        AssertionInfo {
160            assert_type,
161            display_type,
162            condition,
163            message,
164            location,
165            hit,
166            must_hit,
167            id,
168            details
169        }
170    }
171} 
172
173#[cfg(feature = "full")]
174impl AssertionInfo<'_> {
175    // AssertionInfo::track_entry() determines if the assertion should
176    // actually be emitted:
177    //
178    // [X] If this is an assertion catalog
179    // registration (assertion.hit == false) then it is emitted.
180    //
181    // [X] if `condition` is true increment the tracker_entry.pass_count,
182    // otherwise increment the tracker_entry.fail_count.
183    //
184    // [X] if `condition` is true and tracker_entry_pass_count == 1 then
185    // actually emit the assertion.
186    //
187    // [X] if `condition` is false and tracker_entry_fail_count == 1 then
188    // actually emit the assertion.
189
190    fn track_entry(&self, info: Option<&TrackingInfo>) {
191        // Requirement: Catalog entries must always will emit()
192        if !self.hit {
193            self.emit();
194            return;
195        }
196
197        // Record the condition in the associated TrackingInfo entry,
198        // and emit the assertion when first seeing a condition
199        let emitting = match (info, self.condition) {
200            (None, _) => true,
201            (Some(info), true) => {
202                let prior_value = info.pass_count.fetch_add(1, Ordering::SeqCst);
203                prior_value == 0
204            }
205            (Some(info), false) => {
206                let prior_value = info.fail_count.fetch_add(1, Ordering::SeqCst);
207                prior_value == 0
208            }
209        };
210        if emitting {
211            Lazy::force(&INIT_CATALOG);
212            self.emit();
213        }
214    }
215
216    fn emit(&self) {
217        let mut assertion = serde_json::to_value(self).unwrap();
218        if self.hit {
219            if let Some(details) = self.details.object(self.id) {
220                assertion["details"] = Value::Object(details);
221            }
222        }
223        let json_event = json!({ "antithesis_assert": assertion });
224        internal::dispatch_output(&json_event)
225    }
226}
227
228#[cfg(not(feature = "full"))]
229impl AssertionInfo<'_> {
230    fn track_entry(&self, _info: Option<&TrackingInfo>) {
231        return
232    }
233}
234
235
236/// This is a low-level method designed to be used by third-party frameworks.
237/// Regular users of the assert package should not call it.
238///
239/// This is primarily intended for use by adapters from other diagnostic tools
240/// that intend to output Antithesis-style assertions.
241///
242/// Be certain to provide an assertion catalog entry for each assertion issued
243/// with ``assert_raw()``.  Assertion catalog entries are also created using
244/// ``assert_raw()``, by setting the value of the ``hit`` parameter to false.
245///
246/// Details may be any borrowed value implementing `Serialize`, handled as
247/// the assertion macros handle theirs: objects are preserved, other non-null
248/// values are wrapped under `"value"`, `&Value::Null` means no details, and
249/// a serialization error emits an `antithesis_error` naming the assertion
250/// `id` while the assertion itself is still emitted without details.
251/// Catalog entries (`hit == false`) never serialize details.
252///
253/// Please refer to the general Antithesis documentation regarding the use of
254/// the
255/// [Fallback SDK](https://antithesis.com/docs/using_antithesis/sdk/fallback/assert/)
256/// for additional information.
257///
258///
259///
260/// # Example
261///
262/// ```
263/// use serde_json::{json};
264/// use antithesis_sdk::{assert, random};
265///
266/// struct Votes {
267///     num_voters: u32,
268///     candidate_1: u32,
269///     candidate_2: u32,
270/// }
271///
272/// fn main() {
273///     establish_catalog();
274///    
275///     let mut all_votes = Votes {
276///         num_voters: 0,
277///         candidate_1: 0,
278///         candidate_2: 0,
279///     };
280///
281///     for _voter in 0..100 {
282///         tally_vote(&mut all_votes, random_bool(), random_bool());
283///     }
284/// }
285///
286/// fn random_bool() -> bool {
287///     let v1 = random::get_random() % 2;
288///     v1 == 1
289/// }
290///
291/// fn establish_catalog() {
292///     assert::assert_raw(
293///         false,                            /* condition */
294///         "Never extra votes".to_owned(),   /* message */
295///         &json!({}),                       /* details */
296///         "mycrate::stuff".to_owned(),      /* class */
297///         "mycrate::tally_vote".to_owned(), /* function */
298///         "src/voting.rs".to_owned(),       /* file */
299///         20,                               /* line */
300///         3,                                /* column */
301///         false,                            /* hit */
302///         true,                             /* must_hit */
303///         assert::AssertType::Always,       /* assert_type */
304///         "Always".to_owned(),              /* display_type */
305///         "42-1005".to_owned()              /* id */
306///     );
307/// }
308///
309/// fn tally_vote(votes: &mut Votes, candidate_1: bool, candidate_2: bool) {
310///     if candidate_1 || candidate_2 {
311///         votes.num_voters += 1;
312///     }
313///     if candidate_1 {
314///         votes.candidate_1 += 1;
315///     };
316///     if candidate_2 {
317///         votes.candidate_2 += 1;
318///     };
319///
320///     let num_votes = votes.candidate_1 + votes.candidate_2;
321///     assert::assert_raw(
322///         num_votes == votes.num_voters,    /* condition */
323///         "Never extra votes".to_owned(),   /* message */
324///         &json!({                          /* details */
325///             "votes": num_votes,
326///             "voters": votes.num_voters
327///         }),                        
328///         "mycrate::stuff".to_owned(),      /* class */
329///         "mycrate::tally_vote".to_owned(), /* function */
330///         "src/voting.rs".to_owned(),       /* file */
331///         20,                               /* line */
332///         3,                                /* column */
333///         true,                             /* hit */
334///         true,                             /* must_hit */
335///         assert::AssertType::Always,       /* assert_type */
336///         "Always".to_owned(),              /* display_type */
337///         "42-1005".to_owned()              /* id */
338///     );
339/// }
340///
341/// // Run example with output to /tmp/x7.json
342/// // ANTITHESIS_SDK_LOCAL_OUTPUT=/tmp/x7.json cargo test --doc
343/// //
344/// // Example output from /tmp/x7.json
345/// // Contents may vary due to use of random::get_random()
346/// //
347/// // {"antithesis_sdk":{"language":{"name":"Rust","version":"1.69.0"},"sdk_version":"0.1.2","protocol_version":"1.0.0"}}
348/// // {"assert_type":"always","display_type":"Always","condition":false,"message":"Never extra votes","location":{"class":"mycrate::stuff","function":"mycrate::tally_vote","file":"src/voting.rs","begin_line":20,"begin_column":3},"hit":false,"must_hit":true,"id":"42-1005"}
349/// // {"assert_type":"always","display_type":"Always","condition":true,"message":"Never extra votes","location":{"class":"mycrate::stuff","function":"mycrate::tally_vote","file":"src/voting.rs","begin_line":20,"begin_column":3},"hit":true,"must_hit":true,"id":"42-1005","details":{"voters":1,"votes":1}}
350/// // {"assert_type":"always","display_type":"Always","condition":false,"message":"Never extra votes","location":{"class":"mycrate::stuff","function":"mycrate::tally_vote","file":"src/voting.rs","begin_line":20,"begin_column":3},"hit":true,"must_hit":true,"id":"42-1005","details":{"voters":3,"votes":4}}
351/// ```
352#[allow(clippy::too_many_arguments)]
353#[cfg(feature = "full")]
354pub fn assert_raw<T: Serialize>(
355    condition: bool,
356    message: String,
357    details: &T,
358    class: String,
359    function: String,
360    file: String,
361    begin_line: u32,
362    begin_column: u32,
363    hit: bool,
364    must_hit: bool,
365    assert_type: AssertType,
366    display_type: String,
367    id: String,
368) {
369    // Only this thin shim is generic (and so compiled into the caller's
370    // crate); the tracker lookup and everything downstream are erased
371    // behind `dyn Details` and compiled once, here.
372    let info = raw_tracker_entry(&id);
373    assert_impl(
374        assert_type,
375        display_type.as_str(),
376        condition,
377        message.as_str(),
378        class.as_str(),
379        function.as_str(),
380        file.as_str(),
381        begin_line,
382        begin_column,
383        hit,
384        must_hit,
385        id.as_str(),
386        details,
387        Some(&*info),
388    )
389}
390
391// The per-id tracking entries of raw assertions (the macros hold theirs in
392// a static per call site).
393#[cfg(feature = "full")]
394fn raw_tracker_entry(id: &str) -> Arc<TrackingInfo> {
395    static ASSERT_TRACKER: Lazy<RwLock<HashMap<String, Arc<TrackingInfo>>>> =
396        Lazy::new(|| RwLock::new(HashMap::new()));
397
398    // The read guard must drop before the write lock is taken.
399    let existing = ASSERT_TRACKER.read().unwrap().get(id).cloned();
400    match existing {
401        Some(info) => info,
402        None => ASSERT_TRACKER
403            .write()
404            .unwrap()
405            .entry(id.to_owned())
406            .or_default()
407            .clone(),
408    }
409}
410
411#[allow(clippy::too_many_arguments)]
412#[cfg(not(feature = "full"))]
413pub fn assert_raw<T: Serialize>(
414    condition: bool,
415    message: String,
416    details: &T,
417    class: String,
418    function: String,
419    file: String,
420    begin_line: u32,
421    begin_column: u32,
422    hit: bool,
423    must_hit: bool,
424    assert_type: AssertType,
425    display_type: String,
426    id: String,
427) {
428    assert_impl(
429        assert_type,
430        display_type.as_str(),
431        condition,
432        message.as_str(),
433        class.as_str(),
434        function.as_str(),
435        file.as_str(),
436        begin_line,
437        begin_column,
438        hit,
439        must_hit,
440        id.as_str(),
441        details,
442        None,
443    )
444}
445
446#[doc(hidden)]
447#[allow(clippy::too_many_arguments)]
448pub fn assert_impl<'a>(
449    assert_type: AssertType,
450    display_type: &'a str,
451    condition: bool,
452    message: &'a str,
453    class: &'a str,
454    function: &'a str,
455    file: &'a str,
456    begin_line: u32,
457    begin_column: u32,
458    hit: bool,
459    must_hit: bool,
460    id: &'a str,
461    details: &dyn Details,
462    info: Option<&TrackingInfo>,
463) {
464    let assertion = AssertionInfo::new(
465        assert_type,
466        display_type,
467        condition,
468        message,
469        class,
470        function,
471        file,
472        begin_line,
473        begin_column,
474        hit,
475        must_hit,
476        id,
477        details,
478    );
479
480    let _ = &assertion.track_entry(info);
481}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486    #[cfg(not(feature = "full"))] use serde_json::json;
487    #[cfg(not(feature = "full"))] use std::sync::atomic::Ordering;
488
489    //--------------------------------------------------------------------------------
490    // Tests for TrackingInfo
491    //--------------------------------------------------------------------------------
492    #[test]
493    fn new_tracking_info() {
494        let ti = TrackingInfo::new();
495        assert_eq!(ti.pass_count.load(Ordering::SeqCst), 0);
496        assert_eq!(ti.fail_count.load(Ordering::SeqCst), 0);
497    }
498
499    #[test]
500    fn default_tracking_info() {
501        let ti: TrackingInfo = Default::default();
502        assert_eq!(ti.pass_count.load(Ordering::SeqCst), 0);
503        assert_eq!(ti.fail_count.load(Ordering::SeqCst), 0);
504    }
505
506    //--------------------------------------------------------------------------------
507    // Tests for AssertionInfo
508    //--------------------------------------------------------------------------------
509
510    #[test]
511    fn new_assertion_info_always() {
512        let this_assert_type = AssertType::Always;
513        let this_display_type = "Always";
514        let this_condition = true;
515        let this_message = "Always message";
516        let this_class = "binary::always";
517        let this_function = "binary::always::always_function";
518        let this_file = "/home/user/binary/src/always_binary.rs";
519        let this_begin_line = 10;
520        let this_begin_column = 5;
521        let this_hit = true;
522        let this_must_hit = true;
523        let this_id = "ID Always message";
524        let this_details = json!({
525            "color": "always red",
526            "extent": 15,
527        });
528
529        let ai = AssertionInfo::new(
530            this_assert_type,
531            this_display_type,
532            this_condition,
533            this_message,
534            this_class,
535            this_function,
536            this_file,
537            this_begin_line,
538            this_begin_column,
539            this_hit,
540            this_must_hit,
541            this_id,
542            &this_details,
543        );
544        assert_eq!(ai.display_type, this_display_type);
545        assert_eq!(ai.condition, this_condition);
546        assert_eq!(ai.message, this_message);
547        assert_eq!(ai.location.class, this_class);
548        assert_eq!(ai.location.function, this_function);
549        assert_eq!(ai.location.file, this_file);
550        assert_eq!(ai.location.begin_line, this_begin_line);
551        assert_eq!(ai.location.begin_column, this_begin_column);
552        assert_eq!(ai.hit, this_hit);
553        assert_eq!(ai.must_hit, this_must_hit);
554        assert_eq!(ai.id, this_id);
555        #[cfg(feature = "full")]
556        assert_eq!(ai.details.object(ai.id), this_details.as_object().cloned());
557    }
558
559    #[test]
560    fn new_assertion_info_sometimes() {
561        let this_assert_type = AssertType::Sometimes;
562        let this_display_type = "Sometimes";
563        let this_condition = true;
564        let this_message = "Sometimes message";
565        let this_class = "binary::sometimes";
566        let this_function = "binary::sometimes::sometimes_function";
567        let this_file = "/home/user/binary/src/sometimes_binary.rs";
568        let this_begin_line = 11;
569        let this_begin_column = 6;
570        let this_hit = true;
571        let this_must_hit = true;
572        let this_id = "ID Sometimes message";
573        let this_details = json!({
574            "color": "sometimes red",
575            "extent": 17,
576        });
577
578        let ai = AssertionInfo::new(
579            this_assert_type,
580            this_display_type,
581            this_condition,
582            this_message,
583            this_class,
584            this_function,
585            this_file,
586            this_begin_line,
587            this_begin_column,
588            this_hit,
589            this_must_hit,
590            this_id,
591            &this_details,
592        );
593        assert_eq!(ai.display_type, this_display_type);
594        assert_eq!(ai.condition, this_condition);
595        assert_eq!(ai.message, this_message);
596        assert_eq!(ai.location.class, this_class);
597        assert_eq!(ai.location.function, this_function);
598        assert_eq!(ai.location.file, this_file);
599        assert_eq!(ai.location.begin_line, this_begin_line);
600        assert_eq!(ai.location.begin_column, this_begin_column);
601        assert_eq!(ai.hit, this_hit);
602        assert_eq!(ai.must_hit, this_must_hit);
603        assert_eq!(ai.id, this_id);
604        #[cfg(feature = "full")]
605        assert_eq!(ai.details.object(ai.id), this_details.as_object().cloned());
606    }
607
608    #[test]
609    fn new_assertion_info_reachable() {
610        let this_assert_type = AssertType::Reachability;
611        let this_display_type = "Reachable";
612        let this_condition = true;
613        let this_message = "Reachable message";
614        let this_class = "binary::reachable";
615        let this_function = "binary::reachable::reachable_function";
616        let this_file = "/home/user/binary/src/reachable_binary.rs";
617        let this_begin_line = 12;
618        let this_begin_column = 7;
619        let this_hit = true;
620        let this_must_hit = true;
621        let this_id = "ID Reachable message";
622        let this_details = json!({
623            "color": "reachable red",
624            "extent": 19,
625        });
626
627        let ai = AssertionInfo::new(
628            this_assert_type,
629            this_display_type,
630            this_condition,
631            this_message,
632            this_class,
633            this_function,
634            this_file,
635            this_begin_line,
636            this_begin_column,
637            this_hit,
638            this_must_hit,
639            this_id,
640            &this_details,
641        );
642        assert_eq!(ai.display_type, this_display_type);
643        assert_eq!(ai.condition, this_condition);
644        assert_eq!(ai.message, this_message);
645        assert_eq!(ai.location.class, this_class);
646        assert_eq!(ai.location.function, this_function);
647        assert_eq!(ai.location.file, this_file);
648        assert_eq!(ai.location.begin_line, this_begin_line);
649        assert_eq!(ai.location.begin_column, this_begin_column);
650        assert_eq!(ai.hit, this_hit);
651        assert_eq!(ai.must_hit, this_must_hit);
652        assert_eq!(ai.id, this_id);
653        #[cfg(feature = "full")]
654        assert_eq!(ai.details.object(ai.id), this_details.as_object().cloned());
655    }
656
657    #[test]
658    fn assert_impl_pass() {
659        let this_assert_type = AssertType::Always;
660        let this_display_type = "Always";
661        let this_condition = true;
662        let this_message = "Always message 2";
663        let this_class = "binary::always";
664        let this_function = "binary::always::always_function";
665        let this_file = "/home/user/binary/src/always_binary.rs";
666        let this_begin_line = 10;
667        let this_begin_column = 5;
668        let this_hit = true;
669        let this_must_hit = true;
670        let this_id = "ID Always message 2";
671        let this_details = json!({
672            "color": "always red",
673            "extent": 15,
674        });
675
676        let tracker = TrackingInfo::new();
677
678        let before_tracker = clone_tracker(&tracker);
679
680        assert_impl(
681            this_assert_type,
682            this_display_type,
683            this_condition,
684            this_message,
685            this_class,
686            this_function,
687            this_file,
688            this_begin_line,
689            this_begin_column,
690            this_hit,
691            this_must_hit,
692            this_id,
693            &this_details,
694            Some(&tracker),
695        );
696
697        let after_tracker: TrackingInfo = clone_tracker(&tracker);
698
699        if this_condition {
700            assert_eq!(before_tracker.pass_count.load(Ordering::SeqCst) + 1, after_tracker.pass_count.load(Ordering::SeqCst));
701            assert_eq!(before_tracker.fail_count.load(Ordering::SeqCst), after_tracker.fail_count.load(Ordering::SeqCst));
702        } else {
703            assert_eq!(before_tracker.fail_count.load(Ordering::SeqCst) + 1, after_tracker.fail_count.load(Ordering::SeqCst));
704            assert_eq!(before_tracker.pass_count.load(Ordering::SeqCst), after_tracker.pass_count.load(Ordering::SeqCst));
705        };
706    }
707
708    #[test]
709    fn assert_impl_fail() {
710        let this_assert_type = AssertType::Always;
711        let this_display_type = "Always";
712        let this_condition = false;
713        let this_message = "Always message 3";
714        let this_class = "binary::always";
715        let this_function = "binary::always::always_function";
716        let this_file = "/home/user/binary/src/always_binary.rs";
717        let this_begin_line = 10;
718        let this_begin_column = 5;
719        let this_hit = true;
720        let this_must_hit = true;
721        let this_id = "ID Always message 3";
722        let this_details = json!({
723            "color": "always red",
724            "extent": 15,
725        });
726
727        let tracker = TrackingInfo::new();
728
729        let before_tracker = clone_tracker(&tracker);
730
731        assert_impl(
732            this_assert_type,
733            this_display_type,
734            this_condition,
735            this_message,
736            this_class,
737            this_function,
738            this_file,
739            this_begin_line,
740            this_begin_column,
741            this_hit,
742            this_must_hit,
743            this_id,
744            &this_details,
745            Some(&tracker),
746        );
747
748        let after_tracker: TrackingInfo = clone_tracker(&tracker);
749
750        if this_condition {
751            assert_eq!(before_tracker.pass_count.load(Ordering::SeqCst) + 1, after_tracker.pass_count.load(Ordering::SeqCst));
752            assert_eq!(before_tracker.fail_count.load(Ordering::SeqCst), after_tracker.fail_count.load(Ordering::SeqCst));
753        } else {
754            assert_eq!(before_tracker.fail_count.load(Ordering::SeqCst) + 1, after_tracker.fail_count.load(Ordering::SeqCst));
755            assert_eq!(before_tracker.pass_count.load(Ordering::SeqCst), after_tracker.pass_count.load(Ordering::SeqCst));
756        };
757    }
758
759    fn clone_tracker(old: &TrackingInfo) -> TrackingInfo {
760        let tracking_data = TrackingInfo::new();
761        tracking_data.pass_count.store(old.pass_count.load(Ordering::SeqCst), Ordering::SeqCst);
762        tracking_data.fail_count.store(old.fail_count.load(Ordering::SeqCst), Ordering::SeqCst);
763        tracking_data
764
765    }
766}