Skip to content

Commit

Permalink
Add Spectre tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dreymonde committed Feb 11, 2018
1 parent 49795a2 commit 9f294dd
Show file tree
Hide file tree
Showing 18 changed files with 1,099 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
## Build generated
build/
DerivedData/
*.DS_Store

## Various settings
*.pbxuser
Expand Down
8 changes: 8 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"object": {
"pins": [

]
},
"version": 1
}
42 changes: 42 additions & 0 deletions Tests/TimeTests/Spectre/Case.swift
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))
}
}
}
106 changes: 106 additions & 0 deletions Tests/TimeTests/Spectre/Context.swift
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()
}
}
}
}
227 changes: 227 additions & 0 deletions Tests/TimeTests/Spectre/Expectation.swift
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)")
}
}
Loading

0 comments on commit 9f294dd

Please sign in to comment.
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy