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 Dec 28, 2019
1 parent 795fb77 commit e10b32a
Show file tree
Hide file tree
Showing 3 changed files with 129 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 @@ -287,6 +287,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 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326123B78BF90047BED9 /* StartWith.swift */; };
62FB326323B78BF90047BED9 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326123B78BF90047BED9 /* StartWith.swift */; };
62FB326423B78BF90047BED9 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326123B78BF90047BED9 /* StartWith.swift */; };
62FB326923B78D4F0047BED9 /* StartWithTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326523B78D4A0047BED9 /* StartWithTest.swift */; };
62FB326A23B78D500047BED9 /* StartWithTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326523B78D4A0047BED9 /* StartWithTest.swift */; };
62FB326B23B78D510047BED9 /* StartWithTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB326523B78D4A0047BED9 /* StartWithTest.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 @@ -589,6 +595,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 /* StartWith.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartWith.swift; sourceTree = "<group>"; };
62FB326523B78D4A0047BED9 /* StartWithTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartWithTest.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,18 +811,19 @@
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 */,
62FB326523B78D4A0047BED9 /* StartWithTest.swift */,
1F4BB8B31DACA0D00048464B /* ThrowAssertionTest.swift */,
29EA59621B551ED2002D767E /* ThrowErrorTest.swift */,
7A6AB2C11E7F547E00A2F694 /* ToSucceedTest.swift */,
);
path = Matchers;
Expand Down Expand Up @@ -855,9 +864,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 @@ -866,8 +875,9 @@
1FCF91521C61C8A400B15DCB /* PostNotification.swift */,
1FA0C3FE1E30B14500623165 /* Predicate.swift */,
1FD8CD1E1968AB07008ED995 /* RaisesException.swift */,
7B5358BD1C38479700A23FAA /* SatisfyAnyOf.swift */,
A8F6B5BC2070186D00FCB5ED /* SatisfyAllOf.swift */,
7B5358BD1C38479700A23FAA /* SatisfyAnyOf.swift */,
62FB326123B78BF90047BED9 /* StartWith.swift */,
964CFEFC1C4FF48900513336 /* ThrowAssertion.swift */,
29EA59651B551EE6002D767E /* ThrowError.swift */,
7A0A26221E7F52360092A34E /* ToSucceed.swift */,
Expand Down Expand Up @@ -1331,6 +1341,7 @@
1FCF91531C61C8A400B15DCB /* PostNotification.swift in Sources */,
1FD8CD2E1968AB07008ED995 /* AssertionRecorder.swift in Sources */,
29EA59661B551EE6002D767E /* ThrowError.swift in Sources */,
62FB326323B78BF90047BED9 /* StartWith.swift in Sources */,
1FD8CD5A1968AB07008ED995 /* Equal.swift in Sources */,
1FD8CD4C1968AB07008ED995 /* BeLessThan.swift in Sources */,
1F1871CC1CA89EDB00A34BF2 /* NMBObjCMatcher.swift in Sources */,
Expand Down Expand Up @@ -1377,6 +1388,7 @@
files = (
1F4A569A1A3B3539009E1637 /* ObjCEqualTest.m in Sources */,
1F925EEC195C12C800ED456B /* RaisesExceptionTest.swift in Sources */,
62FB326A23B78D500047BED9 /* StartWithTest.swift in Sources */,
1F925EFF195C187600ED456B /* EndWithTest.swift in Sources */,
1F1B5AD41963E13900CA8BF9 /* BeAKindOfTest.swift in Sources */,
1F925F0E195C18F500ED456B /* BeLessThanOrEqualToTest.swift in Sources */,
Expand Down Expand Up @@ -1452,6 +1464,7 @@
1F1871D71CA89EEF00A34BF2 /* NMBExceptionCapture.m in Sources */,
1F5DF16E1BDCA0F500C3A531 /* NimbleXCTestHandler.swift in Sources */,
7A6AB2C61E7F628A00A2F694 /* ToSucceed.swift in Sources */,
62FB326423B78BF90047BED9 /* StartWith.swift in Sources */,
1F5DF1751BDCA0F500C3A531 /* FailureMessage.swift in Sources */,
A8A3B6EC2071487F00E25A08 /* SatisfyAllOf.swift in Sources */,
CD3D9A79232647BC00802581 /* CwlCatchBadInstructionPosix.swift in Sources */,
Expand Down Expand Up @@ -1512,6 +1525,7 @@
files = (
CD79C9AD1D2CC848004B6F9A /* ObjCBeTrueTest.m in Sources */,
CD79C9B41D2CC848004B6F9A /* ObjCRaiseExceptionTest.m in Sources */,
62FB326923B78D4F0047BED9 /* StartWithTest.swift in Sources */,
1F5DF1A31BDCA10200C3A531 /* BeLogicalTest.swift in Sources */,
1F5DF1951BDCA10200C3A531 /* utils.swift in Sources */,
CD79C9B01D2CC848004B6F9A /* ObjCEndWithTest.m in Sources */,
Expand Down Expand Up @@ -1606,6 +1620,7 @@
1FCF91541C61C8A400B15DCB /* PostNotification.swift in Sources */,
DDB4D5EE19FE43C200E9D9FE /* Match.swift in Sources */,
29EA59671B551EE6002D767E /* ThrowError.swift in Sources */,
62FB326223B78BF90047BED9 /* StartWith.swift in Sources */,
1FD8CD5B1968AB07008ED995 /* Equal.swift in Sources */,
1FD8CD4D1968AB07008ED995 /* BeLessThan.swift in Sources */,
1FD8CD471968AB07008ED995 /* BeGreaterThan.swift in Sources */,
Expand Down Expand Up @@ -1652,6 +1667,7 @@
files = (
1F4A569B1A3B3539009E1637 /* ObjCEqualTest.m in Sources */,
1F925EED195C12C800ED456B /* RaisesExceptionTest.swift in Sources */,
62FB326B23B78D510047BED9 /* StartWithTest.swift in Sources */,
1F925F00195C187600ED456B /* EndWithTest.swift in Sources */,
1F1B5AD51963E13900CA8BF9 /* BeAKindOfTest.swift in Sources */,
1F925F0F195C18F500ED456B /* BeLessThanOrEqualToTest.swift in Sources */,
Expand Down
42 changes: 42 additions & 0 deletions Sources/Nimble/Matchers/StartWith.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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 startWith<Seq1: Sequence, Seq2: Sequence>(_ expectedInitialValues: Seq2?)
-> Predicate<Seq1> where Seq1.Element: Equatable, Seq1.Element == Seq2.Element
{ //swiftlint:disable:this opening_brace
return Predicate.define("startWith <\(stringify(expectedInitialValues))>") { (actualExpression, msg) in
let actualValues = try actualExpression.evaluate()
switch (expectedInitialValues, actualValues) {
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 startWith<Seq1: Sequence, Seq2: Sequence>(
_ expectedInitialValues: Seq2?,
by areEquivalent: @escaping (Seq1.Element, Seq2.Element) -> Bool
) -> Predicate<Seq1> {
return Predicate.define("startWith <\(stringify(expectedInitialValues))>") { (actualExpression, msg) in
let actualValues = try actualExpression.evaluate()
switch (expectedInitialValues, actualValues) {
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/StartWithTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Foundation
import Nimble
import XCTest

final class StartWithTest: XCTestCase {

func testStartWithSequencePrefix() {
failsWithErrorMessageForNil("expected to startWith <nil>, got <nil>") {
expect(nil as [Int]?).to(startWith(nil as [Int]?))
}

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

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

let sequence = [1, 2, 3]
expect(sequence).toNot(startWith([1, 2, 3, 4]))
expect(sequence).toNot(startWith([2, 3]))

expect(sequence).to(startWith([1, 2, 3]))
expect(sequence).to(startWith([1, 2]))
expect(sequence).to(startWith([]))

expect([]).toNot(startWith([1]))
expect([]).to(startWith([] as [Int]))
}

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

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

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

let sequence = [1, 2, 3]
expect(sequence).toNot(startWith([1, 2, 3, 4], by: { $0 == $1 }))
expect(sequence).toNot(startWith([2, 3], by: { $0 == $1 }))

expect(sequence).to(startWith([1, 2, 3], by: { $0 == $1 }))
expect(sequence).to(startWith([1, 2], by: { $0 == $1 }))
expect(sequence).to(startWith([], by: { $0 == $1 }))

expect([]).toNot(startWith([1], by: { $0 == $1 }))
expect([]).to(startWith([] as [Int], by: { $0 == $1 }))
}

func testStartWithSequencePrefixWithDifferentSequenceTypes() {
expect(1...3).to(startWith([1, 2, 3]))
expect(1...3).toNot(startWith([1, 2, 3, 4, 5]))

expect(1...3).to(startWith([1, 2, 3], by: { $0 == $1 }))
expect(1...3).toNot(startWith([1, 2, 3, 4, 5], by: { $0 == $1 }))
}
}

0 comments on commit e10b32a

Please sign in to comment.