Skip to content

Commit cfcf2b6

Browse files
committed
run Style.sh
1 parent a0b811f commit cfcf2b6

File tree

1 file changed

+168
-29
lines changed

1 file changed

+168
-29
lines changed

FirebasePerformance/Tests/Unit/FPRTraceBackgroundActivityTrackerTest.m

Lines changed: 168 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -61,74 +61,213 @@ - (void)testBackgroundTracking {
6161
}];
6262
}
6363

64-
/** Tests that synchronous observer registration works correctly and observers are immediately available. */
64+
/** Tests that synchronous observer registration works correctly and observers are immediately
65+
* available. */
6566
- (void)testObservers_synchronousRegistrationAddsObserver {
6667
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
6768
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
6869
XCTAssertNotNil(tracker);
69-
70+
7071
[notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
71-
object:[UIApplication sharedApplication]];
72+
object:[UIApplication sharedApplication]];
7273
XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
73-
74+
7475
tracker = nil;
7576
XCTAssertNil(tracker);
7677
XCTAssertNoThrow([notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
7778
object:[UIApplication sharedApplication]]);
78-
XCTAssertNoThrow([notificationCenter postNotificationName:UIApplicationDidEnterBackgroundNotification
79-
object:[UIApplication sharedApplication]]);
79+
XCTAssertNoThrow([notificationCenter
80+
postNotificationName:UIApplicationDidEnterBackgroundNotification
81+
object:[UIApplication sharedApplication]]);
8082
}
8183

8284
/** Tests rapid creation and deallocation to verify race condition fix. */
8385
- (void)testRapidCreationAndDeallocation_noRaceCondition {
84-
for (int i = 0; i < 100; i++) {
86+
// This test simulates the real crash scenario by forcing async dispatch timing
87+
XCTestExpectation *expectation =
88+
[self expectationWithDescription:@"All async operations complete"];
89+
90+
__block int completedOperations = 0;
91+
const int totalOperations = 50;
92+
93+
for (int i = 0; i < totalOperations; i++) {
8594
@autoreleasepool {
8695
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
8796
XCTAssertNotNil(tracker);
88-
89-
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification
90-
object:[UIApplication sharedApplication]];
97+
98+
// Force multiple runloop cycles to increase chance of race condition
99+
dispatch_async(dispatch_get_main_queue(), ^{
100+
// This would crash with old async registration if tracker is deallocated
101+
[[NSNotificationCenter defaultCenter]
102+
postNotificationName:UIApplicationDidBecomeActiveNotification
103+
object:[UIApplication sharedApplication]];
104+
105+
// Increment counter and fulfill expectation when done
106+
completedOperations++;
107+
if (completedOperations == totalOperations) {
108+
[expectation fulfill];
109+
}
110+
});
111+
112+
// Tracker deallocates here immediately due to @autoreleasepool
91113
}
92114
}
93-
94-
XCTAssertNoThrow([[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification
95-
object:[UIApplication sharedApplication]]);
96-
XCTAssertNoThrow([[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidEnterBackgroundNotification
97-
object:[UIApplication sharedApplication]]);
115+
116+
// Wait for all async operations to complete
117+
[self waitForExpectationsWithTimeout:10.0
118+
handler:^(NSError *error) {
119+
XCTAssertNil(
120+
error, @"Operations timed out - potential deadlock or crash");
121+
}];
122+
123+
// Additional safety check - post more notifications after everything is done
124+
XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
125+
postNotificationName:UIApplicationDidBecomeActiveNotification
126+
object:[UIApplication sharedApplication]]);
127+
XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
128+
postNotificationName:UIApplicationDidEnterBackgroundNotification
129+
object:[UIApplication sharedApplication]]);
98130
}
99131

100132
/** Tests that observers are registered immediately after init on main thread. */
101133
- (void)testObservers_immediateRegistrationOnMainThread {
102134
XCTAssertTrue([NSThread isMainThread]);
103-
135+
104136
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
105-
106-
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification
107-
object:[UIApplication sharedApplication]];
108-
137+
138+
[[NSNotificationCenter defaultCenter]
139+
postNotificationName:UIApplicationDidBecomeActiveNotification
140+
object:[UIApplication sharedApplication]];
141+
109142
XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
110143
}
111144

