mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
2749 lines
78 KiB
Swift
2749 lines
78 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'''
|
|
|
|
# When the parameter list would start with a ${TRACE}, we use
|
|
# ${TRACE1} instead, to avoid the warning about an extraneous
|
|
# '_' on the first parameter.
|
|
TRACE1 = TRACE.replace(' _ ', ' ', 1)
|
|
|
|
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(${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 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 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(${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
|
|
}
|
|
|
|
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__"
|
|
|
|
@_silgen_name("swift_stdlib_installTrapInterceptor")
|
|
func _stdlib_installTrapInterceptor()
|
|
|
|
func _childProcess() {
|
|
_stdlib_installTrapInterceptor()
|
|
while let line = _stdlib_getline() {
|
|
let parts = line._split(separator: ";")
|
|
let testSuiteName = parts[0]
|
|
let testName = parts[1]
|
|
|
|
let testSuite = _allTestSuites[_testSuiteNameToIndex[testSuiteName]!]
|
|
_anyExpectFailed = false
|
|
testSuite._runTest(testName)
|
|
|
|
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.
|
|
mutating func _runTestInChild(testSuite: TestSuite, _ testName: String)
|
|
-> (anyExpectFailed: Bool, seenExpectCrash: Bool,
|
|
status: ProcessTerminationStatus?,
|
|
crashStdout: [String], crashStderr: [String]) {
|
|
if _pid <= 0 {
|
|
_spawnChild()
|
|
}
|
|
|
|
print("\(testSuite.name);\(testName)", 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("out>>> \(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)
|
|
}
|
|
print("err>>> \(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)
|
|
}
|
|
|
|
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 {
|
|
let fullTestName = "\(testSuite.name).\(t.name)"
|
|
if let filter = _filter where findSubstring(fullTestName, filter) == nil {
|
|
continue
|
|
}
|
|
|
|
let activeSkips = t.getActiveSkipPredicates()
|
|
if !activeSkips.isEmpty {
|
|
skippedTests += [ t.name ]
|
|
print("[ SKIP ] \(fullTestName) (skip: \(activeSkips))")
|
|
continue
|
|
}
|
|
|
|
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(t.name)
|
|
}
|
|
} else {
|
|
(_anyExpectFailed, expectCrash, childTerminationStatus, crashStdout,
|
|
crashStderr) =
|
|
_runTestInChild(testSuite, t.name)
|
|
}
|
|
|
|
// 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 expectedCrashOutput in t.crashOutputMatches {
|
|
var found = false
|
|
for s in crashOutput {
|
|
if findSubstring(s, expectedCrashOutput) != nil {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
print("did not find expected string after crash: \(expectedCrashOutput.debugDescription)")
|
|
testPassed = false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply XFAILs.
|
|
switch (testPassed, expectXFail) {
|
|
case (true, false):
|
|
print("[ OK ] \(fullTestName)")
|
|
|
|
case (true, true):
|
|
uxpassedTests += [ t.name ]
|
|
print("[ UXPASS ] \(fullTestName)")
|
|
|
|
case (false, false):
|
|
failedTests += [ t.name ]
|
|
print("[ FAIL ] \(fullTestName)")
|
|
|
|
case (false, true):
|
|
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(testName: String) {
|
|
PersistentState.ranSomething = true
|
|
for r in _allResettables {
|
|
r.reset()
|
|
}
|
|
LifetimeTracked.instances = 0
|
|
if let f = _testSetUpCode {
|
|
f()
|
|
}
|
|
let test = _testByName(testName)
|
|
test.code()
|
|
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]!]
|
|
}
|
|
|
|
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: () -> Void
|
|
|
|
/// 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() }
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
public func code(testFunction: () -> Void) {
|
|
_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: testFunction))
|
|
_testSuite._testNameToIndex[_name] = _testSuite._tests.count - 1
|
|
}
|
|
}
|
|
|
|
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, 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:
|