-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,099 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
## Build generated | ||
build/ | ||
DerivedData/ | ||
*.DS_Store | ||
|
||
## Various settings | ||
*.pbxuser | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"object": { | ||
"pins": [ | ||
|
||
] | ||
}, | ||
"version": 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
protocol CaseType { | ||
/// Run a test case in the given reporter | ||
func run(reporter: ContextReporter) | ||
} | ||
|
||
class Case : CaseType { | ||
let name:String | ||
let disabled: Bool | ||
let closure:() throws -> () | ||
|
||
let function: String | ||
let file: String | ||
let line: Int | ||
|
||
init(name: String, disabled: Bool = false, closure: @escaping () throws -> (), function: String = #function, file: String = #file, line: Int = #line) { | ||
self.name = name | ||
self.disabled = disabled | ||
self.closure = closure | ||
|
||
self.function = function | ||
self.file = file | ||
self.line = line | ||
} | ||
|
||
func run(reporter: ContextReporter) { | ||
if disabled { | ||
reporter.addDisabled(name) | ||
return | ||
} | ||
|
||
do { | ||
try closure() | ||
reporter.addSuccess(name) | ||
} catch _ as Skip { | ||
reporter.addDisabled(name) | ||
} catch let error as FailureType { | ||
reporter.addFailure(name, failure: error) | ||
} catch { | ||
reporter.addFailure(name, failure: Failure(reason: "Unhandled error: \(error)", function: function, file: file, line: line)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
public protocol ContextType { | ||
/// Creates a new sub-context | ||
func context(_ name: String, closure: (ContextType) -> Void) | ||
|
||
/// Creates a new sub-context | ||
func describe(_ name: String, closure: (ContextType) -> Void) | ||
|
||
/// Creates a new disabled sub-context | ||
func xcontext(_ name: String, closure: (ContextType) -> Void) | ||
|
||
/// Creates a new disabled sub-context | ||
func xdescribe(_ name: String, closure: (ContextType) -> Void) | ||
|
||
func before(_ closure: @escaping () -> Void) | ||
func after(_ closure: @escaping () -> Void) | ||
|
||
/// Adds a new test case | ||
func it(_ name: String, closure: @escaping () throws -> Void) | ||
|
||
/// Adds a disabled test case | ||
func xit(_ name: String, closure: @escaping () throws -> Void) | ||
} | ||
|
||
class Context : ContextType, CaseType { | ||
let name: String | ||
let disabled: Bool | ||
fileprivate weak var parent: Context? | ||
var cases = [CaseType]() | ||
|
||
typealias Before = (() -> Void) | ||
typealias After = (() -> Void) | ||
|
||
var befores = [Before]() | ||
var afters = [After]() | ||
|
||
init(name: String, disabled: Bool = false, parent: Context? = nil) { | ||
self.name = name | ||
self.disabled = disabled | ||
self.parent = parent | ||
} | ||
|
||
func context(_ name: String, closure: (ContextType) -> Void) { | ||
let context = Context(name: name, parent: self) | ||
closure(context) | ||
cases.append(context) | ||
} | ||
|
||
func describe(_ name: String, closure: (ContextType) -> Void) { | ||
let context = Context(name: name, parent: self) | ||
closure(context) | ||
cases.append(context) | ||
} | ||
|
||
func xcontext(_ name: String, closure: (ContextType) -> Void) { | ||
let context = Context(name: name, disabled: true, parent: self) | ||
closure(context) | ||
cases.append(context) | ||
} | ||
|
||
func xdescribe(_ name: String, closure: (ContextType) -> Void) { | ||
let context = Context(name: name, disabled: true, parent: self) | ||
closure(context) | ||
cases.append(context) | ||
} | ||
|
||
func before(_ closure: @escaping () -> Void) { | ||
befores.append(closure) | ||
} | ||
|
||
func after(_ closure: @escaping () -> Void) { | ||
afters.append(closure) | ||
} | ||
|
||
func it(_ name: String, closure: @escaping () throws -> Void) { | ||
cases.append(Case(name: name, closure: closure)) | ||
} | ||
|
||
func xit(_ name: String, closure: @escaping () throws -> Void) { | ||
cases.append(Case(name: name, disabled: true, closure: closure)) | ||
} | ||
|
||
func runBefores() { | ||
parent?.runBefores() | ||
befores.forEach { $0() } | ||
} | ||
|
||
func runAfters() { | ||
afters.forEach { $0() } | ||
parent?.runAfters() | ||
} | ||
|
||
func run(reporter: ContextReporter) { | ||
if disabled { | ||
reporter.addDisabled(name) | ||
return | ||
} | ||
|
||
reporter.report(name) { reporter in | ||
cases.forEach { | ||
runBefores() | ||
$0.run(reporter: reporter) | ||
runAfters() | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
public protocol ExpectationType { | ||
associatedtype ValueType | ||
var expression: () throws -> ValueType? { get } | ||
func failure(_ reason: String) -> FailureType | ||
} | ||
|
||
|
||
struct ExpectationFailure : FailureType { | ||
let file: String | ||
let line: Int | ||
let function: String | ||
|
||
let reason: String | ||
|
||
init(reason: String, file: String, line: Int, function: String) { | ||
self.reason = reason | ||
self.file = file | ||
self.line = line | ||
self.function = function | ||
} | ||
} | ||
|
||
open class Expectation<T> : ExpectationType { | ||
public typealias ValueType = T | ||
open let expression: () throws -> ValueType? | ||
|
||
let file: String | ||
let line: Int | ||
let function: String | ||
|
||
open var to: Expectation<T> { | ||
return self | ||
} | ||
|
||
init(file: String, line: Int, function: String, expression: @escaping () throws -> ValueType?) { | ||
self.file = file | ||
self.line = line | ||
self.function = function | ||
self.expression = expression | ||
} | ||
|
||
open func failure(_ reason: String) -> FailureType { | ||
return ExpectationFailure(reason: reason, file: file, line: line, function: function) | ||
} | ||
} | ||
|
||
public func expect<T>( _ expression: @autoclosure @escaping () throws -> T?, file: String = #file, line: Int = #line, function: String = #function) -> Expectation<T> { | ||
return Expectation(file: file, line: line, function: function, expression: expression) | ||
} | ||
|
||
public func expect<T>(_ file: String = #file, line: Int = #line, function: String = #function, expression: @escaping () throws -> T?) -> Expectation<T> { | ||
return Expectation(file: file, line: line, function: function, expression: expression) | ||
} | ||
|
||
// MARK: Equatability | ||
|
||
public func == <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Equatable { | ||
if let value = try lhs.expression() { | ||
if value != rhs { | ||
throw lhs.failure("\(String(describing: value)) is not equal to \(rhs)") | ||
} | ||
} else { | ||
throw lhs.failure("given value is nil") | ||
} | ||
} | ||
|
||
public func != <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Equatable { | ||
let value = try lhs.expression() | ||
if value == rhs { | ||
throw lhs.failure("\(String(describing: value)) is equal to \(rhs)") | ||
} | ||
} | ||
|
||
// MARK: Array Equatability | ||
|
||
public func == <Element: Equatable> (lhs: Expectation<[Element]>, rhs: [Element]) throws { | ||
if let value = try lhs.expression() { | ||
if value != rhs { | ||
throw lhs.failure("\(String(describing: value)) is not equal to \(rhs)") | ||
} | ||
} else { | ||
throw lhs.failure("given value is nil") | ||
} | ||
} | ||
|
||
public func != <Element: Equatable> (lhs: Expectation<[Element]>, rhs: [Element]) throws { | ||
if let value = try lhs.expression() { | ||
if value == rhs { | ||
throw lhs.failure("\(String(describing: value)) is equal to \(rhs)") | ||
} | ||
} else { | ||
throw lhs.failure("given value is nil") | ||
} | ||
} | ||
|
||
// MARK: Dictionary Equatability | ||
|
||
public func == <Key, Value: Equatable> (lhs: Expectation<[Key: Value]>, rhs: [Key: Value]) throws { | ||
if let value = try lhs.expression() { | ||
if value != rhs { | ||
throw lhs.failure("\(String(describing: value)) is not equal to \(rhs)") | ||
} | ||
} else { | ||
throw lhs.failure("given value is nil") | ||
} | ||
} | ||
|
||
public func != <Key, Value: Equatable> (lhs: Expectation<[Key: Value]>, rhs: [Key: Value]) throws { | ||
if let value = try lhs.expression() { | ||
if value == rhs { | ||
throw lhs.failure("\(String(describing: value)) is equal to \(rhs)") | ||
} | ||
} else { | ||
throw lhs.failure("given value is nil") | ||
} | ||
} | ||
|
||
// MARK: Nil | ||
|
||
extension ExpectationType { | ||
public func beNil() throws { | ||
let value = try expression() | ||
if value != nil { | ||
throw failure("value is not nil") | ||
} | ||
} | ||
} | ||
|
||
// MARK: Boolean | ||
|
||
extension ExpectationType where ValueType == Bool { | ||
public func beTrue() throws { | ||
let value = try expression() | ||
if value != true { | ||
throw failure("value is not true") | ||
} | ||
} | ||
|
||
public func beFalse() throws { | ||
let value = try expression() | ||
if value != false { | ||
throw failure("value is not false") | ||
} | ||
} | ||
} | ||
|
||
// Mark: Types | ||
|
||
extension ExpectationType { | ||
public func beOfType(_ expectedType: Any.Type) throws { | ||
guard let value = try expression() else { throw failure("cannot determine type: expression threw an error or value is nil") } | ||
let valueType = Mirror(reflecting: value).subjectType | ||
if valueType != expectedType { | ||
throw failure("'\(valueType)' is not the expected type '\(expectedType)'") | ||
} | ||
} | ||
} | ||
|
||
// MARK: Error Handling | ||
|
||
extension ExpectationType { | ||
public func toThrow() throws { | ||
var didThrow = false | ||
|
||
do { | ||
_ = try expression() | ||
} catch { | ||
didThrow = true | ||
} | ||
|
||
if !didThrow { | ||
throw failure("expression did not throw an error") | ||
} | ||
} | ||
|
||
public func toThrow<T: Equatable>(_ error: T) throws { | ||
var thrownError: Error? = nil | ||
|
||
do { | ||
_ = try expression() | ||
} catch { | ||
thrownError = error | ||
} | ||
|
||
if let thrownError = thrownError { | ||
if let thrownError = thrownError as? T { | ||
if error != thrownError { | ||
throw failure("\(thrownError) is not \(error)") | ||
} | ||
} else { | ||
throw failure("\(thrownError) is not \(error)") | ||
} | ||
} else { | ||
throw failure("expression did not throw an error") | ||
} | ||
} | ||
} | ||
|
||
// MARK: Comparable | ||
|
||
public func > <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable { | ||
let value = try lhs.expression() | ||
guard value! > rhs else { | ||
throw lhs.failure("\(String(describing: value)) is not more than \(rhs)") | ||
} | ||
} | ||
|
||
public func >= <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable { | ||
let value = try lhs.expression() | ||
guard value! >= rhs else { | ||
throw lhs.failure("\(String(describing: value)) is not more than or equal to \(rhs)") | ||
} | ||
} | ||
|
||
public func < <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable { | ||
let value = try lhs.expression() | ||
guard value! < rhs else { | ||
throw lhs.failure("\(String(describing: value)) is not less than \(rhs)") | ||
} | ||
} | ||
|
||
public func <= <E: ExpectationType>(lhs: E, rhs: E.ValueType) throws where E.ValueType: Comparable { | ||
let value = try lhs.expression() | ||
guard value! <= rhs else { | ||
throw lhs.failure("\(String(describing: value)) is not less than or equal to \(rhs)") | ||
} | ||
} |
Oops, something went wrong.