Files
swift-mirror/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb
2016-04-01 14:12:17 -07:00

2231 lines
62 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
//
//===----------------------------------------------------------------------===//
%{
from gyb_stdlib_unittest_support import TRACE, TRACE1, stackTrace, trace
}%
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())))
}
}
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(${TRACE1}, 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 (actual == nil) != (expected == nil)
|| actual != nil && expected! != actual! {
expectationFailure(
"expected: \"\(expected)\" (of type \(String(reflecting: expected.dynamicType)))\n"
+ "actual: \"\(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 expectTrapping<R : _RangeProtocol>(
point: R.Bound, in range: R, ${TRACE}
) {
if !range.contains(point) {
expectationFailure("\(point) in \(range)", trace: ${trace})
_trappingExpectationFailedCallback()
}
}
extension _RangeProtocol {
internal var _isHalfOpen: Bool {
return !self.contains(self.upperBound)
}
}
extension _HalfOpenRange {
internal func _contains<
Other : _RangeProtocol
where
Other.Bound == Bound
>(other: Other) -> Bool {
if other.lowerBound < lowerBound { return false }
if other._isHalfOpen {
if upperBound < other.upperBound { return false }
} else {
if upperBound <= other.upperBound { return false }
}
return true
}
}
extension _ClosedRange {
internal func _contains<
Other : _ClosedRange
where
Other.Bound == Bound
>(other: Other) -> Bool {
if other.lowerBound < lowerBound { return false }
if upperBound < other.upperBound { return false }
return true
}
}
public func expectTrapping<
R1 : _RangeProtocol, R2 : _HalfOpenRange
where
R1.Bound == R2.Bound
>(
subRange: R1, in range: R2, ${TRACE}
) {
if !range._contains(subRange) {
expectationFailure("\(subRange) in \(range)", trace: ${trace})
_trappingExpectationFailedCallback()
}
}
public func expectTrapping<
R1 : _ClosedRange, R2 : _ClosedRange
where
R1.Bound == R2.Bound
>(
subRange: R1, in range: R2, ${TRACE}
) {
if !range._contains(subRange) {
expectationFailure("\(subRange) in \(range)", trace: ${trace})
_trappingExpectationFailedCallback()
}
}
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 that all associated types of a `Collection` are what we expect them
/// to be.
public func expectCollectionAssociatedTypes<
X : Collection
where
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
X._Element == X.Iterator.Element,
X.SubSequence : Collection,
X.SubSequence.Iterator.Element == X.Iterator.Element,
X.SubSequence.SubSequence == X.SubSequence,
X.Indices : Collection,
X.Indices.Iterator.Element == X.Index,
X.Indices.Index == X.Index,
X.Indices.SubSequence == X.Indices
>(
collectionType collectionType: X.Type,
iteratorType: X.Iterator.Type,
subSequenceType: X.SubSequence.Type,
indexType: X.Index.Type,
indexDistanceType: X.IndexDistance.Type,
indicesType: X.Indices.Type
) {}
/// Check that all associated types of a `BidirectionalCollection` are what we
/// expect them to be.
public func expectBidirectionalCollectionAssociatedTypes<
X : BidirectionalCollection
where
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
X._Element == X.Iterator.Element,
X.SubSequence : BidirectionalCollection,
X.SubSequence.Iterator.Element == X.Iterator.Element,
X.SubSequence.SubSequence == X.SubSequence,
X.Indices : BidirectionalCollection,
X.Indices.Iterator.Element == X.Index,
X.Indices.Index == X.Index,
X.Indices.SubSequence == X.Indices
>(
collectionType collectionType: X.Type,
iteratorType: X.Iterator.Type,
subSequenceType: X.SubSequence.Type,
indexType: X.Index.Type,
indexDistanceType: X.IndexDistance.Type,
indicesType: X.Indices.Type
) {}
/// Check that all associated types of a `RandomAccessCollection` are what we
/// expect them to be.
public func expectRandomAccessCollectionAssociatedTypes<
X : RandomAccessCollection
where
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
X._Element == X.Iterator.Element,
X.SubSequence : RandomAccessCollection,
X.SubSequence.Iterator.Element == X.Iterator.Element,
X.SubSequence.SubSequence == X.SubSequence,
X.Indices : RandomAccessCollection,
X.Indices.Iterator.Element == X.Index,
X.Indices.Index == X.Index,
X.Indices.SubSequence == X.Indices
>(
collectionType collectionType: X.Type,
iteratorType: X.Iterator.Type,
subSequenceType: X.SubSequence.Type,
indexType: X.Index.Type,
indexDistanceType: X.IndexDistance.Type,
indicesType: X.Indices.Type
) {}
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(${TRACE1}) {
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
}
func _defaultTrappingExpectationFailedCallback() {
abort()
}
var _trappingExpectationFailedCallback: () -> Void
= _defaultTrappingExpectationFailedCallback
public func _setTrappingExpectationFailedCallback(callback: () -> Void) {
_trappingExpectationFailedCallback = callback
}
extension ProcessTerminationStatus {
var isSwiftTrap: Bool {
switch self {
case .exit(_):
return false
case .signal(let signal):
return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP
}
}
}
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:")
print("$ \(Process.arguments[0]) " +
"--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 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)
}
var 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 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()
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 != nil) ? String(cString: cs) : nil
}
#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, ClosedRange<Int>, reason: String)
case osxBugFix(Int, Int, Int, reason: String)
case osxBugFixRange(Int, Int, ClosedRange<Int>, reason: String)
case iOSAny(/*reason:*/ String)
case iOSMajor(Int, reason: String)
case iOSMinor(Int, Int, reason: String)
case iOSMinorRange(Int, ClosedRange<Int>, reason: String)
case iOSBugFix(Int, Int, Int, reason: String)
case iOSBugFixRange(Int, Int, ClosedRange<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, ClosedRange<Int>, reason: String)
case tvOSBugFix(Int, Int, Int, reason: String)
case tvOSBugFixRange(Int, Int, ClosedRange<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, ClosedRange<Int>, reason: String)
case watchOSBugFix(Int, Int, Int, reason: String)
case watchOSBugFixRange(Int, Int, ClosedRange<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,
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
Instances.Indices.Iterator.Element == Instances.Index
>(
instances: Instances,
oracle: (Instances.Index, Instances.Index) -> Bool,
${TRACE}
) {
// TODO: swift-3-indexing-model: add tests for this function.
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
let predictedYZ = oracle(j, k)
if predictedXY && predictedYZ {
expectTrue(
oracle(i, k),
"bad oracle: broken transitivity at indices \(i), \(j), \(k)")
expectEqual(y, z, ${trace})
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,
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
Instances.Indices.Iterator.Element == Instances.Index
>(
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})
}
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,
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
Instances.Indices.Iterator.Element == Instances.Index
>(
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,
Instances.Iterator.Element.Stride == Strides.Iterator.Element,
// FIXME(compiler limitation): these constraints should be applied to
// associated types of Collection.
Instances.Indices.Iterator.Element == Instances.Index,
Strides.Indices.Iterator.Element == Strides.Index
>(
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 func nthIndex<C: Collection>(x: C, _ n: Int) -> C.Index {
return x.index(numericCast(n), stepsFrom: x.startIndex)
}
public func nth<C: Collection>(x: C, _ n: Int) -> C.Iterator.Element {
return x[nthIndex(x, n)]
}
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})
}
}
func compose<A, B, C>(f: A -> B, _ g: B -> C) -> A -> C {
return { a in
return g(f(a))
}
}
// ${'Local Variables'}:
// eval: (read-only-mode 1)
// End: