Skip to content

Commit

Permalink
Merge pull request #8 from daniel-hall/AddOptionalSetupForExpectAndGi…
Browse files Browse the repository at this point in the history
…venHandlers

Added optional setup closure for expect / given handlers
  • Loading branch information
daniel-hall authored Apr 27, 2023
2 parents 8135bff + 49f42a4 commit bd65620
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 17 deletions.
4 changes: 4 additions & 0 deletions Sources/RequirementsKit/RequirementsTestConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ fileprivate extension XCTestCase {
var issue = issue
let context = XCTSourceCodeContext(callStack: issue.sourceCodeContext.callStack, location: .init(fileURL: runner.file.url, lineNumber: line))
issue.sourceCodeContext = context
if runner.setupActive {
issue.compactDescription = "Failure in setup closure: " + issue.compactDescription
issue.detailedDescription = issue.detailedDescription.map { "Failure in setup closure: " + $0 }
}
self.modifiedRecord(issue)
}
self.modifiedRecord(issue)
Expand Down
25 changes: 25 additions & 0 deletions Sources/RequirementsKit/RequirementsTestRunner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class RequirementTestRunner: NSObject, XCTestObservation {
var requirement: Requirement
var hasFailed = false
var continueAfterFailure = false
var setupActive = false
var currentStatement: Requirement.Example.Statement?
var shouldDisplayFailuresOnRequirement: Bool = true
let matchLabels: LabelExpression?
Expand Down Expand Up @@ -68,6 +69,30 @@ class RequirementTestRunner: NSObject, XCTestObservation {
}
XCTContext.runActivity(named: "💡 " + example.activity(syntax: file.syntax)) { _ in
self.beforeEachExample?(example)
let setups = statementHandlers.filter { $0.setup != nil }
setups.forEach { handler in
if let match = example.statements.first(where: {
$0.type == handler.type && handler.getMatch($0) != nil
}) {
currentStatement = match
setupActive = true
defer { setupActive = false }
do {
let timeoutWorkItem = DispatchWorkItem {
XCTFail("Statement setup timed out after \(handler.timeout ?? timeout) seconds")
}
self.timeoutDispatchWorkItem?.cancel()
self.timeoutDispatchWorkItem = timeoutWorkItem
DispatchQueue.global().asyncAfter(deadline: .now() + (handler.timeout ?? timeout), execute: timeoutWorkItem)
try handler.setup?(handler.getMatch(match))
} catch {
XCTFail(error.localizedDescription)
if !continueAfterFailure {
hasFailed = true
}
}
}
}
for statement in example.statements {
guard !self.hasFailed || continueAfterFailure else { break }
currentStatement = statement
Expand Down
83 changes: 66 additions & 17 deletions Sources/RequirementsKit/StatementHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,71 +37,120 @@ public struct StatementHandler {
let timeout: TimeInterval?
let getMatch: (Requirement.Example.Statement) -> Any?
let action: (Any?) throws -> Void
let setup: ((Any?) throws -> Void)?

fileprivate init<T>(statementType: Requirement.Example.StatementType, statement: Regex<T>, timeout: TimeInterval?, handler: @escaping (Input<T>) throws -> Void) {
fileprivate init<T>(statementType: Requirement.Example.StatementType, statement: Regex<T>, timeout: TimeInterval?, handler: @escaping (Input<T>) throws -> Void, setup: ((Input<T>) throws -> Void)?) {
type = statementType
getMatch = { exampleStatement in
exampleStatement.description.wholeMatch(of: statement).map { Input(statement: exampleStatement, match: $0.output) }
}
self.action = { try handler($0 as! Input<T>) }
self.timeout = timeout
self.setup = { try setup?($0 as! Input<T>) }
}

fileprivate init(statementType: Requirement.Example.StatementType, statement: [String], timeout: TimeInterval?, handler: @escaping (Input<AnyRegexOutput>) throws -> Void) {
fileprivate init(statementType: Requirement.Example.StatementType, statement: [String], timeout: TimeInterval?, handler: @escaping (Input<AnyRegexOutput>) throws -> Void, setup: ((Input<AnyRegexOutput>) throws -> Void)?) {
type = statementType
getMatch = { exampleStatement in

exampleStatement.description.wholeMatch(of: try! Regex("(" + statement.joined(separator: "|") + ")")).map { Input(statement: exampleStatement, match: AnyRegexOutput($0)) }
}
self.action = { try handler($0 as! Input<AnyRegexOutput>) }
self.timeout = timeout
self.setup = { try setup?($0 as! Input<AnyRegexOutput>) }
}
}

public extension StatementHandler {
static func `if`<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.init(statementType: .if, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .if, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func `if`(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .if, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .if, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func `if`(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .if, statement: statement, timeout: timeout, handler: { _ in try handler() })
.init(statementType: .if, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: nil)
}
static func given<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.if(statement, timeout: timeout, handler: handler)
}
static func given(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .if, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .if, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func given(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .if, statement: statement, timeout: timeout, handler: { _ in try handler() })
.init(statementType: .if, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: nil)
}
static func when<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.init(statementType: .when, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .when, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func when(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .when, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .when, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func when(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .when, statement: statement, timeout: timeout, handler: { _ in try handler() })
.init(statementType: .when, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: nil)
}
static func expect<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func expect(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler)
static func expect<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void, setup: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: setup)
}
static func expect(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() })
static func expect<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void, setup: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: { _ in try setup() })
}
static func expect<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: setup)
}
static func expect<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: { _ in try setup() })
}
static func expect<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: nil)
}
static func expect(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void, setup: ((Input<AnyRegexOutput>) throws -> Void)? = nil) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: setup)
}
static func expect(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: ((Input<AnyRegexOutput>) throws -> Void)? = nil) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: {_ in try handler() }, setup: setup)
}
static func expect(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void, setup: (() throws -> Void)? = nil) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: { _ in try setup?() })
}
static func expect(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: (() throws -> Void)? = nil) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: { _ in try setup?() })
}
static func then<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void, setup: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.expect(statement, timeout: timeout, handler: handler, setup: setup)
}
static func then<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.expect(statement, timeout: timeout, handler: { _ in try handler() }, setup: setup)
}
static func then<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: @escaping () throws -> Void) -> StatementHandler {
.expect(statement, timeout: timeout, handler: { _ in try handler() }, setup: { _ in try setup() })
}
static func then<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void, setup: @escaping () throws -> Void) -> StatementHandler {
.expect(statement, timeout: timeout, handler: handler, setup: { _ in try setup() })
}
static func then<T>(_ statement: Regex<T>, timeout: TimeInterval? = nil, handler: @escaping (Input<T>) throws -> Void) -> StatementHandler {
.expect(statement, timeout: timeout, handler: handler)
}
static func then(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void, setup: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: setup)
}
static func then(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: setup)
}
static func then(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void, setup: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: { _ in try setup() })
}
static func then(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping (Input<AnyRegexOutput>) throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler)
.init(statementType: .expect, statement: statement, timeout: timeout, handler: handler, setup: nil)
}
static func then(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void, setup: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: { _ in try setup() })
}

static func then(_ statement: String..., timeout: TimeInterval? = nil, handler: @escaping () throws -> Void) -> StatementHandler {
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() })
.init(statementType: .expect, statement: statement, timeout: timeout, handler: { _ in try handler() }, setup: nil)
}
}

0 comments on commit bd65620

Please sign in to comment.