Skip to content

Commit

Permalink
Merge pull request #1466 from OneSignal/tests_fix_flaky_tests
Browse files Browse the repository at this point in the history
[tests] Fix flaky tests
  • Loading branch information
nan-li authored Aug 6, 2024
2 parents 836c73a + f79af94 commit d63855d
Show file tree
Hide file tree
Showing 15 changed files with 947 additions and 269 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ jobs:
- name: Test
env:
scheme: ${{ 'UnitTestApp' }}
test_plan: ${{ 'UnitTestApp_TestPlan_Reduced' }}
platform: ${{ 'iOS Simulator' }}
file_to_build: ${{ 'iOS_SDK/OneSignalSDK/OneSignal.xcodeproj' }}
filetype_parameter: ${{ 'project' }}
run: |
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"
xcodebuild test-without-building -scheme "$scheme" -testPlan "$test_plan" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"
323 changes: 323 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand Down Expand Up @@ -143,6 +143,15 @@
ReferencedContainer = "container:OneSignal.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<TestPlans>
<TestPlanReference
reference = "container:UnitTestApp/UnitTestApp_TestPlan_Full.xctestplan"
default = "YES">
</TestPlanReference>
<TestPlanReference
reference = "container:UnitTestApp/UnitTestApp_TestPlan_Reduced.xctestplan">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
Expand Down
22 changes: 10 additions & 12 deletions iOS_SDK/OneSignalSDK/OneSignalCoreMocks/OneSignalCoreMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,18 @@ import XCTest
public class OneSignalCoreMocks: NSObject {
@objc
public static func clearUserDefaults() {
guard let userDefaults = OneSignalUserDefaults.initStandard().userDefaults else {
return
}
let dictionary = userDefaults.dictionaryRepresentation()
for key in dictionary.keys {
userDefaults.removeObject(forKey: key)
if let userDefaults = OneSignalUserDefaults.initStandard().userDefaults {
let dictionary = userDefaults.dictionaryRepresentation()
for key in dictionary.keys {
userDefaults.removeObject(forKey: key)
}
}

guard let sharedUserDefaults = OneSignalUserDefaults.initShared().userDefaults else {
return
}
let sharedDictionary = sharedUserDefaults.dictionaryRepresentation()
for key in sharedDictionary.keys {
sharedUserDefaults.removeObject(forKey: key)
if let sharedUserDefaults = OneSignalUserDefaults.initShared().userDefaults {
let sharedDictionary = sharedUserDefaults.dictionaryRepresentation()
for key in sharedDictionary.keys {
sharedUserDefaults.removeObject(forKey: key)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@ final class OSLiveActivitiesExecutorTests: XCTestCase {

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

override func tearDownWithError() throws {
// TODO: Need to clear all data between tests for user manager, models, etc.
OneSignalCoreMocks.clearUserDefaults()
OneSignalUserMocks.reset()
}
override func tearDownWithError() throws { }

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

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

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

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

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

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

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

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

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

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

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

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

let request1 = OSRequestSetUpdateToken(key: "my-activity-id", token: "my-token-1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class OSOperationRepo: NSObject {
// Maps delta names to the interfaces for the operation executors
var deltasToExecutorMap: [String: OSOperationExecutor] = [:]
var executors: [OSOperationExecutor] = []
private var deltaQueue: [OSDelta] = []
var deltaQueue: [OSDelta] = [] // non-private for unit test access

// TODO: This could come from a config, plist, method, remote params
var pollIntervalMilliseconds = Int(POLL_INTERVAL_MS)
Expand Down
50 changes: 50 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalOSCoreMocks/OSCoreMocks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Modified MIT License
Copyright 2024 OneSignal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
2. All copies of substantial portions of the Software may only be used in connection
with services provided by OneSignal.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

import Foundation
import OneSignalCore
@testable import OneSignalOSCore

@objc
public class OSCoreMocks: NSObject {
public static func resetOperationRepo() {
OSOperationRepo.sharedInstance.reset()
}
}

extension OSOperationRepo {
/**
The Operation Repo needs to reset between tests until we dependency inject the Operation Repo,
to prevent state from carrying over between tests.
*/
func reset() {
deltaQueue.removeAll()
executors.removeAll()
deltasToExecutorMap.removeAll()
paused = false
}
}
38 changes: 38 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalOSCoreMocks/OneSignalOSCoreMocks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Modified MIT License
Copyright 2024 OneSignal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
2. All copies of substantial portions of the Software may only be used in connection
with services provided by OneSignal.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#import <Foundation/Foundation.h>

//! Project version number for OneSignalOSCoreMocks.
FOUNDATION_EXPORT double OneSignalOSCoreMocksVersionNumber;

//! Project version string for OneSignalOSCoreMocks.
FOUNDATION_EXPORT const unsigned char OneSignalOSCoreMocksVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <OneSignalOSCoreMocks/PublicHeader.h>


Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import Foundation
import OneSignalOSCore
import OneSignalOSCoreMocks
@testable import OneSignalUser

@objc
Expand All @@ -35,8 +36,7 @@ public class OneSignalUserMocks: NSObject {
// TODO: create mocked server responses to user requests
@objc
public static func reset() {
// TODO: Reset Operation Repo first
// OSCoreMocks.resetOperationRepo()
OSCoreMocks.resetOperationRepo()
OneSignalUserManagerImpl.sharedInstance.reset()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,16 @@ @implementation OneSignalUserObjcTests

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

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

/**
Tests passing purchase data to the User Manager to process and send.
Expand Down
Loading

0 comments on commit d63855d

Please sign in to comment.