Further isolate tests that should be isolated
This commit is contained in:
parent
ce833bd48d
commit
bb9f83f69c
@ -157,7 +157,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
// This should be the first thing we do.
|
||||
let mainAppContext = MainAppContext()
|
||||
SetCurrentAppContext(mainAppContext)
|
||||
SetCurrentAppContext(mainAppContext, isRunningTests: false)
|
||||
|
||||
let debugLogger = DebugLogger.shared
|
||||
debugLogger.enableTTYLoggingIfNeeded()
|
||||
|
||||
@ -33,7 +33,7 @@ class DebugUISyncMessages: DebugUIPage {
|
||||
}
|
||||
|
||||
private static func sendBlockListSyncMessage() {
|
||||
Task { await SSKEnvironment.shared.blockingManagerRef.syncBlockList() }
|
||||
_ = SSKEnvironment.shared.blockingManagerRef.syncBlockList()
|
||||
}
|
||||
|
||||
private static func sendConfigurationSyncMessage() {
|
||||
|
||||
@ -8,7 +8,7 @@ import XCTest
|
||||
|
||||
@testable import SignalUI
|
||||
|
||||
final class PhoneNumberCountryTest: XCTestCase {
|
||||
final class PhoneNumberCountryTest: SignalBaseTest {
|
||||
func testCountryCodesForSearchTerm() {
|
||||
func countryCodes(forSearchTerm searchTerm: String?) -> [String] {
|
||||
return PhoneNumberCountry.buildCountries(searchText: searchTerm).map(\.countryCode)
|
||||
|
||||
@ -8,11 +8,13 @@ public import XCTest
|
||||
@testable import SignalServiceKit
|
||||
|
||||
open class SignalBaseTest: XCTestCase {
|
||||
private var oldContext: (any AppContext)!
|
||||
|
||||
@MainActor
|
||||
public override func setUp() {
|
||||
super.setUp()
|
||||
let setupExpectation = expectation(description: "mock ssk environment setup completed")
|
||||
self.oldContext = CurrentAppContext()
|
||||
Task {
|
||||
await MockSSKEnvironment.activate()
|
||||
setupExpectation.fulfill()
|
||||
@ -20,8 +22,9 @@ open class SignalBaseTest: XCTestCase {
|
||||
waitForExpectations(timeout: 2)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
open override func tearDown() {
|
||||
MockSSKEnvironment.flushAndWait()
|
||||
MockSSKEnvironment.deactivate(oldContext: self.oldContext)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
@ -57,11 +57,12 @@ class CollectionViewLogger: MediaGalleryCollectionViewUpdaterDelegate {
|
||||
|
||||
}
|
||||
|
||||
final class MediaGalleryCollectionViewUpdaterTest: SignalBaseTest {
|
||||
final class MediaGalleryCollectionViewUpdaterTest: XCTestCase {
|
||||
private var logger: CollectionViewLogger!
|
||||
private var updater: MediaGalleryCollectionViewUpdater!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
logger = CollectionViewLogger()
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ class OWSContactsManagerTest: SignalBaseTest {
|
||||
|
||||
override func tearDown() {
|
||||
mockUsernameLookupMananger.clearAllUsernames()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
private func makeContactsManager() -> OWSContactsManager {
|
||||
|
||||
@ -12,7 +12,7 @@ class NSEEnvironment {
|
||||
|
||||
init() {
|
||||
self.appContext = NSEContext()
|
||||
SetCurrentAppContext(self.appContext)
|
||||
SetCurrentAppContext(self.appContext, isRunningTests: false)
|
||||
appReadiness = AppReadinessImpl()
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,14 @@ public class DependenciesBridge {
|
||||
}
|
||||
private static var _shared: DependenciesBridge?
|
||||
|
||||
static func setShared(_ dependenciesBridge: DependenciesBridge) {
|
||||
#if TESTABLE_BUILD
|
||||
static var hasShared: Bool {
|
||||
return _shared != nil
|
||||
}
|
||||
#endif
|
||||
|
||||
static func setShared(_ dependenciesBridge: DependenciesBridge?, isRunningTests: Bool) {
|
||||
owsPrecondition((_shared == nil && dependenciesBridge != nil) || isRunningTests)
|
||||
Self._shared = dependenciesBridge
|
||||
}
|
||||
|
||||
|
||||
@ -1366,7 +1366,7 @@ public class AppSetup {
|
||||
wallpaperImageStore: wallpaperImageStore,
|
||||
wallpaperStore: wallpaperStore
|
||||
)
|
||||
DependenciesBridge.setShared(dependenciesBridge)
|
||||
DependenciesBridge.setShared(dependenciesBridge, isRunningTests: appContext.isRunningTests)
|
||||
|
||||
let proximityMonitoringManager = OWSProximityMonitoringManagerImpl()
|
||||
let avatarBuilder = AvatarBuilder(appReadiness: appReadiness)
|
||||
|
||||
@ -29,6 +29,14 @@ public class BlockingManager {
|
||||
private let blockedGroupStore: BlockedGroupStore
|
||||
private let blockedRecipientStore: BlockedRecipientStore
|
||||
|
||||
private let syncQueue = SerialTaskQueue()
|
||||
|
||||
#if TESTABLE_BUILD
|
||||
func flushSyncQueueTask() -> Task<Void, any Error> {
|
||||
return self.syncQueue.enqueue {}
|
||||
}
|
||||
#endif
|
||||
|
||||
init(
|
||||
appReadiness: AppReadiness,
|
||||
blockedGroupStore: BlockedGroupStore,
|
||||
@ -48,7 +56,9 @@ public class BlockingManager {
|
||||
|
||||
private func syncIfNeeded() {
|
||||
appReadiness.runNowOrWhenMainAppDidBecomeReadyAsync {
|
||||
Task { await self.sendBlockListSyncMessage(force: false) }
|
||||
self.syncQueue.enqueue {
|
||||
await self.sendBlockListSyncMessage(force: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +76,9 @@ public class BlockingManager {
|
||||
private func setNeedsSync(tx: DBWriteTransaction) {
|
||||
setChangeToken(fetchChangeToken(tx: tx) + 1, tx: tx)
|
||||
tx.addSyncCompletion {
|
||||
Task { await self.sendBlockListSyncMessage(force: false) }
|
||||
self.syncQueue.enqueue {
|
||||
await self.sendBlockListSyncMessage(force: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,8 +419,10 @@ public class BlockingManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func syncBlockList() async {
|
||||
await sendBlockListSyncMessage(force: true)
|
||||
public func syncBlockList() -> Task<Void, any Error> {
|
||||
return self.syncQueue.enqueue {
|
||||
await self.sendBlockListSyncMessage(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func sendBlockListSyncMessage(force: Bool) async {
|
||||
|
||||
@ -688,7 +688,7 @@ public final class MessageReceiver {
|
||||
let pendingTask = Self.buildPendingTask(label: "syncBlockList")
|
||||
Task {
|
||||
defer { pendingTask.complete() }
|
||||
await SSKEnvironment.shared.blockingManagerRef.syncBlockList()
|
||||
try? await SSKEnvironment.shared.blockingManagerRef.syncBlockList().value
|
||||
}
|
||||
|
||||
case .configuration:
|
||||
|
||||
@ -14,8 +14,8 @@ public class SSKEnvironment: NSObject {
|
||||
@objc
|
||||
public static var shared: SSKEnvironment { _shared! }
|
||||
|
||||
public static func setShared(_ env: SSKEnvironment, isRunningTests: Bool) {
|
||||
owsPrecondition(_shared == nil || isRunningTests)
|
||||
public static func setShared(_ env: SSKEnvironment?, isRunningTests: Bool) {
|
||||
owsPrecondition((_shared == nil && env != nil) || isRunningTests)
|
||||
_shared = env
|
||||
}
|
||||
|
||||
|
||||
@ -11,10 +11,28 @@ import GRDB
|
||||
public class MockSSKEnvironment {
|
||||
/// Set up a mock SSK environment as well as ``DependenciesBridge``.
|
||||
@MainActor
|
||||
public static func activate() async {
|
||||
public static func activate(
|
||||
appReadiness: any AppReadiness = AppReadinessImpl(),
|
||||
callMessageHandler: any CallMessageHandler = NoopCallMessageHandler(),
|
||||
currentCallProvider: any CurrentCallProvider = CurrentCallNoOpProvider(),
|
||||
notificationPresenter: any NotificationPresenter = NoopNotificationPresenterImpl(),
|
||||
incrementalMessageTSAttachmentMigratorFactory: any IncrementalMessageTSAttachmentMigratorFactory = IncrementalMessageTSAttachmentMigratorFactoryMock(),
|
||||
testDependencies: AppSetup.TestDependencies? = nil
|
||||
) async {
|
||||
owsPrecondition(!(CurrentAppContext() is TestAppContext))
|
||||
owsPrecondition(!SSKEnvironment.hasShared)
|
||||
owsPrecondition(!DependenciesBridge.hasShared)
|
||||
|
||||
let testAppContext = TestAppContext()
|
||||
SetCurrentAppContext(testAppContext)
|
||||
let appReadiness = AppReadinessImpl()
|
||||
SetCurrentAppContext(testAppContext, isRunningTests: true)
|
||||
|
||||
/// Note that ``SDSDatabaseStorage/grdbDatabaseFileUrl``, through a few
|
||||
/// layers of abstraction, uses the "current app context" to decide
|
||||
/// where to put the database,
|
||||
///
|
||||
/// For a ``TestAppContext`` as configured above, this will be a
|
||||
/// subdirectory of our temp directory unique to the instantiation of
|
||||
/// the app context.
|
||||
|
||||
_ = await AppSetup().start(
|
||||
appContext: testAppContext,
|
||||
@ -26,12 +44,12 @@ public class MockSSKEnvironment {
|
||||
),
|
||||
paymentsEvents: PaymentsEventsNoop(),
|
||||
mobileCoinHelper: MobileCoinHelperMock(),
|
||||
callMessageHandler: NoopCallMessageHandler(),
|
||||
currentCallProvider: CurrentCallNoOpProvider(),
|
||||
notificationPresenter: NoopNotificationPresenterImpl(),
|
||||
incrementalMessageTSAttachmentMigratorFactory: IncrementalMessageTSAttachmentMigratorFactoryMock(),
|
||||
callMessageHandler: callMessageHandler,
|
||||
currentCallProvider: currentCallProvider,
|
||||
notificationPresenter: notificationPresenter,
|
||||
incrementalMessageTSAttachmentMigratorFactory: incrementalMessageTSAttachmentMigratorFactory,
|
||||
messageBackupErrorPresenterFactory: NoOpMessageBackupErrorPresenterFactory(),
|
||||
testDependencies: AppSetup.TestDependencies(
|
||||
testDependencies: testDependencies ?? AppSetup.TestDependencies(
|
||||
contactManager: FakeContactsManager(),
|
||||
groupV2Updates: MockGroupV2Updates(),
|
||||
groupsV2: MockGroupsV2(),
|
||||
@ -56,7 +74,8 @@ public class MockSSKEnvironment {
|
||||
).prepareDatabase()
|
||||
}
|
||||
|
||||
public static func flushAndWait() {
|
||||
@MainActor
|
||||
private static func flushAndWait() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
waitForMainQueue()
|
||||
@ -68,6 +87,30 @@ public class MockSSKEnvironment {
|
||||
waitForMainQueue()
|
||||
}
|
||||
|
||||
public static func deactivateAsync(oldContext: any AppContext) async {
|
||||
await withCheckedContinuation { continuation in
|
||||
DispatchQueue.main.async {
|
||||
SSKEnvironment.shared.databaseStorageRef.grdbStorage.pool.barrierWriteWithoutTransaction { _ in }
|
||||
DispatchQueue.main.async {
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
_deactivate(oldContext: oldContext)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public static func deactivate(oldContext: any AppContext) {
|
||||
flushAndWait()
|
||||
_deactivate(oldContext: oldContext)
|
||||
}
|
||||
|
||||
private static func _deactivate(oldContext: any AppContext) {
|
||||
SetCurrentAppContext(oldContext, isRunningTests: true)
|
||||
SSKEnvironment.setShared(nil, isRunningTests: true)
|
||||
DependenciesBridge.setShared(nil, isRunningTests: true)
|
||||
}
|
||||
|
||||
private static func waitForMainQueue() {
|
||||
// Spin the main run loop to flush any remaining async work.
|
||||
var done = false
|
||||
|
||||
@ -152,7 +152,8 @@ public func CurrentAppContext() -> any AppContext {
|
||||
currentAppContext!
|
||||
}
|
||||
|
||||
public func SetCurrentAppContext(_ appContext: any AppContext) {
|
||||
public func SetCurrentAppContext(_ appContext: any AppContext, isRunningTests: Bool) {
|
||||
owsPrecondition(currentAppContext == nil || isRunningTests)
|
||||
currentAppContext = appContext
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import Foundation
|
||||
import XCTest
|
||||
@testable import SignalServiceKit
|
||||
|
||||
final class PreKeyTaskTests: XCTestCase {
|
||||
final class PreKeyTaskTests: SSKBaseTest {
|
||||
|
||||
private var mockTSAccountManager: MockTSAccountManager!
|
||||
private var mockIdentityManager: PreKey.Mocks.IdentityManager!
|
||||
@ -57,6 +57,7 @@ final class PreKeyTaskTests: XCTestCase {
|
||||
|
||||
override func tearDown() {
|
||||
mockAPIClient.setPreKeysResult.ensureUnset()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@ -23,6 +23,19 @@ class BlockingManagerTests: SSKBaseTest {
|
||||
)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
let flushTask = blockingManager.flushSyncQueueTask()
|
||||
let otherFlushTask = otherBlockingManager.flushSyncQueueTask()
|
||||
let flushExpectation = self.expectation(description: "flush sync queues")
|
||||
Task {
|
||||
try! await flushTask.value
|
||||
try! await otherFlushTask.value
|
||||
flushExpectation.fulfill()
|
||||
}
|
||||
self.wait(for: [flushExpectation], timeout: 60)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testAddBlockedAddress() {
|
||||
// Setup
|
||||
let aci = Aci.randomForTesting()
|
||||
|
||||
@ -286,18 +286,11 @@ class MessageBackupIntegrationTests: XCTestCase {
|
||||
/// by LibSignal. They should be equivalent; any disparity indicates that
|
||||
/// some data was dropped or modified as part of the import/export process,
|
||||
/// which should be idempotent.
|
||||
@MainActor
|
||||
private func runRoundTripTest(
|
||||
testCaseFileUrl: URL,
|
||||
failureLogOutput: LibSignalComparisonFailureLogOutput
|
||||
) async throws {
|
||||
/// A backup doesn't contain our own local identifiers. Rather, those
|
||||
/// are determined as part of registration for a backup import, and are
|
||||
/// already-known for a backup export.
|
||||
///
|
||||
/// Consequently, we can use any local identifiers for our test
|
||||
/// purposes without worrying about the contents of each test case's
|
||||
/// backup file.
|
||||
let localIdentifiers: LocalIdentifiers = .forUnitTests
|
||||
|
||||
/// Backup files hardcode timestamps, some of which are interpreted
|
||||
/// relative to "now". For example, "deleted" story distribution lists
|
||||
@ -312,7 +305,32 @@ class MessageBackupIntegrationTests: XCTestCase {
|
||||
/// that as our "now" during import.
|
||||
let backupTimeMs = try await readBackupTimeMs(testCaseFileUrl: testCaseFileUrl)
|
||||
|
||||
let oldContext = CurrentAppContext()
|
||||
await initializeApp(dateProvider: { Date(millisecondsSince1970: backupTimeMs) })
|
||||
let result = await Result {
|
||||
try await self._runRoundTripTest(
|
||||
testCaseFileUrl: testCaseFileUrl,
|
||||
backupTimeMs: backupTimeMs,
|
||||
failureLogOutput: failureLogOutput
|
||||
)
|
||||
}
|
||||
await deinitializeApp(oldContext: oldContext)
|
||||
try result.get()
|
||||
}
|
||||
|
||||
private func _runRoundTripTest(
|
||||
testCaseFileUrl: URL,
|
||||
backupTimeMs: UInt64,
|
||||
failureLogOutput: LibSignalComparisonFailureLogOutput
|
||||
) async throws {
|
||||
/// A backup doesn't contain our own local identifiers. Rather, those
|
||||
/// are determined as part of registration for a backup import, and are
|
||||
/// already-known for a backup export.
|
||||
///
|
||||
/// Consequently, we can use any local identifiers for our test
|
||||
/// purposes without worrying about the contents of each test case's
|
||||
/// backup file.
|
||||
let localIdentifiers: LocalIdentifiers = .forUnitTests
|
||||
|
||||
try await deps.messageBackupManager.importPlaintextBackup(
|
||||
fileUrl: testCaseFileUrl,
|
||||
@ -429,44 +447,28 @@ class MessageBackupIntegrationTests: XCTestCase {
|
||||
|
||||
@MainActor
|
||||
private func initializeApp(dateProvider: DateProvider?) async {
|
||||
let testAppContext = TestAppContext()
|
||||
SetCurrentAppContext(testAppContext)
|
||||
let appReadiness = AppReadinessMock()
|
||||
|
||||
/// Note that ``SDSDatabaseStorage/grdbDatabaseFileUrl``, through a few
|
||||
/// layers of abstraction, uses the "current app context" to decide
|
||||
/// where to put the database,
|
||||
///
|
||||
/// For a ``TestAppContext`` as configured above, this will be a
|
||||
/// subdirectory of our temp directory unique to the instantiation of
|
||||
/// the app context.
|
||||
let databaseStorage = try! SDSDatabaseStorage(
|
||||
appReadiness: appReadiness,
|
||||
databaseFileUrl: SDSDatabaseStorage.grdbDatabaseFileUrl,
|
||||
keychainStorage: MockKeychainStorage()
|
||||
)
|
||||
|
||||
/// We use crashy versions of dependencies that should never be called
|
||||
/// during backups, and no-op implementations of payments because those
|
||||
/// are bound to the SignalUI target.
|
||||
_ = await AppSetup().start(
|
||||
appContext: testAppContext,
|
||||
await MockSSKEnvironment.activate(
|
||||
appReadiness: appReadiness,
|
||||
databaseStorage: databaseStorage,
|
||||
paymentsEvents: PaymentsEventsNoop(),
|
||||
mobileCoinHelper: MobileCoinHelperMock(),
|
||||
callMessageHandler: CrashyMocks.MockCallMessageHandler(),
|
||||
currentCallProvider: CrashyMocks.MockCurrentCallThreadProvider(),
|
||||
notificationPresenter: CrashyMocks.MockNotificationPresenter(),
|
||||
incrementalMessageTSAttachmentMigratorFactory: NoOpIncrementalMessageTSAttachmentMigratorFactory(),
|
||||
messageBackupErrorPresenterFactory: NoOpMessageBackupErrorPresenterFactory(),
|
||||
testDependencies: AppSetup.TestDependencies(
|
||||
backupAttachmentDownloadManager: BackupAttachmentDownloadManagerMock(),
|
||||
dateProvider: dateProvider,
|
||||
networkManager: CrashyMocks.MockNetworkManager(appReadiness: appReadiness, libsignalNet: nil),
|
||||
webSocketFactory: CrashyMocks.MockWebSocketFactory()
|
||||
)
|
||||
).prepareDatabase()
|
||||
)
|
||||
}
|
||||
|
||||
private func deinitializeApp(oldContext: any AppContext) async {
|
||||
await MockSSKEnvironment.deactivateAsync(oldContext: oldContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,10 +8,13 @@ public import XCTest
|
||||
import CocoaLumberjack
|
||||
|
||||
public class SSKBaseTest: XCTestCase {
|
||||
private var oldContext: (any AppContext)!
|
||||
|
||||
@MainActor
|
||||
public override func setUp() {
|
||||
DDLog.add(DDTTYLogger.sharedInstance!)
|
||||
let setupExpectation = expectation(description: "mock ssk environment setup completed")
|
||||
self.oldContext = CurrentAppContext()
|
||||
Task {
|
||||
await MockSSKEnvironment.activate()
|
||||
setupExpectation.fulfill()
|
||||
@ -19,8 +22,9 @@ public class SSKBaseTest: XCTestCase {
|
||||
waitForExpectations(timeout: 2)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public override func tearDown() {
|
||||
MockSSKEnvironment.flushAndWait()
|
||||
MockSSKEnvironment.deactivate(oldContext: self.oldContext)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
|
||||
@ -38,13 +38,15 @@ class SystemStoryManagerTest: SSKBaseTest {
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
|
||||
let flushExpectation = self.expectation(description: "flush")
|
||||
DispatchQueue.main.async {
|
||||
self.manager.chainedPromise.enqueue { .value(()) }.ensure {
|
||||
self.manager = nil
|
||||
flushExpectation.fulfill()
|
||||
}.cauterize()
|
||||
}
|
||||
self.wait(for: [flushExpectation], timeout: 60)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// MARK: - Downloading
|
||||
|
||||
@ -52,7 +52,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
||||
|
||||
// This should be the first thing we do.
|
||||
let appContext = ShareAppExtensionContext(rootViewController: self)
|
||||
SetCurrentAppContext(appContext)
|
||||
SetCurrentAppContext(appContext, isRunningTests: false)
|
||||
|
||||
let debugLogger = DebugLogger.shared
|
||||
debugLogger.enableTTYLoggingIfNeeded()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user