1414
1515use std:: cell:: { RefCell , RefMut } ;
1616use std:: fmt:: { Debug , Display , Error , Formatter } ;
17+ use std:: sync:: atomic:: { AtomicBool , Ordering as AtomicOrdering } ;
18+ use std:: sync:: Arc ;
1719use std:: thread_local;
1820
1921/// The outcome hitherto of running a test.
@@ -23,16 +25,45 @@ use std::thread_local;
2325///
2426/// **For internal use only. API stablility is not guaranteed!**
2527#[ doc( hidden) ]
26- pub enum TestOutcome {
27- /// The test ran or is currently running and no assertions have failed.
28- Success ,
29- /// The test ran or is currently running and at least one assertion has
30- /// failed.
31- Failure ,
28+ pub struct TestOutcome {
29+ is_success : AtomicBool ,
30+ }
31+
32+ impl Default for TestOutcome {
33+ fn default ( ) -> Self {
34+ Self :: new ( )
35+ }
36+ }
37+
38+ impl TestOutcome {
39+ pub fn new ( ) -> Self {
40+ Self { is_success : AtomicBool :: new ( true ) }
41+ }
42+ pub fn fail ( & self ) {
43+ self . is_success . store ( false , AtomicOrdering :: Relaxed )
44+ }
45+ #[ must_use]
46+ pub fn is_success ( & self ) -> bool {
47+ self . is_success . load ( AtomicOrdering :: Relaxed )
48+ }
49+ #[ must_use]
50+ pub fn is_failed ( & self ) -> bool {
51+ self . is_success . load ( AtomicOrdering :: Relaxed )
52+ }
3253}
3354
3455thread_local ! {
35- static CURRENT_TEST_OUTCOME : RefCell <Option <TestOutcome >> = const { RefCell :: new( None ) } ;
56+ // Whether or not the current test has failed.
57+ //
58+ // If inside a `#[gtest]` function, this value will initially be set to a new `TestOutcome`.
59+ // Upon assertion failure (e.g. `expect_that!` failing), the `TestOutcome` will be updated to
60+ // indicate failure.
61+ //
62+ // The `Arc` is used to share the `TestOutcome` across threads that have been spawned by the
63+ // `#[gtest]` function, which can then set it to fail upon an assertion failure in a thread.
64+ static CURRENT_TEST_OUTCOME : RefCell <Option <Arc <TestOutcome >>> = const { RefCell :: new( None ) } ;
65+ #[ cfg( feature = "unstable_thread_spawn_hook" ) ]
66+ static HAS_SET_SPAWN_HOOK : std:: cell:: Cell <bool > = const { std:: cell:: Cell :: new( false ) } ;
3667}
3768
3869impl TestOutcome {
@@ -44,8 +75,26 @@ impl TestOutcome {
4475 #[ doc( hidden) ]
4576 pub fn init_current_test_outcome ( ) {
4677 Self :: with_current_test_outcome ( |mut current_test_outcome| {
47- * current_test_outcome = Some ( TestOutcome :: Success ) ;
48- } )
78+ * current_test_outcome = Some ( Arc :: new ( TestOutcome :: new ( ) ) ) ;
79+ } ) ;
80+
81+ #[ cfg( feature = "unstable_thread_spawn_hook" ) ]
82+ if !HAS_SET_SPAWN_HOOK . get ( ) {
83+ // Ensure that the spawn hook is only set once so that we don't accumulate spawn
84+ // hooks for threads that run multiple tests.
85+ HAS_SET_SPAWN_HOOK . set ( true ) ;
86+ std:: thread:: add_spawn_hook ( |_thread| {
87+ let outcome: Option < Arc < TestOutcome > > =
88+ Self :: with_current_test_outcome ( |current_test_outcome| {
89+ current_test_outcome. clone ( )
90+ } ) ;
91+ move || {
92+ Self :: with_current_test_outcome ( |mut current_test_outcome| {
93+ * current_test_outcome = outcome;
94+ } ) ;
95+ }
96+ } )
97+ }
4998 }
5099
51100 /// Evaluates the current test's [`TestOutcome`], producing a suitable
@@ -62,23 +111,21 @@ impl TestOutcome {
62111 /// **For internal use only. API stablility is not guaranteed!**
63112 #[ doc( hidden) ]
64113 pub fn close_current_test_outcome < E : Display > (
65- inner_result : Result < ( ) , E > ,
114+ test_return_value : Result < ( ) , E > ,
66115 ) -> Result < ( ) , TestFailure > {
67- TestOutcome :: with_current_test_outcome ( |mut outcome| {
68- let outer_result = match & * outcome {
69- Some ( TestOutcome :: Success ) => match inner_result {
70- Ok ( ( ) ) => Ok ( ( ) ) ,
71- Err ( _) => Err ( TestFailure ) ,
72- } ,
73- Some ( TestOutcome :: Failure ) => Err ( TestFailure ) ,
74- None => {
75- panic ! ( "No test context found. This indicates a bug in GoogleTest." )
76- }
116+ TestOutcome :: with_current_test_outcome ( |mut outcome_arc| {
117+ let Some ( outcome) = outcome_arc. as_ref ( ) else {
118+ panic ! ( "No test context found. This indicates a bug in GoogleTest." )
119+ } ;
120+ let outer_result = if outcome. is_success ( ) && test_return_value. is_ok ( ) {
121+ Ok ( ( ) )
122+ } else {
123+ Err ( TestFailure )
77124 } ;
78- if let Err ( fatal_assertion_failure) = inner_result {
125+ if let Err ( fatal_assertion_failure) = test_return_value {
79126 println ! ( "{fatal_assertion_failure}" ) ;
80127 }
81- * outcome = None ;
128+ * outcome_arc = None ;
82129 outer_result
83130 } )
84131 }
@@ -88,31 +135,35 @@ impl TestOutcome {
88135 #[ track_caller]
89136 pub ( crate ) fn get_current_test_outcome ( ) -> Result < ( ) , TestAssertionFailure > {
90137 TestOutcome :: with_current_test_outcome ( |mut outcome| {
91- let outcome = outcome
138+ let is_success = outcome
92139 . as_mut ( )
93- . expect ( "No test context found. This indicates a bug in GoogleTest." ) ;
94- match outcome {
95- TestOutcome :: Success => Ok ( ( ) ) ,
96- TestOutcome :: Failure => Err ( TestAssertionFailure :: create ( "Test failed" . into ( ) ) ) ,
140+ . expect ( "No test context found. This indicates a bug in GoogleTest." )
141+ . is_success ( ) ;
142+ if is_success {
143+ Ok ( ( ) )
144+ } else {
145+ Err ( TestAssertionFailure :: create ( "Test failed" . into ( ) ) )
97146 }
98147 } )
99148 }
100149
101150 /// Records that the currently running test has failed.
102151 fn fail_current_test ( ) {
103152 TestOutcome :: with_current_test_outcome ( |mut outcome| {
104- let outcome = outcome
153+ outcome
105154 . as_mut ( )
106- . expect ( "No test context found. This indicates a bug in GoogleTest." ) ;
107- * outcome = TestOutcome :: Failure ;
155+ . expect ( "No test context found. This indicates a bug in GoogleTest." )
156+ . fail ( ) ;
108157 } )
109158 }
110159
111160 /// Runs `action` with the [`TestOutcome`] for the currently running test.
112161 ///
113162 /// This is primarily intended for use by assertion macros like
114163 /// `expect_that!`.
115- fn with_current_test_outcome < T > ( action : impl FnOnce ( RefMut < Option < TestOutcome > > ) -> T ) -> T {
164+ fn with_current_test_outcome < T > (
165+ action : impl FnOnce ( RefMut < Option < Arc < TestOutcome > > > ) -> T ,
166+ ) -> T {
116167 CURRENT_TEST_OUTCOME . with ( |current_test_outcome| action ( current_test_outcome. borrow_mut ( ) ) )
117168 }
118169
0 commit comments