Skip to content

Commit 2a0aad0

Browse files
test: Polish concurrency tests
Signed-off-by: Fabrizio Demaria <fabrizio.f.demaria@gmail.com>
1 parent 14fa5e7 commit 2a0aad0

File tree

1 file changed

+10
-62
lines changed

1 file changed

+10
-62
lines changed
Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import XCTest
32
import Combine
43
@testable import OpenFeature
@@ -23,7 +22,7 @@ class ConcurrencyRaceConditionTests: XCTestCase {
2322

2423
let concurrentOperations = 100
2524
let expectedTargetingKeys = Set((0..<concurrentOperations).map { "user\($0)" })
26-
25+
2726
await withTaskGroup(of: Void.self) { group in
2827
for i in 0..<concurrentOperations {
2928
group.addTask {
@@ -34,26 +33,24 @@ class ConcurrencyRaceConditionTests: XCTestCase {
3433
"timestamp": .string("\(Date().timeIntervalSince1970)")
3534
])
3635
)
37-
38-
// This should trigger the race condition in updateContext
36+
3937
await OpenFeatureAPI.shared.setEvaluationContextAndWait(evaluationContext: ctx)
4038
}
4139
}
4240
}
43-
41+
4442
cancellable.cancel()
45-
46-
// Verify final state is consistent and correct
43+
4744
let finalContext = OpenFeatureAPI.shared.getEvaluationContext()
4845
XCTAssertNotNil(finalContext, "Final evaluation context should not be nil after concurrent operations")
49-
46+
5047
if let context = finalContext {
5148
let targetingKey = context.getTargetingKey()
5249
XCTAssertTrue(
5350
expectedTargetingKeys.contains(targetingKey),
5451
"Final targeting key '\(targetingKey)' should be one of the expected keys from concurrent operations"
5552
)
56-
53+
5754
let contextMap = context.asObjectMap()
5855
XCTAssertTrue(contextMap.keys.contains("id"), "Context should contain 'id' attribute")
5956
XCTAssertTrue(contextMap.keys.contains("timestamp"), "Context should contain 'timestamp' attribute")
@@ -67,15 +64,11 @@ class ConcurrencyRaceConditionTests: XCTestCase {
6764
}
6865
}
6966

