mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This changes 'if let' conditions to take general refutable patterns, instead of
taking a irrefutable pattern and implicitly matching against an optional.
Where before you might have written:
if let x = foo() {
you now need to write:
if let x? = foo() {
The upshot of this is that you can write anything in an 'if let' that you can
write in a 'case let' in a switch statement, which is pretty general.
To aid with migration, this special cases certain really common patterns like
the above (and any other irrefutable cases, like "if let (a,b) = foo()", and
tells you where to insert the ?. It also special cases type annotations like
"if let x : AnyObject = " since they are no longer allowed.
For transitional purposes, I have intentionally downgraded the most common
diagnostic into a warning instead of an error. This means that you'll get:
t.swift:26:10: warning: condition requires a refutable pattern match; did you mean to match an optional?
if let a = f() {
^
?
I think this is important to stage in, because this is a pretty significant
source breaking change and not everyone internally may want to deal with it
at the same time. I filed 20166013 to remember to upgrade this to an error.
In addition to being a nice user feature, this is a nice cleanup of the guts
of the compiler, since it eliminates the "isConditional()" bit from
PatternBindingDecl, along with the special case logic in the compiler to handle
it (which variously added and removed Optional around these things).
Swift SVN r26150
2065 lines
59 KiB
Swift
2065 lines
59 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)
|
|
import Darwin
|
|
#elseif os(Linux)
|
|
import Glibc
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
import ObjectiveC
|
|
#endif
|
|
|
|
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()
|
|
}
|
|
}
|
|
|
|
public func expectEqual<T : Equatable>(
|
|
expected: T?, actual: T?,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
if (actual == nil) != (expected == nil)
|
|
|| 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>', 'ArraySlice<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()
|
|
}
|
|
}
|
|
|
|
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 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)
|
|
}
|
|
|
|
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()
|
|
}
|
|
|
|
%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
|
|
}
|
|
|
|
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.count == 0 {
|
|
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
|
|
do {
|
|
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 {
|
|
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 {
|
|
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
|
|
|
|
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() {
|
|
#if _runtime(ObjC)
|
|
autoreleasepool {
|
|
_stdlib_initializeReturnAutoreleased()
|
|
}
|
|
#endif
|
|
|
|
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()
|
|
}
|
|
_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)
|
|
@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 : Printable {
|
|
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 { $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
|
|
#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 : 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)
|
|
|
|
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 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
|
|
}
|
|
|
|
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 : Printable {
|
|
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)
|
|
}
|
|
|
|
// 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 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 lexicographicalCompare(
|
|
[ lhs.first, lhs.second ],
|
|
[ 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 !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))
|
|
}
|
|
}
|
|
|
|
/// 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
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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 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.underestimatedCount = data.count
|
|
|
|
case .Overestimate:
|
|
self.underestimatedCount = data.count * 3 + 5
|
|
|
|
case .Value(let count):
|
|
self.underestimatedCount = count
|
|
}
|
|
}
|
|
|
|
public func generate() -> MinimalGenerator<T> {
|
|
return MinimalGenerator(_sharedState)
|
|
}
|
|
|
|
public var underestimatedCount: Int
|
|
|
|
internal let _sharedState: _MinimalGeneratorSharedState<T>
|
|
}
|
|
|
|
public func ~> <T> (
|
|
s: MinimalSequence<T>, _: (_UnderestimateCount, ())
|
|
) -> Int {
|
|
return s.underestimatedCount
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MinimalForwardIndex
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
public struct MinimalForwardIndex : ForwardIndexType {
|
|
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() -> MinimalForwardIndex {
|
|
expectNotEqual(_endIndex, _position)
|
|
return MinimalForwardIndex(position: _position + 1, endIndex: _endIndex)
|
|
}
|
|
|
|
internal var _position: Int
|
|
internal var _endIndex: Int
|
|
}
|
|
|
|
public func == (lhs: MinimalForwardIndex, rhs: MinimalForwardIndex) -> Bool {
|
|
return lhs._position == rhs._position
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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)
|
|
}
|
|
|
|
internal var _position: Int
|
|
internal var _startIndex: Int
|
|
internal var _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)
|
|
}
|
|
|
|
internal var _position: Int
|
|
internal var _startIndex: Int
|
|
internal var _endIndex: Int
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalRandomAccessIndex,
|
|
rhs: MinimalRandomAccessIndex
|
|
) -> Bool {
|
|
return lhs._position == rhs._position
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Minimal***Collection
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
%for traversal in [ 'Forward', 'Bidirectional', 'RandomAccess' ]:
|
|
% Self = 'Minimal%sCollection' % traversal
|
|
% Index = 'Minimal%sIndex' % traversal
|
|
|
|
/// A minimal implementation of CollectionType with extra checks.
|
|
public struct ${Self}<T> : CollectionType {
|
|
public init<S : SequenceType where S.Generator.Element == T>(
|
|
_ s: S,
|
|
underestimatedCount: UnderestimateCountBehavior = .Value(0)
|
|
) {
|
|
self._data = Array(s)
|
|
|
|
switch underestimatedCount {
|
|
case .Precise:
|
|
self.underestimatedCount = _data.count
|
|
|
|
case .Overestimate:
|
|
self.underestimatedCount = _data.count * 3 + 5
|
|
|
|
case .Value(let count):
|
|
self.underestimatedCount = count
|
|
}
|
|
}
|
|
|
|
public func generate() -> MinimalGenerator<T> {
|
|
return MinimalGenerator(_data)
|
|
}
|
|
|
|
public var startIndex: ${Index} {
|
|
return ${Index}(position: 0, startIndex: 0, endIndex: _data.endIndex)
|
|
}
|
|
|
|
public var endIndex: ${Index} {
|
|
return ${Index}(
|
|
position: _data.endIndex, startIndex: 0, endIndex: _data.endIndex)
|
|
}
|
|
|
|
public subscript(i: ${Index}) -> T {
|
|
return _data[i._position]
|
|
}
|
|
|
|
public var underestimatedCount: Int
|
|
|
|
internal let _data: [T]
|
|
}
|
|
|
|
public func ~> <T> (
|
|
c: ${Self}<T>, _: (_UnderestimateCount, ())
|
|
) -> Int {
|
|
return c.underestimatedCount
|
|
}
|
|
|
|
%end
|
|
|
|
// ${'Local Variables'}:
|
|
// eval: (read-only-mode 1)
|
|
// End:
|