mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Implements SE-0055: https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md - Add NULL as an extra inhabitant of Builtin.RawPointer (currently hardcoded to 0 rather than being target-dependent). - Import non-object pointers as Optional/IUO when nullable/null_unspecified (like everything else). - Change the type checker's *-to-pointer conversions to handle a layer of optional. - Use 'AutoreleasingUnsafeMutablePointer<NSError?>?' as the type of error parameters exported to Objective-C. - Drop NilLiteralConvertible conformance for all pointer types. - Update the standard library and then all the tests. I've decided to leave this commit only updating existing tests; any new tests will come in the following commits. (That may mean some additional implementation work to follow.) The other major piece that's missing here is migration. I'm hoping we get a lot of that with Swift 1.1's work for optional object references, but I still need to investigate.
2883 lines
81 KiB
Swift
2883 lines
81 KiB
Swift
//===--- StdlibUnittest.swift.gyb -----------------------------*- swift -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftPrivate
|
|
import SwiftPrivatePthreadExtras
|
|
import SwiftPrivateLibcExtras
|
|
|
|
#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
|
|
import Darwin
|
|
#elseif os(Linux) || os(FreeBSD)
|
|
import Glibc
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
import ObjectiveC
|
|
#endif
|
|
|
|
public struct SourceLoc {
|
|
public let file: String
|
|
public let line: UInt
|
|
public let comment: String?
|
|
|
|
public init(_ file: String, _ line: UInt, comment: String? = nil) {
|
|
self.file = file
|
|
self.line = line
|
|
self.comment = comment
|
|
}
|
|
|
|
public func withCurrentLoc(
|
|
_ file: String = #file, line: UInt = #line
|
|
) -> SourceLocStack {
|
|
return SourceLocStack(self).with(SourceLoc(file, line))
|
|
}
|
|
}
|
|
|
|
public struct SourceLocStack {
|
|
let locs: [SourceLoc]
|
|
|
|
public init() {
|
|
locs = []
|
|
}
|
|
|
|
public init(_ loc: SourceLoc) {
|
|
locs = [ loc ]
|
|
}
|
|
|
|
init(_locs: [SourceLoc]) {
|
|
locs = _locs
|
|
}
|
|
|
|
var isEmpty: Bool {
|
|
return locs.isEmpty
|
|
}
|
|
|
|
public func with(_ loc: SourceLoc) -> SourceLocStack {
|
|
var locs = self.locs
|
|
locs.append(loc)
|
|
return SourceLocStack(_locs: locs)
|
|
}
|
|
|
|
public func pushIf(
|
|
_ showFrame: Bool, file: String, line: UInt
|
|
) -> SourceLocStack {
|
|
return showFrame ? self.with(SourceLoc(file, line)) : self
|
|
}
|
|
|
|
public func withCurrentLoc(
|
|
_ file: String = #file, line: UInt = #line
|
|
) -> SourceLocStack {
|
|
return with(SourceLoc(file, line))
|
|
}
|
|
|
|
public func print() {
|
|
let top = locs.first!
|
|
Swift.print("check failed at \(top.file), line \(top.line)")
|
|
_printStackTrace(SourceLocStack(_locs: Array(locs.dropFirst())))
|
|
}
|
|
}
|
|
|
|
%{
|
|
TRACE = '''@autoclosure _ message: () -> String = "",
|
|
showFrame: Bool = true,
|
|
stackTrace: SourceLocStack = SourceLocStack(),
|
|
file: String = #file, line: UInt = #line'''
|
|
|
|
stackTrace = 'stackTrace.pushIf(showFrame, file: file, line: line)'
|
|
|
|
trace = 'message(),\n stackTrace: ' + stackTrace
|
|
}%
|
|
|
|
func _printStackTrace(_ stackTrace: SourceLocStack?) {
|
|
guard let s = stackTrace where !s.locs.isEmpty else { return }
|
|
print("stacktrace:")
|
|
for (i, loc) in s.locs.reversed().enumerated() {
|
|
let comment = (loc.comment != nil) ? " ; \(loc.comment!)" : ""
|
|
print(" #\(i): \(loc.file):\(loc.line)\(comment)")
|
|
}
|
|
}
|
|
|
|
// FIXME: these variables should be atomic, since multiple threads can call
|
|
// `expect*()` functions.
|
|
var _anyExpectFailed = false
|
|
var _seenExpectCrash = false
|
|
|
|
/// Run `body` and expect a failure to happen.
|
|
///
|
|
/// The check passes iff `body` triggers one or more failures.
|
|
public func expectFailure(${TRACE}, body: () -> Void) {
|
|
let startAnyExpectFailed = _anyExpectFailed
|
|
_anyExpectFailed = false
|
|
body()
|
|
let endAnyExpectFailed = _anyExpectFailed
|
|
_anyExpectFailed = false
|
|
expectTrue(
|
|
endAnyExpectFailed, "running `body` should produce an expected failure",
|
|
stackTrace: ${stackTrace}
|
|
)
|
|
_anyExpectFailed = _anyExpectFailed || startAnyExpectFailed
|
|
}
|
|
|
|
public func identity(_ element: OpaqueValue<Int>) -> OpaqueValue<Int> {
|
|
return element
|
|
}
|
|
|
|
public func identityEq(_ element: MinimalEquatableValue) -> MinimalEquatableValue {
|
|
return element
|
|
}
|
|
|
|
public func identityComp(_ element: MinimalComparableValue)
|
|
-> MinimalComparableValue {
|
|
return element
|
|
}
|
|
|
|
public func expectEqual<T : Equatable>(_ expected: T, _ actual: T, ${TRACE}) {
|
|
expectEqual(expected, actual, ${trace}, showFrame: false) {$0 == $1}
|
|
}
|
|
|
|
public func expectEqual<T : Equatable, U : Equatable>(
|
|
_ expected: (T, U), _ actual: (T, U), ${TRACE}) {
|
|
expectEqual(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1}
|
|
expectEqual(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1}
|
|
}
|
|
|
|
public func expectationFailure(
|
|
_ reason: String,
|
|
trace message: String,
|
|
stackTrace: SourceLocStack) {
|
|
_anyExpectFailed = true
|
|
stackTrace.print()
|
|
print(reason, terminator: reason == "" ? "" : "\n")
|
|
print(message, terminator: message == "" ? "" : "\n")
|
|
}
|
|
|
|
public func expectEqual<T>(
|
|
_ expected: T, _ actual: T, ${TRACE}, sameValue equal: (T,T) -> Bool
|
|
) {
|
|
if !equal(expected, actual) {
|
|
expectationFailure(
|
|
"expected: \(String(reflecting: expected)) (of type \(String(reflecting: expected.dynamicType)))\n"
|
|
+ "actual: \(String(reflecting: actual)) (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace}
|
|
)
|
|
}
|
|
}
|
|
|
|
public func expectNotEqual<T : Equatable>(_ expected: T, _ actual: T, ${TRACE}) {
|
|
if expected == actual {
|
|
expectationFailure(
|
|
"unexpected value: \"\(actual)\" (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace}
|
|
)
|
|
}
|
|
}
|
|
|
|
// Cannot write a sane set of overloads using generics because of:
|
|
// <rdar://problem/17015923> Array->NSArray implicit conversion insanity
|
|
public func expectOptionalEqual<T : Equatable>(
|
|
_ expected: T, _ actual: T?, ${TRACE}
|
|
) {
|
|
expectOptionalEqual(expected, actual, ${trace}, showFrame: false) {$0 == $1}
|
|
}
|
|
|
|
public func expectOptionalEqual<T>(
|
|
_ expected: T, _ actual: T?, ${TRACE}, sameValue equal: (T,T) -> Bool
|
|
) {
|
|
if (actual == nil) || !equal(expected, actual!) {
|
|
expectationFailure(
|
|
"expected: \"\(expected)\" (of type \(String(reflecting: expected.dynamicType)))\n"
|
|
+ "actual: \"\(actual)\" (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectEqual<T : Equatable>(_ expected: T?, _ actual: T?, ${TRACE}) {
|
|
if expected != actual {
|
|
expectationFailure(
|
|
"expected: \"\(expected)\" (of type \(String(reflecting: expected.dynamicType)))\n"
|
|
+ "actual: \"\(actual)\" (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectNotEqual<T : Equatable>(
|
|
_ expected: T?, _ actual: T?, ${TRACE}
|
|
) {
|
|
if expected == actual {
|
|
expectationFailure(
|
|
"unexpected value: \"\(actual)\" (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
// Array<T> is not Equatable if T is. Provide additional overloads.
|
|
// Same for Dictionary.
|
|
%for (Generic, EquatableType) in [
|
|
% ('<T : Equatable>', 'ContiguousArray<T>'),
|
|
% ('<T : Equatable>', 'ArraySlice<T>'),
|
|
% ('<T : Equatable>', 'Array<T>'),
|
|
% ('<T, U : Equatable>', 'Dictionary<T, U>')]:
|
|
|
|
public func expectEqual${Generic}(
|
|
_ expected: ${EquatableType}, _ actual: ${EquatableType}, ${TRACE}
|
|
) {
|
|
expectEqual(expected, actual, ${trace}, showFrame: false) { $0 == $1 }
|
|
}
|
|
|
|
public func expectOptionalEqual${Generic}(
|
|
_ expected: ${EquatableType}, _ actual: ${EquatableType}?, ${TRACE}) {
|
|
if (actual == nil) || expected != actual! {
|
|
expectationFailure(
|
|
"expected: \"\(expected)\" (of type \(String(reflecting: expected.dynamicType)))"
|
|
+ "actual: \"\(actual)\" (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
%end
|
|
|
|
public func expectLT(_ lhs: Int, _ rhs: Int, ${TRACE}) {
|
|
if !(lhs < rhs) {
|
|
expectationFailure("\(lhs) < \(rhs)", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectLE(_ lhs: Int, _ rhs: Int, ${TRACE}) {
|
|
if !(lhs <= rhs) {
|
|
expectationFailure("\(lhs) <= \(rhs)", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectGT(_ lhs: Int, _ rhs: Int, ${TRACE}) {
|
|
if !(lhs > rhs) {
|
|
expectationFailure("\(lhs) > \(rhs)", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectGE(_ lhs: Int, _ rhs: Int, ${TRACE}) {
|
|
if !(lhs >= rhs) {
|
|
expectationFailure("\(lhs) >= \(rhs)", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectType<T>(_: T.Type, _ x: inout T) {}
|
|
public func expectEqualType<T>(_: T.Type, _: T.Type) {}
|
|
|
|
public func expectSequenceType<
|
|
X : Sequence
|
|
where
|
|
X.SubSequence : Sequence,
|
|
X.SubSequence.Iterator.Element == X.Iterator.Element,
|
|
X.SubSequence.SubSequence == X.SubSequence
|
|
>(_ x: X) -> X { return x }
|
|
|
|
public func expectIndexable<X : Indexable>(_ x: X) -> X { return x }
|
|
public func expectCollectionType<
|
|
X : Collection
|
|
where
|
|
X.SubSequence : Collection,
|
|
X.SubSequence.Iterator.Element == X.Iterator.Element,
|
|
X.SubSequence.SubSequence == X.SubSequence
|
|
>(_ x: X) -> X { return x }
|
|
|
|
/// A slice is a `Collection` that when sliced returns an instance of
|
|
/// itself.
|
|
public func expectSliceType<
|
|
X : Collection
|
|
where
|
|
X.SubSequence == X
|
|
>(_ sliceType: X.Type) {}
|
|
|
|
/// A mutable slice is a `MutableCollection` that when sliced returns an
|
|
/// instance of itself.
|
|
public func expectMutableSliceType<
|
|
X : MutableCollection
|
|
where
|
|
X.SubSequence == X
|
|
>(_ mutableSliceType: X.Type) {}
|
|
|
|
/// Check all associated types of a `Collection`.
|
|
public func expectCollectionAssociatedTypes<
|
|
X : Collection,
|
|
Iterator : IteratorProtocol,
|
|
SubSequence : Sequence,
|
|
Index : ForwardIndex
|
|
where
|
|
X.Index == Index
|
|
>(
|
|
collectionType: X.Type,
|
|
iteratorType: Iterator.Type,
|
|
subSequenceType: SubSequence.Type,
|
|
indexType: Index.Type
|
|
) {}
|
|
|
|
public func expectForwardIndexType<X : ForwardIndex>(_ x: X) -> X { return x }
|
|
public func expectIsBooleanType<X : Boolean>(_ x: inout X) -> X { return x }
|
|
|
|
public struct AssertionResult : CustomStringConvertible, Boolean {
|
|
init(isPass: Bool) {
|
|
self._isPass = isPass
|
|
}
|
|
|
|
public var boolValue: Bool {
|
|
return _isPass
|
|
}
|
|
|
|
public func withDescription(_ description: String) -> AssertionResult {
|
|
var result = self
|
|
result.description += description
|
|
return result
|
|
}
|
|
|
|
let _isPass: Bool
|
|
|
|
public var description: String = ""
|
|
}
|
|
|
|
public func assertionSuccess() -> AssertionResult {
|
|
return AssertionResult(isPass: true)
|
|
}
|
|
|
|
public func assertionFailure() -> AssertionResult {
|
|
return AssertionResult(isPass: false)
|
|
}
|
|
|
|
public func expectUnreachable(${TRACE}) {
|
|
expectationFailure("this code should not be executed", trace: ${trace})
|
|
}
|
|
|
|
public func expectUnreachableCatch(_ error: ErrorProtocol, ${TRACE}) {
|
|
expectationFailure(
|
|
"error should not be thrown: \"\(error)\"", trace: ${trace})
|
|
}
|
|
|
|
%for BoolType in ['Bool', 'AssertionResult']:
|
|
|
|
public func expectTrue(_ actual: ${BoolType}, ${TRACE}) {
|
|
if !actual {
|
|
expectationFailure("expected: true", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectFalse(_ actual: ${BoolType}, ${TRACE}) {
|
|
if actual {
|
|
expectationFailure("expected: false", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
%end
|
|
|
|
public func expectEmpty<T>(_ value: T?, ${TRACE}) {
|
|
if value != nil {
|
|
expectationFailure(
|
|
"expected optional to be empty\nactual: \"\(value)\"", trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectNotEmpty<T>(_ value: T?, ${TRACE}) -> T? {
|
|
if value == nil {
|
|
expectationFailure("expected optional to be non-empty", trace: ${trace})
|
|
}
|
|
return value
|
|
}
|
|
|
|
public func expectCrashLater() {
|
|
print("\(_stdlibUnittestStreamPrefix);expectCrash;\(_anyExpectFailed)")
|
|
|
|
var stderr = _Stderr()
|
|
print("\(_stdlibUnittestStreamPrefix);expectCrash", to: &stderr)
|
|
|
|
_seenExpectCrash = true
|
|
}
|
|
|
|
func _defaultTestSuiteFailedCallback() {
|
|
abort()
|
|
}
|
|
|
|
var _testSuiteFailedCallback: () -> Void = _defaultTestSuiteFailedCallback
|
|
|
|
public func _setTestSuiteFailedCallback(_ callback: () -> Void) {
|
|
_testSuiteFailedCallback = callback
|
|
}
|
|
|
|
extension ProcessTerminationStatus {
|
|
var isSwiftTrap: Bool {
|
|
switch self {
|
|
case .exit(_):
|
|
return false
|
|
case .signal(let signal):
|
|
return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP
|
|
default:
|
|
fatalError("Bad ProcessTerminationStatus")
|
|
}
|
|
}
|
|
}
|
|
|
|
func _stdlib_getline() -> String? {
|
|
var result: [UInt8] = []
|
|
while true {
|
|
let c = getchar()
|
|
if c == EOF {
|
|
if result.isEmpty {
|
|
return nil
|
|
}
|
|
return String._fromWellFormedCodeUnitSequence(UTF8.self, input: result)
|
|
}
|
|
if c == CInt(UnicodeScalar("\n").value) {
|
|
return String._fromWellFormedCodeUnitSequence(UTF8.self, input: result)
|
|
}
|
|
result.append(UInt8(c))
|
|
}
|
|
}
|
|
|
|
func _printDebuggingAdvice(_ fullTestName: String) {
|
|
print("To debug, run:")
|
|
var invocation = [Process.arguments[0]]
|
|
let interpreter = getenv("SWIFT_INTERPRETER")
|
|
if interpreter != nil {
|
|
if let interpreterCmd = String(validatingUTF8: interpreter) {
|
|
invocation.insert(interpreterCmd, at: 0)
|
|
}
|
|
}
|
|
print("$ \(invocation.joined(separator: " ")) " +
|
|
"--stdlib-unittest-in-process --stdlib-unittest-filter \"\(fullTestName)\"")
|
|
}
|
|
|
|
var _allTestSuites: [TestSuite] = []
|
|
var _testSuiteNameToIndex: [String : Int] = [:]
|
|
|
|
let _stdlibUnittestStreamPrefix = "__STDLIB_UNITTEST__"
|
|
let _crashedPrefix = "CRASHED:"
|
|
|
|
@_silgen_name("swift_stdlib_installTrapInterceptor")
|
|
func _stdlib_installTrapInterceptor()
|
|
|
|
#if _runtime(_ObjC)
|
|
@objc protocol _StdlibUnittestNSException {
|
|
optional var name: AnyObject { get }
|
|
}
|
|
#endif
|
|
|
|
func _childProcess() {
|
|
_stdlib_installTrapInterceptor()
|
|
|
|
#if _runtime(_ObjC)
|
|
objc_setUncaughtExceptionHandler {
|
|
var stderr = _Stderr()
|
|
let maybeNSException = unsafeBitCast($0, to:_StdlibUnittestNSException.self)
|
|
if let name = maybeNSException.name {
|
|
print("*** [StdlibUnittest] Terminating due to uncaught exception " +
|
|
"\(name): \($0)",
|
|
to: &stderr)
|
|
} else {
|
|
print("*** [StdlibUnittest] Terminating due to uncaught exception: \($0)",
|
|
to: &stderr)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while let line = _stdlib_getline() {
|
|
let parts = line._split(separator: ";")
|
|
let testSuiteName = parts[0]
|
|
let testName = parts[1]
|
|
var testParameter: Int?
|
|
if parts.count > 2 {
|
|
testParameter = Int(parts[2])!
|
|
} else {
|
|
testParameter = nil
|
|
}
|
|
|
|
let testSuite = _allTestSuites[_testSuiteNameToIndex[testSuiteName]!]
|
|
_anyExpectFailed = false
|
|
testSuite._runTest(name: testName, parameter: testParameter)
|
|
|
|
print("\(_stdlibUnittestStreamPrefix);end;\(_anyExpectFailed)")
|
|
|
|
var stderr = _Stderr()
|
|
print("\(_stdlibUnittestStreamPrefix);end", to: &stderr)
|
|
|
|
if !testSuite._testByName(testName).canReuseChildProcessAfterTest {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
struct _ParentProcess {
|
|
internal var _pid: pid_t = -1
|
|
internal var _childStdin: _FDOutputStream = _FDOutputStream(fd: -1)
|
|
internal var _childStdout: _FDInputStream = _FDInputStream(fd: -1)
|
|
internal var _childStderr: _FDInputStream = _FDInputStream(fd: -1)
|
|
|
|
internal var _runTestsInProcess: Bool
|
|
internal var _filter: String?
|
|
internal var _args: [String]
|
|
|
|
init(runTestsInProcess: Bool, args: [String], filter: String?) {
|
|
self._runTestsInProcess = runTestsInProcess
|
|
self._filter = filter
|
|
self._args = args
|
|
}
|
|
|
|
mutating func _spawnChild() {
|
|
let params = [ "--stdlib-unittest-run-child" ] + _args
|
|
let (pid, childStdinFD, childStdoutFD, childStderrFD) = spawnChild(params)
|
|
_pid = pid
|
|
_childStdin = _FDOutputStream(fd: childStdinFD)
|
|
_childStdout = _FDInputStream(fd: childStdoutFD)
|
|
_childStderr = _FDInputStream(fd: childStderrFD)
|
|
}
|
|
|
|
mutating func _waitForChild() -> ProcessTerminationStatus {
|
|
let status = posixWaitpid(_pid)
|
|
_pid = -1
|
|
_childStdin.close()
|
|
_childStdout.close()
|
|
_childStderr.close()
|
|
_childStdin = _FDOutputStream(fd: -1)
|
|
_childStdout = _FDInputStream(fd: -1)
|
|
_childStderr = _FDInputStream(fd: -1)
|
|
return status
|
|
}
|
|
|
|
/// Returns the values of the corresponding variables in the child process.
|
|
internal mutating func _runTestInChild(
|
|
_ testSuite: TestSuite,
|
|
_ testName: String,
|
|
parameter: Int?
|
|
) -> (anyExpectFailed: Bool, seenExpectCrash: Bool,
|
|
status: ProcessTerminationStatus?,
|
|
crashStdout: [String], crashStderr: [String]) {
|
|
if _pid <= 0 {
|
|
_spawnChild()
|
|
}
|
|
|
|
print("\(testSuite.name);\(testName)", terminator: "", to: &_childStdin)
|
|
if let parameter = parameter {
|
|
print(";", terminator: "", to: &_childStdin)
|
|
print(parameter, terminator: "", to: &_childStdin)
|
|
}
|
|
print("", to: &_childStdin)
|
|
|
|
let currentTest = testSuite._testByName(testName)
|
|
if let stdinText = currentTest.stdinText {
|
|
print(stdinText, terminator: "", to: &_childStdin)
|
|
}
|
|
if currentTest.stdinEndsWithEOF {
|
|
_childStdin.close()
|
|
}
|
|
|
|
var readfds = _stdlib_fd_set()
|
|
var writefds = _stdlib_fd_set()
|
|
var errorfds = _stdlib_fd_set()
|
|
var stdoutSeenCrashDelimiter = false
|
|
var stderrSeenCrashDelimiter = false
|
|
var stdoutEnd = false
|
|
var stderrEnd = false
|
|
var capturedCrashStdout: [String] = []
|
|
var capturedCrashStderr: [String] = []
|
|
var anyExpectFailedInChild = false
|
|
while !((_childStdout.isEOF && _childStderr.isEOF) ||
|
|
(stdoutEnd && stderrEnd)) {
|
|
|
|
readfds.zero()
|
|
errorfds.zero()
|
|
if !_childStdout.isEOF {
|
|
readfds.set(_childStdout.fd)
|
|
errorfds.set(_childStdout.fd)
|
|
}
|
|
if !_childStderr.isEOF {
|
|
readfds.set(_childStderr.fd)
|
|
errorfds.set(_childStderr.fd)
|
|
}
|
|
var ret: CInt
|
|
repeat {
|
|
ret = _stdlib_select(&readfds, &writefds, &errorfds, nil)
|
|
} while ret == -1 && errno == EINTR
|
|
if ret <= 0 {
|
|
fatalError("select() returned an error")
|
|
}
|
|
if readfds.isset(_childStdout.fd) || errorfds.isset(_childStdout.fd) {
|
|
_childStdout.read()
|
|
while var line = _childStdout.getline() {
|
|
if let index = findSubstring(line, _stdlibUnittestStreamPrefix) {
|
|
let controlMessage =
|
|
line[index..<line.endIndex]._split(separator: ";")
|
|
switch controlMessage[1] {
|
|
case "expectCrash":
|
|
stdoutSeenCrashDelimiter = true
|
|
anyExpectFailedInChild = controlMessage[2] == "true"
|
|
case "end":
|
|
stdoutEnd = true
|
|
anyExpectFailedInChild = controlMessage[2] == "true"
|
|
default:
|
|
fatalError("unexpected message")
|
|
}
|
|
line = line[line.startIndex..<index]
|
|
if line.isEmpty {
|
|
continue
|
|
}
|
|
}
|
|
if stdoutSeenCrashDelimiter {
|
|
capturedCrashStdout.append(line)
|
|
}
|
|
print("stdout>>> \(line)")
|
|
}
|
|
continue
|
|
}
|
|
if readfds.isset(_childStderr.fd) || errorfds.isset(_childStderr.fd) {
|
|
_childStderr.read()
|
|
while var line = _childStderr.getline() {
|
|
if let index = findSubstring(line, _stdlibUnittestStreamPrefix) {
|
|
let controlMessage =
|
|
line[index..<line.endIndex]._split(separator: ";")
|
|
switch controlMessage[1] {
|
|
case "expectCrash":
|
|
stderrSeenCrashDelimiter = true
|
|
case "end":
|
|
stderrEnd = true
|
|
default:
|
|
fatalError("unexpected message")
|
|
}
|
|
line = line[line.startIndex..<index]
|
|
if line.isEmpty {
|
|
continue
|
|
}
|
|
}
|
|
if stderrSeenCrashDelimiter {
|
|
capturedCrashStderr.append(line)
|
|
if findSubstring(line, _crashedPrefix) != nil {
|
|
line = "OK: saw expected \"\(line.lowercased())\""
|
|
}
|
|
}
|
|
print("stderr>>> \(line)")
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Check if the child has sent us "end" markers for the current test.
|
|
if stdoutEnd && stderrEnd {
|
|
testSuite._testByName(testName)
|
|
var status: ProcessTerminationStatus? = nil
|
|
if !testSuite._testByName(testName).canReuseChildProcessAfterTest {
|
|
status = _waitForChild()
|
|
switch status! {
|
|
case .exit(0):
|
|
status = nil
|
|
default:
|
|
()
|
|
}
|
|
}
|
|
return (
|
|
anyExpectFailedInChild,
|
|
stdoutSeenCrashDelimiter || stderrSeenCrashDelimiter, status,
|
|
capturedCrashStdout, capturedCrashStderr)
|
|
}
|
|
|
|
// We reached EOF on stdout and stderr and we did not see "end" markers, so
|
|
// it looks like child crashed (of course it could have closed the file
|
|
// descriptors, but we assume it did not, since it prevent further
|
|
// communication with the parent).
|
|
let status = _waitForChild()
|
|
return (
|
|
anyExpectFailedInChild,
|
|
stdoutSeenCrashDelimiter || stderrSeenCrashDelimiter, status,
|
|
capturedCrashStdout, capturedCrashStderr)
|
|
}
|
|
|
|
internal enum _TestStatus {
|
|
case skip([TestRunPredicate])
|
|
case pass
|
|
case fail
|
|
case uxPass
|
|
case xFail
|
|
}
|
|
|
|
internal mutating func runOneTest(
|
|
fullTestName: String,
|
|
testSuite: TestSuite,
|
|
test t: TestSuite._Test,
|
|
testParameter: Int?
|
|
) -> _TestStatus {
|
|
let activeSkips = t.getActiveSkipPredicates()
|
|
if !activeSkips.isEmpty {
|
|
return .skip(activeSkips)
|
|
}
|
|
|
|
let activeXFails = t.getActiveXFailPredicates()
|
|
let expectXFail = !activeXFails.isEmpty
|
|
let activeXFailsText = expectXFail ? " (XFAIL: \(activeXFails))" : ""
|
|
print("[ RUN ] \(fullTestName)\(activeXFailsText)")
|
|
|
|
var expectCrash = false
|
|
var childTerminationStatus: ProcessTerminationStatus? = nil
|
|
var crashStdout: [String] = []
|
|
var crashStderr: [String] = []
|
|
if _runTestsInProcess {
|
|
if t.stdinText != nil {
|
|
print("The test \(fullTestName) requires stdin input and can't be run in-process, marking as failed")
|
|
_anyExpectFailed = true
|
|
} else {
|
|
_anyExpectFailed = false
|
|
testSuite._runTest(name: t.name, parameter: testParameter)
|
|
}
|
|
} else {
|
|
(_anyExpectFailed, expectCrash, childTerminationStatus, crashStdout,
|
|
crashStderr) =
|
|
_runTestInChild(testSuite, t.name, parameter: testParameter)
|
|
}
|
|
|
|
// Determine if the test passed, not taking XFAILs into account.
|
|
var testPassed = false
|
|
switch (childTerminationStatus, expectCrash) {
|
|
case (.none, false):
|
|
testPassed = !_anyExpectFailed
|
|
|
|
case (.none, true):
|
|
testPassed = false
|
|
print("expecting a crash, but the test did not crash")
|
|
|
|
case (.some(_), false):
|
|
testPassed = false
|
|
print("the test crashed unexpectedly")
|
|
|
|
case (.some(_), true):
|
|
testPassed = !_anyExpectFailed
|
|
}
|
|
if testPassed && t.crashOutputMatches.count > 0 {
|
|
// If we still think that the test passed, check if the crash
|
|
// output matches our expectations.
|
|
let crashOutput = crashStdout + crashStderr
|
|
for expectedSubstring in t.crashOutputMatches {
|
|
var found = false
|
|
for s in crashOutput {
|
|
if findSubstring(s, expectedSubstring) != nil {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
print("did not find expected string after crash: \(expectedSubstring.debugDescription)")
|
|
testPassed = false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply XFAILs.
|
|
switch (testPassed, expectXFail) {
|
|
case (true, false):
|
|
return .pass
|
|
|
|
case (true, true):
|
|
return .uxPass
|
|
|
|
case (false, false):
|
|
return .fail
|
|
|
|
case (false, true):
|
|
return .xFail
|
|
}
|
|
}
|
|
|
|
mutating func run() {
|
|
if let filter = _filter {
|
|
print("StdlibUnittest: using filter: \(filter)")
|
|
}
|
|
for testSuite in _allTestSuites {
|
|
var uxpassedTests: [String] = []
|
|
var failedTests: [String] = []
|
|
var skippedTests: [String] = []
|
|
for t in testSuite._tests {
|
|
for testParameter in t.parameterValues {
|
|
var testName = t.name
|
|
if let testParameter = testParameter {
|
|
testName += "/"
|
|
testName += String(testParameter)
|
|
}
|
|
let fullTestName = "\(testSuite.name).\(testName)"
|
|
if let filter = _filter
|
|
where findSubstring(fullTestName, filter) == nil {
|
|
|
|
continue
|
|
}
|
|
|
|
switch runOneTest(
|
|
fullTestName: fullTestName,
|
|
testSuite: testSuite,
|
|
test: t,
|
|
testParameter: testParameter
|
|
) {
|
|
case .skip(let activeSkips):
|
|
skippedTests.append(testName)
|
|
print("[ SKIP ] \(fullTestName) (skip: \(activeSkips))")
|
|
|
|
case .pass:
|
|
print("[ OK ] \(fullTestName)")
|
|
|
|
case .uxPass:
|
|
uxpassedTests.append(testName)
|
|
print("[ UXPASS ] \(fullTestName)")
|
|
|
|
case .fail:
|
|
failedTests.append(testName)
|
|
print("[ FAIL ] \(fullTestName)")
|
|
|
|
case .xFail:
|
|
print("[ XFAIL ] \(fullTestName)")
|
|
}
|
|
}
|
|
}
|
|
|
|
if !uxpassedTests.isEmpty || !failedTests.isEmpty {
|
|
print("\(testSuite.name): Some tests failed, aborting")
|
|
print("UXPASS: \(uxpassedTests)")
|
|
print("FAIL: \(failedTests)")
|
|
print("SKIP: \(skippedTests)")
|
|
if !uxpassedTests.isEmpty {
|
|
_printDebuggingAdvice(uxpassedTests[0])
|
|
}
|
|
if !failedTests.isEmpty {
|
|
_printDebuggingAdvice(failedTests[0])
|
|
}
|
|
_testSuiteFailedCallback()
|
|
} else {
|
|
print("\(testSuite.name): All tests passed")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Track repeated calls to runAllTests() and/or runNoTests().
|
|
// Complain if a file runs no tests without calling runNoTests().
|
|
struct PersistentState {
|
|
static var runAllTestsWasCalled: Bool = false
|
|
static var runNoTestsWasCalled: Bool = false
|
|
static var ranSomething: Bool = false
|
|
static var complaintInstalled = false
|
|
|
|
static func complainIfNothingRuns() {
|
|
if !complaintInstalled {
|
|
complaintInstalled = true
|
|
atexit {
|
|
if !PersistentState.ranSomething {
|
|
print("Ran no tests and runNoTests() was not called. Aborting. ")
|
|
print("Did you forget to call runAllTests()?")
|
|
_testSuiteFailedCallback()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Call runNoTests() if you want to deliberately run no tests.
|
|
public func runNoTests() {
|
|
if PersistentState.runAllTestsWasCalled {
|
|
print("runNoTests() called after runAllTests(). Aborting.")
|
|
_testSuiteFailedCallback()
|
|
return
|
|
}
|
|
if PersistentState.runNoTestsWasCalled {
|
|
print("runNoTests() called twice. Aborting.")
|
|
_testSuiteFailedCallback()
|
|
return
|
|
}
|
|
PersistentState.runNoTestsWasCalled = true
|
|
PersistentState.ranSomething = true
|
|
}
|
|
|
|
public func runAllTests() {
|
|
if PersistentState.runNoTestsWasCalled {
|
|
print("runAllTests() called after runNoTests(). Aborting.")
|
|
_testSuiteFailedCallback()
|
|
return
|
|
}
|
|
if PersistentState.runAllTestsWasCalled {
|
|
print("runAllTests() called twice. Aborting.")
|
|
_testSuiteFailedCallback()
|
|
return
|
|
}
|
|
PersistentState.runAllTestsWasCalled = true
|
|
PersistentState.ranSomething = true
|
|
|
|
#if _runtime(_ObjC)
|
|
autoreleasepool {
|
|
_stdlib_initializeReturnAutoreleased()
|
|
}
|
|
#endif
|
|
|
|
let _isChildProcess: Bool =
|
|
Process.arguments.contains("--stdlib-unittest-run-child")
|
|
|
|
if _isChildProcess {
|
|
_childProcess()
|
|
} else {
|
|
var runTestsInProcess: Bool = false
|
|
var filter: String? = nil
|
|
var args = [String]()
|
|
var i = 0
|
|
i += 1 // Skip the name of the executable.
|
|
while i < Process.arguments.count {
|
|
let arg = Process.arguments[i]
|
|
if arg == "--stdlib-unittest-in-process" {
|
|
runTestsInProcess = true
|
|
i += 1
|
|
continue
|
|
}
|
|
if arg == "--stdlib-unittest-filter" {
|
|
filter = Process.arguments[i + 1]
|
|
i += 2
|
|
continue
|
|
}
|
|
if arg == "--help" {
|
|
let message =
|
|
"optional arguments:\n" +
|
|
"--stdlib-unittest-in-process\n" +
|
|
" run tests in-process without intercepting crashes.\n" +
|
|
" Useful for running under a debugger.\n" +
|
|
"--stdlib-unittest-filter FILTER-STRING\n" +
|
|
" only run tests whose names contain FILTER-STRING as\n" +
|
|
" a substring."
|
|
print(message)
|
|
return
|
|
}
|
|
|
|
// Pass through unparsed arguments to the child process.
|
|
args.append(Process.arguments[i])
|
|
|
|
i += 1
|
|
}
|
|
|
|
var parent = _ParentProcess(
|
|
runTestsInProcess: runTestsInProcess, args: args, filter: filter)
|
|
parent.run()
|
|
}
|
|
}
|
|
|
|
public final class TestSuite {
|
|
public init(_ name: String) {
|
|
self.name = name
|
|
_precondition(
|
|
_testNameToIndex[name] == nil,
|
|
"test suite with the same name already exists")
|
|
_allTestSuites.append(self)
|
|
_testSuiteNameToIndex[name] = _allTestSuites.count - 1
|
|
PersistentState.complainIfNothingRuns()
|
|
}
|
|
|
|
public func test(
|
|
_ name: String,
|
|
file: String = #file, line: UInt = #line,
|
|
_ testFunction: () -> Void
|
|
) {
|
|
_TestBuilder(testSuite: self, name: name, loc: SourceLoc(file, line))
|
|
.code(testFunction)
|
|
}
|
|
|
|
public func test(
|
|
_ name: String, file: String = #file, line: UInt = #line
|
|
) -> _TestBuilder {
|
|
return _TestBuilder(testSuite: self, name: name, loc: SourceLoc(file, line))
|
|
}
|
|
|
|
public func setUp(_ code: () -> Void) {
|
|
_precondition(_testSetUpCode == nil, "set-up code already set")
|
|
_testSetUpCode = code
|
|
}
|
|
|
|
public func tearDown(_ code: () -> Void) {
|
|
_precondition(_testTearDownCode == nil, "tear-down code already set")
|
|
_testTearDownCode = code
|
|
}
|
|
|
|
func _runTest(name testName: String, parameter: Int?) {
|
|
PersistentState.ranSomething = true
|
|
for r in _allResettables {
|
|
r.reset()
|
|
}
|
|
LifetimeTracked.instances = 0
|
|
if let f = _testSetUpCode {
|
|
f()
|
|
}
|
|
let test = _testByName(testName)
|
|
switch test.code {
|
|
case .single(let code):
|
|
precondition(
|
|
parameter == nil,
|
|
"can't pass parameters to non-parameterized tests")
|
|
code()
|
|
case .parameterized(code: let code, _):
|
|
code(parameter!)
|
|
}
|
|
if let f = _testTearDownCode {
|
|
f()
|
|
}
|
|
expectEqual(
|
|
0, LifetimeTracked.instances, "leaked LifetimeTracked instances:",
|
|
file: test.testLoc.file, line: test.testLoc.line)
|
|
}
|
|
|
|
func _testByName(_ testName: String) -> _Test {
|
|
return _tests[_testNameToIndex[testName]!]
|
|
}
|
|
|
|
internal enum _TestCode {
|
|
case single(code: () -> Void)
|
|
case parameterized(code: (Int) -> Void, count: Int)
|
|
}
|
|
|
|
internal struct _Test {
|
|
let name: String
|
|
let testLoc: SourceLoc
|
|
let xfail: [TestRunPredicate]
|
|
let skip: [TestRunPredicate]
|
|
let stdinText: String?
|
|
let stdinEndsWithEOF: Bool
|
|
let crashOutputMatches: [String]
|
|
let code: _TestCode
|
|
|
|
/// Whether the test harness should stop reusing the child process after
|
|
/// running this test.
|
|
var canReuseChildProcessAfterTest: Bool {
|
|
return stdinText == nil
|
|
}
|
|
|
|
func getActiveXFailPredicates() -> [TestRunPredicate] {
|
|
return xfail.filter { $0.evaluate() }
|
|
}
|
|
|
|
func getActiveSkipPredicates() -> [TestRunPredicate] {
|
|
return skip.filter { $0.evaluate() }
|
|
}
|
|
|
|
var parameterValues: [Int?] {
|
|
switch code {
|
|
case .single:
|
|
return [nil]
|
|
case .parameterized(code: _, count: let count):
|
|
return (0..<count).map { $0 }
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct _TestBuilder {
|
|
let _testSuite: TestSuite
|
|
var _name: String
|
|
var _data: _Data = _Data()
|
|
|
|
internal final class _Data {
|
|
var _xfail: [TestRunPredicate] = []
|
|
var _skip: [TestRunPredicate] = []
|
|
var _stdinText: String? = nil
|
|
var _stdinEndsWithEOF: Bool = false
|
|
var _crashOutputMatches: [String] = []
|
|
var _testLoc: SourceLoc? = nil
|
|
}
|
|
|
|
init(testSuite: TestSuite, name: String, loc: SourceLoc) {
|
|
_testSuite = testSuite
|
|
_name = name
|
|
_data._testLoc = loc
|
|
}
|
|
|
|
public func xfail(_ predicate: TestRunPredicate) -> _TestBuilder {
|
|
_data._xfail.append(predicate)
|
|
return self
|
|
}
|
|
|
|
public func skip(_ predicate: TestRunPredicate) -> _TestBuilder {
|
|
_data._skip.append(predicate)
|
|
return self
|
|
}
|
|
|
|
public func stdin(_ stdinText: String, eof: Bool = false) -> _TestBuilder {
|
|
_data._stdinText = stdinText
|
|
_data._stdinEndsWithEOF = eof
|
|
return self
|
|
}
|
|
|
|
public func crashOutputMatches(_ string: String) -> _TestBuilder {
|
|
_data._crashOutputMatches.append(string)
|
|
return self
|
|
}
|
|
|
|
internal func _build(_ testCode: _TestCode) {
|
|
_testSuite._tests.append(
|
|
_Test(
|
|
name: _name, testLoc: _data._testLoc!, xfail: _data._xfail,
|
|
skip: _data._skip,
|
|
stdinText: _data._stdinText,
|
|
stdinEndsWithEOF: _data._stdinEndsWithEOF,
|
|
crashOutputMatches: _data._crashOutputMatches,
|
|
code: testCode))
|
|
_testSuite._testNameToIndex[_name] = _testSuite._tests.count - 1
|
|
}
|
|
|
|
public func code(_ testFunction: () -> Void) {
|
|
_build(.single(code: testFunction))
|
|
}
|
|
|
|
public func forEach<Data>(
|
|
in parameterSets: [Data],
|
|
testFunction: (Data) -> Void
|
|
) {
|
|
_build(.parameterized(
|
|
code: { (i: Int) in testFunction(parameterSets[i]) },
|
|
count: parameterSets.count))
|
|
}
|
|
}
|
|
|
|
var name: String
|
|
var _tests: [_Test] = []
|
|
|
|
/// Code that is run before every test.
|
|
var _testSetUpCode: (() -> Void)?
|
|
|
|
/// Code that is run after every test.
|
|
var _testTearDownCode: (() -> Void)?
|
|
|
|
/// Maps test name to index in `_tests`.
|
|
var _testNameToIndex: [String : Int] = [:]
|
|
}
|
|
|
|
#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
|
|
@_silgen_name("swift_stdlib_getSystemVersionPlistProperty")
|
|
func _stdlib_getSystemVersionPlistPropertyImpl(
|
|
_ propertyName: UnsafePointer<CChar>) -> UnsafePointer<CChar>?
|
|
|
|
func _stdlib_getSystemVersionPlistProperty(_ propertyName: String) -> String? {
|
|
let cs = _stdlib_getSystemVersionPlistPropertyImpl(propertyName)
|
|
return cs.map(String.init(cString:))
|
|
}
|
|
#endif
|
|
|
|
public enum OSVersion : CustomStringConvertible {
|
|
case osx(major: Int, minor: Int, bugFix: Int)
|
|
case iOS(major: Int, minor: Int, bugFix: Int)
|
|
case tvOS(major: Int, minor: Int, bugFix: Int)
|
|
case watchOS(major: Int, minor: Int, bugFix: Int)
|
|
case iOSSimulator
|
|
case tvOSSimulator
|
|
case watchOSSimulator
|
|
case linux
|
|
case freeBSD
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case osx(let major, let minor, let bugFix):
|
|
return "OS X \(major).\(minor).\(bugFix)"
|
|
case iOS(let major, let minor, let bugFix):
|
|
return "iOS \(major).\(minor).\(bugFix)"
|
|
case tvOS(let major, let minor, let bugFix):
|
|
return "TVOS \(major).\(minor).\(bugFix)"
|
|
case watchOS(let major, let minor, let bugFix):
|
|
return "watchOS \(major).\(minor).\(bugFix)"
|
|
case iOSSimulator:
|
|
return "iOSSimulator"
|
|
case tvOSSimulator:
|
|
return "TVOSSimulator"
|
|
case watchOSSimulator:
|
|
return "watchOSSimulator"
|
|
case linux:
|
|
return "Linux"
|
|
case freeBSD:
|
|
return "FreeBSD"
|
|
}
|
|
}
|
|
}
|
|
|
|
func _parseDottedVersion(_ s: String) -> [Int] {
|
|
return Array(s._split(separator: ".").lazy.map { Int($0)! })
|
|
}
|
|
|
|
public func _parseDottedVersionTriple(_ s: String) -> (Int, Int, Int) {
|
|
var array = _parseDottedVersion(s)
|
|
if array.count >= 4 {
|
|
fatalError("unexpected version")
|
|
}
|
|
return (
|
|
array.count >= 1 ? array[0] : 0,
|
|
array.count >= 2 ? array[1] : 0,
|
|
array.count >= 3 ? array[2] : 0)
|
|
}
|
|
|
|
func _getOSVersion() -> OSVersion {
|
|
#if os(iOS) && (arch(i386) || arch(x86_64))
|
|
// On simulator, the plist file that we try to read turns out to be host's
|
|
// plist file, which indicates OS X.
|
|
//
|
|
// FIXME: how to get the simulator version *without* UIKit?
|
|
return .iOSSimulator
|
|
#elseif os(tvOS) && (arch(i386) || arch(x86_64))
|
|
return .tvOSSimulator
|
|
#elseif os(watchOS) && (arch(i386) || arch(x86_64))
|
|
return .watchOSSimulator
|
|
#elseif os(Linux)
|
|
return .linux
|
|
#elseif os(FreeBSD)
|
|
return .freeBSD
|
|
#else
|
|
let productVersion = _stdlib_getSystemVersionPlistProperty("ProductVersion")!
|
|
let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion)
|
|
#if os(OSX)
|
|
return .osx(major: major, minor: minor, bugFix: bugFix)
|
|
#elseif os(iOS)
|
|
return .iOS(major: major, minor: minor, bugFix: bugFix)
|
|
#elseif os(tvOS)
|
|
return .tvOS(major: major, minor: minor, bugFix: bugFix)
|
|
#elseif os(watchOS)
|
|
return .watchOS(major: major, minor: minor, bugFix: bugFix)
|
|
#else
|
|
fatalError("could not determine OS version")
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
var _runningOSVersion: OSVersion = _getOSVersion()
|
|
var _overrideOSVersion: OSVersion? = nil
|
|
|
|
/// Override the OS version for testing.
|
|
public func _setOverrideOSVersion(_ v: OSVersion) {
|
|
_overrideOSVersion = v
|
|
}
|
|
|
|
func _getRunningOSVersion() -> OSVersion {
|
|
// Allow overriding the OS version for testing.
|
|
return _overrideOSVersion ?? _runningOSVersion
|
|
}
|
|
|
|
public enum TestRunPredicate : CustomStringConvertible {
|
|
case custom(() -> Bool, reason: String)
|
|
|
|
case always(/*reason:*/ String)
|
|
case never
|
|
|
|
case osxAny(/*reason:*/ String)
|
|
case osxMajor(Int, reason: String)
|
|
case osxMinor(Int, Int, reason: String)
|
|
case osxMinorRange(Int, Range<Int>, reason: String)
|
|
case osxBugFix(Int, Int, Int, reason: String)
|
|
case osxBugFixRange(Int, Int, Range<Int>, reason: String)
|
|
|
|
case iOSAny(/*reason:*/ String)
|
|
case iOSMajor(Int, reason: String)
|
|
case iOSMinor(Int, Int, reason: String)
|
|
case iOSMinorRange(Int, Range<Int>, reason: String)
|
|
case iOSBugFix(Int, Int, Int, reason: String)
|
|
case iOSBugFixRange(Int, Int, Range<Int>, reason: String)
|
|
|
|
case iOSSimulatorAny(/*reason:*/ String)
|
|
|
|
case tvOSAny(/*reason:*/ String)
|
|
case tvOSMajor(Int, reason: String)
|
|
case tvOSMinor(Int, Int, reason: String)
|
|
case tvOSMinorRange(Int, Range<Int>, reason: String)
|
|
case tvOSBugFix(Int, Int, Int, reason: String)
|
|
case tvOSBugFixRange(Int, Int, Range<Int>, reason: String)
|
|
|
|
case tvOSSimulatorAny(/*reason:*/ String)
|
|
|
|
case watchOSAny(/*reason:*/ String)
|
|
case watchOSMajor(Int, reason: String)
|
|
case watchOSMinor(Int, Int, reason: String)
|
|
case watchOSMinorRange(Int, Range<Int>, reason: String)
|
|
case watchOSBugFix(Int, Int, Int, reason: String)
|
|
case watchOSBugFixRange(Int, Int, Range<Int>, reason: String)
|
|
|
|
case watchOSSimulatorAny(/*reason:*/ String)
|
|
|
|
case linuxAny(reason: String)
|
|
|
|
case freeBSDAny(reason: String)
|
|
|
|
case objCRuntime(/*reason:*/ String)
|
|
case nativeRuntime(/*reason:*/ String)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case custom(_, let reason):
|
|
return "Custom(reason: \(reason))"
|
|
|
|
case always(let reason):
|
|
return "Always(reason: \(reason))"
|
|
case never:
|
|
return ""
|
|
|
|
case osxAny(let reason):
|
|
return "osx(*, reason: \(reason))"
|
|
case osxMajor(let major, let reason):
|
|
return "osx(\(major).*, reason: \(reason))"
|
|
case osxMinor(let major, let minor, let reason):
|
|
return "osx(\(major).\(minor), reason: \(reason))"
|
|
case osxMinorRange(let major, let minorRange, let reason):
|
|
return "osx(\(major).[\(minorRange)], reason: \(reason))"
|
|
case osxBugFix(let major, let minor, let bugFix, let reason):
|
|
return "osx(\(major).\(minor).\(bugFix), reason: \(reason))"
|
|
case osxBugFixRange(let major, let minor, let bugFixRange, let reason):
|
|
return "osx(\(major).\(minor).[\(bugFixRange)], reason: \(reason))"
|
|
|
|
case iOSAny(let reason):
|
|
return "iOS(*, reason: \(reason))"
|
|
case iOSMajor(let major, let reason):
|
|
return "iOS(\(major).*, reason: \(reason))"
|
|
case iOSMinor(let major, let minor, let reason):
|
|
return "iOS(\(major).\(minor), reason: \(reason))"
|
|
case iOSMinorRange(let major, let minorRange, let reason):
|
|
return "iOS(\(major).[\(minorRange)], reason: \(reason))"
|
|
case iOSBugFix(let major, let minor, let bugFix, let reason):
|
|
return "iOS(\(major).\(minor).\(bugFix), reason: \(reason))"
|
|
case iOSBugFixRange(let major, let minor, let bugFixRange, let reason):
|
|
return "iOS(\(major).\(minor).[\(bugFixRange)], reason: \(reason))"
|
|
|
|
case iOSSimulatorAny(let reason):
|
|
return "iOSSimulatorAny(*, reason: \(reason))"
|
|
|
|
case tvOSAny(let reason):
|
|
return "tvOS(*, reason: \(reason))"
|
|
case tvOSMajor(let major, let reason):
|
|
return "tvOS(\(major).*, reason: \(reason))"
|
|
case tvOSMinor(let major, let minor, let reason):
|
|
return "tvOS(\(major).\(minor), reason: \(reason))"
|
|
case tvOSMinorRange(let major, let minorRange, let reason):
|
|
return "tvOS(\(major).[\(minorRange)], reason: \(reason))"
|
|
case tvOSBugFix(let major, let minor, let bugFix, let reason):
|
|
return "tvOS(\(major).\(minor).\(bugFix), reason: \(reason))"
|
|
case tvOSBugFixRange(let major, let minor, let bugFixRange, let reason):
|
|
return "tvOS(\(major).\(minor).[\(bugFixRange)], reason: \(reason))"
|
|
|
|
case tvOSSimulatorAny(let reason):
|
|
return "tvOSSimulatorAny(*, reason: \(reason))"
|
|
|
|
case watchOSAny(let reason):
|
|
return "watchOS(*, reason: \(reason))"
|
|
case watchOSMajor(let major, let reason):
|
|
return "watchOS(\(major).*, reason: \(reason))"
|
|
case watchOSMinor(let major, let minor, let reason):
|
|
return "watchOS(\(major).\(minor), reason: \(reason))"
|
|
case watchOSMinorRange(let major, let minorRange, let reason):
|
|
return "watchOS(\(major).[\(minorRange)], reason: \(reason))"
|
|
case watchOSBugFix(let major, let minor, let bugFix, let reason):
|
|
return "watchOS(\(major).\(minor).\(bugFix), reason: \(reason))"
|
|
case watchOSBugFixRange(let major, let minor, let bugFixRange, let reason):
|
|
return "watchOS(\(major).\(minor).[\(bugFixRange)], reason: \(reason))"
|
|
|
|
case watchOSSimulatorAny(let reason):
|
|
return "watchOSSimulatorAny(*, reason: \(reason))"
|
|
|
|
case linuxAny(reason: let reason):
|
|
return "linuxAny(*, reason: \(reason))"
|
|
|
|
case freeBSDAny(reason: let reason):
|
|
return "freeBSDAny(*, reason: \(reason))"
|
|
|
|
case objCRuntime(let reason):
|
|
return "Objective-C runtime, reason: \(reason))"
|
|
case nativeRuntime(let reason):
|
|
return "Native runtime (no ObjC), reason: \(reason))"
|
|
}
|
|
}
|
|
|
|
public func evaluate() -> Bool {
|
|
switch self {
|
|
case custom(let predicate, _):
|
|
return predicate()
|
|
|
|
case always:
|
|
return true
|
|
case never:
|
|
return false
|
|
|
|
case osxAny:
|
|
switch _getRunningOSVersion() {
|
|
case .osx:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case osxMajor(let major, _):
|
|
switch _getRunningOSVersion() {
|
|
case .osx(major, _, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case osxMinor(let major, let minor, _):
|
|
switch _getRunningOSVersion() {
|
|
case .osx(major, minor, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case osxMinorRange(let major, let minorRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .osx(major, let runningMinor, _):
|
|
return minorRange.contains(runningMinor)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case osxBugFix(let major, let minor, let bugFix, _):
|
|
switch _getRunningOSVersion() {
|
|
case .osx(major, minor, bugFix):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case osxBugFixRange(let major, let minor, let bugFixRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .osx(major, minor, let runningBugFix):
|
|
return bugFixRange.contains(runningBugFix)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSAny:
|
|
switch _getRunningOSVersion() {
|
|
case .iOS:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSMajor(let major, _):
|
|
switch _getRunningOSVersion() {
|
|
case .iOS(major, _, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSMinor(let major, let minor, _):
|
|
switch _getRunningOSVersion() {
|
|
case .iOS(major, minor, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSMinorRange(let major, let minorRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .iOS(major, let runningMinor, _):
|
|
return minorRange.contains(runningMinor)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSBugFix(let major, let minor, let bugFix, _):
|
|
switch _getRunningOSVersion() {
|
|
case .iOS(major, minor, bugFix):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSBugFixRange(let major, let minor, let bugFixRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .iOS(major, minor, let runningBugFix):
|
|
return bugFixRange.contains(runningBugFix)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSSimulatorAny:
|
|
switch _getRunningOSVersion() {
|
|
case .iOSSimulator:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSAny:
|
|
switch _getRunningOSVersion() {
|
|
case .tvOS:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSMajor(let major, _):
|
|
switch _getRunningOSVersion() {
|
|
case .tvOS(major, _, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSMinor(let major, let minor, _):
|
|
switch _getRunningOSVersion() {
|
|
case .tvOS(major, minor, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSMinorRange(let major, let minorRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .tvOS(major, let runningMinor, _):
|
|
return minorRange.contains(runningMinor)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSBugFix(let major, let minor, let bugFix, _):
|
|
switch _getRunningOSVersion() {
|
|
case .tvOS(major, minor, bugFix):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSBugFixRange(let major, let minor, let bugFixRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .tvOS(major, minor, let runningBugFix):
|
|
return bugFixRange.contains(runningBugFix)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case tvOSSimulatorAny:
|
|
switch _getRunningOSVersion() {
|
|
case .tvOSSimulator:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSAny:
|
|
switch _getRunningOSVersion() {
|
|
case .watchOS:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSMajor(let major, _):
|
|
switch _getRunningOSVersion() {
|
|
case .watchOS(major, _, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSMinor(let major, let minor, _):
|
|
switch _getRunningOSVersion() {
|
|
case .watchOS(major, minor, _):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSMinorRange(let major, let minorRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .watchOS(major, let runningMinor, _):
|
|
return minorRange.contains(runningMinor)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSBugFix(let major, let minor, let bugFix, _):
|
|
switch _getRunningOSVersion() {
|
|
case .watchOS(major, minor, bugFix):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSBugFixRange(let major, let minor, let bugFixRange, _):
|
|
switch _getRunningOSVersion() {
|
|
case .watchOS(major, minor, let runningBugFix):
|
|
return bugFixRange.contains(runningBugFix)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case watchOSSimulatorAny:
|
|
switch _getRunningOSVersion() {
|
|
case .watchOSSimulator:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case linuxAny:
|
|
switch _getRunningOSVersion() {
|
|
case .linux:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case freeBSDAny:
|
|
switch _getRunningOSVersion() {
|
|
case .freeBSD:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case objCRuntime:
|
|
#if _runtime(_ObjC)
|
|
return true
|
|
#else
|
|
return false
|
|
#endif
|
|
|
|
case nativeRuntime:
|
|
#if _runtime(_ObjC)
|
|
return false
|
|
#else
|
|
return true
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Semantic tests for protocol conformance
|
|
//
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `Equatable`, using `oracle` to generate equality
|
|
/// expectations from pairs of positions in `instances`.
|
|
///
|
|
/// - Note: `oracle` is also checked for conformance to the
|
|
/// laws.
|
|
public func checkEquatable<
|
|
Instances: Collection where Instances.Iterator.Element : Equatable
|
|
>(
|
|
_ instances: Instances,
|
|
oracle: (Instances.Index, Instances.Index) -> Bool,
|
|
${TRACE}
|
|
) {
|
|
for i in instances.indices {
|
|
expectTrue(oracle(i, i), "bad oracle: broken reflexivity at index \(i)")
|
|
let x = instances[i]
|
|
// Reflexivity
|
|
expectEqual(x, x, ${trace})
|
|
for j in instances.indices {
|
|
let predictedXY = oracle(i, j)
|
|
expectEqual(
|
|
predictedXY, oracle(j, i),
|
|
"bad oracle: broken symmetry between indices \(i), \(j)")
|
|
|
|
let y = instances[j]
|
|
|
|
let xy = x == y
|
|
expectEqual(predictedXY, xy, ${trace})
|
|
|
|
// Not-equal is an inverse of equal
|
|
expectNotEqual(xy, x != y, ${trace})
|
|
|
|
// Symmetry
|
|
expectEqual(xy, y == x, ${trace})
|
|
|
|
for k in instances.indices {
|
|
let z = instances[k]
|
|
// Transitivity
|
|
if xy && y == z {
|
|
expectTrue(
|
|
oracle(i, k),
|
|
"bad oracle: broken transitivity at indices \(i), \(j), \(k)")
|
|
expectEqual(x, z, ${trace})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkEquatable<T : Equatable>(
|
|
_ expectedEqual: Bool, _ lhs: T, _ rhs: T, ${TRACE}
|
|
) {
|
|
checkEquatable(
|
|
[lhs, rhs],
|
|
oracle: { expectedEqual || $0 == $1 }, ${trace}, showFrame: false)
|
|
}
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `Hashable`, using `equalityOracle` to generate
|
|
/// equality expectations from pairs of positions in `instances`.
|
|
public func checkHashable<
|
|
Instances: Collection where Instances.Iterator.Element : Hashable
|
|
>(
|
|
_ instances: Instances,
|
|
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
|
|
${TRACE}
|
|
) {
|
|
checkEquatable(instances, oracle: equalityOracle, ${trace})
|
|
|
|
for x in instances {
|
|
for y in instances {
|
|
if x == y {
|
|
expectEqual(x.hashValue, y.hashValue, ${trace})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkHashable<T : Hashable>(
|
|
_ expectedEqual: Bool, _ lhs: T, _ rhs: T, ${TRACE}
|
|
) {
|
|
checkHashable(
|
|
[lhs, rhs], equalityOracle: { expectedEqual || $0 == $1 }, ${trace})
|
|
}
|
|
|
|
% for inc, protocol, successor, end in (
|
|
% ('inc', '_Incrementable', 'successor', 'end'),
|
|
% ('dec', 'BidirectionalIndex', 'predecessor', 'start')):
|
|
|
|
/// Test that the elements of `instances` satisfy
|
|
/// ${'some of ' if inc == 'dec' else ''}the semantic
|
|
/// requirements of `${protocol}`, using `equalityOracle` to
|
|
/// generate equality expectations from pairs of positions in
|
|
/// `instances`.
|
|
///
|
|
/// - Precondition: ${'''`endIndex` is reachable from all
|
|
/// elements of `instances`.''' if inc == 'inc' else '''all
|
|
/// elements of `instances` are reachable from `startIndex`.'''}
|
|
public func check${inc.capitalize()}rementable<
|
|
Instances: Collection
|
|
where Instances.Iterator.Element : ${protocol}
|
|
>(
|
|
_ instances: Instances,
|
|
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
|
|
${end}Index: Instances.Iterator.Element, ${TRACE}
|
|
) {
|
|
checkEquatable(instances, oracle: equalityOracle, ${trace})
|
|
for i in instances {
|
|
if i != ${end}Index {
|
|
let next = i.${successor}()
|
|
// ${successor} gets us a new index value
|
|
expectNotEqual(i, next, ${trace})
|
|
|
|
// Which is the same as if we apply _${successor}InPlace
|
|
var j = i
|
|
j._${successor}InPlace()
|
|
expectEqual(j, next, ${trace})
|
|
}
|
|
}
|
|
}
|
|
%end
|
|
|
|
public enum ExpectedComparisonResult {
|
|
case lt, eq, gt
|
|
|
|
public func isLT() -> Bool {
|
|
return self == .lt
|
|
}
|
|
|
|
public func isEQ() -> Bool {
|
|
return self == .eq
|
|
}
|
|
|
|
public func isGT() -> Bool {
|
|
return self == .gt
|
|
}
|
|
|
|
public func isLE() -> Bool {
|
|
return isLT() || isEQ()
|
|
}
|
|
|
|
public func isGE() -> Bool {
|
|
return isGT() || isEQ()
|
|
}
|
|
|
|
public func isNE() -> Bool {
|
|
return !isEQ()
|
|
}
|
|
|
|
public func flip() -> ExpectedComparisonResult {
|
|
switch self {
|
|
case .lt:
|
|
return .gt
|
|
case .eq:
|
|
return .eq
|
|
case .gt:
|
|
return .lt
|
|
}
|
|
}
|
|
}
|
|
|
|
extension ExpectedComparisonResult : CustomStringConvertible {
|
|
public var description: String {
|
|
switch self {
|
|
case .lt:
|
|
return "<"
|
|
case .eq:
|
|
return "=="
|
|
case .gt:
|
|
return ">"
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `Comparable`, using `oracle` to generate comparison
|
|
/// expectations from pairs of positions in `instances`.
|
|
///
|
|
/// - Note: `oracle` is also checked for conformance to the
|
|
/// laws.
|
|
public func checkComparable<
|
|
Instances: Collection where Instances.Iterator.Element : Comparable
|
|
>(
|
|
_ instances: Instances,
|
|
oracle: (Instances.Index, Instances.Index) -> ExpectedComparisonResult,
|
|
${TRACE}
|
|
) {
|
|
// Also checks that equality is consistent with comparison and that
|
|
// the oracle obeys the equality laws
|
|
checkEquatable(instances, oracle: { oracle($0, $1).isEQ() }, ${trace})
|
|
|
|
for i in instances.indices {
|
|
let x = instances[i]
|
|
|
|
expectFalse(x < x, ${trace})
|
|
expectFalse(x > x, ${trace})
|
|
expectTrue(x <= x, ${trace})
|
|
expectTrue(x >= x, ${trace})
|
|
|
|
for j in instances.indices where i != j {
|
|
let y = instances[j]
|
|
|
|
let expected = oracle(i, j)
|
|
|
|
expectEqual(
|
|
expected.flip(), oracle(j, i),
|
|
"bad oracle: missing antisymmetry: "
|
|
+ "(\(String(reflecting: i)), \(String(reflecting: j)))",
|
|
stackTrace: ${stackTrace})
|
|
|
|
expectEqual(expected.isLT(), x < y, ${trace})
|
|
expectEqual(expected.isLE(), x <= y, ${trace})
|
|
expectEqual(expected.isGE(), x >= y, ${trace})
|
|
expectEqual(expected.isGT(), x > y, ${trace})
|
|
|
|
for k in instances.indices {
|
|
let expected2 = oracle(j, k)
|
|
if expected == expected2 {
|
|
expectEqual(
|
|
expected, oracle(i, k),
|
|
"bad oracle: missing transitivity "
|
|
+ "(\(String(reflecting: i)), \(String(reflecting: j)), "
|
|
+ "\(String(reflecting: k)))", stackTrace: ${stackTrace})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkComparable<T : Comparable>(
|
|
_ expected: ExpectedComparisonResult, _ lhs: T, _ rhs: T, ${TRACE}
|
|
) {
|
|
checkComparable(
|
|
[lhs, rhs],
|
|
oracle: { [[ .eq, expected], [ expected.flip(), .eq]][$0][$1] },
|
|
${trace})
|
|
}
|
|
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `Strideable`, using `advanceOracle` and
|
|
/// 'distanceOracle' to generate expectations about the results of
|
|
/// `advanced(by:)` and `distance(to:)` from pairs of positions in
|
|
/// `instances` and `strides`.
|
|
///
|
|
/// - Note: `oracle` is also checked for conformance to the
|
|
/// laws.
|
|
public func checkStrideable<
|
|
Instances: Collection, Strides: Collection
|
|
where
|
|
Instances.Iterator.Element : Strideable,
|
|
Strides.Iterator.Element == Instances.Iterator.Element.Stride
|
|
>(
|
|
_ instances: Instances, strides: Strides,
|
|
distanceOracle:
|
|
(Instances.Index, Instances.Index) -> Strides.Iterator.Element,
|
|
advanceOracle:
|
|
(Instances.Index, Strides.Index) -> Instances.Iterator.Element,
|
|
${TRACE}
|
|
) {
|
|
checkComparable(
|
|
instances,
|
|
oracle: {
|
|
let d = distanceOracle($1, $0);
|
|
return d < 0 ? .lt : d == 0 ? .eq : .gt
|
|
},
|
|
${trace})
|
|
|
|
for i in instances.indices {
|
|
let x = instances[i]
|
|
expectEqual(x, x.advanced(by: 0))
|
|
|
|
for j in strides.indices {
|
|
let y = strides[j]
|
|
expectEqual(advanceOracle(i, j), x.advanced(by: y))
|
|
}
|
|
|
|
for j in instances.indices {
|
|
let y = instances[j]
|
|
expectEqual(distanceOracle(i, j), x.distance(to: y))
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct CollectionMisuseResiliencyChecks {
|
|
public enum FailureKind {
|
|
case none
|
|
case trap
|
|
case expectationFailure
|
|
}
|
|
|
|
public var callNextOnExhaustedGenerator: Bool = true
|
|
public var creatingOutOfBoundsIndicesBehavior: FailureKind = .trap
|
|
public var subscriptOnOutOfBoundsIndicesBehavior: FailureKind = .trap
|
|
public var subscriptRangeOnOutOfBoundsRangesBehavior: FailureKind = .trap
|
|
|
|
public static var all: CollectionMisuseResiliencyChecks {
|
|
return CollectionMisuseResiliencyChecks()
|
|
}
|
|
|
|
public static var none: CollectionMisuseResiliencyChecks {
|
|
return CollectionMisuseResiliencyChecks(
|
|
callNextOnExhaustedGenerator: false,
|
|
creatingOutOfBoundsIndicesBehavior: .none,
|
|
subscriptOnOutOfBoundsIndicesBehavior: .none,
|
|
subscriptRangeOnOutOfBoundsRangesBehavior: .none)
|
|
}
|
|
}
|
|
|
|
internal func _checkIncrementalAdvance<
|
|
Instances : Collection
|
|
where
|
|
Instances.Iterator.Element : ForwardIndex
|
|
>(
|
|
_ instances: Instances,
|
|
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
|
|
limit: Instances.Iterator.Element,
|
|
sign: Instances.Iterator.Element.Distance, // 1 or -1
|
|
next: (Instances.Iterator.Element) -> Instances.Iterator.Element,
|
|
${TRACE}
|
|
) {
|
|
for i in instances {
|
|
let d = sign > 0 ? i.distance(to: limit) : -limit.distance(to: i)
|
|
|
|
var offset: Instances.Iterator.Element.Distance = 0
|
|
for _ in 0...(d * sign).toIntMax() {
|
|
let j = i.advanced(by: offset)
|
|
let k = i.advanced(by: offset + sign, limit: limit)
|
|
let jAtLimit = offset == d
|
|
if jAtLimit {
|
|
expectEqual(limit, j, ${trace})
|
|
}
|
|
expectEqual(jAtLimit ? j : next(j), k, ${trace})
|
|
offset += sign
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `ForwardIndex`, using `equalityOracle` to
|
|
/// generate equality expectations from pairs of positions in
|
|
/// `instances`.
|
|
///
|
|
/// - Precondition: `endIndex` is reachable from all elements of
|
|
/// `instances`
|
|
public func checkForwardIndex<
|
|
Instances : Collection
|
|
where
|
|
Instances.Iterator.Element : ForwardIndex
|
|
>(
|
|
_ instances: Instances,
|
|
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
|
|
endIndex: Instances.Iterator.Element, ${TRACE}
|
|
) {
|
|
typealias Index = Instances.Iterator.Element
|
|
|
|
checkIncrementable(
|
|
instances, equalityOracle: equalityOracle, endIndex: endIndex, ${trace})
|
|
|
|
_checkIncrementalAdvance(
|
|
instances, equalityOracle: equalityOracle, limit: endIndex,
|
|
sign: 1, next: { $0.successor() }, ${trace})
|
|
}
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `BidirectionalIndex`, using `equalityOracle`
|
|
/// to generate equality expectations from pairs of positions in
|
|
/// `instances`.
|
|
///
|
|
/// - Precondition:
|
|
/// - all elements of `instances` are reachable from `startIndex`.
|
|
/// - `endIndex` is reachable from all elements of `instances`.
|
|
public func checkBidirectionalIndex<
|
|
Instances: Collection
|
|
where
|
|
Instances.Iterator.Element : BidirectionalIndex
|
|
>(
|
|
_ instances: Instances,
|
|
equalityOracle: (Instances.Index, Instances.Index) -> Bool,
|
|
startIndex: Instances.Iterator.Element,
|
|
endIndex: Instances.Iterator.Element,
|
|
${TRACE}
|
|
) {
|
|
typealias Index = Instances.Iterator.Element
|
|
|
|
checkForwardIndex(
|
|
instances, equalityOracle: equalityOracle, endIndex: endIndex)
|
|
|
|
checkDecrementable(
|
|
instances, equalityOracle: equalityOracle, startIndex: startIndex, ${trace})
|
|
|
|
_checkIncrementalAdvance(
|
|
instances, equalityOracle: equalityOracle, limit: startIndex,
|
|
sign: -1, next: { $0.predecessor() }, ${trace})
|
|
}
|
|
|
|
/// Test that the elements of `instances` satisfy the semantic
|
|
/// requirements of `RandomAccessIndex`, using
|
|
/// `advanceOracle` and 'distanceOracle' to generate expectations
|
|
/// about the results of `advanced(by:)` and `distance(to:)` from pairs of
|
|
/// positions in `instances` and `distances`.
|
|
///
|
|
/// - Precondition:
|
|
/// - all elements of `instances` are reachable from `startIndex`.
|
|
/// - `endIndex` is reachable from all elements of `instances`.
|
|
public func checkRandomAccessIndex<
|
|
Instances : Collection, Distances : Collection
|
|
where
|
|
Instances.Iterator.Element : RandomAccessIndex,
|
|
Distances.Iterator.Element == Instances.Iterator.Element.Distance,
|
|
Instances.Iterator.Element.Distance == Instances.Iterator.Element.Stride
|
|
>(
|
|
_ instances: Instances, distances: Distances,
|
|
distanceOracle:
|
|
(Instances.Index, Instances.Index) -> Distances.Iterator.Element,
|
|
advanceOracle:
|
|
(Instances.Index, Distances.Index) -> Instances.Iterator.Element,
|
|
startIndex: Instances.Iterator.Element,
|
|
endIndex: Instances.Iterator.Element,
|
|
${TRACE}
|
|
) {
|
|
checkBidirectionalIndex(
|
|
instances, equalityOracle: { distanceOracle($0, $1) == 0 },
|
|
startIndex: startIndex, endIndex: endIndex, ${trace})
|
|
|
|
checkStrideable(
|
|
instances, strides: distances,
|
|
distanceOracle: distanceOracle,
|
|
advanceOracle: advanceOracle, ${trace})
|
|
}
|
|
|
|
// Generate two overloads: one for Array (which will get
|
|
// picked up when the caller passes a literal), and another that
|
|
// accepts any appropriate Collection type.
|
|
% for genericParam, Element, Expected in zip(
|
|
% ('Expected: Collection', 'Element'),
|
|
% ('Expected.Iterator.Element', 'Element'),
|
|
% ('Expected', 'Array<Element>')):
|
|
|
|
public func checkIterator<
|
|
${genericParam}, I : IteratorProtocol
|
|
where I.Element == ${Element}
|
|
>(
|
|
_ expected: ${Expected},
|
|
_ iterator: I,
|
|
${TRACE},
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
sameValue: (${Element}, ${Element}) -> Bool
|
|
) {
|
|
// Copying a `IteratorProtocol` is allowed.
|
|
var mutableGen = iterator
|
|
var actual: [${Element}] = []
|
|
while let e = mutableGen.next() {
|
|
actual.append(e)
|
|
}
|
|
expectEqualSequence(
|
|
expected, actual, ${trace}, sameValue: sameValue)
|
|
|
|
if resiliencyChecks.callNextOnExhaustedGenerator {
|
|
// Having returned `.None` once, a `IteratorProtocol` should not generate more
|
|
// elements.
|
|
for _ in 0..<10 {
|
|
expectEmpty(mutableGen.next(), ${trace})
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkIterator<
|
|
${genericParam}, I : IteratorProtocol
|
|
where I.Element == ${Element}, ${Element} : Equatable
|
|
>(
|
|
_ expected: ${Expected},
|
|
_ iterator: I,
|
|
${TRACE},
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all
|
|
) {
|
|
checkIterator(
|
|
expected, iterator, ${trace},
|
|
resiliencyChecks: resiliencyChecks, showFrame: false
|
|
) { $0 == $1 }
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : Sequence
|
|
where
|
|
S.Iterator.Element == ${Element}
|
|
>(
|
|
_ expected: ${Expected},
|
|
_ sequence: S,
|
|
${TRACE},
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
sameValue: (${Element}, ${Element}) -> Bool
|
|
) {
|
|
let expectedCount: Int = numericCast(expected.count)
|
|
checkIterator(
|
|
expected, sequence.makeIterator(), ${trace},
|
|
resiliencyChecks: resiliencyChecks,
|
|
sameValue: sameValue)
|
|
|
|
expectGE(
|
|
expectedCount, sequence.underestimatedCount, ${trace})
|
|
|
|
// Check that _copyContents does the right thing if we can do so
|
|
// without destroying the sequence.
|
|
sequence._preprocessingPass { () -> Void in
|
|
var count = 0
|
|
for _ in sequence { count += 1 }
|
|
let buf = UnsafeMutablePointer<S.Iterator.Element>(allocatingCapacity: count)
|
|
let end = sequence._copyContents(initializing: buf)
|
|
expectTrue(end == buf + count, "_copyContents returned the wrong value")
|
|
var j = expected.startIndex
|
|
for i in 0..<(end - buf) {
|
|
expectTrue(sameValue(expected[j], buf[i]))
|
|
j = j.successor()
|
|
}
|
|
buf.deinitialize(count: end - buf)
|
|
buf.deallocateCapacity(count)
|
|
}
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : Sequence
|
|
where
|
|
S.Iterator.Element == ${Element},
|
|
S.Iterator.Element : Equatable
|
|
>(
|
|
_ expected: ${Expected},
|
|
_ sequence: S,
|
|
${TRACE},
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all
|
|
) {
|
|
checkSequence(
|
|
expected, sequence, ${trace},
|
|
resiliencyChecks: resiliencyChecks, showFrame: false
|
|
) { $0 == $1 }
|
|
}
|
|
|
|
%for traversal in ['Forward', 'Bidirectional', 'RandomAccess']:
|
|
|
|
public func check${traversal}Collection<
|
|
${genericParam}, C : Collection
|
|
where
|
|
C.Iterator.Element == ${Element},
|
|
C.Index : ${traversal}Index
|
|
% if traversal == 'RandomAccess':
|
|
, C.Index.Stride == C.Index.Distance
|
|
% end
|
|
>(
|
|
_ expected: ${Expected}, _ collection: C,
|
|
${TRACE},
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
sameValue: (${Element}, ${Element}) -> Bool
|
|
) {
|
|
// A `Collection` is a multi-pass `Sequence`.
|
|
for _ in 0..<3 {
|
|
checkSequence(
|
|
expected, collection, ${trace},
|
|
resiliencyChecks: resiliencyChecks, sameValue: sameValue)
|
|
}
|
|
|
|
// Advances up to 1 positions without passing endIndex. Don't use
|
|
// advanced(by: n) to do this because it's under test here.
|
|
let next = { $0 == collection.endIndex ? $0 : $0.successor() }
|
|
|
|
// advances up to 5 positions without passing endIndex. Picking a
|
|
// small constant to avoid complexity explosion on large input
|
|
// collections.
|
|
let next5 = { next(next(next(next(next($0))))) }
|
|
|
|
let partWay0 = next5(collection.startIndex)
|
|
let partWay1 = next5(partWay0)
|
|
% if traversal == 'Forward':
|
|
checkForwardIndex(
|
|
collection.startIndex..<partWay0, equalityOracle: { $0 == $1 },
|
|
endIndex: partWay1, ${trace})
|
|
% elif traversal == 'Bidirectional':
|
|
checkBidirectionalIndex(
|
|
partWay0..<partWay1, equalityOracle: { $0 == $1 },
|
|
startIndex: collection.startIndex,
|
|
endIndex: next5(partWay1), ${trace})
|
|
% else:
|
|
|
|
% assert(traversal == 'RandomAccess')
|
|
typealias Distance = C.Index.Distance
|
|
let count: Distance = collection.count
|
|
let offset0 = min(5, count)
|
|
let offset1 = min(10, count)
|
|
let offset2 = min(15, count)
|
|
|
|
let distanceCandidates: [Distance] = [
|
|
-11, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 11]
|
|
|
|
let distances = distanceCandidates.filter { (x: Distance) -> Bool in
|
|
x + offset0 >= 0 && x + offset1 <= count
|
|
}
|
|
|
|
func nextN(_ n: C.Index.Distance, _ i: C.Index) -> C.Index {
|
|
var i = i
|
|
if n < 0 {
|
|
for _ in 0 ..< -(n.toIntMax()) {
|
|
i -= 1
|
|
}
|
|
}
|
|
else {
|
|
for _ in 0..<n.toIntMax() {
|
|
i += 1
|
|
}
|
|
}
|
|
return i
|
|
}
|
|
|
|
let instances = Array(partWay0..<partWay1)
|
|
typealias Distances = [Distance]
|
|
checkRandomAccessIndex(
|
|
instances,
|
|
distances: distances,
|
|
distanceOracle: { (x:Int,y:Int) in Distance(IntMax(y - x)) },
|
|
advanceOracle: { x,y in nextN(distances[y], instances[x]) },
|
|
startIndex: collection.startIndex,
|
|
endIndex: next5(partWay1), ${trace})
|
|
% end
|
|
|
|
let expectedArray = Array(expected)
|
|
|
|
expectEqual(
|
|
expectedArray.count.toIntMax(), collection.count.toIntMax(), ${trace})
|
|
|
|
for _ in 0..<3 {
|
|
if true {
|
|
let startIndex = collection.startIndex
|
|
let endIndex = collection.endIndex
|
|
|
|
for _ in collection.indices {
|
|
expectEqual(
|
|
startIndex, collection.startIndex,
|
|
"Iteration should not change startIndex",
|
|
stackTrace: ${stackTrace})
|
|
|
|
expectEqual(
|
|
endIndex, collection.endIndex,
|
|
"Iteration should not change endIndex",
|
|
stackTrace: ${stackTrace})
|
|
}
|
|
}
|
|
|
|
var allIndices = Array(collection.indices)
|
|
|
|
if expectedArray.count >= 2 {
|
|
for i in 0..<allIndices.count-1 {
|
|
let successor1 = allIndices[i].successor()
|
|
var successor2 = allIndices[i]
|
|
successor2 = successor2.successor()
|
|
var successor3 = allIndices[i]
|
|
successor3 = successor3.successor()
|
|
for s in [ successor1, successor2, successor3 ] {
|
|
expectEqual(allIndices[i + 1], s, ${trace})
|
|
expectEqual(
|
|
expectedArray[i + 1], collection[s], ${trace}, sameValue: sameValue)
|
|
}
|
|
}
|
|
% if traversal == "Bidirectional":
|
|
for i in 1..<allIndices.count {
|
|
let predecessor1 = allIndices[i].predecessor()
|
|
var predecessor2 = allIndices[i]
|
|
predecessor2 = predecessor2.predecessor()
|
|
var predecessor3 = allIndices[i]
|
|
predecessor3 = predecessor3.predecessor()
|
|
for p in [ predecessor1, predecessor2, predecessor3 ] {
|
|
expectEqual(allIndices[i - 1], p, ${trace})
|
|
expectEqual(
|
|
expectedArray[i - 1], collection[p], ${trace}, sameValue: sameValue)
|
|
}
|
|
}
|
|
for i in 1..<allIndices.count {
|
|
var index = allIndices[i]
|
|
index = index.predecessor()
|
|
index = index.successor()
|
|
expectEqual(allIndices[i], index, ${trace})
|
|
expectEqual(
|
|
expectedArray[i], collection[index], ${trace}, sameValue: sameValue)
|
|
}
|
|
% end
|
|
}
|
|
|
|
if true {
|
|
var allIndices2: [C.Index] = []
|
|
for i in collection.indices {
|
|
allIndices2.append(i)
|
|
}
|
|
|
|
expectEqualSequence(
|
|
allIndices, allIndices2, "iteration should not invalidate indices",
|
|
stackTrace: ${stackTrace})
|
|
|
|
expectEqualSequence(
|
|
expectedArray, allIndices.map { collection[$0] },
|
|
stackTrace: ${stackTrace}, sameValue: sameValue)
|
|
expectEqualSequence(
|
|
expectedArray, allIndices2.map { collection[$0] },
|
|
stackTrace: ${stackTrace}, sameValue: sameValue)
|
|
}
|
|
}
|
|
|
|
// FIXME: more checks for bidirectional and random access collections.
|
|
}
|
|
|
|
public func check${traversal}Collection<
|
|
${genericParam}, C : Collection
|
|
where
|
|
C.Iterator.Element == ${Element},
|
|
C.Index : ${traversal}Index,
|
|
${Element} : Equatable
|
|
% if traversal == 'RandomAccess':
|
|
, C.Index.Stride == C.Index.Distance
|
|
% end
|
|
>(
|
|
_ expected: ${Expected}, _ collection: C,
|
|
${TRACE},
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all
|
|
) {
|
|
check${traversal}Collection(
|
|
expected, collection, ${trace},
|
|
resiliencyChecks: resiliencyChecks) { $0 == $1 }
|
|
}
|
|
% end
|
|
|
|
// FIXME: merge into checkCollection()
|
|
public func checkSliceableWithBidirectionalIndex<
|
|
${genericParam}, S : Collection
|
|
where
|
|
S.Iterator.Element == ${Element},
|
|
S.SubSequence.Iterator.Element == ${Element},
|
|
S.Index : BidirectionalIndex,
|
|
S.SubSequence : Collection,
|
|
S.SubSequence.Index : BidirectionalIndex,
|
|
${Element} : Equatable
|
|
>(
|
|
_ expected: ${Expected}, _ sliceable: S, ${TRACE}
|
|
) {
|
|
// A `Sliceable` is a `Collection`.
|
|
checkBidirectionalCollection(expected, sliceable, ${trace})
|
|
|
|
let expectedArray = Array(expected)
|
|
|
|
var start = sliceable.startIndex
|
|
for startNumericIndex in 0...expectedArray.count {
|
|
if start != sliceable.endIndex {
|
|
start = start.successor()
|
|
start = start.predecessor()
|
|
start = start.successor()
|
|
start = start.predecessor()
|
|
}
|
|
var end = start
|
|
for endNumericIndex in startNumericIndex...expectedArray.count {
|
|
if end != sliceable.endIndex {
|
|
end = end.successor()
|
|
end = end.predecessor()
|
|
end = end.successor()
|
|
end = end.predecessor()
|
|
}
|
|
let expectedSlice = expectedArray[startNumericIndex..<endNumericIndex]
|
|
let slice = sliceable[start..<end]
|
|
checkBidirectionalCollection(expectedSlice, slice, ${trace})
|
|
|
|
if end != sliceable.endIndex {
|
|
end = end.successor()
|
|
}
|
|
}
|
|
if start != sliceable.endIndex {
|
|
start = start.successor()
|
|
}
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
public func nthIndex<C: Collection>(_ x: C, _ n: Int) -> C.Index {
|
|
return x.startIndex.advanced(by: numericCast(n))
|
|
}
|
|
|
|
public func nth<C: Collection>(_ x: C, _ n: Int) -> C.Iterator.Element {
|
|
return x[nthIndex(x, n)]
|
|
}
|
|
|
|
public func checkRangeReplaceable<
|
|
C: RangeReplaceableCollection,
|
|
N: Collection
|
|
where
|
|
C.Iterator.Element : Equatable,
|
|
C.Iterator.Element == N.Iterator.Element
|
|
>(
|
|
_ makeCollection: () -> C,
|
|
_ makeNewValues: (Int) -> N
|
|
) {
|
|
typealias A = C
|
|
|
|
// First make an independent copy of the array that we can use for
|
|
// comparison later.
|
|
let source = Array<A.Iterator.Element>(makeCollection())
|
|
|
|
for (ix, i) in source.indices.enumerated() {
|
|
for (jx_, j) in (i..<source.endIndex).enumerated() {
|
|
let jx = jx_ + ix
|
|
|
|
let oldCount = jx - ix
|
|
for newCount in 0..<(2 * oldCount) {
|
|
let newValues = makeNewValues(newCount)
|
|
|
|
func reportFailure(_ a: inout A, _ message: String) {
|
|
print("\(message) when replacing indices \(ix)...\(jx)")
|
|
print(" in \(Array(source)) with \(Array(newValues))")
|
|
print(" yielding \(Array(a))")
|
|
print("====================================")
|
|
expectTrue(false)
|
|
}
|
|
|
|
var a = makeCollection()
|
|
|
|
a.replaceSubrange(nthIndex(a, ix)..<nthIndex(a, jx), with: newValues)
|
|
let growth = newCount - oldCount
|
|
|
|
let expectedCount = source.count + growth
|
|
let actualCount = numericCast(a.count) as Int
|
|
if actualCount != expectedCount {
|
|
reportFailure(
|
|
&a, "\(actualCount) != expected count \(expectedCount)")
|
|
}
|
|
|
|
for (kx, k) in a.indices.enumerated() {
|
|
let expectedValue = kx < ix ? nth(source, kx)
|
|
: kx < jx + growth ? nth(newValues, kx - ix)
|
|
: nth(source, kx - growth)
|
|
|
|
if a[k] != expectedValue {
|
|
reportFailure(
|
|
&a,
|
|
// FIXME: why do we need to break this string into two parts?
|
|
"a[\(kx)] = "
|
|
+ "\(a[k]) != expected value \(expectedValue)")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func expectEqualSequence<
|
|
Expected: Sequence,
|
|
Actual: Sequence
|
|
where
|
|
Expected.Iterator.Element == Actual.Iterator.Element,
|
|
Expected.Iterator.Element : Equatable
|
|
>(_ expected: Expected, _ actual: Actual, ${TRACE}) {
|
|
expectEqualSequence(expected, actual, ${trace}) { $0 == $1 }
|
|
}
|
|
|
|
public func expectEqualSequence<
|
|
Expected : Sequence,
|
|
Actual : Sequence,
|
|
T : Equatable,
|
|
U : Equatable
|
|
where
|
|
Expected.Iterator.Element == Actual.Iterator.Element,
|
|
Expected.Iterator.Element == (T, U)
|
|
>(_ expected: Expected, _ actual: Actual, ${TRACE}) {
|
|
expectEqualSequence(
|
|
expected, actual, ${trace}) {
|
|
(lhs: (T, U), rhs: (T, U)) -> Bool in
|
|
lhs.0 == rhs.0 && lhs.1 == rhs.1
|
|
}
|
|
}
|
|
|
|
public func expectEqualSequence<
|
|
Expected: Sequence,
|
|
Actual: Sequence
|
|
where
|
|
Expected.Iterator.Element == Actual.Iterator.Element
|
|
>(_ expected: Expected, _ actual: Actual, ${TRACE},
|
|
sameValue: (Expected.Iterator.Element, Expected.Iterator.Element) -> Bool) {
|
|
|
|
if !expected.elementsEqual(actual, isEquivalent: sameValue) {
|
|
expectationFailure("expected elements: \"\(expected)\"\n"
|
|
+ "actual: \"\(actual)\" (of type \(String(reflecting: actual.dynamicType)))",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectEqualsUnordered<
|
|
Expected : Sequence,
|
|
Actual : Sequence
|
|
where
|
|
Expected.Iterator.Element == Actual.Iterator.Element
|
|
>(
|
|
_ expected: Expected, _ actual: Actual, ${TRACE},
|
|
compare: (Expected.Iterator.Element, Expected.Iterator.Element)
|
|
-> ExpectedComparisonResult
|
|
) {
|
|
let x: [Expected.Iterator.Element] =
|
|
expected.sorted(isOrderedBefore: compose(compare, { $0.isLT() }))
|
|
let y: [Actual.Iterator.Element] =
|
|
actual.sorted(isOrderedBefore: compose(compare, { $0.isLT() }))
|
|
expectEqualSequence(
|
|
x, y, ${trace}, sameValue: compose(compare, { $0.isEQ() }))
|
|
}
|
|
|
|
public func expectEqualsUnordered<
|
|
Expected : Sequence,
|
|
Actual : Sequence
|
|
where
|
|
Expected.Iterator.Element == Actual.Iterator.Element,
|
|
Expected.Iterator.Element : Comparable
|
|
>(
|
|
_ expected: Expected, _ actual: Actual, ${TRACE}
|
|
) {
|
|
expectEqualsUnordered(expected, actual, ${trace}) {
|
|
$0 < $1 ? .lt : $0 == $1 ? .eq : .gt
|
|
}
|
|
}
|
|
|
|
public func expectEqualsUnordered<T : Comparable>(
|
|
_ expected: [T], _ actual: [T], ${TRACE}
|
|
) {
|
|
let x = expected.sorted()
|
|
let y = actual.sorted()
|
|
expectEqualSequence(x, y, ${trace})
|
|
}
|
|
|
|
/// A nominal type that is equivalent to a tuple of two elements.
|
|
///
|
|
/// We need a nominal type because we can't add protocol conformances to
|
|
/// tuples.
|
|
struct Pair<T : Comparable> : Comparable {
|
|
init(_ first: T, _ second: T) {
|
|
self.first = first
|
|
self.second = second
|
|
}
|
|
|
|
var first: T
|
|
var second: T
|
|
}
|
|
|
|
func == <T>(lhs: Pair<T>, rhs: Pair<T>) -> Bool {
|
|
return lhs.first == rhs.first && lhs.second == rhs.second
|
|
}
|
|
|
|
func < <T>(lhs: Pair<T>, rhs: Pair<T>) -> Bool {
|
|
return [ lhs.first, lhs.second ].lexicographicallyPrecedes(
|
|
[ rhs.first, rhs.second ])
|
|
}
|
|
|
|
public func expectEqualsUnordered<
|
|
Expected : Sequence,
|
|
Actual : Sequence,
|
|
T : Comparable
|
|
where
|
|
Actual.Iterator.Element == (key: T, value: T),
|
|
Expected.Iterator.Element == (T, T)
|
|
>(
|
|
_ expected: Expected, _ actual: Actual, ${TRACE}
|
|
) {
|
|
func comparePairLess(_ lhs: (T, T), rhs: (T, T)) -> Bool {
|
|
return [ lhs.0, lhs.1 ].lexicographicallyPrecedes([ rhs.0, rhs.1 ])
|
|
}
|
|
|
|
let x: [(T, T)] =
|
|
expected.sorted(isOrderedBefore: comparePairLess)
|
|
let y: [(T, T)] =
|
|
actual.map { ($0.0, $0.1) }
|
|
.sorted(isOrderedBefore: comparePairLess)
|
|
|
|
func comparePairEquals(_ lhs: (T, T), rhs: (key: T, value: T)) -> Bool {
|
|
return lhs.0 == rhs.0 && lhs.1 == rhs.1
|
|
}
|
|
|
|
expectEqualSequence(x, y, ${trace}, sameValue: comparePairEquals)
|
|
}
|
|
|
|
public func expectEqualFunctionsForDomain<ArgumentType, Result : Equatable>(
|
|
_ arguments: [ArgumentType], _ function1: ArgumentType -> Result,
|
|
_ function2: ArgumentType -> Result
|
|
) {
|
|
for a in arguments {
|
|
let expected = function1(a)
|
|
let actual = function2(a)
|
|
expectEqual(expected, actual, "where the argument is: \(a)")
|
|
}
|
|
}
|
|
|
|
public func expectEqualMethodsForDomain<
|
|
SelfType, ArgumentType, Result : Equatable
|
|
>(
|
|
_ selfs: [SelfType], _ arguments: [ArgumentType],
|
|
_ function1: SelfType -> ArgumentType -> Result,
|
|
_ function2: SelfType -> ArgumentType -> Result
|
|
) {
|
|
for s in selfs {
|
|
for a in arguments {
|
|
let expected = function1(s)(a)
|
|
let actual = function2(s)(a)
|
|
expectEqual(
|
|
expected, actual,
|
|
"where the first argument is: \(s)\nand the second argument is: \(a)"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func expectEqualUnicodeScalars(
|
|
_ expected: [UInt32], _ actual: String, ${TRACE}) {
|
|
let actualUnicodeScalars = Array(
|
|
actual.unicodeScalars.lazy.map { $0.value })
|
|
|
|
if !expected.elementsEqual(actualUnicodeScalars) {
|
|
expectationFailure(
|
|
"expected elements: \"\(asHex(expected))\"\n"
|
|
+ "actual: \"\(asHex(actualUnicodeScalars))\"",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectPrinted<T>(
|
|
expectedOneOf patterns: [String], _ object: T, ${TRACE}
|
|
) {
|
|
let actual = String(object)
|
|
if !patterns.contains(actual) {
|
|
expectationFailure(
|
|
"expected: any of \(String(reflecting: patterns))\n"
|
|
+ "actual: \(String(reflecting: actual))",
|
|
trace: ${trace})
|
|
}
|
|
}
|
|
|
|
public func expectPrinted<T>(
|
|
_ expected: String, _ object: T, ${TRACE}
|
|
) {
|
|
expectPrinted(expectedOneOf: [expected], object, ${trace})
|
|
}
|
|
|
|
public func expectDebugPrinted<T>(
|
|
expectedOneOf patterns: [String], _ object: T, ${TRACE}
|
|
) {
|
|
expectPrinted(expectedOneOf: patterns, String(reflecting: object), ${trace})
|
|
}
|
|
|
|
public func expectDebugPrinted<T>(
|
|
_ expected: String, _ object: T, ${TRACE}
|
|
) {
|
|
expectDebugPrinted(expectedOneOf: [expected], object, ${trace})
|
|
}
|
|
|
|
func compose<A, B, C>(_ f: A -> B, _ g: B -> C) -> A -> C {
|
|
return { a in
|
|
return g(f(a))
|
|
}
|
|
}
|
|
|
|
/// A type that does not conform to any protocols.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// conformances.
|
|
public struct OpaqueValue<Underlying> {
|
|
public var value: Underlying
|
|
public var identity: Int
|
|
|
|
public init(_ value: Underlying) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Underlying, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
|
|
/// A type that conforms only to `Equatable`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct MinimalEquatableValue : Equatable {
|
|
public static var timesEqualEqualWasCalled: Int = 0
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
public func == (
|
|
lhs: MinimalEquatableValue,
|
|
rhs: MinimalEquatableValue
|
|
) -> Bool {
|
|
MinimalEquatableValue.timesEqualEqualWasCalled += 1
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
/// A type that conforms only to `Equatable` and `Comparable`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct MinimalComparableValue : Equatable, Comparable {
|
|
public static var timesEqualEqualWasCalled = ResettableValue(0)
|
|
public static var timesLessWasCalled = ResettableValue(0)
|
|
|
|
public static var equalImpl =
|
|
ResettableValue<(Int, Int) -> Bool>({ $0 == $1 })
|
|
public static var lessImpl =
|
|
ResettableValue<(Int, Int) -> Bool>({ $0 < $1 })
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalComparableValue,
|
|
rhs: MinimalComparableValue
|
|
) -> Bool {
|
|
MinimalComparableValue.timesEqualEqualWasCalled.value += 1
|
|
return MinimalComparableValue.equalImpl.value(lhs.value, rhs.value)
|
|
}
|
|
|
|
public func < (
|
|
lhs: MinimalComparableValue,
|
|
rhs: MinimalComparableValue
|
|
) -> Bool {
|
|
MinimalComparableValue.timesLessWasCalled.value += 1
|
|
return MinimalComparableValue.lessImpl.value(lhs.value, rhs.value)
|
|
}
|
|
|
|
/// A type that conforms only to `Comparable` and `ForwardIndexType`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct MinimalComparableIndexValue : Comparable, ForwardIndex {
|
|
public static var timesEqualEqualWasCalled = ResettableValue(0)
|
|
public static var timesLessWasCalled = ResettableValue(0)
|
|
|
|
public func successor() -> MinimalComparableIndexValue {
|
|
return MinimalComparableIndexValue(value.successor())
|
|
}
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalComparableIndexValue,
|
|
rhs: MinimalComparableIndexValue
|
|
) -> Bool {
|
|
MinimalComparableIndexValue.timesEqualEqualWasCalled.value += 1
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
public func < (
|
|
lhs: MinimalComparableIndexValue,
|
|
rhs: MinimalComparableIndexValue
|
|
) -> Bool {
|
|
MinimalComparableIndexValue.timesLessWasCalled.value += 1
|
|
return lhs.value < rhs.value
|
|
}
|
|
|
|
% for kind in ['Value', 'Class']:
|
|
% Self = 'MinimalHashable%s' % kind
|
|
|
|
/// A type that conforms only to `Equatable` and `Hashable`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct ${Self} : Equatable, Hashable {
|
|
public static var timesEqualEqualWasCalled: Int = 0
|
|
public static var timesHashValueWasCalled: Int = 0
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
|
|
public var hashValue: Int {
|
|
${Self}.timesHashValueWasCalled += 1
|
|
return value.hashValue
|
|
}
|
|
}
|
|
|
|
public func == (
|
|
lhs: ${Self},
|
|
rhs: ${Self}
|
|
) -> Bool {
|
|
${Self}.timesEqualEqualWasCalled += 1
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
% end
|
|
|
|
// ${'Local Variables'}:
|
|
// eval: (read-only-mode 1)
|
|
// End:
|