diff --git a/Nimble.xcodeproj/project.pbxproj b/Nimble.xcodeproj/project.pbxproj index d100009e4..7191d9a7c 100644 --- a/Nimble.xcodeproj/project.pbxproj +++ b/Nimble.xcodeproj/project.pbxproj @@ -455,6 +455,10 @@ B20058C520E92CE400C1264D /* ElementsEqualTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20058C420E92CE400C1264D /* ElementsEqualTest.swift */; }; B20058C620E92CE400C1264D /* ElementsEqualTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20058C420E92CE400C1264D /* ElementsEqualTest.swift */; }; B20058C720E92CE400C1264D /* ElementsEqualTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20058C420E92CE400C1264D /* ElementsEqualTest.swift */; }; + C576224D2A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C576224C2A61D3AE00BD6A8C /* Equal+TupleArray.swift */; }; + C576224E2A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C576224C2A61D3AE00BD6A8C /* Equal+TupleArray.swift */; }; + C576224F2A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C576224C2A61D3AE00BD6A8C /* Equal+TupleArray.swift */; }; + C57622502A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C576224C2A61D3AE00BD6A8C /* Equal+TupleArray.swift */; }; CD3D9A79232647BC00802581 /* CwlCatchBadInstructionPosix.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3D9A78232647BC00802581 /* CwlCatchBadInstructionPosix.swift */; }; CD79C99E1D2CC832004B6F9A /* ObjCAsyncTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A56651A3B305F009E1637 /* ObjCAsyncTest.m */; }; CD79C99F1D2CC835004B6F9A /* ObjCSyncTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F8A37AF1B7C5042001C8357 /* ObjCSyncTest.m */; }; @@ -811,6 +815,7 @@ AE7ADE481C80C00D00B94CD3 /* MatchErrorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatchErrorTest.swift; sourceTree = ""; }; B20058C020E92C7500C1264D /* ElementsEqual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementsEqual.swift; sourceTree = ""; }; B20058C420E92CE400C1264D /* ElementsEqualTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementsEqualTest.swift; sourceTree = ""; }; + C576224C2A61D3AE00BD6A8C /* Equal+TupleArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Equal+TupleArray.swift"; sourceTree = ""; }; CD3D9A78232647BC00802581 /* CwlCatchBadInstructionPosix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CwlCatchBadInstructionPosix.swift; sourceTree = ""; }; CDBC39B82462EA7D00069677 /* PredicateTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PredicateTest.swift; sourceTree = ""; }; CDC157902511957100EAA480 /* DSLTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSLTest.swift; sourceTree = ""; }; @@ -1101,6 +1106,7 @@ 1FD8CD1B1968AB07008ED995 /* EndWith.swift */, 1FD8CD1C1968AB07008ED995 /* Equal.swift */, CDF5C57A2647B89B0036532C /* Equal+Tuple.swift */, + C576224C2A61D3AE00BD6A8C /* Equal+TupleArray.swift */, 472FD1341B9E085700C7B8DA /* HaveCount.swift */, DDB4D5EC19FE43C200E9D9FE /* Match.swift */, 1FD8CD1D1968AB07008ED995 /* MatcherProtocols.swift */, @@ -1670,6 +1676,7 @@ F8A1BE2F1CB3710900031679 /* XCTestObservationCenter+Register.m in Sources */, 1F1871C61CA89EDB00A34BF2 /* DSL.m in Sources */, 89F5E092290B9D5C001F9377 /* AssertionRecorder+Async.swift in Sources */, + C576224E2A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */, 1FD8CD301968AB07008ED995 /* AdapterProtocols.swift in Sources */, AE7ADE451C80BF8000B94CD3 /* MatchError.swift in Sources */, 1FC494AA1C29CBA40010975C /* NimbleEnvironment.swift in Sources */, @@ -1815,6 +1822,7 @@ 1F5DF1761BDCA0F500C3A531 /* AllPass.swift in Sources */, AE4BA9AF1C88DDB500B73906 /* Errors.swift in Sources */, 1F5DF1861BDCA0F500C3A531 /* HaveCount.swift in Sources */, + C576224F2A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */, 1F5DF1811BDCA0F500C3A531 /* BeLogical.swift in Sources */, 1F1871DB1CA89EF100A34BF2 /* NMBExpectation.swift in Sources */, CDFB6A4F1F7E084600AD8CC7 /* CwlMachBadInstructionHandler.m in Sources */, @@ -1991,6 +1999,7 @@ F8A1BE301CB3710900031679 /* XCTestObservationCenter+Register.m in Sources */, 1FD8CD311968AB07008ED995 /* AdapterProtocols.swift in Sources */, 89F5E091290B9D5C001F9377 /* AssertionRecorder+Async.swift in Sources */, + C576224D2A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */, 1F1871D21CA89EEE00A34BF2 /* DSL.m in Sources */, AE7ADE461C80BF8000B94CD3 /* MatchError.swift in Sources */, 1FC494AB1C29CBA40010975C /* NimbleEnvironment.swift in Sources */, @@ -2136,6 +2145,7 @@ D95F897E267EA20A004B1B4D /* BeWithin.swift in Sources */, D95F8928267EA1CA004B1B4D /* NMBExceptionCapture.m in Sources */, D95F8981267EA20A004B1B4D /* BeginWith.swift in Sources */, + C57622502A61D3AE00BD6A8C /* Equal+TupleArray.swift in Sources */, D95F8965267EA20A004B1B4D /* BeLogical.swift in Sources */, D95F895D267EA205004B1B4D /* Expectation.swift in Sources */, D95F8959267EA205004B1B4D /* NMBExpectation.swift in Sources */, diff --git a/Sources/Nimble/Matchers/Equal+TupleArray.swift b/Sources/Nimble/Matchers/Equal+TupleArray.swift new file mode 100644 index 000000000..20b826505 --- /dev/null +++ b/Sources/Nimble/Matchers/Equal+TupleArray.swift @@ -0,0 +1,204 @@ +// swiftlint:disable large_tuple vertical_whitespace + +// MARK: Tuple2 Array + +/// A Nimble matcher that succeeds when the actual array of tuples is equal to the expected array of tuples. +/// Values can support equal by supporting the Equatable protocol. +public func equal( + _ expectedValue: [(T1, T2)]? +) -> Predicate<[(T1, T2)]> { + equalTupleArray(expectedValue, by: ==) +} + +public func ==( + lhs: SyncExpectation<[(T1, T2)]>, + rhs: [(T1, T2)]? +) { + lhs.to(equal(rhs)) +} + +public func ==( + lhs: AsyncExpectation<[(T1, T2)]>, + rhs: [(T1, T2)]? +) async { + await lhs.to(equal(rhs)) +} + +public func !=( + lhs: SyncExpectation<[(T1, T2)]>, + rhs: [(T1, T2)]? +) { + lhs.toNot(equal(rhs)) +} + +public func !=( + lhs: AsyncExpectation<[(T1, T2)]>, + rhs: [(T1, T2)]? +) async { + await lhs.toNot(equal(rhs)) +} + +// MARK: Tuple3 Array + +/// A Nimble matcher that succeeds when the actual array of tuples is equal to the expected array of tuples. +/// Values can support equal by supporting the Equatable protocol. +public func equal( + _ expectedValue: [(T1, T2, T3)]? +) -> Predicate<[(T1, T2, T3)]> { + equalTupleArray(expectedValue, by: ==) +} + +public func ==( + lhs: SyncExpectation<[(T1, T2, T3)]>, + rhs: [(T1, T2, T3)]? +) { + lhs.to(equal(rhs)) +} + +public func ==( + lhs: AsyncExpectation<[(T1, T2, T3)]>, + rhs: [(T1, T2, T3)]? +) async { + await lhs.to(equal(rhs)) +} + +public func !=( + lhs: SyncExpectation<[(T1, T2, T3)]>, + rhs: [(T1, T2, T3)]? +) { + lhs.toNot(equal(rhs)) +} + +public func !=( + lhs: AsyncExpectation<[(T1, T2, T3)]>, + rhs: [(T1, T2, T3)]? +) async { + await lhs.toNot(equal(rhs)) +} + +// MARK: Tuple4 Array + +/// A Nimble matcher that succeeds when the actual array of tuples is equal to the expected array of tuples. +/// Values can support equal by supporting the Equatable protocol. +public func equal( + _ expectedValue: [(T1, T2, T3, T4)]? +) -> Predicate<[(T1, T2, T3, T4)]> { + equalTupleArray(expectedValue, by: ==) +} + +public func ==( + lhs: SyncExpectation<[(T1, T2, T3, T4)]>, + rhs: [(T1, T2, T3, T4)]? +) { + lhs.to(equal(rhs)) +} + +public func ==( + lhs: AsyncExpectation<[(T1, T2, T3, T4)]>, + rhs: [(T1, T2, T3, T4)]? +) async { + await lhs.to(equal(rhs)) +} + +public func !=( + lhs: SyncExpectation<[(T1, T2, T3, T4)]>, + rhs: [(T1, T2, T3, T4)]? +) { + lhs.toNot(equal(rhs)) +} + +public func !=( + lhs: AsyncExpectation<[(T1, T2, T3, T4)]>, + rhs: [(T1, T2, T3, T4)]? +) async { + await lhs.toNot(equal(rhs)) +} + +// MARK: Tuple5 Array + +/// A Nimble matcher that succeeds when the actual array of tuples is equal to the expected array of tuples. +/// Values can support equal by supporting the Equatable protocol. +public func equal( + _ expectedValue: [(T1, T2, T3, T4, T5)]? +) -> Predicate<[(T1, T2, T3, T4, T5)]> { + equalTupleArray(expectedValue, by: ==) +} + +public func ==( + lhs: SyncExpectation<[(T1, T2, T3, T4, T5)]>, + rhs: [(T1, T2, T3, T4, T5)]? +) { + lhs.to(equal(rhs)) +} + +public func ==( + lhs: AsyncExpectation<[(T1, T2, T3, T4, T5)]>, + rhs: [(T1, T2, T3, T4, T5)]? +) async { + await lhs.to(equal(rhs)) +} + +public func !=( + lhs: SyncExpectation<[(T1, T2, T3, T4, T5)]>, + rhs: [(T1, T2, T3, T4, T5)]? +) { + lhs.toNot(equal(rhs)) +} + +public func !=( + lhs: AsyncExpectation<[(T1, T2, T3, T4, T5)]>, + rhs: [(T1, T2, T3, T4, T5)]? +) async { + await lhs.toNot(equal(rhs)) +} + +// MARK: Tuple6 Array + +/// A Nimble matcher that succeeds when the actual array of tuples is equal to the expected array of tuples. +/// Values can support equal by supporting the Equatable protocol. +public func equal( + _ expectedValue: [(T1, T2, T3, T4, T5, T6)]? +) -> Predicate<[(T1, T2, T3, T4, T5, T6)]> { + equalTupleArray(expectedValue, by: ==) +} + +public func ==( + lhs: SyncExpectation<[(T1, T2, T3, T4, T5, T6)]>, + rhs: [(T1, T2, T3, T4, T5, T6)]? +) { + lhs.to(equal(rhs)) +} + +public func ==( + lhs: AsyncExpectation<[(T1, T2, T3, T4, T5, T6)]>, + rhs: [(T1, T2, T3, T4, T5, T6)]? +) async { + await lhs.to(equal(rhs)) +} + +public func !=( + lhs: SyncExpectation<[(T1, T2, T3, T4, T5, T6)]>, + rhs: [(T1, T2, T3, T4, T5, T6)]? +) { + lhs.toNot(equal(rhs)) +} + +public func !=( + lhs: AsyncExpectation<[(T1, T2, T3, T4, T5, T6)]>, + rhs: [(T1, T2, T3, T4, T5, T6)]? +) async { + await lhs.toNot(equal(rhs)) +} + +// swiftlint:enable large_tuple vertical_whitespace + +// MARK: Implementation Helpers + +private func equalTupleArray( + _ expectedValue: [(Tuple)]?, + by areTuplesEquivalent: @escaping (Tuple, Tuple) -> Bool +) -> Predicate<[Tuple]> { + equal(expectedValue) { + $0.elementsEqual($1, by: areTuplesEquivalent) + } +} diff --git a/Tests/NimbleTests/Matchers/EqualTest.swift b/Tests/NimbleTests/Matchers/EqualTest.swift index 82569b7e3..72832aef5 100644 --- a/Tests/NimbleTests/Matchers/EqualTest.swift +++ b/Tests/NimbleTests/Matchers/EqualTest.swift @@ -296,6 +296,163 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length expect((1, "2", 3, four: "4", 5, "6")) == (1, "2", 3, "4", five: 5, "6") } + func testTuple2Array() async { + typealias OriginalArray = [(Int, String)] + typealias ExpectedArray = [(Int, named: String)] + let originalArray: OriginalArray = [(0, "0"), (1, "1"), (2, "2")] + let expectedArray: ExpectedArray = [(0, named: "0"), (1, named: "1"), (2, named: "2")] + expect(OriginalArray()).to(equal([])) + expect(OriginalArray()) == [] + expect(OriginalArray()).toNot(equal(expectedArray)) + expect(OriginalArray()) != expectedArray + expect(originalArray).to(equal(expectedArray)) + expect(originalArray).toNot(equal(expectedArray.reversed())) + expect(originalArray).toNot(equal([])) + expect(originalArray) == expectedArray + expect(originalArray) != expectedArray.reversed() + expect(originalArray) != [] + + let originalArrayAsync = { () async in originalArray } + await expect(originalArrayAsync).toEventually(equal(expectedArray)) + await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) + await expect(originalArrayAsync).toEventuallyNot(equal([])) + await expect(originalArrayAsync) == expectedArray + await expect(originalArrayAsync) != expectedArray.reversed() + await expect(originalArrayAsync) != [] + } + + func testTuple3Array() async { + typealias OriginalArray = [(Int, String, Double)] + typealias ExpectedArray = [(Int, named: String, Double)] + let originalArray: OriginalArray = [ + (0, "0", 0.0), + (1, "1", 1.0), + (2, "2", 2.0) + ] + let expectedArray: ExpectedArray = [ + (0, named: "0", 0.0), + (1, named: "1", 1.0), + (2, named: "2", 2.0) + ] + expect(OriginalArray()).to(equal([])) + expect(OriginalArray()) == [] + expect(OriginalArray()).toNot(equal(expectedArray)) + expect(OriginalArray()) != expectedArray + expect(originalArray).to(equal(expectedArray)) + expect(originalArray).toNot(equal(expectedArray.reversed())) + expect(originalArray).toNot(equal([])) + expect(originalArray) == expectedArray + expect(originalArray) != expectedArray.reversed() + expect(originalArray) != [] + + let originalArrayAsync = { () async in originalArray } + await expect(originalArrayAsync).toEventually(equal(expectedArray)) + await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) + await expect(originalArrayAsync).toEventuallyNot(equal([])) + await expect(originalArrayAsync) == expectedArray + await expect(originalArrayAsync) != expectedArray.reversed() + await expect(originalArrayAsync) != [] + } + + func testTuple4Array() async { + typealias OriginalArray = [(Int, String, Double, Int)] + typealias ExpectedArray = [(Int, named: String, Double, negative: Int)] + let originalArray: OriginalArray = [ + (0, "0", 0.0, -0), + (1, "1", 1.0, -1), + (2, "2", 2.0, -2) + ] + let expectedArray: ExpectedArray = [ + (0, named: "0", 0.0, negative: -0), + (1, named: "1", 1.0, negative: -1), + (2, named: "2", 2.0, negative: -2) + ] + expect(OriginalArray()).to(equal([])) + expect(OriginalArray()) == [] + expect(OriginalArray()).toNot(equal(expectedArray)) + expect(OriginalArray()) != expectedArray + expect(originalArray).to(equal(expectedArray)) + expect(originalArray).toNot(equal(expectedArray.reversed())) + expect(originalArray).toNot(equal([])) + expect(originalArray) == expectedArray + expect(originalArray) != expectedArray.reversed() + expect(originalArray) != [] + + let originalArrayAsync = { () async in originalArray } + await expect(originalArrayAsync).toEventually(equal(expectedArray)) + await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) + await expect(originalArrayAsync).toEventuallyNot(equal([])) + await expect(originalArrayAsync) == expectedArray + await expect(originalArrayAsync) != expectedArray.reversed() + await expect(originalArrayAsync) != [] + } + + func testTuple5Array() async { + typealias OriginalArray = [(Int, String, Double, Int, String)] + typealias ExpectedArray = [(Int, named: String, Double, negative: Int, String)] + let originalArray: OriginalArray = [ + (0, "0", 0.0, -0, "-0"), + (1, "1", 1.0, -1, "-1"), + (2, "2", 2.0, -2, "-2") + ] + let expectedArray: ExpectedArray = [ + (0, named: "0", 0.0, negative: -0, "-0"), + (1, named: "1", 1.0, negative: -1, "-1"), + (2, named: "2", 2.0, negative: -2, "-2") + ] + expect(OriginalArray()).to(equal([])) + expect(OriginalArray()) == [] + expect(OriginalArray()).toNot(equal(expectedArray)) + expect(OriginalArray()) != expectedArray + expect(originalArray).to(equal(expectedArray)) + expect(originalArray).toNot(equal(expectedArray.reversed())) + expect(originalArray).toNot(equal([])) + expect(originalArray) == expectedArray + expect(originalArray) != expectedArray.reversed() + expect(originalArray) != [] + + let originalArrayAsync = { () async in originalArray } + await expect(originalArrayAsync).toEventually(equal(expectedArray)) + await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) + await expect(originalArrayAsync).toEventuallyNot(equal([])) + await expect(originalArrayAsync) == expectedArray + await expect(originalArrayAsync) != expectedArray.reversed() + await expect(originalArrayAsync) != [] + } + + func testTuple6Array() async { + typealias OriginalArray = [(Int, String, Double, Int, String, Double)] + typealias ExpectedArray = [(Int, named: String, Double, negative: Int, String, Double)] + let originalArray: OriginalArray = [ + (0, "0", 0.0, -0, "-0", -0.0), + (1, "1", 1.0, -1, "-1", -1.0), + (2, "2", 2.0, -2, "-2", -2.0) + ] + let expectedArray: ExpectedArray = [ + (0, named: "0", 0.0, negative: -0, "-0", -0.0), + (1, named: "1", 1.0, negative: -1, "-1", -1.0), + (2, named: "2", 2.0, negative: -2, "-2", -2.0) + ] + expect(OriginalArray()).to(equal([])) + expect(OriginalArray()) == [] + expect(OriginalArray()).toNot(equal(expectedArray)) + expect(OriginalArray()) != expectedArray + expect(originalArray).to(equal(expectedArray)) + expect(originalArray).toNot(equal(expectedArray.reversed())) + expect(originalArray).toNot(equal([])) + expect(originalArray) == expectedArray + expect(originalArray) != expectedArray.reversed() + expect(originalArray) != [] + + let originalArrayAsync = { () async in originalArray } + await expect(originalArrayAsync).toEventually(equal(expectedArray)) + await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) + await expect(originalArrayAsync).toEventuallyNot(equal([])) + await expect(originalArrayAsync) == expectedArray + await expect(originalArrayAsync) != expectedArray.reversed() + await expect(originalArrayAsync) != [] + } + // see: https://github.com/Quick/Nimble/issues/867 and https://github.com/Quick/Nimble/issues/937 func testImplicitMemberSyntax() { let xxx = Xxx(value: 123)