You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/_SharedTestUtilities/SynchronousStorage.swift

150 lines
5.4 KiB
Swift

// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Combine
import GRDB
@testable import SessionUtilitiesKit
class SynchronousStorage: Storage {
public init(
customWriter: DatabaseWriter? = nil,
migrationTargets: [MigratableTarget.Type]? = nil,
migrations: [Storage.KeyedMigration]? = nil,
Merge remote-tracking branch 'upstream/dev' into feature/groups-rebuild # Conflicts: # Session.xcodeproj/project.pbxproj # Session/Conversations/Settings/ThreadSettingsViewModel.swift # Session/Meta/Translations/de.lproj/Localizable.strings # Session/Meta/Translations/en.lproj/Localizable.strings # Session/Meta/Translations/es-ES.lproj/Localizable.strings # Session/Meta/Translations/fa.lproj/Localizable.strings # Session/Meta/Translations/fi.lproj/Localizable.strings # Session/Meta/Translations/fil.lproj/Localizable.strings # Session/Meta/Translations/fr.lproj/Localizable.strings # Session/Meta/Translations/hi.lproj/Localizable.strings # Session/Meta/Translations/hr.lproj/Localizable.strings # Session/Meta/Translations/it.lproj/Localizable.strings # Session/Meta/Translations/ja.lproj/Localizable.strings # Session/Meta/Translations/nl.lproj/Localizable.strings # Session/Meta/Translations/pl.lproj/Localizable.strings # Session/Meta/Translations/pt-BR.lproj/Localizable.strings # Session/Meta/Translations/ru.lproj/Localizable.strings # Session/Meta/Translations/sk.lproj/Localizable.strings # Session/Meta/Translations/sl.lproj/Localizable.strings # Session/Meta/Translations/sv-SE.lproj/Localizable.strings # Session/Meta/Translations/th.lproj/Localizable.strings # Session/Meta/Translations/vi.lproj/Localizable.strings # Session/Meta/Translations/zh-CN.lproj/Localizable.strings # Session/Meta/Translations/zh-TW.lproj/Localizable.strings # SessionMessagingKit/Calls/WebRTCSession.swift # SessionMessagingKit/Configuration.swift # SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+VisibleMessages.swift # SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+Contacts.swift # SessionMessagingKit/Utilities/ProfileManager.swift # SessionMessagingKitTests/Jobs/Types/MessageSendJobSpec.swift # SessionMessagingKitTests/LibSessionUtil/LibSessionSpec.swift # SessionMessagingKitTests/LibSessionUtil/SessionUtilSpec.swift # SessionMessagingKitTests/Open Groups/Models/BatchRequestInfoSpec.swift # SessionMessagingKitTests/Open Groups/Models/SOGSMessageSpec.swift # SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift # SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift # SessionMessagingKitTests/Open Groups/Types/SOGSEndpointSpec.swift # SessionMessagingKitTests/Sending & Receiving/MessageReceiverDecryptionSpec.swift # SessionMessagingKitTests/Sending & Receiving/MessageSenderEncryptionSpec.swift # SessionMessagingKitTests/Shared Models/SessionThreadViewModelSpec.swift # SessionMessagingKitTests/Utilities/CryptoSMKSpec.swift # SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift # SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift # SessionTests/Settings/NotificationContentViewModelSpec.swift # SessionUtilitiesKitTests/Database/Models/IdentitySpec.swift # SessionUtilitiesKitTests/Database/Utilities/PersistableRecordUtilitiesSpec.swift # SessionUtilitiesKitTests/General/DependenciesSpec.swift # SessionUtilitiesKitTests/JobRunner/JobRunnerSpec.swift # _SharedTestUtilities/MockCaches.swift
2 years ago
using dependencies: Dependencies,
initialData: ((Database) throws -> ())? = nil
) {
super.init(customWriter: customWriter)
// Process any migration targets first
if let migrationTargets: [MigratableTarget.Type] = migrationTargets {
perform(
migrationTargets: migrationTargets,
async: false,
onProgressUpdate: nil,
onMigrationRequirement: { _, _ in },
onComplete: { _, _ in },
using: dependencies
)
}
// Then process any provided migration info
if let migrations: [Storage.KeyedMigration] = migrations {
perform(
sortedMigrations: migrations,
async: false,
onProgressUpdate: nil,
onMigrationRequirement: { _, _ in },
onComplete: { _, _ in },
using: dependencies
)
}
write { db in try initialData?(db) }
}
@discardableResult override func write<T>(
fileName: String = #file,
functionName: String = #function,
lineNumber: Int = #line,
using dependencies: Dependencies = Dependencies(),
updates: @escaping (Database) throws -> T?
) -> T? {
guard isValid, let dbWriter: DatabaseWriter = testDbWriter else { return nil }
// If 'forceSynchronous' is true then it's likely that we will access the database in
// a reentrant way, the 'unsafeReentrant...' functions allow us to interact with the
// database without worrying about reentrant access during tests because we can be
// confident that the tests are running on the correct thread
guard !dependencies.forceSynchronous else {
return try? dbWriter.unsafeReentrantWrite(updates)
}
return super.write(
fileName: fileName,
functionName: functionName,
lineNumber: lineNumber,
using: dependencies,
updates: updates
)
}
@discardableResult override func read<T>(
fileName: String = #file,
functionName: String = #function,
lineNumber: Int = #line,
using dependencies: Dependencies = Dependencies(),
_ value: @escaping (Database) throws -> T?
) -> T? {
guard isValid, let dbWriter: DatabaseWriter = testDbWriter else { return nil }
// If 'forceSynchronous' is true then it's likely that we will access the database in
// a reentrant way, the 'unsafeReentrant...' functions allow us to interact with the
// database without worrying about reentrant access during tests because we can be
// confident that the tests are running on the correct thread
guard !dependencies.forceSynchronous else {
return try? dbWriter.unsafeReentrantRead(value)
}
return super.read(
fileName: fileName,
functionName: functionName,
lineNumber: lineNumber,
using: dependencies,
value
)
}
// MARK: - Async Methods
override func readPublisher<T>(
fileName: String = #file,
functionName: String = #function,
lineNumber: Int = #line,
using dependencies: Dependencies = Dependencies(),
value: @escaping (Database) throws -> T
) -> AnyPublisher<T, Error> {
guard let result: T = self.read(fileName: fileName, functionName: functionName, lineNumber: lineNumber, using: dependencies, value) else {
return Fail(error: StorageError.generic)
.eraseToAnyPublisher()
}
return Just(result)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
override func writeAsync<T>(
fileName: String = #file,
functionName: String = #function,
lineNumber: Int = #line,
using dependencies: Dependencies = Dependencies(),
updates: @escaping (Database) throws -> T,
completion: @escaping (Database, Result<T, Error>) throws -> Void
) {
do {
let result: T = try write(using: dependencies, updates: updates) ?? { throw StorageError.failedToSave }()
write { db in try completion(db, Result.success(result)) }
}
catch {
write { db in try completion(db, Result.failure(error)) }
}
}
override func writePublisher<T>(
fileName: String = #file,
functionName: String = #function,
lineNumber: Int = #line,
using dependencies: Dependencies = Dependencies(),
updates: @escaping (Database) throws -> T
) -> AnyPublisher<T, Error> {
guard let result: T = super.write(fileName: fileName, functionName: functionName, lineNumber: lineNumber, using: dependencies, updates: updates) else {
return Fail(error: StorageError.generic)
.eraseToAnyPublisher()
}
return Just(result)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
}