Skip to content

Commit

Permalink
adding StartWith matcher for sequence prefixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Skoti committed May 20, 2020
1 parent ca652f9 commit 6e73399
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 7 deletions.
30 changes: 23 additions & 7 deletions Nimble.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -597,6 +603,8 @@
472FD1341B9E085700C7B8DA /* HaveCount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HaveCount.swift; sourceTree = "<group>"; };
472FD1361B9E094B00C7B8DA /* HaveCountTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HaveCountTest.swift; sourceTree = "<group>"; };
4793854C1BA0BB2500296F85 /* ObjCHaveCountTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCHaveCountTest.m; sourceTree = "<group>"; };
62FB326123B78BF90047BED9 /* BeginWithPrefix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeginWithPrefix.swift; sourceTree = "<group>"; };
62FB326523B78D4A0047BED9 /* BeginWithPrefixTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeginWithPrefixTest.swift; sourceTree = "<group>"; };
6CAEDD091CAEA86F003F1584 /* LinuxSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinuxSupport.swift; sourceTree = "<group>"; };
7A0A26221E7F52360092A34E /* ToSucceed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToSucceed.swift; sourceTree = "<group>"; };
7A6AB2C11E7F547E00A2F694 /* ToSucceedTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToSucceedTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 */,
Expand All @@ -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;
Expand Down Expand Up @@ -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 */,
Expand All @@ -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 */,
Expand All @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
41 changes: 41 additions & 0 deletions Sources/Nimble/Matchers/BeginWithPrefix.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Foundation

/// 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<Seq1: Sequence, Seq2: Sequence>(prefix expectedPrefix: Seq2?)
-> Predicate<Seq1> where Seq1.Element: Equatable, Seq1.Element == Seq2.Element {
return Predicate.define("beginWith <\(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<Seq1: Sequence, Seq2: Sequence>(
prefix expectedPrefix: Seq2?,
by areEquivalent: @escaping (Seq1.Element, Seq2.Element) -> Bool
) -> Predicate<Seq1> {
return Predicate.define("beginWith <\(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)
}
}
}
64 changes: 64 additions & 0 deletions Tests/NimbleTests/Matchers/BeginWithPrefixTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Foundation
import Nimble
import XCTest

final class BeginWithPrefixTest: XCTestCase {

func testBeginWithSequencePrefix() {
failsWithErrorMessageForNil("expected to beginWith <nil>, got <nil>") {
expect(nil as [Int]?).to(beginWith(prefix: nil as [Int]?))
}

failsWithErrorMessageForNil("expected to beginWith <[1, 2]>, got <nil>") {
expect(nil as [Int]?).to(beginWith(prefix: [1, 2]))
}

failsWithErrorMessageForNil("expected to beginWith <nil>, 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 beginWith <nil>, got <nil>") {
expect(nil as [Int]?).to(beginWith(prefix: nil as [Int]?, by: { $0 == $1 }))
}

failsWithErrorMessageForNil("expected to beginWith <[1, 2]>, got <nil>") {
expect(nil as [Int]?).to(beginWith(prefix: [1, 2], by: { $0 == $1 }))
}

failsWithErrorMessageForNil("expected to beginWith <nil>, 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 }))
}
}

0 comments on commit 6e73399

Please sign in to comment.