Skip to content

Commit d63855d

Browse files
authored
Merge pull request #1466 from OneSignal/tests_fix_flaky_tests
[tests] Fix flaky tests
2 parents 836c73a + f79af94 commit d63855d

15 files changed

Lines changed: 947 additions & 269 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ jobs:
4040
- name: Test
4141
env:
4242
scheme: ${{ 'UnitTestApp' }}
43+
test_plan: ${{ 'UnitTestApp_TestPlan_Reduced' }}
4344
platform: ${{ 'iOS Simulator' }}
4445
file_to_build: ${{ 'iOS_SDK/OneSignalSDK/OneSignal.xcodeproj' }}
4546
filetype_parameter: ${{ 'project' }}
4647
run: |
4748
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
4849
device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
49-
xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"
50+
xcodebuild test-without-building -scheme "$scheme" -testPlan "$test_plan" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Lines changed: 323 additions & 0 deletions
Large diffs are not rendered by default.

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/xcshareddata/xcschemes/UnitTestApp.xcscheme

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
33
LastUpgradeVersion = "1200"
4-
version = "1.3">
4+
version = "1.7">
55
<BuildAction
66
parallelizeBuildables = "YES"
77
buildImplicitDependencies = "YES">
@@ -143,6 +143,15 @@
143143
ReferencedContainer = "container:OneSignal.xcodeproj">
144144
</BuildableReference>
145145
</CodeCoverageTargets>
146+
<TestPlans>
147+
<TestPlanReference
148+
reference = "container:UnitTestApp/UnitTestApp_TestPlan_Full.xctestplan"
149+
default = "YES">
150+
</TestPlanReference>
151+
<TestPlanReference
152+
reference = "container:UnitTestApp/UnitTestApp_TestPlan_Reduced.xctestplan">
153+
</TestPlanReference>
154+
</TestPlans>
146155
<Testables>
147156
<TestableReference
148157
skipped = "NO">

