14
14
15
15
use std:: cell:: { RefCell , RefMut } ;
16
16
use std:: fmt:: { Debug , Display , Error , Formatter } ;
17
+ use std:: sync:: atomic:: { AtomicBool , Ordering as AtomicOrdering } ;
18
+ use std:: sync:: Arc ;
17
19
use std:: thread_local;
18
20
19
21
/// The outcome hitherto of running a test.
@@ -23,16 +25,45 @@ use std::thread_local;
23
25
///
24
26
/// **For internal use only. API stablility is not guaranteed!**
25
27
#[ 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
+ }
32
53
}
33
54
34
55
thread_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 ) } ;
36
67
}
37
68
38
69
impl TestOutcome {
@@ -44,8 +75,26 @@ impl TestOutcome {
44
75
#[ doc( hidden) ]
45
76
pub fn init_current_test_outcome ( ) {
46
77
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
+ }
49
98
}
50
99
51
100
/// Evaluates the current test's [`TestOutcome`], producing a suitable
@@ -62,23 +111,21 @@ impl TestOutcome {
62
111
/// **For internal use only. API stablility is not guaranteed!**
63
112
#[ doc( hidden) ]
64
113
pub fn close_current_test_outcome < E : Display > (
65
- inner_result : Result < ( ) , E > ,
114
+ test_return_value : Result < ( ) , E > ,
66
115
) -> 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 )
77
124
} ;
78
- if let Err ( fatal_assertion_failure) = inner_result {
125
+ if let Err ( fatal_assertion_failure) = test_return_value {
79
126
println ! ( "{fatal_assertion_failure}" ) ;
80
127
}
81
- * outcome = None ;
128
+ * outcome_arc = None ;
82
129
outer_result
83
130
} )
84
131
}
@@ -88,31 +135,35 @@ impl TestOutcome {
88
135
#[ track_caller]
89
136
pub ( crate ) fn get_current_test_outcome ( ) -> Result < ( ) , TestAssertionFailure > {
90
137
TestOutcome :: with_current_test_outcome ( |mut outcome| {
91
- let outcome = outcome
138
+ let is_success = outcome
92
139
. 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 ( ) ) )
97
146
}
98
147
} )
99
148
}
100
149
101
150
/// Records that the currently running test has failed.
102
151
fn fail_current_test ( ) {
103
152
TestOutcome :: with_current_test_outcome ( |mut outcome| {
104
- let outcome = outcome
153
+ outcome
105
154
. 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 ( ) ;
108
157
} )
109
158
}
110
159
111
160
/// Runs `action` with the [`TestOutcome`] for the currently running test.
112
161
///
113
162
/// This is primarily intended for use by assertion macros like
114
163
/// `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 {
116
167
CURRENT_TEST_OUTCOME . with ( |current_test_outcome| action ( current_test_outcome. borrow_mut ( ) ) )
117
168
}
118
169
0 commit comments