mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
* FIRInstallationsSingleOperationPromiseCache: a failing test for race condition added. * ./scripts/style.sh * FIRInstallationsSingleOperationPromiseCache: synchronize entire code blocks not relying on `atomic` property. The atomic property synchronization was insufficient to synchronize `newOperationHandler()`. * ./scripts/style.sh
83 lines
2.8 KiB
Objective-C
83 lines
2.8 KiB
Objective-C
/*
|
|
* Copyright 2019 Google
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "FBLPromise+Testing.h"
|
|
#import "FBLPromises.h"
|
|
|
|
#import "FIRInstallationsSingleOperationPromiseCache.h"
|
|
|
|
@interface FIRInstallationsSingleOperationPromiseCacheTests : XCTestCase
|
|
|
|
@end
|
|
|
|
@implementation FIRInstallationsSingleOperationPromiseCacheTests
|
|
|
|
// This test is flaky by definition.
|
|
// If this test fails at least once it means there must be a concurrency issue.
|
|
- (void)testRaceCondition {
|
|
for (NSInteger i = 0; i < 100; i++) {
|
|
[self assertRaceConditionWithParallelOperationCount:10000];
|
|
}
|
|
}
|
|
|
|
- (void)assertRaceConditionWithParallelOperationCount:(NSInteger)count {
|
|
FIRInstallationsSingleOperationPromiseCache *promiseCache =
|
|
[[FIRInstallationsSingleOperationPromiseCache alloc]
|
|
initWithNewOperationHandler:^FBLPromise *_Nonnull {
|
|
[NSThread sleepForTimeInterval:0.001];
|
|
return [[FBLPromise resolvedWith:[[NSObject alloc] init]] delay:0.001];
|
|
}];
|
|
|
|
XCTestExpectation *operationsExpectation =
|
|
[self expectationWithDescription:@"operationsExpectation"];
|
|
operationsExpectation.expectedFulfillmentCount = count;
|
|
|
|
for (NSInteger i = 0; i < count; i++) {
|
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
XCTAssertNoThrow([promiseCache getExistingPendingPromise]);
|
|
});
|
|
|
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
FBLPromise *promise = [promiseCache getExistingPendingOrCreateNewPromise];
|
|
XCTAssertNotNil(promise);
|
|
[operationsExpectation fulfill];
|
|
});
|
|
}
|
|
|
|
XCTAssert(FBLWaitForPromisesWithTimeout(10));
|
|
[self waitForExpectations:@[ operationsExpectation ] timeout:10];
|
|
|
|
// The resolved promise cleanup may not happen instantly. Wait until it happens.
|
|
FBLPromise *existingPromise = [promiseCache getExistingPendingPromise];
|
|
if (existingPromise) {
|
|
XCTestExpectation *cleanupExpectation = [self expectationWithDescription:@""];
|
|
|
|
[existingPromise then:^id _Nullable(id _Nullable value) {
|
|
[cleanupExpectation fulfill];
|
|
return nil;
|
|
}];
|
|
|
|
[self waitForExpectations:@[ cleanupExpectation ] timeout:0.5];
|
|
}
|
|
|
|
// Check if resolved promise cleaned up.
|
|
XCTAssertNil([promiseCache getExistingPendingPromise]);
|
|
}
|
|
|
|
@end
|