From 9b1176e30d583bbfb3c9ce4eb258c633539d76fd Mon Sep 17 00:00:00 2001 From: Skoti <1700160+Skoti@users.noreply.github.com> Date: Sat, 28 Dec 2019 14:45:50 +0100 Subject: [PATCH] adding BeginWithPrefix matcher for sequence prefixes --- Nimble.xcodeproj/project.pbxproj | 30 +++++++-- Sources/Nimble/Matchers/BeginWithPrefix.swift | 39 +++++++++++ .../Matchers/BeginWithPrefixTest.swift | 64 +++++++++++++++++++ 3 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 Sources/Nimble/Matchers/BeginWithPrefix.swift create mode 100644 Tests/NimbleTests/Matchers/BeginWithPrefixTest.swift diff --git a/Nimble.xcodeproj/project.pbxproj b/Nimble.xcodeproj/project.pbxproj index fc2f9a593..3468eb7cf 100644 --- a/Nimble.xcodeproj/project.pbxproj +++ b/Nimble.xcodeproj/project.pbxproj @@ -290,6 +290,12 @@ 472FD13B1B9E0CFE00C7B8DA /* HaveCountTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472FD1361B9E094B00C7B8DA /* HaveCountTest.swift */; }; 4793854D1BA0BB2500296F85 /* ObjCHaveCountTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4793854C1BA0BB2500296F85 /* ObjCHaveCountTest.m */; }; 4793854E1BA0BB2500296F85 /* ObjCHaveCountTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4793854C1BA0BB2500296F85 /* ObjCHaveCountTest.m */; }; + 62FB326223B78BF90047BED9 /* BeginWithPrefix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326123B78BF90047BED9 /* BeginWithPrefix.swift */; }; + 62FB326323B78BF90047BED9 /* BeginWithPrefix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326123B78BF90047BED9 /* BeginWithPrefix.swift */; }; + 62FB326423B78BF90047BED9 /* BeginWithPrefix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326123B78BF90047BED9 /* BeginWithPrefix.swift */; }; + 62FB326923B78D4F0047BED9 /* BeginWithPrefixTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326523B78D4A0047BED9 /* BeginWithPrefixTest.swift */; }; + 62FB326A23B78D500047BED9 /* BeginWithPrefixTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326523B78D4A0047BED9 /* BeginWithPrefixTest.swift */; }; + 62FB326B23B78D510047BED9 /* BeginWithPrefixTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326523B78D4A0047BED9 /* BeginWithPrefixTest.swift */; }; 6CAEDD0A1CAEA86F003F1584 /* LinuxSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CAEDD091CAEA86F003F1584 /* LinuxSupport.swift */; }; 6CAEDD0B1CAEA86F003F1584 /* LinuxSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CAEDD091CAEA86F003F1584 /* LinuxSupport.swift */; }; 6CAEDD0C1CAEA86F003F1584 /* LinuxSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CAEDD091CAEA86F003F1584 /* LinuxSupport.swift */; }; @@ -597,6 +603,8 @@ 472FD1341B9E085700C7B8DA /* HaveCount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HaveCount.swift; sourceTree = ""; }; 472FD1361B9E094B00C7B8DA /* HaveCountTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HaveCountTest.swift; sourceTree = ""; }; 4793854C1BA0BB2500296F85 /* ObjCHaveCountTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCHaveCountTest.m; sourceTree = ""; }; + 62FB326123B78BF90047BED9 /* BeginWithPrefix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeginWithPrefix.swift; sourceTree = ""; }; + 62FB326523B78D4A0047BED9 /* BeginWithPrefixTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeginWithPrefixTest.swift; sourceTree = ""; }; 6CAEDD091CAEA86F003F1584 /* LinuxSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinuxSupport.swift; sourceTree = ""; }; 7A0A26221E7F52360092A34E /* ToSucceed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToSucceed.swift; sourceTree = ""; }; 7A6AB2C11E7F547E00A2F694 /* ToSucceedTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToSucceedTest.swift; sourceTree = ""; }; @@ -803,6 +811,7 @@ 1F925EE8195C124400ED456B /* BeAnInstanceOfTest.swift */, 1F925EF5195C147800ED456B /* BeCloseToTest.swift */, 1F299EAA19627B2D002641AF /* BeEmptyTest.swift */, + 62FB326523B78D4A0047BED9 /* BeginWithPrefixTest.swift */, 1F925EFB195C186800ED456B /* BeginWithTest.swift */, 1F925F10195C190B00ED456B /* BeGreaterThanOrEqualToTest.swift */, 1F925F07195C18CF00ED456B /* BeGreaterThanTest.swift */, @@ -815,18 +824,18 @@ 1F91DD2C1C74BF36002C309F /* BeVoidTest.swift */, 7B13BA091DD360DE00C9098C /* ContainElementSatisfyingTest.swift */, 1F925F01195C189500ED456B /* ContainTest.swift */, + B20058C420E92CE400C1264D /* ElementsEqualTest.swift */, 1F925EFE195C187600ED456B /* EndWithTest.swift */, 1F925F04195C18B700ED456B /* EqualTest.swift */, - B20058C420E92CE400C1264D /* ElementsEqualTest.swift */, 472FD1361B9E094B00C7B8DA /* HaveCountTest.swift */, + AE7ADE481C80C00D00B94CD3 /* MatchErrorTest.swift */, DDB4D5EF19FE442800E9D9FE /* MatchTest.swift */, + 1FCF914E1C61C85A00B15DCB /* PostNotificationTest.swift */, 1F925EEB195C12C800ED456B /* RaisesExceptionTest.swift */, - 29EA59621B551ED2002D767E /* ThrowErrorTest.swift */, - 7B5358B91C3846C900A23FAA /* SatisfyAnyOfTest.swift */, A8A3B6E920712FC100E25A08 /* SatisfyAllOfTest.swift */, - 1FCF914E1C61C85A00B15DCB /* PostNotificationTest.swift */, - AE7ADE481C80C00D00B94CD3 /* MatchErrorTest.swift */, + 7B5358B91C3846C900A23FAA /* SatisfyAnyOfTest.swift */, 1F4BB8B31DACA0D00048464B /* ThrowAssertionTest.swift */, + 29EA59621B551ED2002D767E /* ThrowErrorTest.swift */, 7A6AB2C11E7F547E00A2F694 /* ToSucceedTest.swift */, ); path = Matchers; @@ -857,6 +866,7 @@ 1FD8CD0F1968AB07008ED995 /* BeCloseTo.swift */, 1FD8CD101968AB07008ED995 /* BeEmpty.swift */, 1FD8CD111968AB07008ED995 /* BeginWith.swift */, + 62FB326123B78BF90047BED9 /* BeginWithPrefix.swift */, 1FD8CD121968AB07008ED995 /* BeGreaterThan.swift */, 1FD8CD131968AB07008ED995 /* BeGreaterThanOrEqualTo.swift */, 1FD8CD141968AB07008ED995 /* BeIdenticalTo.swift */, @@ -867,9 +877,9 @@ 1F91DD301C74BF61002C309F /* BeVoid.swift */, 1FD8CD1A1968AB07008ED995 /* Contain.swift */, 7B13BA051DD360AA00C9098C /* ContainElementSatisfying.swift */, + B20058C020E92C7500C1264D /* ElementsEqual.swift */, 1FD8CD1B1968AB07008ED995 /* EndWith.swift */, 1FD8CD1C1968AB07008ED995 /* Equal.swift */, - B20058C020E92C7500C1264D /* ElementsEqual.swift */, 472FD1341B9E085700C7B8DA /* HaveCount.swift */, DDB4D5EC19FE43C200E9D9FE /* Match.swift */, 1F1871E51CA89FCD00A34BF2 /* MatcherFunc.swift */, @@ -878,8 +888,8 @@ 1FCF91521C61C8A400B15DCB /* PostNotification.swift */, 1FA0C3FE1E30B14500623165 /* Predicate.swift */, 1FD8CD1E1968AB07008ED995 /* RaisesException.swift */, - 7B5358BD1C38479700A23FAA /* SatisfyAnyOf.swift */, A8F6B5BC2070186D00FCB5ED /* SatisfyAllOf.swift */, + 7B5358BD1C38479700A23FAA /* SatisfyAnyOf.swift */, 964CFEFC1C4FF48900513336 /* ThrowAssertion.swift */, 29EA59651B551EE6002D767E /* ThrowError.swift */, 7A0A26221E7F52360092A34E /* ToSucceed.swift */, @@ -1343,6 +1353,7 @@ 1FCF91531C61C8A400B15DCB /* PostNotification.swift in Sources */, 1FD8CD2E1968AB07008ED995 /* AssertionRecorder.swift in Sources */, 29EA59661B551EE6002D767E /* ThrowError.swift in Sources */, + 62FB326323B78BF90047BED9 /* BeginWithPrefix.swift in Sources */, 1FD8CD5A1968AB07008ED995 /* Equal.swift in Sources */, 1FD8CD4C1968AB07008ED995 /* BeLessThan.swift in Sources */, 1F1871CC1CA89EDB00A34BF2 /* NMBObjCMatcher.swift in Sources */, @@ -1389,6 +1400,7 @@ files = ( 1F4A569A1A3B3539009E1637 /* ObjCEqualTest.m in Sources */, 1F925EEC195C12C800ED456B /* RaisesExceptionTest.swift in Sources */, + 62FB326A23B78D500047BED9 /* BeginWithPrefixTest.swift in Sources */, 1F925EFF195C187600ED456B /* EndWithTest.swift in Sources */, 1F1B5AD41963E13900CA8BF9 /* BeAKindOfTest.swift in Sources */, 1F925F0E195C18F500ED456B /* BeLessThanOrEqualToTest.swift in Sources */, @@ -1466,6 +1478,7 @@ 1F1871D71CA89EEF00A34BF2 /* NMBExceptionCapture.m in Sources */, 1F5DF16E1BDCA0F500C3A531 /* NimbleXCTestHandler.swift in Sources */, 7A6AB2C61E7F628A00A2F694 /* ToSucceed.swift in Sources */, + 62FB326423B78BF90047BED9 /* BeginWithPrefix.swift in Sources */, 1F5DF1751BDCA0F500C3A531 /* FailureMessage.swift in Sources */, A8A3B6EC2071487F00E25A08 /* SatisfyAllOf.swift in Sources */, CD3D9A79232647BC00802581 /* CwlCatchBadInstructionPosix.swift in Sources */, @@ -1527,6 +1540,7 @@ files = ( CD79C9AD1D2CC848004B6F9A /* ObjCBeTrueTest.m in Sources */, CD79C9B41D2CC848004B6F9A /* ObjCRaiseExceptionTest.m in Sources */, + 62FB326923B78D4F0047BED9 /* BeginWithPrefixTest.swift in Sources */, 1F5DF1A31BDCA10200C3A531 /* BeLogicalTest.swift in Sources */, 1F5DF1951BDCA10200C3A531 /* utils.swift in Sources */, CD79C9B01D2CC848004B6F9A /* ObjCEndWithTest.m in Sources */, @@ -1624,6 +1638,7 @@ 1FCF91541C61C8A400B15DCB /* PostNotification.swift in Sources */, DDB4D5EE19FE43C200E9D9FE /* Match.swift in Sources */, 29EA59671B551EE6002D767E /* ThrowError.swift in Sources */, + 62FB326223B78BF90047BED9 /* BeginWithPrefix.swift in Sources */, 1FD8CD5B1968AB07008ED995 /* Equal.swift in Sources */, 1FD8CD4D1968AB07008ED995 /* BeLessThan.swift in Sources */, 1FD8CD471968AB07008ED995 /* BeGreaterThan.swift in Sources */, @@ -1670,6 +1685,7 @@ files = ( 1F4A569B1A3B3539009E1637 /* ObjCEqualTest.m in Sources */, 1F925EED195C12C800ED456B /* RaisesExceptionTest.swift in Sources */, + 62FB326B23B78D510047BED9 /* BeginWithPrefixTest.swift in Sources */, 1F925F00195C187600ED456B /* EndWithTest.swift in Sources */, 1F1B5AD51963E13900CA8BF9 /* BeAKindOfTest.swift in Sources */, 1F925F0F195C18F500ED456B /* BeLessThanOrEqualToTest.swift in Sources */, diff --git a/Sources/Nimble/Matchers/BeginWithPrefix.swift b/Sources/Nimble/Matchers/BeginWithPrefix.swift new file mode 100644 index 000000000..c3a79b18d --- /dev/null +++ b/Sources/Nimble/Matchers/BeginWithPrefix.swift @@ -0,0 +1,39 @@ +/// A Nimble matcher that succeeds when the exepected sequence is a prefix of the actual sequence. +/// +/// This is a matcher abstraction for https://developer.apple.com/documentation/swift/sequence/2854218-starts +public func beginWith(prefix expectedPrefix: Seq2?) + -> Predicate where Seq1.Element: Equatable, Seq1.Element == Seq2.Element { + return Predicate.define("begin with <\(stringify(expectedPrefix))>") { (actualExpression, msg) in + let actualPrefix = try actualExpression.evaluate() + switch (expectedPrefix, actualPrefix) { + case (nil, _?): + return PredicateResult(status: .fail, message: msg.appendedBeNilHint()) + case (nil, nil), (_, nil): + return PredicateResult(status: .fail, message: msg) + case (let expected?, let actual?): + let matches = actual.starts(with: expected) + return PredicateResult(bool: matches, message: msg) + } + } +} + +/// A Nimble matcher that succeeds when the expected sequence is the prefix of the actual sequence, using the given predicate as the equivalence test. +/// +/// This is a matcher abstraction for https://developer.apple.com/documentation/swift/sequence/2996828-starts +public func beginWith( + prefix expectedPrefix: Seq2?, + by areEquivalent: @escaping (Seq1.Element, Seq2.Element) -> Bool +) -> Predicate { + return Predicate.define("begin with <\(stringify(expectedPrefix))>") { (actualExpression, msg) in + let actualPrefix = try actualExpression.evaluate() + switch (expectedPrefix, actualPrefix) { + case (nil, _?): + return PredicateResult(status: .fail, message: msg.appendedBeNilHint()) + case (nil, nil), (_, nil): + return PredicateResult(status: .fail, message: msg) + case (let expected?, let actual?): + let matches = actual.starts(with: expected, by: areEquivalent) + return PredicateResult(bool: matches, message: msg) + } + } +} diff --git a/Tests/NimbleTests/Matchers/BeginWithPrefixTest.swift b/Tests/NimbleTests/Matchers/BeginWithPrefixTest.swift new file mode 100644 index 000000000..e6497907b --- /dev/null +++ b/Tests/NimbleTests/Matchers/BeginWithPrefixTest.swift @@ -0,0 +1,64 @@ +import Foundation +import Nimble +import XCTest + +final class BeginWithPrefixTest: XCTestCase { + + func testBeginWithSequencePrefix() { + failsWithErrorMessageForNil("expected to begin with , got ") { + expect(nil as [Int]?).to(beginWith(prefix: nil as [Int]?)) + } + + failsWithErrorMessageForNil("expected to begin with <[1, 2]>, got ") { + expect(nil as [Int]?).to(beginWith(prefix: [1, 2])) + } + + failsWithErrorMessageForNil("expected to begin with , got <[1, 2]>") { + expect([1, 2]).to(beginWith(prefix: nil as [Int]?)) + } + + let sequence = [1, 2, 3] + expect(sequence).toNot(beginWith(prefix: [1, 2, 3, 4])) + expect(sequence).toNot(beginWith(prefix: [2, 3])) + + expect(sequence).to(beginWith(prefix: [1, 2, 3])) + expect(sequence).to(beginWith(prefix: [1, 2])) + expect(sequence).to(beginWith(prefix: [])) + + expect([]).toNot(beginWith(prefix: [1])) + expect([]).to(beginWith(prefix: [] as [Int])) + } + + func testBeginWithSequencePrefixUsingPredicateClosure() { + failsWithErrorMessageForNil("expected to begin with , got ") { + expect(nil as [Int]?).to(beginWith(prefix: nil as [Int]?, by: { $0 == $1 })) + } + + failsWithErrorMessageForNil("expected to begin with <[1, 2]>, got ") { + expect(nil as [Int]?).to(beginWith(prefix: [1, 2], by: { $0 == $1 })) + } + + failsWithErrorMessageForNil("expected to begin with , got <[1, 2]>") { + expect([1, 2]).to(beginWith(prefix: nil as [Int]?, by: { $0 == $1 })) + } + + let sequence = [1, 2, 3] + expect(sequence).toNot(beginWith(prefix: [1, 2, 3, 4], by: { $0 == $1 })) + expect(sequence).toNot(beginWith(prefix: [2, 3], by: { $0 == $1 })) + + expect(sequence).to(beginWith(prefix: [1, 2, 3], by: { $0 == $1 })) + expect(sequence).to(beginWith(prefix: [1, 2], by: { $0 == $1 })) + expect(sequence).to(beginWith(prefix: [], by: { $0 == $1 })) + + expect([]).toNot(beginWith(prefix: [1], by: { $0 == $1 })) + expect([]).to(beginWith(prefix: [] as [Int], by: { $0 == $1 })) + } + + func testBeginWithSequencePrefixWithDifferentSequenceTypes() { + expect(1...3).to(beginWith(prefix: [1, 2, 3])) + expect(1...3).toNot(beginWith(prefix: [1, 2, 3, 4, 5])) + + expect(1...3).to(beginWith(prefix: [1, 2, 3], by: { $0 == $1 })) + expect(1...3).toNot(beginWith(prefix: [1, 2, 3, 4, 5], by: { $0 == $1 })) + } +}