From 98d860aff6d7cc3a157e615f59668b618884b493 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Fri, 25 Oct 2019 14:34:22 +0900 Subject: [PATCH] Add unit tests for List and Object Publishers --- CoreStore.xcodeproj/project.pbxproj | 8 + CoreStoreTests/ListPublisherTests.swift | 825 ++++++---------------- CoreStoreTests/ObjectPublisherTests.swift | 154 ++++ 3 files changed, 385 insertions(+), 602 deletions(-) create mode 100644 CoreStoreTests/ObjectPublisherTests.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 8227e2d..e216b98 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -522,6 +522,9 @@ B580857A1CDF808C004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; }; B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; }; B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; }; + B581B9322362BB8C002BDB2B /* ObjectPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */; }; + B581B9332362BB8C002BDB2B /* ObjectPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */; }; + B581B9342362BB8C002BDB2B /* ObjectPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */; }; B5831B701F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; B5831B711F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; B5831B721F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; }; @@ -994,6 +997,7 @@ B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestDataTestCase.swift; sourceTree = ""; }; B57D27C11D0BC20100539C58 /* QueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryTests.swift; sourceTree = ""; }; B58085741CDF7F00004C2EEB /* SetupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupTests.swift; sourceTree = ""; }; + B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectPublisherTests.swift; sourceTree = ""; }; B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeProtocol.swift; sourceTree = ""; }; B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipProtocol.swift; sourceTree = ""; }; B5831B791F34ACBA00A9F647 /* Transformable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformable.swift; sourceTree = ""; }; @@ -1262,6 +1266,7 @@ B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */, B5DC47C51C93D22900FA3BF3 /* MigrationChainTests.swift */, B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */, + B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */, B52557771D02826E00E51965 /* OrderByTests.swift */, B57D27C11D0BC20100539C58 /* QueryTests.swift */, B52557831D02A07400E51965 /* SectionByTests.swift */, @@ -2285,6 +2290,7 @@ B5220E0C1D0D0D19009BC71E /* ImportTests.swift in Sources */, B5D339B41E925C2B00C880DE /* DynamicModelTests.swift in Sources */, B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */, + B581B9322362BB8C002BDB2B /* ObjectPublisherTests.swift in Sources */, B52557881D02DE8100E51965 /* FetchTests.swift in Sources */, B5489F501CF603D5008B4978 /* FromTests.swift in Sources */, B52557781D02826E00E51965 /* OrderByTests.swift in Sources */, @@ -2508,6 +2514,7 @@ B5220E0D1D0D0D19009BC71E /* ImportTests.swift in Sources */, B5D339B51E925C2B00C880DE /* DynamicModelTests.swift in Sources */, B525576D1CFAF18F00E51965 /* IntoTests.swift in Sources */, + B581B9332362BB8C002BDB2B /* ObjectPublisherTests.swift in Sources */, B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */, B52557891D02DE8100E51965 /* FetchTests.swift in Sources */, B5489F511CF603D5008B4978 /* FromTests.swift in Sources */, @@ -2731,6 +2738,7 @@ B525576E1CFAF18F00E51965 /* IntoTests.swift in Sources */, B5D339B61E925C2B00C880DE /* DynamicModelTests.swift in Sources */, B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */, + B581B9342362BB8C002BDB2B /* ObjectPublisherTests.swift in Sources */, B525578A1D02DE8100E51965 /* FetchTests.swift in Sources */, B5220E281D1308E5009BC71E /* SectionByTests.swift in Sources */, B5489F521CF603D5008B4978 /* FromTests.swift in Sources */, diff --git a/CoreStoreTests/ListPublisherTests.swift b/CoreStoreTests/ListPublisherTests.swift index c17e179..1b0fc6a 100644 --- a/CoreStoreTests/ListPublisherTests.swift +++ b/CoreStoreTests/ListPublisherTests.swift @@ -41,99 +41,26 @@ class ListPublisherTests: BaseTestDataTestCase { self.prepareStack { (stack) in -// let observer = TestListObserver() - let listPublisher = stack.listPublisher( + let observer = NSObject() + let listPublisher = stack.publishList( From(), SectionBy(#keyPath(TestEntity1.testBoolean)), OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) ) -// monitor.addObserver(observer) -// -// XCTAssertFalse(monitor.hasSections()) -// XCTAssertFalse(monitor.hasObjects()) -// XCTAssertTrue(monitor.objectsInAllSections().isEmpty) -// -// var events = 0 -// -// let willChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 0) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 0 -// } -// ) -// let didInsertSectionExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 1) -// XCTAssertEqual( -// ((note.userInfo as NSDictionary?) ?? [:]), -// [ -// "sectionInfo": monitor.sectionInfo(at: 0), -// "sectionIndex": 0 -// ] as NSDictionary -// ) -// defer { -// -// events += 1 -// } -// return events == 1 -// } -// ) -// let didInsertObjectExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 2) -// -// let userInfo = note.userInfo -// XCTAssertNotNil(userInfo) -// XCTAssertEqual( -// Set(userInfo?.keys.map({ $0 as! String }) ?? []), -// ["indexPath", "object"] -// ) -// -// let indexPath = userInfo?["indexPath"] as? NSIndexPath -// XCTAssertEqual(indexPath?.index(atPosition: 0), 0) -// XCTAssertEqual(indexPath?.index(atPosition: 1), 0) -// -// let object = userInfo?["object"] as? TestEntity1 -// XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) -// XCTAssertEqual(object?.testNumber, NSNumber(value: 1)) -// XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "1")) -// XCTAssertEqual(object?.testString, "nil:TestEntity1:1") -// XCTAssertEqual(object?.testData, ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!) -// XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!) -// defer { -// -// events += 1 -// } -// return events == 2 -// } -// ) -// let didChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 3 -// } -// ) + XCTAssertFalse(listPublisher.snapshot.hasSections()) + XCTAssertFalse(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.itemIDs.isEmpty) + + let didChangeExpectation = self.expectation(description: "didChange") + listPublisher.addObserver(observer) { listPublisher in + + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 1) + + didChangeExpectation.fulfill() + } + let saveExpectation = self.expectation(description: "save") stack.perform( asynchronous: { (transaction) -> Bool in @@ -161,522 +88,216 @@ class ListPublisherTests: BaseTestDataTestCase { self.waitAndCheckExpectations() withExtendedLifetime(listPublisher, {}) + withExtendedLifetime(observer, {}) } } -// @objc -// dynamic func test_ThatListObservers_CanReceiveUpdateNotifications() { -// -// self.prepareStack { (stack) in -// -// self.prepareTestDataForStack(stack) -// -// let observer = TestListObserver() -// let monitor = stack.monitorSectionedList( -// From(), -// SectionBy(#keyPath(TestEntity1.testBoolean)), -// OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) -// ) -// monitor.addObserver(observer) -// -// XCTAssertTrue(monitor.hasSections()) -// XCTAssertEqual(monitor.numberOfSections(), 2) -// XCTAssertTrue(monitor.hasObjects()) -// XCTAssertTrue(monitor.hasObjects(in: 0)) -// XCTAssertEqual(monitor.numberOfObjects(in: 0), 2) -// XCTAssertEqual(monitor.numberOfObjects(in: 1), 3) -// -// var events = 0 -// -// let willChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 0) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 0 -// } -// ) -// -// let didUpdateObjectExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssert(events == 1 || events == 2) -// -// let userInfo = note.userInfo -// XCTAssertNotNil(userInfo) -// XCTAssertEqual( -// Set(userInfo?.keys.map({ $0 as! String }) ?? []), -// ["indexPath", "object"] -// ) -// -// let indexPath = userInfo?["indexPath"] as? NSIndexPath -// let object = userInfo?["object"] as? TestEntity1 -// -// switch object?.testEntityID { -// -// case NSNumber(value: 101)?: -// XCTAssertEqual(indexPath?.index(atPosition: 0), 1) -// XCTAssertEqual(indexPath?.index(atPosition: 1), 0) -// -// XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) -// XCTAssertEqual(object?.testNumber, NSNumber(value: 11)) -// XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "11")) -// XCTAssertEqual(object?.testString, "nil:TestEntity1:11") -// XCTAssertEqual(object?.testData, ("nil:TestEntity1:11" as NSString).data(using: String.Encoding.utf8.rawValue)!) -// XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!) -// -// case NSNumber(value: 102)?: -// XCTAssertEqual(indexPath?.index(atPosition: 0), 0) -// XCTAssertEqual(indexPath?.index(atPosition: 1), 0) -// -// XCTAssertEqual(object?.testBoolean, NSNumber(value: false)) -// XCTAssertEqual(object?.testNumber, NSNumber(value: 22)) -// XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "22")) -// XCTAssertEqual(object?.testString, "nil:TestEntity1:22") -// XCTAssertEqual(object?.testData, ("nil:TestEntity1:22" as NSString).data(using: String.Encoding.utf8.rawValue)!) -// XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-22T00:00:00Z")!) -// -// default: -// XCTFail() -// } -// defer { -// -// events += 1 -// } -// return events == 1 || events == 2 -// } -// ) -// let didChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 3) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 3 -// } -// ) -// let saveExpectation = self.expectation(description: "save") -// stack.perform( -// asynchronous: { (transaction) -> Bool in -// -// if let object = try transaction.fetchOne( -// From(), -// Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) { -// -// object.testNumber = NSNumber(value: 11) -// 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")! -// } -// else { -// -// XCTFail() -// } -// if let object = try transaction.fetchOne( -// From(), -// Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { -// -// object.testNumber = NSNumber(value: 22) -// 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")! -// } -// else { -// -// XCTFail() -// } -// return transaction.hasChanges -// }, -// success: { (hasChanges) in -// -// XCTAssertTrue(hasChanges) -// saveExpectation.fulfill() -// }, -// failure: { _ in -// -// XCTFail() -// } -// ) -// self.waitAndCheckExpectations() -// } -// } -// -// @objc -// dynamic func test_ThatListObservers_CanReceiveMoveNotifications() { -// -// self.prepareStack { (stack) in -// -// self.prepareTestDataForStack(stack) -// -// let observer = TestListObserver() -// let monitor = stack.monitorSectionedList( -// From(), -// SectionBy(#keyPath(TestEntity1.testBoolean)), -// OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) -// ) -// monitor.addObserver(observer) -// -// var events = 0 -// -// let willChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 0) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 0 -// } -// ) -// let didMoveObjectExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 1) -// -// let userInfo = note.userInfo -// XCTAssertNotNil(userInfo) -// XCTAssertEqual( -// Set(userInfo?.keys.map({ $0 as! String }) ?? []), -// ["fromIndexPath", "toIndexPath", "object"] -// ) -// -// let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath -// XCTAssertEqual(fromIndexPath?.index(atPosition: 0), 0) -// XCTAssertEqual(fromIndexPath?.index(atPosition: 1), 0) -// -// let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath -// XCTAssertEqual(toIndexPath?.index(atPosition: 0), 1) -// XCTAssertEqual(toIndexPath?.index(atPosition: 1), 1) -// -// let object = userInfo?["object"] as? TestEntity1 -// XCTAssertEqual(object?.testEntityID, NSNumber(value: 102)) -// XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) -// -// defer { -// -// events += 1 -// } -// return events == 1 -// } -// ) -// let didChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 2) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 2 -// } -// ) -// let saveExpectation = self.expectation(description: "save") -// stack.perform( -// asynchronous: { (transaction) -> Bool in -// -// if let object = try transaction.fetchOne( -// From(), -// Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { -// -// object.testBoolean = NSNumber(value: true) -// } -// else { -// -// XCTFail() -// } -// return transaction.hasChanges -// }, -// success: { (hasChanges) in -// -// XCTAssertTrue(hasChanges) -// saveExpectation.fulfill() -// }, -// failure: { _ in -// -// XCTFail() -// } -// ) -// self.waitAndCheckExpectations() -// } -// } -// -// @objc -// dynamic func test_ThatListObservers_CanReceiveDeleteNotifications() { -// -// self.prepareStack { (stack) in -// -// self.prepareTestDataForStack(stack) -// -// let observer = TestListObserver() -// let monitor = stack.monitorSectionedList( -// From(), -// SectionBy(#keyPath(TestEntity1.testBoolean)), -// OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) -// ) -// monitor.addObserver(observer) -// -// var events = 0 -// -// let willChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 0) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 0 -// } -// ) -// let didUpdateObjectExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssert(events == 1 || events == 2) -// -// let userInfo = note.userInfo -// XCTAssertNotNil(userInfo) -// XCTAssertEqual( -// Set(userInfo?.keys.map({ $0 as! String }) ?? []), -// ["indexPath", "object"] -// ) -// -// let indexPath = userInfo?["indexPath"] as? NSIndexPath -// -// XCTAssertEqual(indexPath?.section, 0) -// XCTAssert(indexPath?.index(atPosition: 1) == 0 || indexPath?.index(atPosition: 1) == 1) -// -// let object = userInfo?["object"] as? TestEntity1 -// XCTAssertEqual(object?.isDeleted, true) -// -// defer { -// -// events += 1 -// } -// return events == 1 || events == 2 -// } -// ) -// let didDeleteSectionExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 3) -// -// let userInfo = note.userInfo -// XCTAssertNotNil(userInfo) -// XCTAssertEqual( -// Set(userInfo?.keys.map({ $0 as! String }) ?? []), -// ["sectionInfo", "sectionIndex"] -// ) -// -// let sectionInfo = userInfo?["sectionInfo"] as? NSFetchedResultsSectionInfo -// XCTAssertNotNil(sectionInfo) -// XCTAssertEqual(sectionInfo?.name, "0") -// -// let sectionIndex = userInfo?["sectionIndex"] -// XCTAssertEqual(sectionIndex as? NSNumber, NSNumber(value: 0)) -// -// defer { -// -// events += 1 -// } -// return events == 3 -// } -// ) -// let didChangeExpectation = self.expectation( -// forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"), -// object: observer, -// handler: { (note) -> Bool in -// -// XCTAssertEqual(events, 4) -// XCTAssertEqual((note.userInfo as NSDictionary?) ?? [:], NSDictionary()) -// defer { -// -// events += 1 -// } -// return events == 4 -// } -// ) -// let saveExpectation = self.expectation(description: "save") -// stack.perform( -// asynchronous: { (transaction) -> Bool in -// -// let count = try transaction.deleteAll( -// From(), -// Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false) -// ) -// XCTAssertEqual(count, 2) -// return transaction.hasChanges -// }, -// success: { (hasChanges) in -// -// XCTAssertTrue(hasChanges) -// saveExpectation.fulfill() -// }, -// failure: { _ in -// -// XCTFail() -// } -// ) -// self.waitAndCheckExpectations() -// } -// } -//} -// -// -//// MARK: TestListObserver -// -//@available(macOS 10.12, *) -//class TestListObserver: ListSectionObserver { -// -// // MARK: ListObserver -// -// typealias ListEntityType = TestEntity1 -// -// func listMonitorWillChange(_ monitor: ListMonitor) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitorWillChange:"), -// object: self, -// userInfo: [:] -// ) -// } -// -// func listMonitorDidChange(_ monitor: ListMonitor) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitorDidChange:"), -// object: self, -// userInfo: [:] -// ) -// } -// -// func listMonitorWillRefetch(_ monitor: ListMonitor) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitorWillRefetch:"), -// object: self, -// userInfo: [:] -// ) -// } -// -// func listMonitorDidRefetch(_ monitor: ListMonitor) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitorDidRefetch:"), -// object: self, -// userInfo: [:] -// ) -// } -// -// -// // MARK: ListObjectObserver -// -// func listMonitor(_ monitor: ListMonitor, didInsertObject object: TestEntity1, toIndexPath indexPath: IndexPath) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"), -// object: self, -// userInfo: [ -// "object": object, -// "indexPath": indexPath -// ] -// ) -// } -// -// func listMonitor(_ monitor: ListMonitor, didDeleteObject object: TestEntity1, fromIndexPath indexPath: IndexPath) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"), -// object: self, -// userInfo: [ -// "object": object, -// "indexPath": indexPath -// ] -// ) -// } -// -// func listMonitor(_ monitor: ListMonitor, didUpdateObject object: TestEntity1, atIndexPath indexPath: IndexPath) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"), -// object: self, -// userInfo: [ -// "object": object, -// "indexPath": indexPath -// ] -// ) -// } -// -// -// func listMonitor(_ monitor: ListMonitor, didMoveObject object: TestEntity1, fromIndexPath: IndexPath, toIndexPath: IndexPath) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"), -// object: self, -// userInfo: [ -// "object": object, -// "fromIndexPath": fromIndexPath, -// "toIndexPath": toIndexPath -// ] -// ) -// } -// -// -// // MARK: ListSectionObserver -// -// func listMonitor(_ monitor: ListMonitor, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"), -// object: self, -// userInfo: [ -// "sectionInfo": sectionInfo, -// "sectionIndex": sectionIndex -// ] -// ) -// } -// -// func listMonitor(_ monitor: ListMonitor, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { -// -// NotificationCenter.default.post( -// name: Notification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"), -// object: self, -// userInfo: [ -// "sectionInfo": sectionInfo, -// "sectionIndex": sectionIndex -// ] -// ) -// } + @objc + dynamic func test_ThatListPublishers_CanReceiveUpdateNotifications() { + + self.prepareStack { (stack) in + + self.prepareTestDataForStack(stack) + + let observer = NSObject() + let listPublisher = stack.publishList( + From(), + SectionBy(#keyPath(TestEntity1.testBoolean)), + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + ) + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0)) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3) + + let didChangeExpectation = self.expectation(description: "didChange") + listPublisher.addObserver(observer) { listPublisher in + + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0)) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3) + + didChangeExpectation.fulfill() + } + + let saveExpectation = self.expectation(description: "save") + stack.perform( + asynchronous: { (transaction) -> Bool in + + if let object = try transaction.fetchOne( + From(), + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) { + + object.testNumber = NSNumber(value: 11) + 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")! + } + else { + + XCTFail() + } + if let object = try transaction.fetchOne( + From(), + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { + + object.testNumber = NSNumber(value: 22) + 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")! + } + else { + + XCTFail() + } + return transaction.hasChanges + }, + success: { (hasChanges) in + + XCTAssertTrue(hasChanges) + saveExpectation.fulfill() + }, + failure: { _ in + + XCTFail() + } + ) + self.waitAndCheckExpectations() + + withExtendedLifetime(listPublisher, {}) + withExtendedLifetime(observer, {}) + } + } + + @objc + dynamic func test_ThatListPublishers_CanReceiveMoveNotifications() { + + self.prepareStack { (stack) in + + self.prepareTestDataForStack(stack) + + let observer = NSObject() + let listPublisher = stack.publishList( + From(), + SectionBy(#keyPath(TestEntity1.testBoolean)), + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + ) + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0)) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3) + + let didChangeExpectation = self.expectation(description: "didChange") + listPublisher.addObserver(observer) { listPublisher in + + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0)) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3) + + didChangeExpectation.fulfill() + } + + let saveExpectation = self.expectation(description: "save") + stack.perform( + asynchronous: { (transaction) -> Bool in + + if let object = try transaction.fetchOne( + From(), + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { + + object.testBoolean = NSNumber(value: true) + } + else { + + XCTFail() + } + return transaction.hasChanges + }, + success: { (hasChanges) in + + XCTAssertTrue(hasChanges) + saveExpectation.fulfill() + }, + failure: { _ in + + XCTFail() + } + ) + self.waitAndCheckExpectations() + + withExtendedLifetime(listPublisher, {}) + withExtendedLifetime(observer, {}) + } + } + + @objc + dynamic func test_ThatListPublishers_CanReceiveDeleteNotifications() { + + self.prepareStack { (stack) in + + self.prepareTestDataForStack(stack) + + let observer = NSObject() + let listPublisher = stack.publishList( + From(), + SectionBy(#keyPath(TestEntity1.testBoolean)), + OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) + ) + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertEqual(listPublisher.snapshot.numberOfSections, 2) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0)) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 2) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 1), 3) + + let didChangeExpectation = self.expectation(description: "didChange") + listPublisher.addObserver(observer) { listPublisher in + + XCTAssertTrue(listPublisher.snapshot.hasSections()) + XCTAssertEqual(listPublisher.snapshot.numberOfSections, 1) + XCTAssertTrue(listPublisher.snapshot.hasItems()) + XCTAssertTrue(listPublisher.snapshot.hasItems(inSectionIndex: 0)) + XCTAssertEqual(listPublisher.snapshot.numberOfItems(inSectionIndex: 0), 3) + + didChangeExpectation.fulfill() + } + + let saveExpectation = self.expectation(description: "save") + stack.perform( + asynchronous: { (transaction) -> Bool in + + let count = try transaction.deleteAll( + From(), + Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false) + ) + XCTAssertEqual(count, 2) + return transaction.hasChanges + }, + success: { (hasChanges) in + + XCTAssertTrue(hasChanges) + saveExpectation.fulfill() + }, + failure: { _ in + + XCTFail() + } + ) + self.waitAndCheckExpectations() + } + } } #endif diff --git a/CoreStoreTests/ObjectPublisherTests.swift b/CoreStoreTests/ObjectPublisherTests.swift new file mode 100644 index 0000000..08c4de4 --- /dev/null +++ b/CoreStoreTests/ObjectPublisherTests.swift @@ -0,0 +1,154 @@ +// +// ObjectPublisherTests.swift +// CoreStore +// +// Copyright © 2018 John Rommel Estropia +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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 XCTest + +@testable +import CoreStore + + +// MARK: - ObjectPublisherTests + +@available(macOS 10.12, *) +class ObjectPublisherTests: BaseTestDataTestCase { + + @objc + dynamic func test_ThatObjectPublishers_CanReceiveUpdateNotifications() { + + self.prepareStack { (stack) in + + self.prepareTestDataForStack(stack) + + guard let object = try stack.fetchOne( + From(), + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { + + XCTFail() + return + } + let observer = NSObject() + let objectPublisher = stack.publishObject(object) + XCTAssertEqual(objectPublisher.object, object) + XCTAssertNotNil(objectPublisher.snapshot) + + let didChangeExpectation = self.expectation(description: "didChange") + objectPublisher.addObserver(observer) { objectPublisher in + + XCTAssertEqual(objectPublisher.snapshot?.testNumber, NSNumber(value: 10)) + XCTAssertEqual(objectPublisher.snapshot?.testString, "nil:TestEntity1:10") + + didChangeExpectation.fulfill() + } + + let saveExpectation = self.expectation(description: "save") + stack.perform( + asynchronous: { (transaction) -> Bool in + + guard let object = transaction.edit(object) else { + + XCTFail() + try transaction.cancel() + } + object.testNumber = NSNumber(value: 10) + object.testString = "nil:TestEntity1:10" + + return transaction.hasChanges + }, + success: { (hasChanges) in + + XCTAssertTrue(hasChanges) + saveExpectation.fulfill() + }, + failure: { _ in + + XCTFail() + } + ) + self.waitAndCheckExpectations() + + withExtendedLifetime(objectPublisher, {}) + withExtendedLifetime(observer, {}) + } + } + + @objc + dynamic func test_ThatObjectPublishers_CanReceiveDeleteNotifications() { + + self.prepareStack { (stack) in + + self.prepareTestDataForStack(stack) + + guard let object = try stack.fetchOne( + From(), + Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { + + XCTFail() + return + } + let observer = NSObject() + let objectPublisher = stack.publishObject(object) + XCTAssertEqual(objectPublisher.object, object) + XCTAssertNotNil(objectPublisher.snapshot) + + let didChangeExpectation = self.expectation(description: "didChange") + objectPublisher.addObserver(observer) { objectPublisher in + + XCTAssertNil(objectPublisher.object) + XCTAssertNil(objectPublisher.snapshot) + + didChangeExpectation.fulfill() + } + + let saveExpectation = self.expectation(description: "save") + stack.perform( + asynchronous: { (transaction) -> Bool in + + guard let object = transaction.edit(object) else { + + XCTFail() + try transaction.cancel() + } + transaction.delete(object) + + return transaction.hasChanges + }, + success: { (hasChanges) in + + XCTAssertTrue(hasChanges) + saveExpectation.fulfill() + }, + failure: { _ in + + XCTFail() + } + ) + self.waitAndCheckExpectations() + + withExtendedLifetime(objectPublisher, {}) + withExtendedLifetime(observer, {}) + } + } +} +