blob: 5ff4643c73a309810be0320ae0c594f0f69a4bca [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//
// XCTWaiter+Validation.swift
//
protocol XCTWaiterValidatableExpectation: Equatable {
var isFulfilled: Bool { get }
var fulfillmentToken: UInt64 { get }
var isInverted: Bool { get }
}
extension XCTWaiter {
struct ValidatableXCTestExpectation: XCTWaiterValidatableExpectation {
let expectation: XCTestExpectation
var isFulfilled: Bool {
return expectation.queue_isFulfilled
}
var fulfillmentToken: UInt64 {
return expectation.queue_fulfillmentToken
}
var isInverted: Bool {
return expectation.queue_isInverted
}
}
}
extension XCTWaiter {
enum ValidationResult<ExpectationType: XCTWaiterValidatableExpectation> {
case complete
case fulfilledInvertedExpectation(invertedExpectation: ExpectationType)
case violatedOrderingConstraints(expectation: ExpectationType, requiredExpectation: ExpectationType)
case timedOut(unfulfilledExpectations: [ExpectationType])
case incomplete
}
static func validateExpectations<ExpectationType: XCTWaiterValidatableExpectation>(_ expectations: [ExpectationType], dueToTimeout didTimeOut: Bool, enforceOrder: Bool) -> ValidationResult<ExpectationType> {
var unfulfilledExpectations = [ExpectationType]()
var fulfilledExpectations = [ExpectationType]()
for expectation in expectations {
if expectation.isFulfilled {
// Check for any fulfilled inverse expectations. If they were fulfilled before wait was called,
// this is where we'd catch that.
if expectation.isInverted {
return .fulfilledInvertedExpectation(invertedExpectation: expectation)
} else {
fulfilledExpectations.append(expectation)
}
} else {
unfulfilledExpectations.append(expectation)
}
}
if enforceOrder {
fulfilledExpectations.sort { $0.fulfillmentToken < $1.fulfillmentToken }
let nonInvertedExpectations = expectations.filter { !$0.isInverted }
assert(fulfilledExpectations.count <= nonInvertedExpectations.count, "Internal error: number of fulfilledExpectations (\(fulfilledExpectations.count)) must not exceed number of non-inverted expectations (\(nonInvertedExpectations.count))")
for (fulfilledExpectation, nonInvertedExpectation) in zip(fulfilledExpectations, nonInvertedExpectations) where fulfilledExpectation != nonInvertedExpectation {
return .violatedOrderingConstraints(expectation: fulfilledExpectation, requiredExpectation: nonInvertedExpectation)
}
}
if unfulfilledExpectations.isEmpty {
return .complete
} else if didTimeOut {
// If we've timed out, our new state is just based on whether or not we have any remaining unfulfilled, non-inverted expectations.
let nonInvertedUnfilledExpectations = unfulfilledExpectations.filter { !$0.isInverted }
if nonInvertedUnfilledExpectations.isEmpty {
return .complete
} else {
return .timedOut(unfulfilledExpectations: nonInvertedUnfilledExpectations)
}
}
return .incomplete
}
}