112145
/** Tests observer registration when created from background thread. */
113146
- (void)testObservers_registrationFromBackgroundThread {
114147
XCTestExpectation *expectation = [self expectationWithDescription:@"Background thread creation"];
115-
148+
116149
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
117150
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
118151
XCTAssertNotNil(tracker);
119-
152+
120153
dispatch_async(dispatch_get_main_queue(), ^{
121-
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification
122-
object:[UIApplication sharedApplication]];
123-
154+
[[NSNotificationCenter defaultCenter]
155+
postNotificationName:UIApplicationDidBecomeActiveNotification
156+
object:[UIApplication sharedApplication]];
157+
124158
XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
125159
[expectation fulfill];
126160
});
127161
});
128-
129-
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
130-
XCTAssertNil(error, @"Test timed out");
131-
}];
162+
163+
[self waitForExpectationsWithTimeout:5.0
164+
handler:^(NSError *error) {
165+
XCTAssertNil(error, @"Test timed out");
166+
}];
167+
}
168+
169+
/** Tests the exact crash scenario with async dispatch timing that would crash with old
170+
* implementation. */
171+
- (void)testAsyncDispatch_wouldCrashWithOldImplementation {
172+
// This test simulates what the OLD code would do and should crash with async registration
173+
// With the NEW synchronous code, this should pass safely
174+
175+
XCTestExpectation *expectation = [self expectationWithDescription:@"Async crash test complete"];
176+
177+
__block int remainingOperations = 200;
178+
179+
for (int i = 0; i < 200; i++) {
180+
@autoreleasepool {
181+
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
182+
XCTAssertNotNil(tracker);
183+
184+
// Simulate the old problematic pattern
185+
__weak typeof(tracker) weakTracker = tracker;
186+
187+
// This mimics what the OLD async registration would do
188+
dispatch_async(dispatch_get_main_queue(), ^{
189+
// In old code: tracker might be deallocated here → CRASH
190+
// In new code: observers already registered synchronously → SAFE
191+
192+
if (weakTracker) {
193+
[[NSNotificationCenter defaultCenter]
194+
postNotificationName:UIApplicationDidBecomeActiveNotification
195+
object:[UIApplication sharedApplication]];
196+
}
197+
198+
remainingOperations--;
199+
if (remainingOperations == 0) {
200+
[expectation fulfill];
201+
}
202+
});
203+
204+
// Immediately deallocate tracker - this creates the race condition window
205+
}
206+
207+
// Force runloop processing to increase race condition likelihood
208+
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
209+
}
210+
211+
[self waitForExpectationsWithTimeout:5.0
212+
handler:^(NSError *error) {
213+
XCTAssertNil(error, @"Async crash test failed");
214+
}];
215+
}
216+
217+
@end
218+
219+
/**
220+
* CRASH REPRODUCTION TEST - Only use this to verify the original bug exists
221+
* This simulates the original async registration pattern that would cause crashes
222+
*/
223+
224+
@interface CrashReproductionTracker : NSObject
225+
@property(nonatomic, readwrite) int traceBackgroundState;
226+
@end
227+
228+
@implementation CrashReproductionTracker
229+
230+
- (instancetype)init {
231+
self = [super init];
232+
if (self) {
233+
_traceBackgroundState = 0;
234+
235+
// This is the ORIGINAL problematic code that would crash
236+
dispatch_async(dispatch_get_main_queue(), ^{
237+
[[NSNotificationCenter defaultCenter] addObserver:self
238+
selector:@selector(handleNotification:)
239+
name:UIApplicationDidBecomeActiveNotification
240+
object:[UIApplication sharedApplication]];
241+
});
242+
}
243+
return self;
244+
}
245+
246+
- (void)handleNotification:(NSNotification *)notification {
247+
_traceBackgroundState = 1;
248+
}
249+
250+
- (void)dealloc {
251+
[[NSNotificationCenter defaultCenter] removeObserver:self];
252+
}
253+
254+
/**
255+
* CRASH REPRODUCTION TEST - Only use this to verify the original bug exists
256+
* This simulates the original async registration pattern that would cause crashes
257+
* WARNING: This test is commented out because it WILL crash with the original async pattern
258+
*/
259+
260+
- (void)testCrashReproduction_originalAsyncBug {
261+
// This test WILL crash with the original async pattern
262+
for (int i = 0; i < 100; i++) {
263+
@autoreleasepool {
264+
CrashReproductionTracker *tracker = [[CrashReproductionTracker alloc] init];
265+
// tracker deallocates here, but async block is still queued → CRASH
266+
}
267+
268+
// Process run loop to execute queued async blocks
269+
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
270+
}
132271
}
133272

134273
@end

0 commit comments

Comments
 (0)