From b98805e4894e1bea84bf59dca7ebf82cbcf8fac6 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 6 Jun 2016 00:12:00 +0900 Subject: [PATCH] WIP: Fetching unit tests --- CoreStore.xcodeproj/project.pbxproj | 16 + CoreStoreTests/BaseTests/BaseTestCase.swift | 8 +- CoreStoreTests/FetchTests.swift | 1028 +++++++++++++++++ CoreStoreTests/FromTests.swift | 60 +- CoreStoreTests/SectionByTests.swift | 53 + CoreStoreTests/SetupTests.swift | 4 + CoreStoreTests/TransactionTests.swift | 587 +++++++++- .../Concrete Clauses/From.swift | 16 +- .../CoreStoreFetchedResultsController.swift | 6 +- .../NSManagedObjectContext+Querying.swift | 48 +- Sources/ObjectiveC/CSFrom.swift | 26 - .../NSManagedObjectContext+ObjectiveC.swift | 48 +- .../Transactions/BaseDataTransaction.swift | 16 +- 13 files changed, 1842 insertions(+), 74 deletions(-) create mode 100644 CoreStoreTests/FetchTests.swift create mode 100644 CoreStoreTests/SectionByTests.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index edb54bd..e3cc9fe 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -117,6 +117,12 @@ B52557801D029D2500E51965 /* TweakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525577F1D029D2500E51965 /* TweakTests.swift */; }; B52557811D029D2500E51965 /* TweakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525577F1D029D2500E51965 /* TweakTests.swift */; }; B52557821D029D2500E51965 /* TweakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525577F1D029D2500E51965 /* TweakTests.swift */; }; + B52557841D02A07400E51965 /* SectionByTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557831D02A07400E51965 /* SectionByTests.swift */; }; + B52557851D02A07400E51965 /* SectionByTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557831D02A07400E51965 /* SectionByTests.swift */; }; + B52557861D02A07400E51965 /* SectionByTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557831D02A07400E51965 /* SectionByTests.swift */; }; + B52557881D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; }; + B52557891D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; }; + B525578A1D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; }; B52661401CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; }; B52661411CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; }; B52661421CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; }; @@ -670,6 +676,8 @@ B52557771D02826E00E51965 /* OrderByTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderByTests.swift; sourceTree = ""; }; B525577B1D0291FE00E51965 /* GroupByTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupByTests.swift; sourceTree = ""; }; B525577F1D029D2500E51965 /* TweakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweakTests.swift; sourceTree = ""; }; + B52557831D02A07400E51965 /* SectionByTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionByTests.swift; sourceTree = ""; }; + B52557871D02DE8100E51965 /* FetchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchTests.swift; sourceTree = ""; }; B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchRequest.swift; sourceTree = ""; }; B529C2031CA4A2DB007E7EBD /* CSSaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSaveResult.swift; sourceTree = ""; }; B52DD1741BE1F8CC00949AFE /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -940,11 +948,13 @@ B5DBE2DE1C9939E100B5CEFA /* BridgingTests.m */, 2F03A53F19C5C6DA005002A5 /* CoreStoreTests.swift */, B5519A3F1CA1B17B002BEF78 /* ErrorTests.swift */, + B52557871D02DE8100E51965 /* FetchTests.swift */, B5489F4F1CF603D5008B4978 /* FromTests.swift */, B525577B1D0291FE00E51965 /* GroupByTests.swift */, B525576B1CFAF18F00E51965 /* IntoTests.swift */, B5DC47C51C93D22900FA3BF3 /* MigrationChainTests.swift */, B52557771D02826E00E51965 /* OrderByTests.swift */, + B52557831D02A07400E51965 /* SectionByTests.swift */, B525576F1D02561A00E51965 /* SelectTests.swift */, B58085741CDF7F00004C2EEB /* SetupTests.swift */, B5DC47C91C93D9C800FA3BF3 /* StorageInterfaceTests.swift */, @@ -1740,6 +1750,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B52557841D02A07400E51965 /* SectionByTests.swift in Sources */, B5519A401CA1B17B002BEF78 /* ErrorTests.swift in Sources */, B525577C1D0291FE00E51965 /* GroupByTests.swift in Sources */, B52557741D02791400E51965 /* WhereTests.swift in Sources */, @@ -1747,6 +1758,7 @@ B5DC47C61C93D22900FA3BF3 /* MigrationChainTests.swift in Sources */, B525576C1CFAF18F00E51965 /* IntoTests.swift in Sources */, B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */, + B52557881D02DE8100E51965 /* FetchTests.swift in Sources */, B5489F501CF603D5008B4978 /* FromTests.swift in Sources */, B52557781D02826E00E51965 /* OrderByTests.swift in Sources */, B5489F421CF5EEBC008B4978 /* TestEntity2.swift in Sources */, @@ -1881,6 +1893,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B52557851D02A07400E51965 /* SectionByTests.swift in Sources */, B5519A411CA1B17B002BEF78 /* ErrorTests.swift in Sources */, B525577D1D0291FE00E51965 /* GroupByTests.swift in Sources */, B52557751D02791400E51965 /* WhereTests.swift in Sources */, @@ -1888,6 +1901,7 @@ B5DBE2E01C9939E100B5CEFA /* BridgingTests.m in Sources */, B525576D1CFAF18F00E51965 /* IntoTests.swift in Sources */, B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */, + B52557891D02DE8100E51965 /* FetchTests.swift in Sources */, B5489F511CF603D5008B4978 /* FromTests.swift in Sources */, B52557791D02826E00E51965 /* OrderByTests.swift in Sources */, B5489F431CF5EEBC008B4978 /* TestEntity2.swift in Sources */, @@ -2003,6 +2017,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B52557861D02A07400E51965 /* SectionByTests.swift in Sources */, B5519A421CA1B17B002BEF78 /* ErrorTests.swift in Sources */, B525577E1D0291FE00E51965 /* GroupByTests.swift in Sources */, B52557761D02791400E51965 /* WhereTests.swift in Sources */, @@ -2010,6 +2025,7 @@ B5DBE2E11C9939E100B5CEFA /* BridgingTests.m in Sources */, B525576E1CFAF18F00E51965 /* IntoTests.swift in Sources */, B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */, + B525578A1D02DE8100E51965 /* FetchTests.swift in Sources */, B5489F521CF603D5008B4978 /* FromTests.swift in Sources */, B525577A1D02826E00E51965 /* OrderByTests.swift in Sources */, B5489F441CF5EEBC008B4978 /* TestEntity2.swift in Sources */, diff --git a/CoreStoreTests/BaseTests/BaseTestCase.swift b/CoreStoreTests/BaseTests/BaseTestCase.swift index 38f1537..0ed5fc6 100644 --- a/CoreStoreTests/BaseTests/BaseTestCase.swift +++ b/CoreStoreTests/BaseTests/BaseTestCase.swift @@ -48,7 +48,9 @@ class BaseTestCase: XCTestCase { try stack.addStorageAndWait( SQLiteStore( - fileName: "\(self.dynamicType)_\($0).sqlite", + fileURL: SQLiteStore.defaultRootDirectory + .URLByAppendingPathComponent(NSUUID().UUIDString) + .URLByAppendingPathComponent("\(self.dynamicType)_\(($0 ?? "-null-")).sqlite"), configuration: $0, localStorageOptions: .RecreateStoreOnModelMismatch ) @@ -80,8 +82,8 @@ class BaseTestCase: XCTestCase { // MARK: Private - private func deleteStores(directory: NSURL = SQLiteStore.defaultRootDirectory) { + private func deleteStores() { - _ = try? NSFileManager.defaultManager().removeItemAtURL(directory) + _ = try? NSFileManager.defaultManager().removeItemAtURL(SQLiteStore.defaultRootDirectory) } } diff --git a/CoreStoreTests/FetchTests.swift b/CoreStoreTests/FetchTests.swift new file mode 100644 index 0000000..a479de7 --- /dev/null +++ b/CoreStoreTests/FetchTests.swift @@ -0,0 +1,1028 @@ +// +// FetchTests.swift +// CoreStore +// +// Copyright © 2016 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: - FetchTests + +final class FetchTests: BaseTestCase { + + @objc + dynamic func test_ThatDataStacks_CanFetchOneFromDefaultConfiguration() { + + let configurations: [String?] = [nil] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let object2 = stack.fetchOne( + From(TestEntity1), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testString, "nil:TestEntity1:2") + + let object3 = stack.fetchOne( + From(TestEntity1), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testString, "nil:TestEntity1:3") + + let nilObject = stack.fetchOne( + From(TestEntity1), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let object2 = stack.fetchOne( + From(nil), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testString, "nil:TestEntity1:2") + + let object3 = stack.fetchOne( + From(nil), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testString, "nil:TestEntity1:3") + + let nilObject = stack.fetchOne( + From(nil), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let nilObject1 = stack.fetchOne( + From("Config1"), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject1) + + let nilObject2 = stack.fetchOne( + From("Config1"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNil(nilObject2) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchOneFromSingleConfiguration() { + + let configurations: [String?] = [nil, "Config1", "Config2"] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let object2 = stack.fetchOne( + From(TestEntity1), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testNumber, 2) // configuration ambiguous + + let object3 = stack.fetchOne( + From(TestEntity1), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testNumber, 3) // configuration ambiguous + + let nilObject = stack.fetchOne( + From(TestEntity1), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let object2 = stack.fetchOne( + From(nil), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testString, "nil:TestEntity1:2") + + let object3 = stack.fetchOne( + From(nil), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testString, "nil:TestEntity1:3") + + let nilObject = stack.fetchOne( + From(nil), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let object2 = stack.fetchOne( + From("Config1"), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testString, "Config1:TestEntity1:2") + + let object3 = stack.fetchOne( + From("Config1"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testString, "Config1:TestEntity1:3") + + let nilObject = stack.fetchOne( + From("Config1"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let nilObject1 = stack.fetchOne( + From("Config2"), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject1) + + let nilObject2 = stack.fetchOne( + From("Config2"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNil(nilObject2) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchOneFromMultipleConfigurations() { + + let configurations: [String?] = [nil, "Config1", "Config2"] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let object2 = stack.fetchOne( + From(nil, "Config1"), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testNumber, 2) // configuration is ambiguous + + let object3 = stack.fetchOne( + From(nil, "Config1"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testNumber, 3) // configuration is ambiguous + + let nilObject = stack.fetchOne( + From(nil, "Config1"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let object2 = stack.fetchOne( + From(nil, "Config2"), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testString, "nil:TestEntity1:2") + + let object3 = stack.fetchOne( + From(nil, "Config2"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testString, "nil:TestEntity1:3") + + let nilObject = stack.fetchOne( + From(nil, "Config2"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + do { + + let object2 = stack.fetchOne( + From("Config1", "Config2"), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(object2) + XCTAssertEqual(object2?.testString, "Config1:TestEntity1:2") + + let object3 = stack.fetchOne( + From("Config1", "Config2"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNotNil(object3) + XCTAssertEqual(object3?.testString, "Config1:TestEntity1:3") + + let nilObject = stack.fetchOne( + From("Config1", "Config2"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchAllFromDefaultConfiguration() { + + let configurations: [String?] = [nil] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let objects2to4 = stack.fetchAll( + From(TestEntity1), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to4) + XCTAssertEqual(objects2to4?.count, 3) + XCTAssertEqual( + (objects2to4 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:2", + "nil:TestEntity1:3", + "nil:TestEntity1:4" + ] + ) + + let objects4to2 = stack.fetchAll( + From(TestEntity1), + Where("%K < %@", "testNumber", 5), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to2) + XCTAssertEqual(objects4to2?.count, 3) + XCTAssertEqual( + (objects4to2 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:4", + "nil:TestEntity1:3", + "nil:TestEntity1:2" + ] + ) + + let emptyObjects = stack.fetchAll( + From(TestEntity1), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let objects2to4 = stack.fetchAll( + From(nil), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to4) + XCTAssertEqual(objects2to4?.count, 3) + XCTAssertEqual( + (objects2to4 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:2", + "nil:TestEntity1:3", + "nil:TestEntity1:4" + ] + ) + + let objects4to2 = stack.fetchAll( + From(nil), + Where("%K < %@", "testNumber", 5), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to2) + XCTAssertEqual(objects4to2?.count, 3) + XCTAssertEqual( + (objects4to2 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:4", + "nil:TestEntity1:3", + "nil:TestEntity1:2" + ] + ) + + let emptyObjects = stack.fetchAll( + From(nil), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let nilObject1 = stack.fetchAll( + From("Config1"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilObject1) + + let nilObject2 = stack.fetchAll( + From("Config1"), + Where("testNumber", isEqualTo: 0), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNil(nilObject2) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchAllFromSingleConfiguration() { + + let configurations: [String?] = [nil, "Config1", "Config2"] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let objects4to5 = stack.fetchAll( + From(TestEntity1), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to5) + XCTAssertEqual(objects4to5?.count, 3) + XCTAssertEqual( + Set((objects4to5 ?? []).map { $0.testNumber!.integerValue }), + [4, 5] as Set + ) // configuration is ambiguous + + let objects2to1 = stack.fetchAll( + From(TestEntity1), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to1) + XCTAssertEqual(objects2to1?.count, 3) + XCTAssertEqual( + Set((objects2to1 ?? []).map { $0.testNumber!.integerValue }), + [1, 2] as Set + ) // configuration is ambiguous + + let emptyObjects = stack.fetchAll( + From(TestEntity1), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let objects4to5 = stack.fetchAll( + From(nil), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to5) + XCTAssertEqual(objects4to5?.count, 2) + XCTAssertEqual( + (objects4to5 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:4", + "nil:TestEntity1:5" + ] + ) + + let objects2to1 = stack.fetchAll( + From(nil), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to1) + XCTAssertEqual(objects2to1?.count, 2) + XCTAssertEqual( + (objects2to1 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:2", + "nil:TestEntity1:1" + ] + ) + + let emptyObjects = stack.fetchAll( + From(nil), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let objects4to5 = stack.fetchAll( + From("Config1"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to5) + XCTAssertEqual(objects4to5?.count, 2) + XCTAssertEqual( + (objects4to5 ?? []).map { $0.testString ?? "" }, + [ + "Config1:TestEntity1:4", + "Config1:TestEntity1:5" + ] + ) + + let objects2to1 = stack.fetchAll( + From("Config1"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to1) + XCTAssertEqual(objects2to1?.count, 2) + XCTAssertEqual( + (objects2to1 ?? []).map { $0.testString ?? "" }, + [ + "Config1:TestEntity1:2", + "Config1:TestEntity1:1" + ] + ) + + let emptyObjects = stack.fetchAll( + From("Config1"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let object1 = stack.fetchOne( + From("Config2"), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(object1) + + let object5 = stack.fetchOne( + From("Config2"), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNil(object5) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchAllFromMultipleConfigurations() { + + let configurations: [String?] = [nil, "Config1", "Config2"] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let objects4to5 = stack.fetchAll( + From(nil, "Config1"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to5) + XCTAssertEqual(objects4to5?.count, 3) + XCTAssertEqual( + Set((objects4to5 ?? []).map { $0.testNumber!.integerValue }), + [4, 5] as Set + ) // configuration is ambiguous + + let objects2to1 = stack.fetchAll( + From(nil, "Config1"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to1) + XCTAssertEqual(objects2to1?.count, 3) + XCTAssertEqual( + Set((objects2to1 ?? []).map { $0.testNumber!.integerValue }), + [1, 2] as Set + ) // configuration is ambiguous + + let emptyObjects = stack.fetchAll( + From(nil, "Config1"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let objects4to5 = stack.fetchAll( + From(nil, "Config2"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to5) + XCTAssertEqual(objects4to5?.count, 2) + XCTAssertEqual( + (objects4to5 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:4", + "nil:TestEntity1:5" + ] + ) + + let objects2to1 = stack.fetchAll( + From(nil, "Config2"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to1) + XCTAssertEqual(objects2to1?.count, 2) + XCTAssertEqual( + (objects2to1 ?? []).map { $0.testString ?? "" }, + [ + "nil:TestEntity1:2", + "nil:TestEntity1:1" + ] + ) + + let emptyObjects = stack.fetchAll( + From(nil, "Config2"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + do { + + let objects4to5 = stack.fetchAll( + From("Config1", "Config2"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects4to5) + XCTAssertEqual(objects4to5?.count, 2) + XCTAssertEqual( + (objects4to5 ?? []).map { $0.testString ?? "" }, + [ + "Config1:TestEntity1:4", + "Config1:TestEntity1:5" + ] + ) + + let objects2to1 = stack.fetchAll( + From("Config1", "Config2"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(objects2to1) + XCTAssertEqual(objects2to1?.count, 2) + XCTAssertEqual( + (objects2to1 ?? []).map { $0.testString ?? "" }, + [ + "Config1:TestEntity1:2", + "Config1:TestEntity1:1" + ] + ) + + let emptyObjects = stack.fetchAll( + From("Config1", "Config2"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyObjects) + XCTAssertEqual(emptyObjects?.count, 0) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchCountFromDefaultConfiguration() { + + let configurations: [String?] = [nil] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let count2to4 = stack.fetchCount( + From(TestEntity1), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to4) + XCTAssertEqual(count2to4, 3) + + let count4to2 = stack.fetchCount( + From(TestEntity1), + Where("%K < %@", "testNumber", 5), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to2) + XCTAssertEqual(count4to2, 3) + + let emptyCount = stack.fetchCount( + From(TestEntity1), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let count2to4 = stack.fetchCount( + From(nil), + Where("%K > %@", "testNumber", 1), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to4) + XCTAssertEqual(count2to4, 3) + + let count4to2 = stack.fetchCount( + From(nil), + Where("%K < %@", "testNumber", 5), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to2) + XCTAssertEqual(count4to2, 3) + + let emptyCount = stack.fetchCount( + From(nil), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let nilCount1 = stack.fetchCount( + From("Config1"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilCount1) + + let nilCount2 = stack.fetchCount( + From("Config1"), + Where("testNumber", isEqualTo: 0), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNil(nilCount2) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchCountFromSingleConfiguration() { + + let configurations: [String?] = [nil, "Config1", "Config2"] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let count4to5 = stack.fetchCount( + From(TestEntity1), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to5) + XCTAssertEqual(count4to5, 3) + + let count2to1 = stack.fetchCount( + From(TestEntity1), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to1) + XCTAssertEqual(count2to1, 3) + + let emptyCount = stack.fetchCount( + From(TestEntity1), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let count4to5 = stack.fetchCount( + From(nil), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to5) + XCTAssertEqual(count4to5, 2) + + let count2to1 = stack.fetchCount( + From(nil), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to1) + XCTAssertEqual(count2to1, 2) + + let emptyCount = stack.fetchCount( + From(nil), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let count4to5 = stack.fetchCount( + From("Config1"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to5) + XCTAssertEqual(count4to5, 2) + + let count2to1 = stack.fetchCount( + From("Config1"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to1) + XCTAssertEqual(count2to1, 2) + + let emptyCount = stack.fetchCount( + From("Config1"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let nilCount1 = stack.fetchCount( + From("Config2"), + Where("%K < %@", "testNumber", 4), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNil(nilCount1) + + let nilCount2 = stack.fetchCount( + From("Config2"), + Where("testNumber", isEqualTo: 0), + OrderBy(.Descending("testEntityID")) + ) + XCTAssertNil(nilCount2) + } + } + } + + @objc + dynamic func test_ThatDataStacks_CanFetchCountFromMultipleConfigurations() { + + let configurations: [String?] = [nil, "Config1", "Config2"] + self.prepareStack(configurations: configurations) { (stack) in + + self.prepareStubsForStack(stack, configurations: configurations) + + do { + + let count4to5 = stack.fetchCount( + From(nil, "Config1"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to5) + XCTAssertEqual(count4to5, 3) + + let count2to1 = stack.fetchCount( + From(nil, "Config1"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to1) + XCTAssertEqual(count2to1, 3) + + let emptyCount = stack.fetchCount( + From(nil, "Config1"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let count4to5 = stack.fetchCount( + From(nil, "Config2"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to5) + XCTAssertEqual(count4to5, 2) + + let count2to1 = stack.fetchCount( + From(nil, "Config2"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to1) + XCTAssertEqual(count2to1, 2) + + let emptyCount = stack.fetchCount( + From(nil, "Config2"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + do { + + let count4to5 = stack.fetchCount( + From("Config1", "Config2"), + Where("%K > %@", "testNumber", 3), + OrderBy(.Ascending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count4to5) + XCTAssertEqual(count4to5, 2) + + let count2to1 = stack.fetchCount( + From("Config1", "Config2"), + Where("%K < %@", "testNumber", 3), + OrderBy(.Descending("testEntityID")), + Tweak { $0.fetchLimit = 3 } + ) + XCTAssertNotNil(count2to1) + XCTAssertEqual(count2to1, 2) + + let emptyCount = stack.fetchCount( + From("Config1", "Config2"), + Where("%K > %@", "testNumber", 5), + OrderBy(.Ascending("testEntityID")) + ) + XCTAssertNotNil(emptyCount) + XCTAssertEqual(emptyCount, 0) + } + } + } + + + // MARK: Private + + @nonobjc + private let dateFormatter: NSDateFormatter = { + + let formatter = NSDateFormatter() + formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") + formatter.timeZone = NSTimeZone(name: "UTC") + formatter.calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) + formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" + return formatter + }() + + @nonobjc + private func prepareStubsForStack(stack: DataStack, configurations: [String?]) { + + stack.beginSynchronous { (transaction) in + + for (configurationIndex, configuration) in configurations.enumerate() { + + if configuration == nil || configuration == "Config1" { + + for idIndex in 1 ... 5 { + + let object = transaction.create(Into(configuration)) + object.testEntityID = NSNumber(integer: (configurationIndex * 100) + idIndex) + object.testString = "\(configuration ?? "nil"):TestEntity1:\(idIndex)" + object.testNumber = idIndex + object.testDate = self.dateFormatter.dateFromString("2000-\(configurationIndex)-\(idIndex)T00:00:00Z") + } + } + if configuration == nil || configuration == "Config2" { + + for idIndex in 1 ... 5 { + + let object = transaction.create(Into(configuration)) + object.testEntityID = NSNumber(integer: (configurationIndex * 200) + idIndex) + object.testString = "\(configuration ?? "nil"):TestEntity2:\(idIndex)" + object.testNumber = idIndex + object.testDate = self.dateFormatter.dateFromString("2000-\(configurationIndex)-\(idIndex)T00:00:00Z") + } + } + } + transaction.commitAndWait() + } + } +} diff --git a/CoreStoreTests/FromTests.swift b/CoreStoreTests/FromTests.swift index f62e43b..de7613a 100644 --- a/CoreStoreTests/FromTests.swift +++ b/CoreStoreTests/FromTests.swift @@ -85,7 +85,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -99,7 +100,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -121,7 +123,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -135,7 +138,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -149,7 +153,8 @@ final class FromTests: BaseTestCase { let from = From("Config2") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -163,7 +168,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -177,7 +183,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -191,7 +198,8 @@ final class FromTests: BaseTestCase { let from = From("Config2") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -213,7 +221,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -227,7 +236,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -241,7 +251,8 @@ final class FromTests: BaseTestCase { let from = From("Config2") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -255,7 +266,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -269,7 +281,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -283,7 +296,8 @@ final class FromTests: BaseTestCase { let from = From("Config2") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -305,7 +319,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -319,7 +334,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -333,7 +349,8 @@ final class FromTests: BaseTestCase { let from = From("Config2") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -347,7 +364,8 @@ final class FromTests: BaseTestCase { let from = From() let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -361,7 +379,8 @@ final class FromTests: BaseTestCase { let from = From("Config1") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertFalse(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) @@ -375,7 +394,8 @@ final class FromTests: BaseTestCase { let from = From("Config2") let request = NSFetchRequest() - from.applyToFetchRequest(request, context: dataStack.mainContext) + let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext) + XCTAssertTrue(storesFound) XCTAssertNotNil(request.entity) XCTAssertNotNil(request.affectedStores) diff --git a/CoreStoreTests/SectionByTests.swift b/CoreStoreTests/SectionByTests.swift new file mode 100644 index 0000000..f13f9ec --- /dev/null +++ b/CoreStoreTests/SectionByTests.swift @@ -0,0 +1,53 @@ +// +// SectionByTests.swift +// CoreStore +// +// Copyright © 2016 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: - SectionByTests + +final class SectionByTests: XCTestCase { + + @objc + dynamic func test_ThatSectionByClauses_ConfigureCorrectly() { + + do { + + let sectionBy = SectionBy("key") + XCTAssertEqual(sectionBy.sectionKeyPath, "key") + XCTAssertEqual(sectionBy.sectionIndexTransformer(sectionName: "key"), "key") + } + do { + + let sectionBy = SectionBy("key") { $0.flatMap { "\($0):suffix" } } + XCTAssertEqual(sectionBy.sectionKeyPath, "key") + XCTAssertEqual(sectionBy.sectionIndexTransformer(sectionName: "key"), "key:suffix") + XCTAssertNil(sectionBy.sectionIndexTransformer(sectionName: nil)) + } + } +} diff --git a/CoreStoreTests/SetupTests.swift b/CoreStoreTests/SetupTests.swift index 0b82d71..8163436 100644 --- a/CoreStoreTests/SetupTests.swift +++ b/CoreStoreTests/SetupTests.swift @@ -39,6 +39,10 @@ class SetupTests: BaseTestCase { let model = NSManagedObjectModel.mergedModelFromBundles([NSBundle(forClass: self.dynamicType)])! let stack = DataStack(model: model, migrationChain: nil) + XCTAssertEqual(stack.coordinator.managedObjectModel, model) + XCTAssertEqual(stack.rootSavingContext.persistentStoreCoordinator, stack.coordinator) + XCTAssertNil(stack.rootSavingContext.parentContext) + XCTAssertEqual(stack.mainContext.parentContext, stack.rootSavingContext) XCTAssertEqual(stack.model, model) XCTAssertTrue(stack.migrationChain.valid) XCTAssertTrue(stack.migrationChain.empty) diff --git a/CoreStoreTests/TransactionTests.swift b/CoreStoreTests/TransactionTests.swift index 3a610b5..963211b 100644 --- a/CoreStoreTests/TransactionTests.swift +++ b/CoreStoreTests/TransactionTests.swift @@ -27,6 +27,8 @@ import XCTest @testable import CoreStore +@testable +import GCDKit //MARK: - TransactionTests @@ -34,13 +36,592 @@ import CoreStore final class TransactionTests: BaseTestCase { @objc - dynamic func test_ThatSynchronousTransactions_ConfigureCorrectly() { + dynamic func test_ThatSynchronousTransactions_CanPerformCRUDs() { self.prepareStack { (stack) in - // TODO: + let testDate = NSDate() + let createExpectation = self.expectationWithDescription("create") + stack.beginSynchronous { (transaction) in + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = testDate + + switch transaction.commitAndWait() { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + createExpectation.fulfill() + + default: + XCTFail() + } + } + do { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1") + XCTAssertEqual(object?.testNumber, 100) + XCTAssertEqual(object?.testDate, testDate) + } + + let updateExpectation = self.expectationWithDescription("update") + stack.beginSynchronous { (transaction) in + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + object.testString = "string1_edit" + object.testNumber = 200 + object.testDate = NSDate.distantFuture() + + switch transaction.commitAndWait() { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + updateExpectation.fulfill() + + default: + XCTFail() + } + } + do { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1_edit") + XCTAssertEqual(object?.testNumber, 200) + XCTAssertEqual(object?.testDate, NSDate.distantFuture()) + } + + let deleteExpectation = self.expectationWithDescription("delete") + stack.beginSynchronous { (transaction) in + + let object = transaction.fetchOne(From(TestEntity1)) + transaction.delete(object) + + switch transaction.commitAndWait() { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + deleteExpectation.fulfill() + + default: + XCTFail() + } + } + do { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 0) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNil(object) + } + } + self.waitForExpectationsWithTimeout(NSTimeInterval(Int8.max), handler: nil) + } + + @objc + dynamic func test_ThatSynchronousTransactions_CanDiscardUncommittedChanges() { + + self.prepareStack { (stack) in + + let createDiscardExpectation = self.expectationWithDescription("create-discard") + stack.beginSynchronous { (transaction) in + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = NSDate() + + createDiscardExpectation.fulfill() + } + do { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 0) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNil(object) + } + + let testDate = NSDate() + let createExpectation = self.expectationWithDescription("create") + stack.beginSynchronous { (transaction) in + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = testDate + + switch transaction.commitAndWait() { + + case .Success(true): + createExpectation.fulfill() + + default: + XCTFail() + } + } + + let updateDiscardExpectation = self.expectationWithDescription("update-discard") + stack.beginSynchronous { (transaction) in + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + object.testString = "string1_edit" + object.testNumber = 200 + object.testDate = NSDate.distantFuture() + + updateDiscardExpectation.fulfill() + } + do { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1") + XCTAssertEqual(object?.testNumber, 100) + XCTAssertEqual(object?.testDate, testDate) + } + + let deleteDiscardExpectation = self.expectationWithDescription("delete-discard") + stack.beginSynchronous { (transaction) in + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + transaction.delete(object) + + deleteDiscardExpectation.fulfill() + } + do { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1") + XCTAssertEqual(object?.testNumber, 100) + XCTAssertEqual(object?.testDate, testDate) + } + } + self.waitForExpectationsWithTimeout(NSTimeInterval(Int8.max), handler: nil) + } + + @objc + dynamic func test_ThatAsynchronousTransactions_CanPerformCRUDs() { + + self.prepareStack { (stack) in + + let testDate = NSDate() + let createExpectation = self.expectationWithDescription("create") + stack.beginAsynchronous { (transaction) in + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = testDate + + transaction.commit { (result) in + + switch result { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1") + XCTAssertEqual(object?.testNumber, 100) + XCTAssertEqual(object?.testDate, testDate) + createExpectation.fulfill() + + default: + XCTFail() + } + } + } + let updateExpectation = self.expectationWithDescription("update") + stack.beginAsynchronous { (transaction) in + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + object.testString = "string1_edit" + object.testNumber = 200 + object.testDate = NSDate.distantFuture() + + transaction.commit { (result) in + + switch result { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1_edit") + XCTAssertEqual(object?.testNumber, 200) + XCTAssertEqual(object?.testDate, NSDate.distantFuture()) + updateExpectation.fulfill() + + default: + XCTFail() + } + } + } + let deleteExpectation = self.expectationWithDescription("delete") + stack.beginAsynchronous { (transaction) in + + let object = transaction.fetchOne(From(TestEntity1)) + transaction.delete(object) + + transaction.commit { (result) in + + switch result { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 0) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNil(object) + deleteExpectation.fulfill() + + default: + XCTFail() + } + } + } + } + self.waitForExpectationsWithTimeout(NSTimeInterval(Int8.max), handler: nil) + } + + @objc + dynamic func test_ThatAsynchronousTransactions_CanDiscardUncommittedChanges() { + + self.prepareStack { (stack) in + + let createDiscardExpectation = self.expectationWithDescription("create-discard") + stack.beginAsynchronous { (transaction) in + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = NSDate() + + createDiscardExpectation.fulfill() + } + + let testDate = NSDate() + let createExpectation = self.expectationWithDescription("create") + stack.beginAsynchronous { (transaction) in + + XCTAssertEqual(transaction.fetchCount(From(TestEntity1)), 0) + XCTAssertNil(transaction.fetchOne(From(TestEntity1))) + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = testDate + + transaction.commit { (result) in + + switch result { + + case .Success(true): + createExpectation.fulfill() + + default: + XCTFail() + } + } + } + + let updateDiscardExpectation = self.expectationWithDescription("update-discard") + stack.beginAsynchronous { (transaction) in + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + object.testString = "string1_edit" + object.testNumber = 200 + object.testDate = NSDate.distantFuture() + + updateDiscardExpectation.fulfill() + } + + let deleteDiscardExpectation = self.expectationWithDescription("delete-discard") + stack.beginAsynchronous { (transaction) in + + XCTAssertEqual(transaction.fetchCount(From(TestEntity1)), 1) + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + XCTAssertNotNil(object) + XCTAssertEqual(object.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object.testString, "string1") + XCTAssertEqual(object.testNumber, 100) + XCTAssertEqual(object.testDate, testDate) + + transaction.delete(object) + + GCDQueue.Main.async { + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1") + XCTAssertEqual(object?.testNumber, 100) + XCTAssertEqual(object?.testDate, testDate) + deleteDiscardExpectation.fulfill() + } + } + } + self.waitForExpectationsWithTimeout(NSTimeInterval(Int8.max), handler: nil) + } + + @objc + dynamic func test_ThatUnsafeTransactions_CanPerformCRUDs() { + + self.prepareStack { (stack) in + + let transaction = stack.beginUnsafe() + + let testDate = NSDate() + do { + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = testDate + + switch transaction.commitAndWait() { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1") + XCTAssertEqual(object?.testNumber, 100) + XCTAssertEqual(object?.testDate, testDate) + + default: + XCTFail() + } + } + do { + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + object.testString = "string1_edit" + object.testNumber = 200 + object.testDate = NSDate.distantFuture() + + switch transaction.commitAndWait() { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + + let object = stack.fetchOne(From(TestEntity1)) + XCTAssertNotNil(object) + XCTAssertEqual(object?.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object?.testString, "string1_edit") + XCTAssertEqual(object?.testNumber, 200) + XCTAssertEqual(object?.testDate, NSDate.distantFuture()) + + default: + XCTFail() + } + } + do { + + let object = transaction.fetchOne(From(TestEntity1)) + transaction.delete(object) + + switch transaction.commitAndWait() { + + case .Success(let hasChanges): + XCTAssertTrue(hasChanges) + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 0) + XCTAssertNil(stack.fetchOne(From(TestEntity1))) + + default: + XCTFail() + } + } } } - // MARK: Private + @objc + dynamic func test_ThatUnsafeTransactions_CanRollbackChanges() { + + self.prepareStack { (stack) in + + let transaction = stack.beginUnsafe(supportsUndo: true) + do { + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = NSDate() + + transaction.rollback() + + XCTAssertEqual(transaction.fetchCount(From(TestEntity1)), 0) + XCTAssertNil(transaction.fetchOne(From(TestEntity1))) + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 0) + XCTAssertNil(stack.fetchOne(From(TestEntity1))) + } + + let testDate = NSDate() + do { + + let object = transaction.create(Into(TestEntity1)) + object.testEntityID = NSNumber(integer: 1) + object.testString = "string1" + object.testNumber = 100 + object.testDate = testDate + + switch transaction.commitAndWait() { + + case .Success(true): + break + + default: + XCTFail() + } + } + + do { + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + object.testString = "string1_edit" + object.testNumber = 200 + object.testDate = NSDate.distantFuture() + + transaction.rollback() + + XCTAssertEqual(transaction.fetchCount(From(TestEntity1)), 1) + if let object = transaction.fetchOne(From(TestEntity1)) { + + XCTAssertEqual(object.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object.testString, "string1") + XCTAssertEqual(object.testNumber, 100) + XCTAssertEqual(object.testDate, testDate) + } + else { + + XCTFail() + } + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + if let object = stack.fetchOne(From(TestEntity1)) { + + XCTAssertEqual(object.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object.testString, "string1") + XCTAssertEqual(object.testNumber, 100) + XCTAssertEqual(object.testDate, testDate) + } + else { + + XCTFail() + } + } + + do { + + guard let object = transaction.fetchOne(From(TestEntity1)) else { + + XCTFail() + return + } + transaction.delete(object) + + transaction.rollback() + + XCTAssertEqual(transaction.fetchCount(From(TestEntity1)), 1) + if let object = transaction.fetchOne(From(TestEntity1)) { + + XCTAssertEqual(object.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object.testString, "string1") + XCTAssertEqual(object.testNumber, 100) + XCTAssertEqual(object.testDate, testDate) + } + else { + + XCTFail() + } + + XCTAssertEqual(stack.fetchCount(From(TestEntity1)), 1) + if let object = stack.fetchOne(From(TestEntity1)) { + + XCTAssertEqual(object.testEntityID, NSNumber(integer: 1)) + XCTAssertEqual(object.testString, "string1") + XCTAssertEqual(object.testNumber, 100) + XCTAssertEqual(object.testDate, testDate) + } + else { + + XCTFail() + } + } + } + } } diff --git a/Sources/Fetching and Querying/Concrete Clauses/From.swift b/Sources/Fetching and Querying/Concrete Clauses/From.swift index 11aaac4..3121534 100644 --- a/Sources/Fetching and Querying/Concrete Clauses/From.swift +++ b/Sources/Fetching and Querying/Concrete Clauses/From.swift @@ -181,13 +181,23 @@ public struct From { internal let entityClass: AnyClass internal let dumpInfo: (key: String, value: Any)? - internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext, applyAffectedStores: Bool = true) { + @warn_unused_result + internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext, applyAffectedStores: Bool = true) -> Bool { fetchRequest.entity = context.entityDescriptionForEntityClass(self.entityClass) - if applyAffectedStores { + guard applyAffectedStores else { - self.applyAffectedStoresForFetchedRequest(fetchRequest, context: context) + return true } + if self.applyAffectedStoresForFetchedRequest(fetchRequest, context: context) { + + return true + } + CoreStore.log( + .Warning, + message: "Attempted to perform a fetch but could not find any persistent store for the entity \(cs_typeName(fetchRequest.entityName))" + ) + return false } internal func applyAffectedStoresForFetchedRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) -> Bool { diff --git a/Sources/Internal/CoreStoreFetchedResultsController.swift b/Sources/Internal/CoreStoreFetchedResultsController.swift index 6db8322..d8c8b40 100644 --- a/Sources/Internal/CoreStoreFetchedResultsController.swift +++ b/Sources/Internal/CoreStoreFetchedResultsController.swift @@ -49,7 +49,11 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll @nonobjc internal init(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From? = nil, sectionBy: SectionBy? = nil, applyFetchClauses: (fetchRequest: NSFetchRequest) -> Void) { - from?.applyToFetchRequest(fetchRequest, context: context, applyAffectedStores: false) + _ = from?.applyToFetchRequest( + fetchRequest, + context: context, + applyAffectedStores: false + ) applyFetchClauses(fetchRequest: fetchRequest) CoreStore.assert( diff --git a/Sources/Internal/NSManagedObjectContext+Querying.swift b/Sources/Internal/NSManagedObjectContext+Querying.swift index d9bc0f3..9563f77 100644 --- a/Sources/Internal/NSManagedObjectContext+Querying.swift +++ b/Sources/Internal/NSManagedObjectContext+Querying.swift @@ -83,12 +83,16 @@ internal extension NSManagedObjectContext { internal func fetchOne(from: From, _ fetchClauses: [FetchClause]) -> T? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 1 fetchRequest.resultType = .ManagedObjectResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchOne(fetchRequest) } @@ -133,12 +137,16 @@ internal extension NSManagedObjectContext { internal func fetchAll(from: From, _ fetchClauses: [FetchClause]) -> [T]? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchAll(fetchRequest) } @@ -183,9 +191,13 @@ internal extension NSManagedObjectContext { internal func fetchCount(from: From, _ fetchClauses: [FetchClause]) -> Int? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchCount(fetchRequest) } @@ -223,12 +235,16 @@ internal extension NSManagedObjectContext { internal func fetchObjectID(from: From, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 1 fetchRequest.resultType = .ManagedObjectIDResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchObjectID(fetchRequest) } @@ -273,12 +289,16 @@ internal extension NSManagedObjectContext { internal func fetchObjectIDs(from: From, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectIDResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchObjectIDs(fetchRequest) } @@ -323,7 +343,7 @@ internal extension NSManagedObjectContext { internal func deleteAll(from: From, _ deleteClauses: [DeleteClause]) -> Int? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectResultType @@ -331,6 +351,10 @@ internal extension NSManagedObjectContext { fetchRequest.includesPropertyValues = false deleteClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.deleteAll(fetchRequest) } @@ -383,7 +407,7 @@ internal extension NSManagedObjectContext { internal func queryValue(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> U? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 @@ -391,6 +415,10 @@ internal extension NSManagedObjectContext { selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.queryValue(selectTerms, fetchRequest: fetchRequest) } @@ -473,13 +501,17 @@ internal extension NSManagedObjectContext { internal func queryAttributes(from: From, _ selectClause: Select, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { let fetchRequest = CoreStoreFetchRequest() - from.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 selectClause.selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.queryAttributes(fetchRequest) } diff --git a/Sources/ObjectiveC/CSFrom.swift b/Sources/ObjectiveC/CSFrom.swift index 4c8b9af..8904c7d 100644 --- a/Sources/ObjectiveC/CSFrom.swift +++ b/Sources/ObjectiveC/CSFrom.swift @@ -84,32 +84,6 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType { return self.init(From(entityClass, configurations.map { $0 is NSNull ? nil : ($0 as! String) })) } - /** - Initializes a `CSFrom` clause with the specified store URLs. - - - parameter entity: the associated `NSManagedObject` entity class - - parameter storeURLs: the persistent store URLs to associate objects from. - - returns: a `CSFrom` clause with the specified store URLs - */ - @objc - public static func entityClass(entityClass: AnyClass, storeURLs: [NSURL]) -> CSFrom { - - return self.init(From(entityClass, storeURLs)) - } - - /** - Initializes a `CSFrom` clause with the specified `NSPersistentStore`s. - - - parameter entity: the associated `NSManagedObject` entity class - - parameter persistentStores: the `NSPersistentStore`s to associate objects from. - - returns: a `CSFrom` clause with the specified `NSPersistentStore`s - */ - @objc - public static func entityClass(entityClass: AnyClass, persistentStores: [NSPersistentStore]) -> CSFrom { - - return self.init(From(entityClass, persistentStores)) - } - // MARK: NSObject diff --git a/Sources/ObjectiveC/NSManagedObjectContext+ObjectiveC.swift b/Sources/ObjectiveC/NSManagedObjectContext+ObjectiveC.swift index d977835..701eb4f 100644 --- a/Sources/ObjectiveC/NSManagedObjectContext+ObjectiveC.swift +++ b/Sources/ObjectiveC/NSManagedObjectContext+ObjectiveC.swift @@ -37,12 +37,16 @@ internal extension NSManagedObjectContext { internal func fetchOne(from: CSFrom, _ fetchClauses: [CSFetchClause]) -> NSManagedObject? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 1 fetchRequest.resultType = .ManagedObjectResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchOne(fetchRequest) } @@ -50,12 +54,16 @@ internal extension NSManagedObjectContext { internal func fetchAll(from: CSFrom, _ fetchClauses: [CSFetchClause]) -> [NSManagedObject]? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchAll(fetchRequest) } @@ -63,9 +71,13 @@ internal extension NSManagedObjectContext { internal func fetchCount(from: CSFrom, _ fetchClauses: [CSFetchClause]) -> Int? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchCount(fetchRequest) } @@ -73,12 +85,16 @@ internal extension NSManagedObjectContext { internal func fetchObjectID(from: CSFrom, _ fetchClauses: [CSFetchClause]) -> NSManagedObjectID? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 1 fetchRequest.resultType = .ManagedObjectIDResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchObjectID(fetchRequest) } @@ -86,12 +102,16 @@ internal extension NSManagedObjectContext { internal func fetchObjectIDs(from: CSFrom, _ fetchClauses: [CSFetchClause]) -> [NSManagedObjectID]? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectIDResultType fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.fetchObjectIDs(fetchRequest) } @@ -99,7 +119,7 @@ internal extension NSManagedObjectContext { internal func deleteAll(from: CSFrom, _ deleteClauses: [CSDeleteClause]) -> Int? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 fetchRequest.resultType = .ManagedObjectResultType @@ -107,6 +127,10 @@ internal extension NSManagedObjectContext { fetchRequest.includesPropertyValues = false deleteClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.deleteAll(fetchRequest) } @@ -114,7 +138,7 @@ internal extension NSManagedObjectContext { internal func queryValue(from: CSFrom, _ selectClause: CSSelect, _ queryClauses: [CSQueryClause]) -> AnyObject? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 @@ -122,6 +146,10 @@ internal extension NSManagedObjectContext { selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.queryValue(selectTerms, fetchRequest: fetchRequest) } @@ -129,13 +157,17 @@ internal extension NSManagedObjectContext { internal func queryAttributes(from: CSFrom, _ selectClause: CSSelect, _ queryClauses: [CSQueryClause]) -> [[NSString: AnyObject]]? { let fetchRequest = CoreStoreFetchRequest() - from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) + let storeFound = from.bridgeToSwift.applyToFetchRequest(fetchRequest, context: self) fetchRequest.fetchLimit = 0 selectClause.selectTerms.applyToFetchRequest(fetchRequest, owner: selectClause) queryClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + guard storeFound else { + + return nil + } return self.queryAttributes(fetchRequest) } } diff --git a/Sources/Transactions/BaseDataTransaction.swift b/Sources/Transactions/BaseDataTransaction.swift index b4562fd..446da6f 100644 --- a/Sources/Transactions/BaseDataTransaction.swift +++ b/Sources/Transactions/BaseDataTransaction.swift @@ -64,7 +64,11 @@ public /*abstract*/ class BaseDataTransaction { let entityClass = (into.entityClass as! NSManagedObject.Type) if into.inferStoreIfPossible { - switch context.parentStack!.persistentStoreForEntityClass(entityClass, configuration: nil, inferStoreIfPossible: true) { + switch context.parentStack!.persistentStoreForEntityClass( + entityClass, + configuration: nil, + inferStoreIfPossible: true + ) { case (let persistentStore?, _): let object = entityClass.createInContext(context) as! T @@ -80,13 +84,21 @@ public /*abstract*/ class BaseDataTransaction { } else { - switch context.parentStack!.persistentStoreForEntityClass(entityClass, configuration: into.configuration, inferStoreIfPossible: false) { + switch context.parentStack!.persistentStoreForEntityClass( + entityClass, + configuration: into.configuration + ?? into.dynamicType.defaultConfigurationName, + inferStoreIfPossible: false + ) { case (let persistentStore?, _): let object = entityClass.createInContext(context) as! T context.assignObject(object, toPersistentStore: persistentStore) return object + case (nil, true): + fatalError("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.") + default: if let configuration = into.configuration {