mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
2710 lines
77 KiB
Swift
2710 lines
77 KiB
Swift
//===--- StdlibUnittest.swift.gyb -----------------------------*- swift -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 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 SwiftPrivateDarwinExtras
|
|
|
|
#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
|
|
import Darwin
|
|
#elseif os(Linux)
|
|
import Glibc
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
import ObjectiveC
|
|
#endif
|
|
|
|
public struct SourceLoc {
|
|
public let file: String
|
|
public let line: UWord
|
|
public let comment: String?
|
|
|
|
public init(_ file: String, _ line: UWord, comment: String? = nil) {
|
|
self.file = file
|
|
self.line = line
|
|
self.comment = comment
|
|
}
|
|
|
|
public func withCurrentLoc(
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) -> SourceLocStack {
|
|
return SourceLocStack(self).with(SourceLoc(file, line))
|
|
}
|
|
}
|
|
|
|
public struct SourceLocStack {
|
|
let locs: _UnitTestArray<SourceLoc>
|
|
|
|
public init() {
|
|
locs = []
|
|
}
|
|
|
|
public init(_ loc: SourceLoc) {
|
|
locs = [ loc ]
|
|
}
|
|
|
|
init(_locs: _UnitTestArray<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 withCurrentLoc(
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) -> SourceLocStack {
|
|
return with(SourceLoc(file, line))
|
|
}
|
|
}
|
|
|
|
func _printStackTrace(stackTrace: SourceLocStack?) {
|
|
guard let s = stackTrace else { return }
|
|
println("stacktrace:")
|
|
for i in 0..<s.locs.count() {
|
|
let loc = s.locs[s.locs.count() - i - 1]
|
|
let comment = (loc.comment != nil) ? " ; \(loc.comment!)" : ""
|
|
println(" #\(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
|
|
|
|
public func expectEqual<T : Equatable>(
|
|
expected: T, _ actual: T,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqual(expected, actual, {$0 == $1},
|
|
stackTrace: stackTrace, file: file, line: line,
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func expectEqual<T : Equatable, U : Equatable>(
|
|
expected: (T, U), _ actual: (T, U),
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqual(expected.0, actual.0, {$0 == $1},
|
|
stackTrace: stackTrace, file: file, line: line,
|
|
collectMoreInfo: collectMoreInfo)
|
|
expectEqual(expected.1, actual.1, {$0 == $1},
|
|
stackTrace: stackTrace, file: file, line: line,
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func expectEqual<T>(
|
|
expected: T, _ actual: T, _ equal: (T,T)->Bool,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if !equal(expected, actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: \"\(expected)\" (of type \(_stdlib_getDemangledTypeName(expected)))")
|
|
println("actual: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(expected)))")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectNotEqual<T : Equatable>(
|
|
expected: T, _ actual: T,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if expected == actual {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("unexpected value: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
// Can not 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?,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if (actual == nil) || expected != actual! {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: \"\(expected)\" (of type \(_stdlib_getDemangledTypeName(expected)))")
|
|
println("actual: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectEqual<T : Equatable>(
|
|
expected: T?, _ actual: T?,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if (actual == nil) != (expected == nil)
|
|
|| actual != nil && expected! != actual! {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: \"\(expected)\" (of type \(_stdlib_getDemangledTypeName(expected)))")
|
|
println("actual: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
println()
|
|
}
|
|
}
|
|
|
|
// Array<T> is not Equatable if T is. Provide additional overloads.
|
|
// Same for Dictionary.
|
|
%for (Generic, EquatableType) in [
|
|
% ('<T : Equatable>', 'ContiguousArray<T>'),
|
|
% ('<T : Equatable>', '_UnitTestArray<T>'),
|
|
% ('<T : Equatable>', 'ArraySlice<T>'),
|
|
% ('<T : Equatable>', 'Array<T>'),
|
|
% ('<T, U : Equatable>', 'Dictionary<T, U>')]:
|
|
|
|
public func expectEqual${Generic}(
|
|
expected: ${EquatableType}, _ actual: ${EquatableType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqual(
|
|
expected, actual,
|
|
// FIXME: Simpler closures don't work here due to
|
|
// <rdar://problem/17716712> and <rdar://problem/17717618>
|
|
{ (x: ${EquatableType}, y: ${EquatableType})->Bool in x == y },
|
|
stackTrace: stackTrace, file: file, line: line,
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
|
|
public func expectEqualSequence${Generic}(
|
|
expected: ${EquatableType}, _ actual: ${EquatableType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqual(
|
|
expected, actual,
|
|
// FIXME: Simpler closures don't work here due to
|
|
// <rdar://problem/17716712> and <rdar://problem/17717618>
|
|
{ (x: ${EquatableType}, y: ${EquatableType})->Bool in x == y },
|
|
stackTrace: stackTrace, file: file, line: line,
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
|
|
func _expectNotEqual${Generic}(
|
|
expected: ${EquatableType}, actual: ${EquatableType},
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if expected == actual {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("unexpected value: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectOptionalEqual${Generic}(
|
|
expected: ${EquatableType}, _ actual: ${EquatableType}?,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if (actual == nil) || expected != actual! {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: \"\(expected)\" (of type \(_stdlib_getDemangledTypeName(expected)))")
|
|
println("actual: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
println()
|
|
}
|
|
}
|
|
|
|
%end
|
|
|
|
%for ComparableType in ['Int']:
|
|
|
|
public func expectLT(
|
|
expected: ${ComparableType}, _ actual: ${ComparableType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if !(expected < actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: \"\(expected)\"")
|
|
println("actual: \"\(actual)\"")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectLE(
|
|
expected: ${ComparableType}, _ actual: ${ComparableType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if !(expected <= actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: \"\(expected)\"")
|
|
println("actual: \"\(actual)\"")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectGE(
|
|
expected: ${ComparableType}, _ actual: ${ComparableType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if !(expected >= actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: \"\(expected)\"")
|
|
println("actual: \"\(actual)\"")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
%end
|
|
|
|
public func expectType<T>(_: T.Type, inout _ x: T) {}
|
|
|
|
public func isSequenceType<X : SequenceType>(x: X) -> X { return x }
|
|
public func expectIsBooleanType<X : BooleanType>(inout x: X) -> X { return x }
|
|
|
|
public struct AssertionResult : CustomStringConvertible, BooleanType {
|
|
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(
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("this code should not be executed")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
|
|
public func expectUnreachableCatch(
|
|
error: _ErrorType,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("error should not be thrown: \"\(error)\"")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
|
|
%for BoolType in ['Bool', 'AssertionResult']:
|
|
|
|
public func expectTrue(
|
|
actual: ${BoolType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if !actual {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: true")
|
|
println("actual: \(actual)")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectFalse(
|
|
actual: ${BoolType},
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if actual {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected: false")
|
|
println("actual: \(actual)")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
%end
|
|
|
|
public func expectEmpty<T>(
|
|
value: Optional<T>,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if value != nil {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected optional to be empty")
|
|
println("actual: \"\(value)\"")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectNotEmpty<T>(
|
|
value: Optional<T>,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) -> T? {
|
|
if value == nil {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected optional to be non-empty")
|
|
println()
|
|
}
|
|
return value
|
|
}
|
|
|
|
public func expectCrashLater() {
|
|
println("\(_stdlibUnittestStreamPrefix);expectCrash;\(_anyExpectFailed)")
|
|
|
|
var stderr = _Stderr()
|
|
println("\(_stdlibUnittestStreamPrefix);expectCrash", &stderr)
|
|
|
|
_seenExpectCrash = true
|
|
}
|
|
|
|
func _defaultTestSuiteFailedCallback() {
|
|
abort()
|
|
}
|
|
|
|
var _testSuiteFailedCallback: () -> () = _defaultTestSuiteFailedCallback
|
|
|
|
public func _setTestSuiteFailedCallback(callback: () -> ()) {
|
|
_testSuiteFailedCallback = callback
|
|
}
|
|
|
|
extension ProcessTerminationStatus {
|
|
var isSwiftTrap: Bool {
|
|
switch self {
|
|
case .Exit(var status):
|
|
return false
|
|
case .Signal(var signal):
|
|
return CInt(signal) == SIGILL || CInt(signal) == SIGTRAP
|
|
}
|
|
}
|
|
}
|
|
|
|
func _stdlib_getline() -> String? {
|
|
var result = _UnitTestArray<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) {
|
|
println("To debug, run:")
|
|
println("$ \(Process.arguments[0]) " +
|
|
"--stdlib-unittest-in-process --stdlib-unittest-filter \"\(fullTestName)\"")
|
|
}
|
|
|
|
var _allTestSuites: _UnitTestArray<TestSuite> = []
|
|
var _testSuiteNameToIndex: [String : Int] = [:]
|
|
|
|
let _stdlibUnittestStreamPrefix = "__STDLIB_UNITTEST__"
|
|
|
|
@asmname("swift_stdlib_installTrapInterceptor")
|
|
func _stdlib_installTrapInterceptor()
|
|
|
|
func _childProcess() {
|
|
_stdlib_installTrapInterceptor()
|
|
while let line = _stdlib_getline() {
|
|
let parts = line._split(";")
|
|
let testSuiteName = parts[0]
|
|
let testName = parts[1]
|
|
|
|
let testSuite = _allTestSuites[_testSuiteNameToIndex[testSuiteName]!]
|
|
_anyExpectFailed = false
|
|
testSuite._runTest(testName)
|
|
|
|
println("\(_stdlibUnittestStreamPrefix);end;\(_anyExpectFailed)")
|
|
|
|
var stderr = _Stderr()
|
|
println("\(_stdlibUnittestStreamPrefix);end", &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?
|
|
|
|
init(runTestsInProcess: Bool, filter: String?) {
|
|
self._runTestsInProcess = runTestsInProcess
|
|
self._filter = filter
|
|
}
|
|
|
|
mutating func _spawnChild() {
|
|
let (pid, childStdinFD, childStdoutFD, childStderrFD) =
|
|
spawnChild([ "--stdlib-unittest-run-child" ])
|
|
_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: _UnitTestArray<String>, crashStderr: _UnitTestArray<String>) {
|
|
if _pid <= 0 {
|
|
_spawnChild()
|
|
}
|
|
|
|
println("\(testSuite.name);\(testName)", &_childStdin)
|
|
|
|
let currentTest = testSuite._testByName(testName)
|
|
if let stdinText = currentTest.stdinText {
|
|
print(stdinText, &_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 = _UnitTestArray<String>()
|
|
var capturedCrashStderr = _UnitTestArray<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(";")
|
|
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)
|
|
}
|
|
println("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(";")
|
|
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)
|
|
}
|
|
println("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 {
|
|
println("StdlibUnittest: using filter: \(filter)")
|
|
}
|
|
for testSuite in _allTestSuites {
|
|
var uxpassedTests = _UnitTestArray<String>()
|
|
var failedTests = _UnitTestArray<String>()
|
|
var skippedTests = _UnitTestArray<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 ]
|
|
println("[ SKIP ] \(fullTestName) (skip: \(activeSkips))")
|
|
continue
|
|
}
|
|
|
|
let activeXFails = t.getActiveXFailPredicates()
|
|
let expectXFail = !activeXFails.isEmpty
|
|
let activeXFailsText = expectXFail ? " (XFAIL: \(activeXFails))" : ""
|
|
println("[ RUN ] \(fullTestName)\(activeXFailsText)")
|
|
|
|
var expectCrash = false
|
|
var childTerminationStatus: ProcessTerminationStatus? = nil
|
|
var crashStdout = _UnitTestArray<String>()
|
|
var crashStderr = _UnitTestArray<String>()
|
|
if _runTestsInProcess {
|
|
if t.stdinText != nil {
|
|
println("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
|
|
var testFailureExplanation = ""
|
|
switch (_anyExpectFailed, childTerminationStatus, expectCrash) {
|
|
case (_, .None, false):
|
|
testPassed = !_anyExpectFailed
|
|
|
|
case (_, .None, true):
|
|
testPassed = false
|
|
testFailureExplanation = "expecting a crash, but the test did not crash"
|
|
|
|
case (_, .Some(let status), false):
|
|
testPassed = false
|
|
testFailureExplanation = "the test crashed unexpectedly"
|
|
|
|
case (_, .Some(let status), 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 {
|
|
println("did not find expected string after crash: \(expectedCrashOutput.debugDescription)")
|
|
testPassed = false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply XFAILs.
|
|
switch (testPassed, expectXFail) {
|
|
case (true, false):
|
|
println("[ OK ] \(fullTestName)")
|
|
|
|
case (true, true):
|
|
uxpassedTests += [ t.name ]
|
|
println("[ UXPASS ] \(fullTestName)")
|
|
|
|
case (false, false):
|
|
failedTests += [ t.name ]
|
|
println("[ FAIL ] \(fullTestName)")
|
|
|
|
case (false, true):
|
|
println("[ XFAIL ] \(fullTestName)")
|
|
}
|
|
}
|
|
|
|
if !uxpassedTests.isEmpty || !failedTests.isEmpty {
|
|
println("\(testSuite.name): Some tests failed, aborting")
|
|
println("UXPASS: \(uxpassedTests)")
|
|
println("FAIL: \(failedTests)")
|
|
println("SKIP: \(skippedTests)")
|
|
if !uxpassedTests.isEmpty {
|
|
_printDebuggingAdvice(uxpassedTests[0])
|
|
}
|
|
if !failedTests.isEmpty {
|
|
_printDebuggingAdvice(failedTests[0])
|
|
}
|
|
_testSuiteFailedCallback()
|
|
} else {
|
|
println("\(testSuite.name): All tests passed")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func runAllTests() {
|
|
struct PersistentState {
|
|
static var runAllTestsWasCalled: Bool = false
|
|
}
|
|
|
|
if PersistentState.runAllTestsWasCalled {
|
|
println("runAllTests() called twice. It is not allowed, aborting.")
|
|
_testSuiteFailedCallback()
|
|
return
|
|
}
|
|
PersistentState.runAllTestsWasCalled = 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
|
|
for var i = 0; i < Process.arguments.count(); {
|
|
let arg = Process.arguments[i]
|
|
if arg == "--stdlib-unittest-in-process" {
|
|
runTestsInProcess = true
|
|
++i
|
|
continue
|
|
}
|
|
if arg == "--stdlib-unittest-filter" {
|
|
filter = Process.arguments[i + 1]
|
|
i += 2
|
|
continue
|
|
}
|
|
if arg == "--help" {
|
|
println(
|
|
"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.")
|
|
return
|
|
}
|
|
|
|
// FIXME: skipping unrecognized parameters.
|
|
++i
|
|
}
|
|
|
|
var parent = _ParentProcess(
|
|
runTestsInProcess: runTestsInProcess, 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
|
|
}
|
|
|
|
public func test(name: String, _ testFunction: () -> ()) {
|
|
_TestBuilder(testSuite: self, name: name).code(testFunction)
|
|
}
|
|
|
|
public func test(name: String) -> _TestBuilder {
|
|
return _TestBuilder(testSuite: self, name: name)
|
|
}
|
|
|
|
public func setUp(code: () -> ()) {
|
|
_precondition(_testSetUpCode == nil, "set-up code already set")
|
|
_testSetUpCode = code
|
|
}
|
|
|
|
public func tearDown(code: () -> ()) {
|
|
_precondition(_testTearDownCode == nil, "tear-down code already set")
|
|
_testTearDownCode = code
|
|
}
|
|
|
|
func _runTest(testName: String) {
|
|
if let f = _testSetUpCode {
|
|
f()
|
|
}
|
|
_testByName(testName).code()
|
|
if let f = _testTearDownCode {
|
|
f()
|
|
}
|
|
}
|
|
|
|
func _testByName(testName: String) -> _Test {
|
|
return _tests[_testNameToIndex[testName]!]
|
|
}
|
|
|
|
struct _Test {
|
|
let name: String
|
|
let xfail: _UnitTestArray<TestRunPredicate>
|
|
let skip: _UnitTestArray<TestRunPredicate>
|
|
let stdinText: String?
|
|
let stdinEndsWithEOF: Bool
|
|
let crashOutputMatches: [String]
|
|
let code: () -> ()
|
|
|
|
/// Whether the test harness should stop reusing the child process after
|
|
/// running this test.
|
|
var canReuseChildProcessAfterTest: Bool {
|
|
return stdinText == nil
|
|
}
|
|
|
|
func getActiveXFailPredicates() -> _UnitTestArray<TestRunPredicate> {
|
|
return xfail.filter { $0.evaluate() }
|
|
}
|
|
|
|
func getActiveSkipPredicates() -> _UnitTestArray<TestRunPredicate> {
|
|
return skip.filter { $0.evaluate() }
|
|
}
|
|
}
|
|
|
|
public struct _TestBuilder {
|
|
let _testSuite: TestSuite
|
|
var _name: String
|
|
var _data: _Data = _Data()
|
|
|
|
class _Data {
|
|
var _xfail: _UnitTestArray<TestRunPredicate> = []
|
|
var _skip: _UnitTestArray<TestRunPredicate> = []
|
|
var _stdinText: String? = nil
|
|
var _stdinEndsWithEOF: Bool = false
|
|
var _crashOutputMatches: [String] = []
|
|
}
|
|
|
|
init(testSuite: TestSuite, name: String) {
|
|
_testSuite = testSuite
|
|
_name = name
|
|
}
|
|
|
|
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: () -> ()) {
|
|
_testSuite._tests.append(_Test(
|
|
name: _name, 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: _UnitTestArray<_Test> = []
|
|
|
|
/// Code that is run before every test.
|
|
var _testSetUpCode: (() -> ())?
|
|
|
|
/// Code that is run after every test.
|
|
var _testTearDownCode: (() -> ())?
|
|
|
|
/// Maps test name to index in `_tests`.
|
|
var _testNameToIndex: [String : Int] = [:]
|
|
}
|
|
|
|
#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
|
|
@asmname("swift_stdlib_getSystemVersionPlistProperty")
|
|
func _stdlib_getSystemVersionPlistPropertyImpl(
|
|
propertyName: UnsafePointer<CChar>) -> UnsafePointer<CChar>
|
|
|
|
func _stdlib_getSystemVersionPlistProperty(propertyName: String) -> String? {
|
|
return String.fromCString(
|
|
_stdlib_getSystemVersionPlistPropertyImpl(propertyName))
|
|
}
|
|
#endif
|
|
|
|
public enum OSVersion : CustomStringConvertible {
|
|
case OSX(major: Int, minor: Int, bugFix: Int)
|
|
case iOS(major: Int, minor: Int, bugFix: Int)
|
|
case iOSSimulator
|
|
case Linux
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case OSX(var major, var minor, var bugFix):
|
|
return "OS X \(major).\(minor).\(bugFix)"
|
|
case iOS(var major, var minor, var bugFix):
|
|
return "iOS \(major).\(minor).\(bugFix)"
|
|
case iOSSimulator:
|
|
return "iOSSimulator"
|
|
case Linux:
|
|
return "Linux"
|
|
}
|
|
}
|
|
}
|
|
|
|
func _parseDottedVersion(s: String) -> _UnitTestArray<Int> {
|
|
return _UnitTestArray(lazy(s._split(".")).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(Linux)
|
|
return .Linux
|
|
#else
|
|
let productName = _stdlib_getSystemVersionPlistProperty("ProductName")!
|
|
let productVersion = _stdlib_getSystemVersionPlistProperty("ProductVersion")!
|
|
let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion)
|
|
switch productName {
|
|
case "Mac OS X":
|
|
return .OSX(major: major, minor: minor, bugFix: bugFix)
|
|
case "iPhone OS":
|
|
return .iOS(major: major, minor: minor, bugFix: bugFix)
|
|
default:
|
|
fatalError("could not determine OS version")
|
|
}
|
|
#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 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 LinuxAny(reason: String)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case Custom(_, let reason):
|
|
return "Custom(reason: \(reason))"
|
|
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 LinuxAny(reason: let reason):
|
|
return "LinuxAny(*, reason: \(reason))"
|
|
}
|
|
}
|
|
|
|
public func evaluate() -> Bool {
|
|
switch self {
|
|
case Custom(let predicate, _):
|
|
return predicate()
|
|
|
|
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 LinuxAny:
|
|
switch _getRunningOSVersion() {
|
|
case .Linux:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Helpers that verify invariants of various stdlib types.
|
|
//
|
|
|
|
public func checkEquatable<T : Equatable>(
|
|
expectedEqual: Bool, _ lhs: T, _ rhs: T, _ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
// Test operator '==' that is found through witness tables.
|
|
expectEqual(
|
|
expectedEqual, lhs == rhs, stackTrace: stackTrace,
|
|
collectMoreInfo: collectMoreInfo)
|
|
expectEqual(
|
|
!expectedEqual, lhs != rhs, stackTrace: stackTrace,
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func checkEquatable<T : Equatable>(
|
|
expectedEqual: Bool, _ lhs: T, _ rhs: T,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkEquatable(
|
|
expectedEqual, lhs, rhs, SourceLocStack().with(SourceLoc(file, line)))
|
|
}
|
|
|
|
public func checkHashable<T : Hashable>(
|
|
expectedEqual: Bool, _ lhs: T, _ rhs: T, _ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
// Test operator '==' that is found through witness tables.
|
|
expectEqual(
|
|
expectedEqual, lhs == rhs, stackTrace: stackTrace,
|
|
collectMoreInfo: collectMoreInfo)
|
|
expectEqual(
|
|
!expectedEqual, lhs != rhs, stackTrace: stackTrace,
|
|
collectMoreInfo: collectMoreInfo)
|
|
|
|
// Test 'hashValue'.
|
|
//
|
|
// If objects are not equal, then the hash value can be different or it can
|
|
// collide.
|
|
if expectedEqual {
|
|
expectEqual(lhs.hashValue, rhs.hashValue,
|
|
stackTrace: stackTrace, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
}
|
|
|
|
public func checkHashable<T : Hashable>(
|
|
expectedEqual: Bool, _ lhs: T, _ rhs: T,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkHashable(
|
|
expectedEqual, lhs, rhs, SourceLocStack(SourceLoc(file, line)),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
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 ">"
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkComparable<T : Comparable>(
|
|
expected: ExpectedComparisonResult,
|
|
_ lhs: T, _ rhs: T, _ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqual(expected.isLT(), lhs < rhs,
|
|
stackTrace: stackTrace, collectMoreInfo: collectMoreInfo)
|
|
expectEqual(expected.isLE(), lhs <= rhs,
|
|
stackTrace: stackTrace, collectMoreInfo: collectMoreInfo)
|
|
expectEqual(expected.isGE(), lhs >= rhs,
|
|
stackTrace: stackTrace, collectMoreInfo: collectMoreInfo)
|
|
expectEqual(expected.isGT(), lhs > rhs,
|
|
stackTrace: stackTrace, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func checkComparable<T : Comparable>(
|
|
expected: ExpectedComparisonResult,
|
|
_ lhs: T, _ rhs: T,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkComparable(expected, lhs, rhs, SourceLocStack(SourceLoc(file, line)),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public struct CollectionMisuseResiliencyChecks {
|
|
public var callNextOnExhaustedGenerator: Bool = true
|
|
|
|
public static var all: CollectionMisuseResiliencyChecks {
|
|
return CollectionMisuseResiliencyChecks()
|
|
}
|
|
|
|
public static var none: CollectionMisuseResiliencyChecks {
|
|
return CollectionMisuseResiliencyChecks(
|
|
callNextOnExhaustedGenerator: false)
|
|
}
|
|
}
|
|
|
|
// Generate two overloads: one for _UnitTestArray (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: CollectionType', 'Element'),
|
|
% ('Expected.Generator.Element', 'Element'),
|
|
% ('Expected', '_UnitTestArray<Element>')):
|
|
|
|
public func checkGenerator<
|
|
G : GeneratorType, ${genericParam}
|
|
where ${Element} == G.Element
|
|
>(
|
|
expected: ${Expected},
|
|
_ generator: G,
|
|
_ sameValue: (${Element}, ${Element}) -> Bool,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
_ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
// Copying a `GeneratorType` is allowed.
|
|
var mutableGen = generator
|
|
var actual: _UnitTestArray<${Element}> = []
|
|
while let e = mutableGen.next() {
|
|
actual.append(e)
|
|
}
|
|
expectEqualSequence(
|
|
expected, actual, sameValue,
|
|
stackTrace: stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
|
|
if resiliencyChecks.callNextOnExhaustedGenerator {
|
|
// Having returned `.None` once, a `GeneratorType` should not generate more
|
|
// elements.
|
|
for i in 0..<10 {
|
|
expectEmpty(
|
|
mutableGen.next(),
|
|
stackTrace: stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkGenerator<
|
|
G : GeneratorType, ${genericParam}
|
|
where ${Element} == G.Element, ${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
_ generator: G,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
_ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkGenerator(
|
|
expected, generator, { $0 == $1 },
|
|
resiliencyChecks: resiliencyChecks,
|
|
stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : SequenceType
|
|
where S.Generator.Element == ${Element}
|
|
>(
|
|
expected: ${Expected},
|
|
_ sequence: S,
|
|
_ sameValue: (${Element}, ${Element}) -> Bool,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
_ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
let expectedCount: Int = numericCast(expected.count())
|
|
checkGenerator(
|
|
expected, sequence.generate(), sameValue,
|
|
resiliencyChecks: resiliencyChecks,
|
|
stackTrace.withCurrentLoc())
|
|
expectGE(
|
|
expectedCount, sequence.underestimateCount(),
|
|
stackTrace: stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : SequenceType
|
|
where S.Generator.Element == ${Element}
|
|
>(
|
|
expected: ${Expected},
|
|
_ sequence: S,
|
|
_ sameValue: (${Element}, ${Element}) -> Bool,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkSequence(
|
|
expected, sequence, sameValue,
|
|
resiliencyChecks: resiliencyChecks,
|
|
SourceLocStack().with(SourceLoc(file, line)),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : SequenceType
|
|
where S.Generator.Element == ${Element}, ${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
_ sequence: S,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
_ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkSequence(
|
|
expected, sequence, { $0 == $1 },
|
|
resiliencyChecks: resiliencyChecks,
|
|
stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : SequenceType
|
|
where S.Generator.Element == ${Element}, ${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
_ sequence: S,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
checkSequence(
|
|
expected, sequence, { $0 == $1 },
|
|
resiliencyChecks: resiliencyChecks,
|
|
SourceLocStack().with(SourceLoc(file, line)),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
%for traversal in [ 'Forward', 'Bidirectional', 'RandomAccess' ]:
|
|
|
|
public func check${traversal}Collection<
|
|
${genericParam}, C : CollectionType
|
|
where
|
|
C.Generator.Element == ${Element},
|
|
C.Index : ${traversal}IndexType
|
|
>(
|
|
expected: ${Expected},
|
|
_ collection: C,
|
|
_ sameValue: (${Element}, ${Element}) -> Bool,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
_ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
// A `CollectionType` is a multi-pass `SequenceType`.
|
|
for i in 0..<3 {
|
|
checkSequence(
|
|
expected, collection, sameValue,
|
|
resiliencyChecks: resiliencyChecks,
|
|
stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
let expectedArray = Array(expected)
|
|
|
|
expectEqual(
|
|
expectedArray.count().toIntMax(),
|
|
collection.count().toIntMax(),
|
|
stackTrace: stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
|
|
for i in 0..<3 {
|
|
if true {
|
|
let startIndex = collection.startIndex
|
|
let endIndex = collection.endIndex
|
|
|
|
for i in collection.indices {
|
|
expectEqual(startIndex, collection.startIndex,
|
|
stackTrace: stackTrace.withCurrentLoc()) {
|
|
"Iteration should not change startIndex"
|
|
}
|
|
expectEqual(endIndex, collection.endIndex,
|
|
stackTrace: stackTrace.withCurrentLoc()) {
|
|
"Iteration should not change endIndex"
|
|
}
|
|
}
|
|
}
|
|
|
|
var allIndices: [C.Index] = []
|
|
for i in collection.indices {
|
|
allIndices.append(i)
|
|
}
|
|
|
|
if expectedArray.count() >= 2 {
|
|
for i in 0..<allIndices.count()-1 {
|
|
var successor1 = allIndices[i].successor()
|
|
var successor2 = allIndices[i]
|
|
successor2++
|
|
var successor3 = allIndices[i]
|
|
++successor3
|
|
for s in [ successor1, successor2, successor3 ] {
|
|
expectEqual(
|
|
allIndices[i + 1], s,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
expectEqual(
|
|
expectedArray[i + 1],
|
|
collection[s],
|
|
sameValue,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
}
|
|
}
|
|
% if traversal == "Bidirectional":
|
|
for i in 1..<allIndices.count() {
|
|
var predecessor1 = allIndices[i].predecessor()
|
|
var predecessor2 = allIndices[i]
|
|
predecessor2--
|
|
var predecessor3 = allIndices[i]
|
|
--predecessor3
|
|
for p in [ predecessor1, predecessor2, predecessor3 ] {
|
|
expectEqual(
|
|
allIndices[i - 1], p,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
expectEqual(
|
|
expectedArray[i - 1], collection[p],
|
|
sameValue,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
}
|
|
}
|
|
for i in 1..<allIndices.count() {
|
|
var index = allIndices[i]
|
|
--index
|
|
++index
|
|
expectEqual(
|
|
allIndices[i], index,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
expectEqual(
|
|
expectedArray[i], collection[index],
|
|
sameValue,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
}
|
|
% end
|
|
}
|
|
|
|
if true {
|
|
var allIndices2: [C.Index] = []
|
|
for i in collection.indices {
|
|
allIndices2.append(i)
|
|
}
|
|
|
|
expectEqualSequence(
|
|
allIndices, allIndices2,
|
|
stackTrace: stackTrace.withCurrentLoc()) {
|
|
"iteration should not invalidate indices"
|
|
}
|
|
|
|
expectEqualSequence(
|
|
expectedArray,
|
|
allIndices._prext_map { collection[$0] },
|
|
sameValue,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
expectEqualSequence(
|
|
expectedArray,
|
|
allIndices2._prext_map { collection[$0] },
|
|
sameValue,
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
// FIXME: more checks for bidirectional and random access collections.
|
|
}
|
|
|
|
public func check${traversal}Collection<
|
|
${genericParam}, C : CollectionType
|
|
where
|
|
C.Generator.Element == ${Element},
|
|
C.Index : ${traversal}IndexType
|
|
>(
|
|
expected: ${Expected},
|
|
_ collection: C,
|
|
_ sameValue: (${Element}, ${Element}) -> Bool,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
check${traversal}Collection(
|
|
expected, collection, sameValue,
|
|
resiliencyChecks: resiliencyChecks,
|
|
SourceLocStack().with(SourceLoc(file, line)),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func check${traversal}Collection<
|
|
${genericParam}, C : CollectionType
|
|
where
|
|
C.Generator.Element == ${Element},
|
|
C.Index : ${traversal}IndexType,
|
|
${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
_ collection: C,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
_ stackTrace: SourceLocStack,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
check${traversal}Collection(
|
|
expected, collection, { $0 == $1 },
|
|
resiliencyChecks: resiliencyChecks,
|
|
stackTrace.withCurrentLoc(),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func check${traversal}Collection<
|
|
${genericParam}, C : CollectionType
|
|
where
|
|
C.Generator.Element == ${Element},
|
|
C.Index : ${traversal}IndexType,
|
|
${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
_ collection: C,
|
|
resiliencyChecks: CollectionMisuseResiliencyChecks = .all,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
check${traversal}Collection(
|
|
expected, collection, { $0 == $1 },
|
|
resiliencyChecks: resiliencyChecks,
|
|
SourceLocStack().with(SourceLoc(file, line)),
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
% end
|
|
|
|
public func checkSliceableWithBidirectionalIndex<
|
|
${genericParam}, S : Sliceable
|
|
where
|
|
S.Generator.Element == ${Element},
|
|
S.SubSlice.Generator.Element == ${Element},
|
|
S.Index : BidirectionalIndexType,
|
|
S.SubSlice.Index : BidirectionalIndexType,
|
|
${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
_ sliceable: S, _ stackTrace: SourceLocStack
|
|
) {
|
|
// A `Sliceable` is a `CollectionType`.
|
|
checkBidirectionalCollection(expected, sliceable, stackTrace.withCurrentLoc())
|
|
|
|
let expectedArray = _UnitTestArray(expected)
|
|
|
|
var start = sliceable.startIndex
|
|
for startNumericIndex in 0...expectedArray.count() {
|
|
if start != sliceable.endIndex {
|
|
++start
|
|
--start
|
|
++start
|
|
--start
|
|
}
|
|
var end = start
|
|
for endNumericIndex in startNumericIndex...expectedArray.count() {
|
|
if end != sliceable.endIndex {
|
|
++end
|
|
--end
|
|
++end
|
|
--end
|
|
}
|
|
let expectedSlice = expectedArray[startNumericIndex..<endNumericIndex]
|
|
let slice = sliceable[start..<end]
|
|
checkBidirectionalCollection(
|
|
expectedSlice, slice, stackTrace.withCurrentLoc())
|
|
|
|
if end != sliceable.endIndex {
|
|
++end
|
|
}
|
|
}
|
|
if start != sliceable.endIndex {
|
|
++start
|
|
}
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
public func nthIndex<C: CollectionType>(x: C, _ n: Int) -> C.Index {
|
|
return advance(x.startIndex, numericCast(n))
|
|
}
|
|
|
|
public func nth<C: CollectionType>(x: C, _ n: Int) -> C.Generator.Element {
|
|
return x[nthIndex(x, n)]
|
|
}
|
|
|
|
public func checkRangeReplaceable<
|
|
C: RangeReplaceableCollectionType,
|
|
N: CollectionType
|
|
where
|
|
C.Generator.Element : Equatable, C.Generator.Element == N.Generator.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 = _UnitTestArray<A.Generator.Element>(makeCollection())
|
|
|
|
for (ix, i) in source.indices.enumerate() {
|
|
for (jx_, j) in (i..<source.endIndex).enumerate() {
|
|
let jx = jx_ + ix
|
|
|
|
let oldCount = jx - ix
|
|
for newCount in 0..<(2 * oldCount) {
|
|
let newValues = makeNewValues(newCount)
|
|
|
|
func reportFailure(inout a: A, _ message: String) {
|
|
println("\(message) when replacing indices \(ix)...\(jx)")
|
|
println(" in \(_UnitTestArray(source)) with \(_UnitTestArray(newValues))")
|
|
println(" yielding \(_UnitTestArray(a))")
|
|
println("====================================")
|
|
expectTrue(false)
|
|
}
|
|
|
|
var a = makeCollection()
|
|
|
|
a.replaceRange(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.enumerate() {
|
|
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: SequenceType,
|
|
Actual: SequenceType
|
|
where Expected.Generator.Element == Actual.Generator.Element,
|
|
Expected.Generator.Element : Equatable
|
|
>(
|
|
expected: Expected, _ actual: Actual,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqualSequence(
|
|
expected, actual, { $0 == $1 }, stackTrace: stackTrace,
|
|
file: file, line: line, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func expectEqualSequence<
|
|
Expected : SequenceType,
|
|
Actual : SequenceType,
|
|
T : Equatable,
|
|
U : Equatable
|
|
where Expected.Generator.Element == Actual.Generator.Element,
|
|
Expected.Generator.Element == (T, U)
|
|
>(
|
|
expected: Expected, _ actual: Actual,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
expectEqualSequence(
|
|
expected, actual, { $0.0 == $1.0 && $0.1 == $1.1 },
|
|
stackTrace: stackTrace, file: file, line: line,
|
|
collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func expectEqualSequence<
|
|
Expected: SequenceType,
|
|
Actual: SequenceType
|
|
where Expected.Generator.Element == Actual.Generator.Element
|
|
>(
|
|
expected: Expected, _ actual: Actual,
|
|
_ sameValue: (Expected.Generator.Element, Expected.Generator.Element)->Bool,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
if !expected.elementsEqual(actual, isEquivalent: sameValue) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected elements: \"\(expected)\"")
|
|
println("actual: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectEqualsUnordered<
|
|
Expected : SequenceType,
|
|
Actual : SequenceType
|
|
where Expected.Generator.Element == Actual.Generator.Element
|
|
>(
|
|
expected: Expected, _ actual: Actual,
|
|
_ compare: (Expected.Generator.Element, Expected.Generator.Element)
|
|
-> ExpectedComparisonResult,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
let x: [Expected.Generator.Element] = sorted(
|
|
Array(expected), compose(compare, { $0.isLT() }))
|
|
let y: [Actual.Generator.Element] = sorted(
|
|
Array(actual), compose(compare, { $0.isLT() }))
|
|
expectEqualSequence(
|
|
x, y, compose(compare, { $0.isEQ() }), stackTrace: stackTrace,
|
|
file: file, line: line, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func expectEqualsUnordered<
|
|
Expected : SequenceType,
|
|
Actual : SequenceType
|
|
where
|
|
Expected.Generator.Element == Actual.Generator.Element,
|
|
Expected.Generator.Element : Comparable
|
|
>(
|
|
expected: Expected, _ actual: Actual,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
func compare(
|
|
lhs: Expected.Generator.Element,
|
|
rhs: Expected.Generator.Element
|
|
) -> ExpectedComparisonResult {
|
|
if lhs < rhs {
|
|
return .LT
|
|
}
|
|
if lhs == rhs {
|
|
return .EQ
|
|
}
|
|
return .GT
|
|
}
|
|
expectEqualsUnordered(
|
|
expected, actual, compare,
|
|
stackTrace: stackTrace.map { $0.withCurrentLoc() },
|
|
file: file, line: line, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
public func expectEqualsUnordered<T : Comparable>(
|
|
expected: [T], _ actual: [T],
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
let x = sorted(expected)
|
|
let y = sorted(actual)
|
|
expectEqualSequence(
|
|
x, y, { $0 == $1 }, stackTrace: stackTrace,
|
|
file: file, line: line, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
/// 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 ].lexicographicalCompare(
|
|
[ rhs.first, rhs.second ])
|
|
}
|
|
|
|
public func expectEqualsUnordered<T : Comparable>(
|
|
expected: [(T, T)], _ actual: [(T, T)],
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
let x = sorted(expected, { Pair($0.0, $0.1) < Pair($1.0, $1.1) })
|
|
let y = sorted(actual, { Pair($0.0, $0.1) < Pair($1.0, $1.1) })
|
|
expectEqualSequence(
|
|
x, y, { Pair($0.0, $0.1) == Pair($1.0, $1.1) }, stackTrace: stackTrace,
|
|
file: file, line: line, collectMoreInfo: collectMoreInfo)
|
|
}
|
|
|
|
/*
|
|
|
|
This code crashes the compiler. If we could use this code, we wouldn't need
|
|
the explicit overload for [(T, T)].
|
|
|
|
rdar://problem/19792730
|
|
rdar://problem/19792768
|
|
|
|
public func expectEqualsUnordered<
|
|
Expected : SequenceType,
|
|
Actual : SequenceType,
|
|
T : Comparable
|
|
where
|
|
Expected.Generator.Element == Actual.Generator.Element,
|
|
Expected.Generator.Element == (T, T)
|
|
>(
|
|
expected: Expected, _ actual: Actual,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
func comparePairLess(lhs: (T, T), rhs: (T, T)) -> Bool {
|
|
return lexicographicalCompare([ lhs.0, lhs.1 ], [ rhs.0, rhs.1 ])
|
|
}
|
|
|
|
let x: [(T, T)] = sorted(Array(expected), comparePairLess)
|
|
let y: [(T, T)] = sorted(Array(actual), comparePairLess)
|
|
|
|
func comparePairEquals(lhs: (T, T), rhs: (T, T)) -> Bool {
|
|
return lhs.0 == rhs.0 && lhs.1 == rhs.1
|
|
}
|
|
|
|
expectEqualSequence(
|
|
x, y, comparePairEquals, stackTrace: stackTrace,
|
|
file: file, line: line, collectMoreInfo: collectMoreInfo)
|
|
}*/
|
|
|
|
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: _UnitTestArray<UInt32>, _ actual: String,
|
|
stackTrace: SourceLocStack? = nil,
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
collectMoreInfo: (()->String)? = nil
|
|
) {
|
|
let actualUnicodeScalars = _UnitTestArray(lazy(actual.unicodeScalars).map { $0.value })
|
|
if !expected.elementsEqual(actualUnicodeScalars) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected elements: \"\(asHex(expected))\"")
|
|
println("actual: \"\(asHex(actualUnicodeScalars))\"")
|
|
if collectMoreInfo != nil { println(collectMoreInfo!()) }
|
|
println()
|
|
}
|
|
|
|
}
|
|
|
|
public func expectPrinted<T>(
|
|
expectedOneOf patterns: _UnitTestArray<String>, _ object: T,
|
|
file: StaticString = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
let actual = String(object)
|
|
if !patterns.contains(actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: any of \(patterns.debugDescription)")
|
|
println("actual: \"\(actual)\"")
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectPrinted<T>(
|
|
expected: String, _ object: T,
|
|
file: StaticString = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
expectPrinted(expectedOneOf: [expected], object, file: file, line: line)
|
|
}
|
|
|
|
public func expectDebugPrinted<T>(
|
|
expectedOneOf patterns: _UnitTestArray<String>, _ object: T,
|
|
file: StaticString = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
let actual = String(reflecting: object)
|
|
if !patterns.contains(actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: any of \(patterns.debugDescription)")
|
|
println("actual: \"\(actual)\"")
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectDebugPrinted<T>(
|
|
expected: String, _ object: T,
|
|
file: StaticString = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
expectDebugPrinted(expectedOneOf: [expected], object, file: file, line: line)
|
|
}
|
|
|
|
func compose<A, B, C>(f: A -> B, _ g: B -> C) -> A -> C {
|
|
return { a in
|
|
return g(f(a))
|
|
}
|
|
}
|
|
|
|
/// State that is created every time a fresh generator is created with
|
|
/// `MinimalSequence.generate()`.
|
|
internal class _MinimalGeneratorPrivateState<T> {
|
|
internal init() {}
|
|
|
|
internal var returnedNilCounter: Int = 0
|
|
}
|
|
|
|
/// State shared by all generators of a MinimalSequence.
|
|
internal class _MinimalGeneratorSharedState<T> {
|
|
internal init(_ data: [T]) {
|
|
self.data = data
|
|
}
|
|
|
|
internal let data: [T]
|
|
internal var i: Int = 0
|
|
internal var underestimatedCount: Int = 0
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MinimalGenerator
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// A GeneratorType that implements the protocol contract in the most
|
|
/// narrow way possible.
|
|
///
|
|
/// This generator will return `nil` only once.
|
|
public struct MinimalGenerator<T> : GeneratorType {
|
|
public init<S : SequenceType where S.Generator.Element == T>(_ s: S) {
|
|
self._sharedState = _MinimalGeneratorSharedState(Array(s))
|
|
}
|
|
|
|
public init(_ data: [T]) {
|
|
self._sharedState = _MinimalGeneratorSharedState(data)
|
|
}
|
|
|
|
internal init(_ _sharedState: _MinimalGeneratorSharedState<T>) {
|
|
self._sharedState = _sharedState
|
|
}
|
|
|
|
public func next() -> T? {
|
|
if _sharedState.i == _sharedState.data.count() {
|
|
if isConsumed {
|
|
expectUnreachable() { "next() was called on a consumed generator" }
|
|
}
|
|
++_privateState.returnedNilCounter
|
|
return nil
|
|
}
|
|
return _sharedState.data[_sharedState.i++]
|
|
}
|
|
|
|
public var isConsumed: Bool {
|
|
return returnedNilCounter >= 1
|
|
}
|
|
|
|
public var returnedNilCounter: Int {
|
|
return _privateState.returnedNilCounter
|
|
}
|
|
|
|
internal let _privateState: _MinimalGeneratorPrivateState<T> =
|
|
_MinimalGeneratorPrivateState()
|
|
internal let _sharedState: _MinimalGeneratorSharedState<T>
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MinimalSequence
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
public enum UnderestimateCountBehavior {
|
|
/// Return the actual number of elements.
|
|
case Precise
|
|
|
|
/// Return the actual number of elements divided by 2.
|
|
case Half
|
|
|
|
/// Return an overestimated count. Useful to test how algorithms reserve
|
|
/// memory.
|
|
case Overestimate
|
|
|
|
/// Return the provided value.
|
|
case Value(Int)
|
|
}
|
|
|
|
/// A SequenceType that implements the protocol contract in the most
|
|
/// narrow way possible.
|
|
///
|
|
/// This sequence is consumed when its generator is advanced.
|
|
public struct MinimalSequence<T> : SequenceType {
|
|
public init<S : SequenceType where S.Generator.Element == T>(
|
|
_ s: S,
|
|
underestimatedCount: UnderestimateCountBehavior = .Value(0)
|
|
) {
|
|
let data = Array(s)
|
|
self._sharedState = _MinimalGeneratorSharedState(data)
|
|
|
|
switch underestimatedCount {
|
|
case .Precise:
|
|
self._sharedState.underestimatedCount = data.count()
|
|
|
|
case .Half:
|
|
self._sharedState.underestimatedCount = data.count() / 2
|
|
|
|
case .Overestimate:
|
|
self._sharedState.underestimatedCount = data.count() * 3 + 5
|
|
|
|
case .Value(let count):
|
|
self._sharedState.underestimatedCount = count
|
|
}
|
|
}
|
|
|
|
public func generate() -> MinimalGenerator<T> {
|
|
return MinimalGenerator(_sharedState)
|
|
}
|
|
|
|
public func underestimateCount() -> Int {
|
|
return max(0, self._sharedState.underestimatedCount - self._sharedState.i)
|
|
}
|
|
|
|
internal let _sharedState: _MinimalGeneratorSharedState<T>
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MinimalForwardIndex
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for Distance in [ '', 'Int32' ]:
|
|
|
|
public struct MinimalForward${Distance}Index : ForwardIndexType {
|
|
% if Distance != '':
|
|
typealias Distance = ${Distance}
|
|
% end
|
|
|
|
public init(position: Int, endIndex: Int) {
|
|
self.position = position
|
|
self.endIndex = endIndex
|
|
}
|
|
|
|
public init(position: Int, startIndex _: Int, endIndex: Int) {
|
|
self.position = position
|
|
self.endIndex = endIndex
|
|
}
|
|
|
|
public func successor() -> MinimalForward${Distance}Index {
|
|
expectNotEqual(endIndex, position)
|
|
return MinimalForward${Distance}Index(
|
|
position: position + 1,
|
|
endIndex: endIndex)
|
|
}
|
|
|
|
public let position: Int
|
|
public let endIndex: Int
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalForward${Distance}Index,
|
|
rhs: MinimalForward${Distance}Index
|
|
) -> Bool {
|
|
return lhs.position == rhs.position
|
|
}
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MinimalBidirectionalIndex
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
public struct MinimalBidirectionalIndex : BidirectionalIndexType {
|
|
public init(position: Int, startIndex: Int, endIndex: Int) {
|
|
self.position = position
|
|
self.startIndex = startIndex
|
|
self.endIndex = endIndex
|
|
}
|
|
|
|
public func successor() -> MinimalBidirectionalIndex {
|
|
expectNotEqual(endIndex, position)
|
|
return MinimalBidirectionalIndex(
|
|
position: position + 1, startIndex: startIndex, endIndex: endIndex)
|
|
}
|
|
|
|
public func predecessor() -> MinimalBidirectionalIndex {
|
|
expectNotEqual(startIndex, position)
|
|
return MinimalBidirectionalIndex(
|
|
position: position - 1, startIndex: startIndex, endIndex: endIndex)
|
|
}
|
|
|
|
public let position: Int
|
|
public let startIndex: Int
|
|
public let endIndex: Int
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalBidirectionalIndex,
|
|
rhs: MinimalBidirectionalIndex
|
|
) -> Bool {
|
|
return lhs.position == rhs.position
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MinimalRandomAccessIndex
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
public struct MinimalRandomAccessIndex : RandomAccessIndexType {
|
|
public init(position: Int, startIndex: Int, endIndex: Int) {
|
|
self.position = position
|
|
self.startIndex = startIndex
|
|
self.endIndex = endIndex
|
|
}
|
|
|
|
public func successor() -> MinimalRandomAccessIndex {
|
|
expectNotEqual(endIndex, position)
|
|
return MinimalRandomAccessIndex(
|
|
position: position + 1, startIndex: startIndex, endIndex: endIndex)
|
|
}
|
|
|
|
public func predecessor() -> MinimalRandomAccessIndex {
|
|
expectNotEqual(startIndex, position)
|
|
return MinimalRandomAccessIndex(
|
|
position: position - 1, startIndex: startIndex, endIndex: endIndex)
|
|
}
|
|
|
|
public func distanceTo(other: MinimalRandomAccessIndex) -> Int {
|
|
return other.position - position
|
|
}
|
|
|
|
public func advancedBy(n: Int) -> MinimalRandomAccessIndex {
|
|
expectNotEqual(endIndex, position)
|
|
let newPosition = position + n
|
|
expectLE(startIndex, newPosition)
|
|
expectGE(endIndex, newPosition)
|
|
return MinimalRandomAccessIndex(
|
|
position: newPosition, startIndex: startIndex, endIndex: endIndex)
|
|
}
|
|
|
|
public let position: Int
|
|
public let startIndex: Int
|
|
public let endIndex: Int
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalRandomAccessIndex,
|
|
rhs: MinimalRandomAccessIndex
|
|
) -> Bool {
|
|
return lhs.position == rhs.position
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Minimal***Collection
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for traversal in [ 'Forward', 'Bidirectional', 'RandomAccess' ]:
|
|
% for mutable in [ False, True ]:
|
|
// This comment is a workaround for <rdar://problem/18900352> gyb miscompiles nested loops
|
|
% Self = 'Minimal%s%sCollection' % ('Mutable' if mutable else '', traversal)
|
|
% Index = 'Minimal%sIndex' % traversal
|
|
|
|
/// A minimal implementation of `CollectionType` with extra checks.
|
|
public struct ${Self}<T> : ${'MutableCollectionType' if mutable else 'CollectionType'} {
|
|
public init<S : SequenceType where S.Generator.Element == T>(
|
|
_ s: S,
|
|
underestimatedCount: UnderestimateCountBehavior = .Value(0)
|
|
) {
|
|
self._elements = Array(s)
|
|
|
|
switch underestimatedCount {
|
|
case .Precise:
|
|
self.underestimatedCount = _elements.count()
|
|
|
|
case .Half:
|
|
self.underestimatedCount = _elements.count() / 2
|
|
|
|
case .Overestimate:
|
|
self.underestimatedCount = _elements.count() * 3 + 5
|
|
|
|
case .Value(let count):
|
|
self.underestimatedCount = count
|
|
}
|
|
}
|
|
|
|
public func generate() -> MinimalGenerator<T> {
|
|
return MinimalGenerator(_elements)
|
|
}
|
|
|
|
public var startIndex: ${Index} {
|
|
return ${Index}(
|
|
position: 0,
|
|
startIndex: 0,
|
|
endIndex: _elements.endIndex)
|
|
}
|
|
|
|
public var endIndex: ${Index} {
|
|
return ${Index}(
|
|
position: _elements.endIndex,
|
|
startIndex: 0,
|
|
endIndex: _elements.endIndex)
|
|
}
|
|
|
|
public subscript(i: ${Index}) -> T {
|
|
get {
|
|
return _elements[i.position]
|
|
}
|
|
% if mutable:
|
|
set {
|
|
_elements[i.position] = newValue
|
|
}
|
|
% end
|
|
}
|
|
|
|
public func underestimateCount() -> Int {
|
|
return underestimatedCount
|
|
}
|
|
|
|
public var underestimatedCount: Int
|
|
|
|
internal var _elements: [T]
|
|
}
|
|
|
|
% end
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Minimal***RangeReplaceableCollectionType
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
%for traversal in [ 'Forward', 'Bidirectional', 'RandomAccess' ]:
|
|
% Self = 'Minimal%sRangeReplaceableCollectionType' % traversal
|
|
% Index = 'Minimal%sIndex' % traversal
|
|
|
|
/// A minimal implementation of `RangeReplaceableCollectionType` with extra
|
|
/// checks.
|
|
public struct ${Self}<T> : RangeReplaceableCollectionType {
|
|
|
|
public init<S : SequenceType where S.Generator.Element == T>(
|
|
_ s: S,
|
|
underestimatedCount: UnderestimateCountBehavior = .Value(0)
|
|
) {
|
|
self.elements = Array(s)
|
|
|
|
switch underestimatedCount {
|
|
case .Precise:
|
|
self.underestimatedCount = elements.count()
|
|
|
|
case .Half:
|
|
self.underestimatedCount = elements.count() / 2
|
|
|
|
case .Overestimate:
|
|
self.underestimatedCount = elements.count() * 3 + 5
|
|
|
|
case .Value(let count):
|
|
self.underestimatedCount = count
|
|
}
|
|
}
|
|
|
|
public init() {
|
|
self.underestimatedCount = 0
|
|
self.elements = []
|
|
}
|
|
|
|
public func generate() -> MinimalGenerator<T> {
|
|
return MinimalGenerator(elements)
|
|
}
|
|
|
|
public func underestimateCount() -> Int {
|
|
return underestimatedCount
|
|
}
|
|
|
|
public var startIndex: ${Index} {
|
|
return ${Index}(
|
|
position: 0,
|
|
startIndex: 0,
|
|
endIndex: elements.endIndex)
|
|
}
|
|
|
|
public var endIndex: ${Index} {
|
|
return ${Index}(
|
|
position: elements.endIndex,
|
|
startIndex: 0,
|
|
endIndex: elements.endIndex)
|
|
}
|
|
|
|
public subscript(i: ${Index}) -> T {
|
|
return elements[i.position]
|
|
}
|
|
|
|
public mutating func reserveCapacity(n: Int) {
|
|
elements.reserveCapacity(n)
|
|
reservedCapacity = max(reservedCapacity, n)
|
|
}
|
|
|
|
public mutating func append(x: T) {
|
|
elements.append(x)
|
|
}
|
|
|
|
public mutating func extend<
|
|
S : SequenceType where S.Generator.Element == T
|
|
>(newElements: S) {
|
|
elements.extend(newElements)
|
|
}
|
|
|
|
public mutating func replaceRange<
|
|
C : CollectionType where C.Generator.Element == T
|
|
>(
|
|
subRange: Range<${Index}>, with newElements: C
|
|
) {
|
|
elements.replaceRange(
|
|
subRange.startIndex.position..<subRange.endIndex.position,
|
|
with: newElements)
|
|
}
|
|
|
|
public mutating func insert(newElement: T, atIndex i: ${Index}) {
|
|
elements.insert(newElement, atIndex: i.position)
|
|
}
|
|
|
|
public mutating func splice<
|
|
S : CollectionType where S.Generator.Element == T
|
|
>(newElements: S, atIndex i: ${Index}) {
|
|
elements.splice(newElements, atIndex: i.position)
|
|
}
|
|
|
|
public mutating func removeAtIndex(i: ${Index}) -> T {
|
|
return elements.removeAtIndex(i.position)
|
|
}
|
|
|
|
public mutating func removeRange(subRange: Range<${Index}>) {
|
|
elements.removeRange(
|
|
subRange.startIndex.position..<subRange.endIndex.position
|
|
)
|
|
}
|
|
|
|
public mutating func removeAll(keepCapacity keepCapacity: Bool = false) {
|
|
// Ignore the value of `keepCapacity`.
|
|
elements.removeAll(keepCapacity: false)
|
|
}
|
|
|
|
public var underestimatedCount: Int
|
|
public var reservedCapacity: Int = 0
|
|
|
|
public var elements: [T]
|
|
}
|
|
|
|
%end
|
|
|
|
/// 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
|
|
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
|
|
return value.hashValue
|
|
}
|
|
}
|
|
|
|
public func == (
|
|
lhs: ${Self},
|
|
rhs: ${Self}
|
|
) -> Bool {
|
|
++${Self}.timesEqualEqualWasCalled
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
% end
|
|
|
|
/// 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: Int = 0
|
|
public static var timesLessWasCalled: 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: MinimalComparableValue,
|
|
rhs: MinimalComparableValue
|
|
) -> Bool {
|
|
++MinimalComparableValue.timesEqualEqualWasCalled
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
public func < (
|
|
lhs: MinimalComparableValue,
|
|
rhs: MinimalComparableValue
|
|
) -> Bool {
|
|
++MinimalComparableValue.timesLessWasCalled
|
|
return lhs.value < rhs.value
|
|
}
|
|
|
|
// ${'Local Variables'}:
|
|
// eval: (read-only-mode 1)
|
|
// End:
|