Skip to content

Commit

Permalink
test wip 2 cleraing state between
Browse files Browse the repository at this point in the history
  • Loading branch information
nan-li committed Apr 29, 2024
1 parent 86b45a2 commit 6f0d5aa
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public class MockOneSignalClient: NSObject, IOneSignalClient {
}

public func setMockResponseForRequest(request: String, response: [String: Any]) {
print("💛 setMockResponseForRequest \(request) \(response)")
mockResponses[request] = response
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ extension OSModelStoreListener {
store.changeSubscription.subscribe(self)
}

func close() {
public func close() {
store.changeSubscription.unsubscribe(self)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {

var identityModelRepo = OSIdentityModelRepo()

private var hasCalledStart = false
var hasCalledStart = false

private var jwtExpiredHandler: OSJwtExpiredHandler?

Expand All @@ -139,7 +139,7 @@ public class OneSignalUserManagerImpl: NSObject, OneSignalUserManager {
return user
}

private var _user: OSUserInternal?
var _user: OSUserInternal?

// This is a user instance to operate on when there is no app_id and/or privacy consent yet, effectively no-op.
// The models are not added to any model stores.
Expand Down
24 changes: 14 additions & 10 deletions iOS_SDK/OneSignalSDK/OneSignalUserMocks/OneSignalUserMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,31 +51,35 @@ public class OneSignalUserMocks: NSObject {
*/
public static func resetUserManager() {
OneSignalUserManagerImpl.sharedInstance.identityModelRepo.reset()


// Model store listeners unsubscribe to their models
// User Manager start() will subscribe
OneSignalUserManagerImpl.sharedInstance.identityModelStoreListener.close()
OneSignalUserManagerImpl.sharedInstance.propertiesModelStoreListener.close()
OneSignalUserManagerImpl.sharedInstance.subscriptionModelStoreListener.close()
OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStoreListener.close()

OneSignalUserManagerImpl.sharedInstance.identityModelStore.clearModelsFromStore()
OneSignalUserManagerImpl.sharedInstance.propertiesModelStore.clearModelsFromStore()
OneSignalUserManagerImpl.sharedInstance.subscriptionModelStore.clearModelsFromStore()
OneSignalUserManagerImpl.sharedInstance.pushSubscriptionModelStore.clearModelsFromStore()

let propertyExecutor = OSPropertyOperationExecutor()
let identityExecutor = OSIdentityOperationExecutor()
let subscriptionExecutor = OSSubscriptionOperationExecutor()

OneSignalUserManagerImpl.sharedInstance.propertyExecutor = propertyExecutor
OneSignalUserManagerImpl.sharedInstance.identityExecutor = identityExecutor
OneSignalUserManagerImpl.sharedInstance.subscriptionExecutor = subscriptionExecutor



// TODO: Reset Operation Repo first
// OSCoreMocks.resetOperationRepo()

OSOperationRepo.sharedInstance.addExecutor(identityExecutor)
OSOperationRepo.sharedInstance.addExecutor(propertyExecutor)
OSOperationRepo.sharedInstance.addExecutor(subscriptionExecutor)

OneSignalUserManagerImpl.sharedInstance._user = nil
OneSignalUserManagerImpl.sharedInstance.hasCalledStart = false
}
}

extension OSIdentityModelRepo {
func reset() {
print("💛 reset")
self.models = [:]
}
}
123 changes: 62 additions & 61 deletions iOS_SDK/OneSignalSDK/OneSignalUserTests/OneSignalUserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,78 +69,79 @@ final class OneSignalUserTests: XCTestCase {
It is possible for two threads to flush concurrently.
However, this test does not crash 100% of the time.
*/
// func testOperationRepoFlushingConcurrency() throws {
// /* Setup */
// OneSignalCoreImpl.setSharedClient(MockOneSignalClient())
//
// /* When */
//
// // 1. Enqueue 10 Deltas to the Operation Repo
// for num in 0...9 {
// OneSignalUserManagerImpl.sharedInstance.addTag(key: "tag\(num)", value: "value")
// }
//
// // 2. Flush the delta queue from 4 multiple threads
// for _ in 1...4 {
// DispatchQueue.global().async {
// print("🧪 flushDeltaQueue on thread \(Thread.current)")
// OSOperationRepo.sharedInstance.addFlushDeltaQueueToDispatchQueue()
// }
// }
//
// /* Then */
// // There are two places that can crash, as multiple threads are manipulating arrays:
// // 1. OpRepo: `deltaQueue.remove(at: index)` index out of bounds
// // 2. OSPropertyOperationExecutor: `deltaQueue.append(delta)` EXC_BAD_ACCESS
// }
func testOperationRepoFlushingConcurrency() throws {
/* Setup */
OneSignalCoreImpl.setSharedClient(MockOneSignalClient())

/* When */

// 1. Enqueue 10 Deltas to the Operation Repo
for num in 0...9 {
OneSignalUserManagerImpl.sharedInstance.addTag(key: "tag\(num)", value: "value")
}

// 2. Flush the delta queue from 4 multiple threads
for _ in 1...4 {
DispatchQueue.global().async {
print("🧪 flushDeltaQueue on thread \(Thread.current)")
OSOperationRepo.sharedInstance.addFlushDeltaQueueToDispatchQueue()
}
}

/* Then */
// There are two places that can crash, as multiple threads are manipulating arrays:
// 1. OpRepo: `deltaQueue.remove(at: index)` index out of bounds
// 2. OSPropertyOperationExecutor: `deltaQueue.append(delta)` EXC_BAD_ACCESS
}

/**
This test reproduced a crash when the property model is being encoded.
*/
// func testEncodingPropertiesModel_withConcurrency_doesNotCrash() throws {
// /* Setup */
// let propertiesModel = OSPropertiesModel(changeNotifier: OSEventProducer())
//
// /* When */
// DispatchQueue.concurrentPerform(iterations: 5_000) { i in
// // 1. Add tags
// for num in 0...9 {
// propertiesModel.addTags(["\(i)tag\(num)": "value"])
// }
//
// // 2. Encode the model
// OneSignalUserDefaults.initShared().saveCodeableData(forKey: "PropertyModel", withValue: propertiesModel)
//
// // 3. Clear the tags
// propertiesModel.clearData()
// }
// }
func testEncodingPropertiesModel_withConcurrency_doesNotCrash() throws {
/* Setup */
let propertiesModel = OSPropertiesModel(changeNotifier: OSEventProducer())

/* When */
DispatchQueue.concurrentPerform(iterations: 5_000) { i in
// 1. Add tags
for num in 0...9 {
propertiesModel.addTags(["\(i)tag\(num)": "value"])
}

// 2. Encode the model
OneSignalUserDefaults.initShared().saveCodeableData(forKey: "PropertyModel", withValue: propertiesModel)

// 3. Clear the tags
propertiesModel.clearData()
}
}

/**
This test reproduced a crash when the identity model is being encoded.
*/
// func testEncodingIdentityModel_withConcurrency_doesNotCrash() throws {
// /* Setup */
// let identityModel = OSIdentityModel(aliases: nil, changeNotifier: OSEventProducer())
//
// /* When */
// DispatchQueue.concurrentPerform(iterations: 5_000) { i in
// // 1. Add aliases
// for num in 0...9 {
// identityModel.addAliases(["\(i)alias\(num)": "value"])
// }
//
// // 2. Encode the model
// OneSignalUserDefaults.initShared().saveCodeableData(forKey: "IdentityModel", withValue: identityModel)
//
// // 2. Clear the aliases
// identityModel.clearData()
// }
// }
func testEncodingIdentityModel_withConcurrency_doesNotCrash() throws {
/* Setup */
let identityModel = OSIdentityModel(aliases: nil, changeNotifier: OSEventProducer())

/* When */
DispatchQueue.concurrentPerform(iterations: 5_000) { i in
// 1. Add aliases
for num in 0...9 {
identityModel.addAliases(["\(i)alias\(num)": "value"])
}

// 2. Encode the model
OneSignalUserDefaults.initShared().saveCodeableData(forKey: "IdentityModel", withValue: identityModel)

// 2. Clear the aliases
identityModel.clearData()
}
}

func testSwitchUser_sendsCorrectTags() throws {
/* Setup */

print("💛 starting tests")
let client = MockOneSignalClient()

// 1. Set up mock responses for the anonymous user
Expand Down Expand Up @@ -203,7 +204,7 @@ final class OneSignalUserTests: XCTestCase {
/* Then */

// Assert that every request SDK makes has a response set, and is handled
XCTAssertTrue(client.allRequestsHandled)
XCTAssertTrue(client.allRequestsHandled)

// Assert there is only one request containing these tags and they are sent to userA
XCTAssertTrue(client.onlyOneRequest(
Expand Down

0 comments on commit 6f0d5aa

Please sign in to comment.