iOS_SDK/OneSignalSDK/OneSignalCoreMocks/OneSignalCoreMocks.swift

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,18 @@ import XCTest
2828
public class OneSignalCoreMocks: NSObject {
2929
@objc
3030
public static func clearUserDefaults() {
31-
guard let userDefaults = OneSignalUserDefaults.initStandard().userDefaults else {
32-
return
33-
}
34-
let dictionary = userDefaults.dictionaryRepresentation()
35-
for key in dictionary.keys {
36-
userDefaults.removeObject(forKey: key)
31+
if let userDefaults = OneSignalUserDefaults.initStandard().userDefaults {
32+
let dictionary = userDefaults.dictionaryRepresentation()
33+
for key in dictionary.keys {
34+
userDefaults.removeObject(forKey: key)
35+
}
3736
}
3837

39-
guard let sharedUserDefaults = OneSignalUserDefaults.initShared().userDefaults else {
40-
return
41-
}
42-
let sharedDictionary = sharedUserDefaults.dictionaryRepresentation()
43-
for key in sharedDictionary.keys {
44-
sharedUserDefaults.removeObject(forKey: key)
38+
if let sharedUserDefaults = OneSignalUserDefaults.initShared().userDefaults {
39+
let sharedDictionary = sharedUserDefaults.dictionaryRepresentation()
40+
for key in sharedDictionary.keys {
41+
sharedUserDefaults.removeObject(forKey: key)
42+
}
4543
}
4644
}
4745

iOS_SDK/OneSignalSDK/OneSignalLiveActivitiesTests/OSLiveActivitiesExecutorTests.swift

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,16 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
3737

3838
override func setUpWithError() throws {
3939
// TODO: Something like the existing [UnitTestCommonMethods beforeEachTest:self];
40+
// TODO: Need to clear all data between tests for user manager, models, etc.
41+
OneSignalCoreMocks.clearUserDefaults()
42+
OneSignalUserMocks.reset()
4043
// App ID is set because User Manager has guards against nil App ID
4144
OneSignalConfigManager.setAppId("test-app-id")
4245
// Temp. logging to help debug during testing
4346
OneSignalLog.setLogLevel(.LL_VERBOSE)
4447
}
4548

46-
override func tearDownWithError() throws {
47-
// TODO: Need to clear all data between tests for user manager, models, etc.
48-
OneSignalCoreMocks.clearUserDefaults()
49-
OneSignalUserMocks.reset()
50-
}
49+
override func tearDownWithError() throws { }
5150

5251
func testAppendSetStartTokenWithSuccessfulRequest() throws {
5352
/* Setup */
@@ -56,6 +55,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
5655
OneSignalCoreImpl.setSharedClient(mockClient)
5756
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
5857
OneSignalUserManagerImpl.sharedInstance.start()
58+
// Wait for any user setup requests to complete
59+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
5960
mockClient.reset()
6061

6162
let request = OSRequestSetStartToken(key: "my-activity-type", token: "my-token")
@@ -81,6 +82,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
8182
OneSignalCoreImpl.setSharedClient(mockClient)
8283
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
8384
OneSignalUserManagerImpl.sharedInstance.start()
85+
// Wait for any user setup requests to complete
86+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
8487
mockClient.reset()
8588

8689
let request = OSRequestRemoveStartToken(key: "my-activity-type")
@@ -104,6 +107,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
104107
OneSignalCoreImpl.setSharedClient(mockClient)
105108
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
106109
OneSignalUserManagerImpl.sharedInstance.start()
110+
// Wait for any user setup requests to complete
111+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
107112
mockClient.reset()
108113

109114
let request = OSRequestSetUpdateToken(key: "my-activity-id", token: "my-token")
@@ -129,6 +134,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
129134
OneSignalCoreImpl.setSharedClient(mockClient)
130135
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
131136
OneSignalUserManagerImpl.sharedInstance.start()
137+
// Wait for any user setup requests to complete
138+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
132139
mockClient.reset()
133140

134141
let request = OSRequestRemoveStartToken(key: "my-activity-id")
@@ -171,6 +178,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
171178
OneSignalCoreImpl.setSharedClient(mockClient)
172179
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
173180
OneSignalUserManagerImpl.sharedInstance.start()
181+
// Wait for any user setup requests to complete
182+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
174183
mockClient.reset()
175184

176185
let request = OSRequestSetStartToken(key: "my-activity-type", token: "my-token")
@@ -196,6 +205,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
196205
OneSignalCoreImpl.setSharedClient(mockClient)
197206
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
198207
OneSignalUserManagerImpl.sharedInstance.start()
208+
// Wait for any user setup requests to complete
209+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
199210
mockClient.reset()
200211

201212
let request = OSRequestSetStartToken(key: "my-activity-type", token: "my-token")
@@ -219,6 +230,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
219230
OneSignalCoreImpl.setSharedClient(mockClient)
220231
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
221232
OneSignalUserManagerImpl.sharedInstance.start()
233+
// Wait for any user setup requests to complete
234+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
222235
mockClient.reset()
223236

224237
let request1 = OSRequestSetStartToken(key: "my-activity-type", token: "my-token")
@@ -246,6 +259,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
246259
OneSignalCoreImpl.setSharedClient(mockClient)
247260
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
248261
OneSignalUserManagerImpl.sharedInstance.start()
262+
// Wait for any user setup requests to complete
263+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
249264
mockClient.reset()
250265

251266
let request1 = OSRequestSetStartToken(key: "my-activity-type", token: "my-token-1")
@@ -274,6 +289,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
274289
OneSignalCoreImpl.setSharedClient(mockClient)
275290
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
276291
OneSignalUserManagerImpl.sharedInstance.start()
292+
// Wait for any user setup requests to complete
293+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
277294
mockClient.reset()
278295

279296
let request1 = OSRequestSetStartToken(key: "my-activity-type", token: "my-token-1")
@@ -301,6 +318,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
301318
OneSignalCoreImpl.setSharedClient(mockClient)
302319
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
303320
OneSignalUserManagerImpl.sharedInstance.start()
321+
// Wait for any user setup requests to complete
322+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
304323
mockClient.reset()
305324

306325
let request1 = OSRequestSetUpdateToken(key: "my-activity-id", token: "my-token")
@@ -328,6 +347,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
328347
OneSignalCoreImpl.setSharedClient(mockClient)
329348
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
330349
OneSignalUserManagerImpl.sharedInstance.start()
350+
// Wait for any user setup requests to complete
351+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
331352
mockClient.reset()
332353

333354
let request1 = OSRequestSetUpdateToken(key: "my-activity-id", token: "my-token-1")
@@ -356,6 +377,8 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {
356377
OneSignalCoreImpl.setSharedClient(mockClient)
357378
OneSignalUserDefaults.initShared().saveString(forKey: OSUD_LEGACY_PLAYER_ID, withValue: "my-subscription-id")
358379
OneSignalUserManagerImpl.sharedInstance.start()
380+
// Wait for any user setup requests to complete
381+
OneSignalCoreMocks.waitForBackgroundThreads(seconds: 0.2)
359382
mockClient.reset()
360383

361384
let request1 = OSRequestSetUpdateToken(key: "my-activity-id", token: "my-token-1")

iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/OSOperationRepo.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class OSOperationRepo: NSObject {
4242
// Maps delta names to the interfaces for the operation executors
4343
var deltasToExecutorMap: [String: OSOperationExecutor] = [:]
4444
var executors: [OSOperationExecutor] = []
45-
private var deltaQueue: [OSDelta] = []
45+
var deltaQueue: [OSDelta] = [] // non-private for unit test access
4646

4747
// TODO: This could come from a config, plist, method, remote params
4848
var pollIntervalMilliseconds = Int(POLL_INTERVAL_MS)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Modified MIT License
3+
4+
Copyright 2024 OneSignal
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
1. The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
2. All copies of substantial portions of the Software may only be used in connection
17+
with services provided by OneSignal.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
*/
27+
28+
import Foundation
29+
import OneSignalCore
30+
@testable import OneSignalOSCore
31+
32+
@objc
33+
public class OSCoreMocks: NSObject {
34+
public static func resetOperationRepo() {
35+
OSOperationRepo.sharedInstance.reset()
36+
}
37+
}
38+
39+
extension OSOperationRepo {
40+
/**
41+
The Operation Repo needs to reset between tests until we dependency inject the Operation Repo,
42+
to prevent state from carrying over between tests.
43+
*/
44+
func reset() {
45+
deltaQueue.removeAll()
46+
executors.removeAll()
47+
deltasToExecutorMap.removeAll()
48+
paused = false
49+
}
50+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Modified MIT License
3+
4+
Copyright 2024 OneSignal
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
1. The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
2. All copies of substantial portions of the Software may only be used in connection
17+
with services provided by OneSignal.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
*/
27+
28+
#import <Foundation/Foundation.h>
29+
30+
//! Project version number for OneSignalOSCoreMocks.
31+
FOUNDATION_EXPORT double OneSignalOSCoreMocksVersionNumber;
32+
33+
//! Project version string for OneSignalOSCoreMocks.
34+
FOUNDATION_EXPORT const unsigned char OneSignalOSCoreMocksVersionString[];
35+
36+
// In this header, you should import all the public headers of your framework using statements like #import <OneSignalOSCoreMocks/PublicHeader.h>
37+
38+

iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import Foundation
2929
import OneSignalOSCore
30+
import OneSignalOSCoreMocks
3031
@testable import OneSignalUser
3132

3233
@objc
@@ -35,8 +36,7 @@ public class OneSignalUserMocks: NSObject {
3536
// TODO: create mocked server responses to user requests
3637
@objc
3738
public static func reset() {
38-
// TODO: Reset Operation Repo first
39-
// OSCoreMocks.resetOperationRepo()
39+
OSCoreMocks.resetOperationRepo()
4040
OneSignalUserManagerImpl.sharedInstance.reset()
4141
}
4242
}

iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserObjcTests.m

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ @implementation OneSignalUserObjcTests
1313

1414
- (void)setUp {
1515
// TODO: Something like the existing [UnitTestCommonMethods beforeEachTest:self];
16+
// TODO: Need to clear all data between tests for client, user manager, models, etc.
17+
[OneSignalCoreMocks clearUserDefaults];
18+
[OneSignalUserMocks reset];
1619
// App ID is set because User Manager has guards against nil App ID
1720
[OneSignalConfigManager setAppId:@"test-app-id"];
1821
// Temp. logging to help debug during testing
1922
[OneSignalLog setLogLevel:ONE_S_LL_VERBOSE];
2023
}
2124

22-
- (void)tearDown {
23-
// TODO: Need to clear all data between tests for client, user manager, models, etc.
24-
[OneSignalCoreMocks clearUserDefaults];
25-
[OneSignalUserMocks reset];
26-
}
25+
- (void)tearDown { }
2726

2827
/**
2928
Tests passing purchase data to the User Manager to process and send.

0 commit comments

Comments
 (0)