Skip to content

Commit

Permalink
satisfyAllOf with toEventually should only evaluate the expression on…
Browse files Browse the repository at this point in the history
…ce each poll

Fixes #529
  • Loading branch information
younata committed Apr 4, 2023
1 parent 6643f29 commit f15ece0
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 3 deletions.
9 changes: 9 additions & 0 deletions Sources/Nimble/Expression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,13 @@ public struct Expression<Value> {
isClosure: isClosure
)
}

public func withCaching() -> Expression<Value> {
return Expression(
memoizedExpression: memoizedClosure { try self.evaluate() },
location: self.location,
withoutCaching: false,
isClosure: isClosure
)
}
}
2 changes: 1 addition & 1 deletion Sources/Nimble/Matchers/PostNotification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private func _postNotifications<Out>(
withoutCaching: true
)

assert(pthread_equal(mainThread, pthread_self()) != 0, "Only expecting closure to be evaluated on main thread.")
assert(Thread.isMainThread, "Only expecting closure to be evaluated on main thread.")
if !once {
once = true
_ = try actualExpression.evaluate()
Expand Down
5 changes: 3 additions & 2 deletions Sources/Nimble/Matchers/SatisfyAllOf.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ public func satisfyAllOf<T>(_ predicates: Predicate<T>...) -> Predicate<T> {
/// provided in the array of matchers.
public func satisfyAllOf<T>(_ predicates: [Predicate<T>]) -> Predicate<T> {
return Predicate.define { actualExpression in
let cachedExpression = actualExpression.withCaching()
var postfixMessages = [String]()
var status: PredicateStatus = .matches
for predicate in predicates {
let result = try predicate.satisfies(actualExpression)
let result = try predicate.satisfies(cachedExpression)
if result.status == .fail {
status = .fail
} else if result.status == .doesNotMatch, status != .fail {
Expand All @@ -21,7 +22,7 @@ public func satisfyAllOf<T>(_ predicates: [Predicate<T>]) -> Predicate<T> {
}

var msg: ExpectationMessage
if let actualValue = try actualExpression.evaluate() {
if let actualValue = try cachedExpression.evaluate() {
msg = .expectedCustomValueTo(
"match all of: " + postfixMessages.joined(separator: ", and "),
actual: "\(actualValue)"
Expand Down
12 changes: 12 additions & 0 deletions Tests/NimbleTests/Matchers/SatisfyAllOfTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,16 @@ final class SatisfyAllOfTest: XCTestCase {
expect(false).toNot(beTrue() && beFalse())
expect(true).toNot(beTruthy() && beFalsy())
}

func testSatisfyAllOfCachesExpressionBeforePassingToPredicates() {
// This is not a great example of assertion writing - functions being asserted on in Expressions should not have side effects.
// But we should still handle those cases anyway.
var value: Int = 0
func testFunction() -> Int {
value += 1
return value
}

expect(testFunction()).toEventually(satisfyAllOf(equal(1), equal(1)))
}
}

0 comments on commit f15ece0

Please sign in to comment.