mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Previously, trailing closures would try to match the first parameter of (possibly optional) function type that didn't seem to have an argument already, but in practice this broke when there were parameters with default arguments before the function parameter. The new rule is far simpler: a trailing closure matches the last parameter. Fixes rdar://problem/17965209. Swift SVN r24898
1591 lines
46 KiB
Swift
1591 lines
46 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 Darwin
|
|
import ObjectiveC
|
|
|
|
public struct SourceLoc {
|
|
let file: String
|
|
let line: UWord
|
|
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?) {
|
|
if let s = stackTrace {
|
|
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__
|
|
) {
|
|
if expected == actual {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("unexpected value: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
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?,
|
|
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()
|
|
}
|
|
}
|
|
|
|
// 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>', 'Slice<T>'),
|
|
% ('<T : Equatable>', 'Array<T>'),
|
|
% ('<T, U : Equatable>', 'Dictionary<T, U>'),
|
|
% ('<T : ForwardIndexType>', 'T')]:
|
|
|
|
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__
|
|
) {
|
|
if expected == actual {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("unexpected value: \"\(actual)\" (of type \(_stdlib_getDemangledTypeName(actual)))")
|
|
println()
|
|
}
|
|
}
|
|
|
|
%end
|
|
|
|
%for ComparableType in ['Int']:
|
|
|
|
public func expectLE(
|
|
expected: ${ComparableType}, actual: ${ComparableType},
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if !(expected <= actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: \"\(expected)\"")
|
|
println("actual: \"\(actual)\"")
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectGE(
|
|
expected: ${ComparableType}, actual: ${ComparableType},
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if !(expected >= actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: \"\(expected)\"")
|
|
println("actual: \"\(actual)\"")
|
|
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 : Printable, 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)
|
|
}
|
|
|
|
%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__
|
|
) {
|
|
if value != nil {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
_printStackTrace(stackTrace)
|
|
println("expected optional to be empty")
|
|
println("actual: \"\(value)\"")
|
|
println()
|
|
}
|
|
}
|
|
|
|
public func expectNotEmpty<T>(
|
|
value: Optional<T>,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if value == nil {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected optional to be non-empty")
|
|
println()
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func _stdlib_getline() -> String? {
|
|
var result = _UnitTestArray<UInt8>()
|
|
while true {
|
|
let c = getchar()
|
|
if c == EOF {
|
|
return nil
|
|
}
|
|
if c == CInt(UnicodeScalar("\n").value) {
|
|
return String._fromWellFormedCodeUnitSequence(UTF8.self, input: result)
|
|
}
|
|
result.append(UInt8(c))
|
|
}
|
|
}
|
|
|
|
struct _FDInputStream {
|
|
let fd: CInt
|
|
var isEOF: Bool = false
|
|
var _buffer = _UnitTestArray<UInt8>(count: 256, repeatedValue: 0)
|
|
var _bufferUsed: Int = 0
|
|
|
|
init(fd: CInt) {
|
|
self.fd = fd
|
|
}
|
|
|
|
mutating func getline() -> String? {
|
|
if let newlineIndex =
|
|
find(_buffer[0..<_bufferUsed], UInt8(UnicodeScalar("\n").value)) {
|
|
var result = String._fromWellFormedCodeUnitSequence(
|
|
UTF8.self, input: _buffer[0..<newlineIndex])
|
|
_buffer.removeRange(0...newlineIndex)
|
|
_bufferUsed -= newlineIndex + 1
|
|
return result
|
|
}
|
|
if isEOF && _bufferUsed > 0 {
|
|
var result = String._fromWellFormedCodeUnitSequence(
|
|
UTF8.self, input: _buffer[0..<_bufferUsed])
|
|
_buffer.removeAll()
|
|
_bufferUsed = 0
|
|
return result
|
|
}
|
|
return nil
|
|
}
|
|
|
|
mutating func read() {
|
|
let minFree = 128
|
|
var bufferFree = _buffer.count - _bufferUsed
|
|
if bufferFree < minFree {
|
|
_buffer.reserveCapacity(minFree - bufferFree)
|
|
while bufferFree < minFree {
|
|
_buffer.append(0)
|
|
++bufferFree
|
|
}
|
|
}
|
|
let readResult: ssize_t = _buffer.withUnsafeMutableBufferPointer {
|
|
(_buffer) in
|
|
let fd = self.fd
|
|
let addr = _buffer.baseAddress + self._bufferUsed
|
|
let size = size_t(bufferFree)
|
|
return Darwin.read(fd, addr, size)
|
|
}
|
|
if readResult == 0 {
|
|
isEOF = true
|
|
return
|
|
}
|
|
if readResult < 0 {
|
|
fatalError("read() returned error")
|
|
}
|
|
_bufferUsed += readResult
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
struct _ParentProcess {
|
|
var _pid: pid_t = -1
|
|
var _childStdinFD: CInt = -1
|
|
var _childStdoutFD: CInt = -1
|
|
var _childStderrFD: CInt = -1
|
|
|
|
internal var _runTestsInProcess: Bool
|
|
internal var _filter: String?
|
|
|
|
init(runTestsInProcess: Bool, filter: String?) {
|
|
self._runTestsInProcess = runTestsInProcess
|
|
self._filter = filter
|
|
}
|
|
|
|
mutating func _spawnChild() {
|
|
(_pid, _childStdinFD, _childStdoutFD, _childStderrFD) =
|
|
spawnChild([ "--stdlib-unittest-run-child" ])
|
|
}
|
|
|
|
mutating func _waitForChild() -> ProcessTerminationStatus {
|
|
let status = posixWaitpid(_pid)
|
|
_pid = -1
|
|
if close(_childStdinFD) != 0 {
|
|
preconditionFailure("close() failed")
|
|
}
|
|
if close(_childStdoutFD) != 0 {
|
|
preconditionFailure("close() failed")
|
|
}
|
|
if close(_childStderrFD) != 0 {
|
|
preconditionFailure("close() failed")
|
|
}
|
|
return status
|
|
}
|
|
|
|
/// Returns the values of the corresponding variables in the child process.
|
|
mutating func _runTestInChild(testSuiteName: String, _ testName: String)
|
|
-> (anyExpectFailed: Bool, seenExpectCrash: Bool,
|
|
status: ProcessTerminationStatus?,
|
|
crashStdout: _UnitTestArray<String>, crashStderr: _UnitTestArray<String>) {
|
|
if _pid <= 0 {
|
|
_spawnChild()
|
|
}
|
|
|
|
var childStdin = _FDOutputStream(fd: _childStdinFD)
|
|
var childStdout = _FDInputStream(fd: _childStdoutFD)
|
|
var childStderr = _FDInputStream(fd: _childStderrFD)
|
|
|
|
println("\(testSuiteName);\(testName)", &childStdin)
|
|
|
|
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(_childStdoutFD)
|
|
errorfds.set(_childStdoutFD)
|
|
}
|
|
if !childStderr.isEOF {
|
|
readfds.set(_childStderrFD)
|
|
errorfds.set(_childStderrFD)
|
|
}
|
|
var ret: CInt
|
|
do {
|
|
ret = _stdlib_select(&readfds, &writefds, &errorfds, nil)
|
|
} while ret == -1 && errno == EINTR
|
|
if ret <= 0 {
|
|
fatalError("select() returned an error")
|
|
}
|
|
if readfds.isset(_childStdoutFD) || errorfds.isset(_childStdoutFD) {
|
|
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(_childStderrFD) || errorfds.isset(_childStderrFD) {
|
|
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
|
|
}
|
|
}
|
|
if stdoutEnd && stderrEnd {
|
|
return (
|
|
anyExpectFailedInChild,
|
|
stdoutSeenCrashDelimiter || stderrSeenCrashDelimiter, nil,
|
|
capturedCrashStdout, capturedCrashStderr)
|
|
}
|
|
|
|
// We reached EOF on stdout and stderr, it looks like child crashed (of
|
|
// course it could have closed the file descriptors, but we assume it did
|
|
// not).
|
|
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 {
|
|
if 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 {
|
|
_anyExpectFailed = false
|
|
testSuite._runTest(t.name)
|
|
} else {
|
|
(_anyExpectFailed, expectCrash, childTerminationStatus, crashStdout,
|
|
crashStderr) =
|
|
_runTestInChild(testSuite.name, 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
|
|
|
|
default:
|
|
preconditionFailure("unreachable")
|
|
}
|
|
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)")
|
|
|
|
default:
|
|
preconditionFailure("unreachable")
|
|
}
|
|
}
|
|
|
|
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() {
|
|
autoreleasepool {
|
|
_stdlib_initializeReturnAutoreleased()
|
|
}
|
|
|
|
let _isChildProcess: Bool =
|
|
contains(Process.arguments, "--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()
|
|
}
|
|
_tests[_testNameToIndex[testName]!].code()
|
|
if let f = _testTearDownCode {
|
|
f()
|
|
}
|
|
}
|
|
|
|
struct _Test {
|
|
let name: String
|
|
let xfail: _UnitTestArray<TestRunPredicate>
|
|
let skip: _UnitTestArray<TestRunPredicate>
|
|
let crashOutputMatches: [String] = []
|
|
let code: () -> ()
|
|
|
|
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 _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 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,
|
|
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] = [:]
|
|
}
|
|
|
|
@asmname("swift_stdlib_getSystemVersionPlistProperty")
|
|
func _stdlib_getSystemVersionPlistPropertyImpl(
|
|
propertyName: UnsafePointer<CChar>) -> UnsafePointer<CChar>
|
|
|
|
func _stdlib_getSystemVersionPlistProperty(propertyName: String) -> String? {
|
|
return String.fromCString(
|
|
_stdlib_getSystemVersionPlistPropertyImpl(propertyName))
|
|
}
|
|
|
|
public enum OSVersion : Printable {
|
|
case OSX(major: Int, minor: Int, bugFix: Int)
|
|
case iOS(major: Int, minor: Int, bugFix: Int)
|
|
case iOSSimulator
|
|
|
|
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"
|
|
}
|
|
}
|
|
}
|
|
|
|
func _parseDottedVersion(s: String) -> _UnitTestArray<Int> {
|
|
return _UnitTestArray(lazy(s._split(".")).map { $0.toInt()! })
|
|
}
|
|
|
|
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
|
|
#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 : Printable {
|
|
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)
|
|
|
|
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))"
|
|
}
|
|
}
|
|
|
|
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 contains(minorRange, 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 contains(bugFixRange, 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 contains(minorRange, 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 contains(bugFixRange, runningBugFix)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
case iOSSimulatorAny:
|
|
switch _getRunningOSVersion() {
|
|
case .iOSSimulator:
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
public func checkComparable<T : Comparable>(
|
|
expected: ExpectedComparisonResult,
|
|
lhs: T, rhs: T, stackTrace: SourceLocStack
|
|
) {
|
|
expectEqual(expected.isLT(), lhs < rhs, stackTrace: stackTrace)
|
|
expectEqual(expected.isLE(), lhs <= rhs, stackTrace: stackTrace)
|
|
expectEqual(expected.isGE(), lhs >= rhs, stackTrace: stackTrace)
|
|
expectEqual(expected.isGT(), lhs > rhs, stackTrace: stackTrace)
|
|
}
|
|
|
|
public func checkComparable<T : Comparable>(
|
|
expected: ExpectedComparisonResult,
|
|
lhs: T, rhs: T,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
checkComparable(expected, lhs, rhs, SourceLocStack(SourceLoc(file, line)))
|
|
}
|
|
|
|
// 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, ${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
generator: G, stackTrace: SourceLocStack
|
|
) {
|
|
// Copying a `GeneratorType` is allowed.
|
|
var mutableGen = generator
|
|
var actual: _UnitTestArray<${Element}> = []
|
|
while let e = mutableGen.next() {
|
|
actual.append(e)
|
|
}
|
|
expectEqualSequence(expected, actual, stackTrace: stackTrace.withCurrentLoc())
|
|
|
|
// Having returned `.None` once, a `GeneratorType` should not generate more
|
|
// elements.
|
|
for i in 0..<10 {
|
|
expectEmpty(mutableGen.next(), stackTrace: stackTrace.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
public func checkSequence<
|
|
${genericParam}, S : SequenceType
|
|
where S.Generator.Element == ${Element}, ${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
sequence: S, stackTrace: SourceLocStack
|
|
) {
|
|
let expectedCount: Int = numericCast(count(expected))
|
|
checkGenerator(expected, sequence.generate(), stackTrace.withCurrentLoc())
|
|
expectGE(expectedCount, underestimateCount(sequence))
|
|
}
|
|
|
|
public func checkCollection<
|
|
${genericParam}, C : CollectionType
|
|
where C.Generator.Element == ${Element}, ${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
collection: C, stackTrace: SourceLocStack
|
|
) {
|
|
// A `CollectionType` is a multi-pass `SequenceType`.
|
|
for i in 0..<3 {
|
|
checkSequence(expected, collection, stackTrace.withCurrentLoc())
|
|
}
|
|
|
|
expectEqual(count(expected).toIntMax(), count(collection).toIntMax(),
|
|
stackTrace: stackTrace.withCurrentLoc())
|
|
|
|
for i in 0..<3 {
|
|
let startIndex = collection.startIndex
|
|
let endIndex = collection.endIndex
|
|
|
|
var actual: _UnitTestArray<${Element}> = []
|
|
var index = collection.startIndex
|
|
while index != collection.endIndex {
|
|
// Iteration should not change `startIndex` or `endIndex`.
|
|
expectEqual(startIndex, collection.startIndex)
|
|
expectEqual(endIndex, collection.endIndex)
|
|
|
|
actual.append(collection[index])
|
|
++index
|
|
}
|
|
|
|
expectEqualSequence(
|
|
expected, actual, stackTrace: stackTrace.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
public func checkSliceableWithBidirectionalIndex<
|
|
${genericParam}, S : Sliceable
|
|
where S.Generator.Element == ${Element},
|
|
S.SubSlice.Generator.Element == ${Element},
|
|
S.Index : BidirectionalIndexType,
|
|
${Element} : Equatable
|
|
>(
|
|
expected: ${Expected},
|
|
sliceable: S, stackTrace: SourceLocStack) {
|
|
// A `Sliceable` is a `CollectionType`.
|
|
checkCollection(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]
|
|
checkCollection(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 enumerate(indices(source)) {
|
|
for (jx_, j) in enumerate(i..<source.endIndex) {
|
|
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(count(a)) as Int
|
|
if actualCount != expectedCount {
|
|
reportFailure(
|
|
&a, "\(actualCount) != expected count \(expectedCount)")
|
|
}
|
|
|
|
for (kx, k) in enumerate(indices(a)) {
|
|
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
|
|
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 !equal(expected, actual, 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 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 !equal(expected, 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: _UnitTestArray<String>, object: T,
|
|
file: StaticString = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
let actual = toString(object)
|
|
if !contains(expectedOneOf, actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: any of \(expectedOneOf.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: _UnitTestArray<String>, object: T,
|
|
file: StaticString = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
let actual = toDebugString(object)
|
|
if !contains(expectedOneOf, actual) {
|
|
_anyExpectFailed = true
|
|
println("check failed at \(file), line \(line)")
|
|
println("expected: any of \(expectedOneOf.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))
|
|
}
|
|
}
|
|
|
|
// ${'Local Variables'}:
|
|
// eval: (read-only-mode 1)
|
|
// End:
|