70-
/// Test the specific race condition between setProvider and setEvaluationContext
71-
/// This was the main issue identified by the external reviewer
7267
func testSetProviderVsSetEvaluationContextRaceCondition() async throws {
7368
let concurrentOperations = 50
7469

7570
await withTaskGroup(of: Void.self) { group in
76-
// Concurrently set providers and evaluation contexts
7771
for i in 0..<concurrentOperations {
78-
// Set provider operations
7972
group.addTask {
8073
let provider = MockProvider()
8174
let ctx = ImmutableContext(
@@ -85,7 +78,6 @@ class ConcurrencyRaceConditionTests: XCTestCase {
8578
await OpenFeatureAPI.shared.setProviderAndWait(provider: provider, initialContext: ctx)
8679
}
8780

88-
// Set evaluation context operations
8981
group.addTask {
9082
let ctx = ImmutableContext(
9183
targetingKey: "context-user\(i)",
@@ -96,13 +88,11 @@ class ConcurrencyRaceConditionTests: XCTestCase {
9688
}
9789
}
9890

99-
// Verify the API is in a consistent state
10091
let finalState = OpenFeatureAPI.shared.getState()
10192
XCTAssertNotNil(finalState.provider, "Provider should not be nil after concurrent operations")
10293
XCTAssertNotNil(finalState.evaluationContext, "Evaluation context should not be nil after concurrent operations")
103-
XCTAssertTrue([.ready, .error, .fatal].contains(finalState.providerStatus), "Provider status should be in a valid final state")
94+
XCTAssertTrue([.ready].contains(finalState.providerStatus), "Provider status should be in a valid final state")
10495

105-
// Verify the final context has expected structure from one of the operations
10696
if let context = finalState.evaluationContext {
10797
let targetingKey = context.getTargetingKey()
10898
XCTAssertTrue(
@@ -120,52 +110,13 @@ class ConcurrencyRaceConditionTests: XCTestCase {
120110
}
121111
}
122112

123-
/// Test the race condition between provider initialization and context updates
124-
func testProviderInitializationVsContextUpdateRaceCondition() async throws {
125-
let concurrentOperations = 30
126-
127-
await withTaskGroup(of: Void.self) { group in
128-
for i in 0..<concurrentOperations {
129-
// Provider initialization with context
130-
group.addTask {
131-
let provider = MockProvider()
132-
let initialCtx = ImmutableContext(
133-
targetingKey: "init-user\(i)",
134-
structure: ImmutableStructure(attributes: ["init": .integer(Int64(i))])
135-
)
136-
await OpenFeatureAPI.shared.setProviderAndWait(provider: provider, initialContext: initialCtx)
137-
}
138-
139-
// Immediate context updates
140-
group.addTask {
141-
let updateCtx = ImmutableContext(
142-
targetingKey: "update-user\(i)",
143-
structure: ImmutableStructure(attributes: ["update": .integer(Int64(i))])
144-
)
145-
await OpenFeatureAPI.shared.setEvaluationContextAndWait(evaluationContext: updateCtx)
146-
}
147-
148-
// Clear provider operations
149-
group.addTask {
150-
OpenFeatureAPI.shared.clearProvider()
151-
}
152-
}
153-
}
154-
155-
// Verify the API ends in a consistent state
156-
let finalState = OpenFeatureAPI.shared.getState()
157-
XCTAssertTrue([.notReady, .ready, .error, .fatal].contains(finalState.providerStatus))
158-
}
159-
160-
/// Test high-frequency state changes to stress test synchronization
161113
func testHighFrequencyStateChangesRaceCondition() async throws {
162114
let highFrequencyOperations = 200
163115
let startTime = Date()
164116

165117
await withTaskGroup(of: Void.self) { group in
166118
for i in 0..<highFrequencyOperations {
167119
group.addTask {
168-
// Rapid fire operations
169120
let provider = MockProvider()
170121
let ctx = ImmutableContext(
171122
targetingKey: "rapid-user\(i)",
@@ -174,8 +125,6 @@ class ConcurrencyRaceConditionTests: XCTestCase {
174125
"timestamp": .string("\(Date().timeIntervalSince1970)")
175126
])
176127
)
177-
178-
// Alternate between different operations
179128
switch i % 4 {
180129
case 0:
181130
await OpenFeatureAPI.shared.setProviderAndWait(provider: provider)
@@ -198,14 +147,11 @@ class ConcurrencyRaceConditionTests: XCTestCase {
198147
// Verify operations completed in reasonable time (no deadlocks)
199148
XCTAssertLessThan(duration, 10.0, "Operations took too long, possible deadlock")
200149

201-
// Verify final state is consistent
202150
let finalState = OpenFeatureAPI.shared.getState()
203151
XCTAssertTrue(
204-
[ProviderStatus.notReady, .ready, .error, .fatal].contains(finalState.providerStatus),
152+
[ProviderStatus.ready].contains(finalState.providerStatus),
205153
"Provider status '\(finalState.providerStatus)' should be in a valid state after high-frequency operations"
206154
)
207-
208-
// If we have a provider and context, verify they're consistent
209155
if finalState.provider != nil && finalState.evaluationContext != nil {
210156
let context = finalState.evaluationContext!
211157
let targetingKey = context.getTargetingKey()
@@ -218,6 +164,8 @@ class ConcurrencyRaceConditionTests: XCTestCase {
218164
if contextMap.keys.contains("iteration") {
219165
XCTAssertTrue(contextMap.keys.contains("timestamp"), "Context with iteration should also have timestamp")
220166
}
167+
} else {
168+
XCTFail()
221169
}
222170
}
223171
}

0 commit comments

Comments
 (0)