mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-09-10 11:51:49 +02:00
Initial prototype for Swift 6 mode
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import os
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
@@ -47,7 +48,11 @@ class BaseTestCase: XCTestCase {
|
||||
// MARK: Internal
|
||||
|
||||
@nonobjc
|
||||
func prepareStack(configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) throws -> Void) {
|
||||
@MainActor
|
||||
func prepareStack(
|
||||
configurations: [ModelConfiguration] = [nil],
|
||||
_ closure: (_ dataStack: DataStack) throws -> Void
|
||||
) {
|
||||
|
||||
let stack = DataStack(
|
||||
xcodeModelName: "Model",
|
||||
@@ -79,6 +84,7 @@ class BaseTestCase: XCTestCase {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@MainActor
|
||||
func expectLogger<T>(_ expectations: [TestLogger.Expectation], closure: () throws -> T) rethrows -> T {
|
||||
|
||||
CoreStoreDefaults.logger = TestLogger(self.prepareLoggerExpectations(expectations))
|
||||
@@ -97,6 +103,7 @@ class BaseTestCase: XCTestCase {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@MainActor
|
||||
func expectError<T>(code: CoreStoreErrorCode, closure: () throws -> T) {
|
||||
|
||||
CoreStoreDefaults.logger = TestLogger(self.prepareLoggerExpectations([.logError]))
|
||||
@@ -135,12 +142,14 @@ class BaseTestCase: XCTestCase {
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@MainActor
|
||||
func checkExpectationsImmediately() {
|
||||
|
||||
self.waitForExpectations(timeout: 0, handler: { _ in })
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
@MainActor
|
||||
func waitAndCheckExpectations() {
|
||||
|
||||
self.waitForExpectations(timeout: 10, handler: {_ in })
|
||||
@@ -174,7 +183,7 @@ class BaseTestCase: XCTestCase {
|
||||
|
||||
// MARK: - TestLogger
|
||||
|
||||
class TestLogger: CoreStoreLogger {
|
||||
final class TestLogger: CoreStoreLogger {
|
||||
|
||||
enum Expectation {
|
||||
|
||||
@@ -187,13 +196,13 @@ class TestLogger: CoreStoreLogger {
|
||||
|
||||
init(_ expectations: [Expectation: XCTestExpectation]) {
|
||||
|
||||
self.expectations = expectations
|
||||
self.expectations = .init(initialState: expectations)
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreLogger
|
||||
|
||||
var enableObjectConcurrencyDebugging: Bool = true
|
||||
let enableObjectConcurrencyDebugging: Bool = true
|
||||
|
||||
func log(level: LogLevel, message: String, fileName: StaticString, lineNumber: Int, functionName: StaticString) {
|
||||
|
||||
@@ -227,18 +236,21 @@ class TestLogger: CoreStoreLogger {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var expectations: [Expectation: XCTestExpectation]
|
||||
private let expectations: OSAllocatedUnfairLock<[Expectation: XCTestExpectation]>
|
||||
|
||||
private func fulfill(_ expectation: Expectation) {
|
||||
|
||||
if let instance = self.expectations[expectation] {
|
||||
self.expectations.withLock {
|
||||
|
||||
instance.fulfill()
|
||||
self.expectations[expectation] = nil
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail("Unexpected Logger Action: \(expectation)")
|
||||
if let instance = $0[expectation] {
|
||||
|
||||
instance.fulfill()
|
||||
$0[expectation] = nil
|
||||
}
|
||||
else {
|
||||
|
||||
XCTFail("Unexpected Logger Action: \(expectation)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import CoreStore
|
||||
class ConvenienceTests: BaseTestCase {
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatDataStacks_CanCreateFetchedResultsControllers() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -62,6 +63,7 @@ class ConvenienceTests: BaseTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatUnsafeDataTransactions_CanCreateFetchedResultsControllers() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
@@ -162,6 +162,18 @@ class Person: CoreStoreObject {
|
||||
@Field.Relationship("_spouseInverse", inverse: \.$spouse)
|
||||
private var spouseInverse: Person?
|
||||
|
||||
@Field.Coded("ownerInfosJson", coder: FieldCoders.Json.self)
|
||||
var ownerInfosJson: [OwnerInfo] = []
|
||||
|
||||
@Field.Coded("ownerInfosPlist", coder: FieldCoders.Plist.self)
|
||||
var ownerInfosPlist: [OwnerInfo] = []
|
||||
|
||||
struct OwnerInfo: Codable, Equatable {
|
||||
var id: UUID
|
||||
var displayName: String?
|
||||
var otherIDs: Set<UUID>
|
||||
}
|
||||
|
||||
private static func getDisplayName(_ object: ObjectProxy<Person>, _ field: ObjectProxy<Person>.FieldProxy<String?>) -> String? {
|
||||
|
||||
if let value = field.primitiveValue {
|
||||
@@ -203,7 +215,7 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
versionLock: [
|
||||
"Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
|
||||
"Dog": [0xad6de93adc5565d, 0x7897e51253eba5a3, 0xd12b9ce0b13600f3, 0x5a4827cd794cd15e],
|
||||
"Person": [0xf3e6ba6016bbedc6, 0x50dedf64f0eba490, 0xa32088a0ee83468d, 0xb72d1d0b37bd0992]
|
||||
"Person": [0xbaec6549a025b59f, 0x3415c71e9f46fcf3, 0xb86b33433cb335eb, 0xfe441fde120dab67]
|
||||
]
|
||||
)
|
||||
)
|
||||
@@ -223,6 +235,14 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
let willSetPriorObserverDone = self.expectation(description: "willSet-observe-prior-done")
|
||||
let willSetNotPriorObserverDone = self.expectation(description: "willSet-observe-notPrior-done")
|
||||
let didSetObserverDone = self.expectation(description: "didSet-observe-done")
|
||||
|
||||
let ownerInfos: [Person.OwnerInfo] = (1...3).map {
|
||||
.init(
|
||||
id: .init(),
|
||||
displayName: "ownerInfo_\($0)",
|
||||
otherIDs: Set((1...3).map({ _ in .init() }))
|
||||
)
|
||||
}
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
@@ -373,11 +393,19 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
person.job = .engineer
|
||||
XCTAssertEqual(person.job, .engineer)
|
||||
|
||||
person.ownerInfosJson = ownerInfos
|
||||
XCTAssertEqual(person.ownerInfosJson, ownerInfos)
|
||||
|
||||
person.ownerInfosPlist = ownerInfos
|
||||
XCTAssertEqual(person.ownerInfosPlist, ownerInfos)
|
||||
|
||||
let personSnapshot2 = person.asSnapshot(in: transaction)!
|
||||
XCTAssertEqual(person.name, personSnapshot2.$name)
|
||||
XCTAssertEqual(person.title, personSnapshot2.$title)
|
||||
XCTAssertEqual(person.displayName, personSnapshot2.$displayName)
|
||||
XCTAssertEqual(person.job, personSnapshot2.$job)
|
||||
XCTAssertEqual(person.ownerInfosJson, personSnapshot2.$ownerInfosJson)
|
||||
XCTAssertEqual(person.ownerInfosPlist, personSnapshot2.$ownerInfosPlist)
|
||||
|
||||
var personSnapshot3 = personSnapshot2
|
||||
personSnapshot3.$name = "James"
|
||||
@@ -390,6 +418,8 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(personSnapshot3.$name, "James")
|
||||
XCTAssertEqual(personSnapshot3.$displayName, "Sir John")
|
||||
XCTAssertEqual(personSnapshot3.$job, .engineer)
|
||||
XCTAssertEqual(personSnapshot3.$ownerInfosJson, ownerInfos)
|
||||
XCTAssertEqual(personSnapshot3.$ownerInfosPlist, ownerInfos)
|
||||
|
||||
|
||||
|
||||
@@ -409,6 +439,8 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(personPublisher.$name, "John")
|
||||
XCTAssertEqual(personPublisher.$displayName, "Sir John")
|
||||
XCTAssertEqual(personPublisher.$job, .engineer)
|
||||
XCTAssertEqual(personPublisher.$ownerInfosJson, ownerInfos)
|
||||
XCTAssertEqual(personPublisher.$ownerInfosPlist, ownerInfos)
|
||||
|
||||
updateDone.fulfill()
|
||||
},
|
||||
@@ -444,6 +476,8 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(person!.customField.string, "customString")
|
||||
XCTAssertEqual(person!.job, .engineer)
|
||||
XCTAssertEqual(person!.pets.first, dog)
|
||||
XCTAssertEqual(person!.ownerInfosJson, ownerInfos)
|
||||
XCTAssertEqual(person!.ownerInfosPlist, ownerInfos)
|
||||
|
||||
let p3 = Where<Dog>({ $0.$age == 10 })
|
||||
XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10))
|
||||
|
||||
@@ -63,6 +63,7 @@ final class GroupByTests: BaseTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatGroupByClauses_ApplyToFetchRequestsCorrectly() {
|
||||
|
||||
self.prepareStack { (dataStack) in
|
||||
|
||||
@@ -36,6 +36,7 @@ import CoreStore
|
||||
class ListPublisherTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatListPublishers_CanReceiveInsertNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -62,7 +63,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
asynchronous: { [dateFormatter] (transaction) -> Bool in
|
||||
|
||||
let object = transaction.create(Into<TestEntity1>())
|
||||
object.testBoolean = NSNumber(value: true)
|
||||
@@ -70,7 +71,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
object.testDecimal = NSDecimalNumber(string: "1")
|
||||
object.testString = "nil:TestEntity1:1"
|
||||
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
object.testDate = dateFormatter.date(from: "2000-01-01T00:00:00Z")!
|
||||
|
||||
return transaction.hasChanges
|
||||
},
|
||||
@@ -92,6 +93,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatListPublishers_CanReceiveUpdateNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -126,7 +128,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
stack.perform(
|
||||
asynchronous: { (transaction) -> Bool in
|
||||
asynchronous: { [dateFormatter] (transaction) -> Bool in
|
||||
|
||||
if let object = try transaction.fetchOne(
|
||||
From<TestEntity1>(),
|
||||
@@ -136,7 +138,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
object.testDecimal = NSDecimalNumber(string: "11")
|
||||
object.testString = "nil:TestEntity1:11"
|
||||
object.testData = ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
object.testDate = dateFormatter.date(from: "2000-01-11T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -150,7 +152,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
object.testDecimal = NSDecimalNumber(string: "22")
|
||||
object.testString = "nil:TestEntity1:22"
|
||||
object.testData = ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!
|
||||
object.testDate = self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
object.testDate = dateFormatter.date(from: "2000-01-22T00:00:00Z")!
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -176,6 +178,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatListPublishers_CanReceiveMoveNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -242,6 +245,7 @@ class ListPublisherTests: BaseTestDataTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatListPublishers_CanReceiveDeleteNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import os
|
||||
|
||||
@testable
|
||||
import CoreStore
|
||||
@@ -34,6 +35,7 @@ import CoreStore
|
||||
class ObjectObserverTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatObjectObservers_CanReceiveUpdateNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -54,23 +56,27 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(monitor.object, object)
|
||||
XCTAssertFalse(monitor.isObjectDeleted)
|
||||
|
||||
var events = 0
|
||||
let events: OSAllocatedUnfairLock<Int> = .init(initialState: 0)
|
||||
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "objectMonitor:willUpdateObject:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
XCTAssertEqual(events, 0)
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
["object": object] as NSDictionary
|
||||
)
|
||||
defer {
|
||||
nonisolated(unsafe) let note = note
|
||||
return events.withLock { events in
|
||||
|
||||
events += 1
|
||||
XCTAssertEqual(events, 0)
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
["object": object] as NSDictionary
|
||||
)
|
||||
defer {
|
||||
|
||||
events += 1
|
||||
}
|
||||
return events == 0
|
||||
}
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
_ = self.expectation(
|
||||
@@ -78,28 +84,32 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
XCTAssertEqual(events, 1)
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
[
|
||||
"object": object,
|
||||
"changedPersistentKeys": Set(
|
||||
[
|
||||
#keyPath(TestEntity1.testNumber),
|
||||
#keyPath(TestEntity1.testString)
|
||||
]
|
||||
)
|
||||
] as NSDictionary
|
||||
)
|
||||
let object = note.userInfo?["object"] as? TestEntity1
|
||||
XCTAssertEqual(object?.testNumber, NSNumber(value: 10))
|
||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:10")
|
||||
|
||||
defer {
|
||||
nonisolated(unsafe) let note = note
|
||||
return events.withLock { events in
|
||||
|
||||
events += 1
|
||||
XCTAssertEqual(events, 1)
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
[
|
||||
"object": object,
|
||||
"changedPersistentKeys": Set(
|
||||
[
|
||||
#keyPath(TestEntity1.testNumber),
|
||||
#keyPath(TestEntity1.testString)
|
||||
]
|
||||
)
|
||||
] as NSDictionary
|
||||
)
|
||||
let object = note.userInfo?["object"] as? TestEntity1
|
||||
XCTAssertEqual(object?.testNumber, NSNumber(value: 10))
|
||||
XCTAssertEqual(object?.testString, "nil:TestEntity1:10")
|
||||
|
||||
defer {
|
||||
|
||||
events += 1
|
||||
}
|
||||
return events == 1
|
||||
}
|
||||
return events == 1
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
@@ -131,6 +141,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatObjectObservers_CanReceiveDeleteNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -151,23 +162,27 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(monitor.object, object)
|
||||
XCTAssertFalse(monitor.isObjectDeleted)
|
||||
|
||||
var events = 0
|
||||
let events: OSAllocatedUnfairLock<Int> = .init(initialState: 0)
|
||||
|
||||
_ = self.expectation(
|
||||
forNotification: NSNotification.Name(rawValue: "objectMonitor:didDeleteObject:"),
|
||||
object: observer,
|
||||
handler: { (note) -> Bool in
|
||||
|
||||
XCTAssertEqual(events, 0)
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
["object": object] as NSDictionary
|
||||
)
|
||||
defer {
|
||||
nonisolated(unsafe) let note = note
|
||||
return events.withLock { events in
|
||||
|
||||
events += 1
|
||||
XCTAssertEqual(events, 0)
|
||||
XCTAssertEqual(
|
||||
((note.userInfo as NSDictionary?) ?? [:]),
|
||||
["object": object] as NSDictionary
|
||||
)
|
||||
defer {
|
||||
|
||||
events += 1
|
||||
}
|
||||
return events == 0
|
||||
}
|
||||
return events == 0
|
||||
}
|
||||
)
|
||||
let saveExpectation = self.expectation(description: "save")
|
||||
@@ -202,7 +217,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
|
||||
|
||||
// MARK: TestObjectObserver
|
||||
|
||||
class TestObjectObserver: ObjectObserver {
|
||||
final class TestObjectObserver: ObjectObserver {
|
||||
|
||||
typealias ObjectEntityType = TestEntity1
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import CoreStore
|
||||
class ObjectPublisherTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatObjectPublishers_CanReceiveUpdateNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
@@ -93,6 +94,7 @@ class ObjectPublisherTests: BaseTestDataTestCase {
|
||||
}
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatObjectPublishers_CanReceiveDeleteNotifications() {
|
||||
|
||||
self.prepareStack { (stack) in
|
||||
|
||||
@@ -35,6 +35,7 @@ import CoreStore
|
||||
class SetupTests: BaseTestDataTestCase {
|
||||
|
||||
@objc
|
||||
@MainActor
|
||||
dynamic func test_ThatDataStacks_ConfigureCorrectly() {
|
||||
|
||||
do {
|
||||
|
||||
Reference in New Issue
Block a user