1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-11-08 05:30:05 +01:00
This commit is contained in:
2022-10-03 22:27:50 +02:00
parent c9a5cd3a9f
commit c26c491340
3 changed files with 62 additions and 36 deletions

View File

@@ -9,6 +9,19 @@
import XCTest import XCTest
class FakeShellTest: XCTestCase { class FakeShellTest: XCTestCase {
func test_fake_shell_output_can_be_declared() {
let greeting = BatchFakeShellOutput(items: [
.instant("Hello world"),
.delayed(0.3, "Goodbye world")
])
let output = greeting.outputInstantaneously()
XCTAssertEqual("Hello world\nGoodbye world", output.out)
}
/*
func test_we_can_predefine_responses_for_dummy_shell() { func test_we_can_predefine_responses_for_dummy_shell() {
let expectedPhpOutput = """ let expectedPhpOutput = """
PHP 8.1.10 (cli) (built: Sep 3 2022 12:09:27) (NTS) PHP 8.1.10 (cli) (built: Sep 3 2022 12:09:27) (NTS)
@@ -45,4 +58,5 @@ class FakeShellTest: XCTestCase {
XCTAssertEqual("Unexpected Command", output.err) XCTAssertEqual("Unexpected Command", output.err)
XCTAssertEqual("", output.out) XCTAssertEqual("", output.out)
} }
*/
} }

View File

@@ -15,13 +15,10 @@ var Shell: Shellable {
class ActiveShell { class ActiveShell {
static var shared: Shellable = SystemShell() static var shared: Shellable = SystemShell()
/// Uses a testable shell with predefined responses. You specify the terminal's output. public static func useTestable(_ expectations: [String: BatchFakeShellOutput]) {
/// they also work with simple String objects.
public static func useTestable(_ expectations: [String: OutputsToShell]) {
Self.shared = TestableShell(expectations: expectations) Self.shared = TestableShell(expectations: expectations)
} }
/// Reverts back to the system shell. You do not need to call this, only after using `useTestable()`.
public static func useSystem() { public static func useSystem() {
Self.shared = SystemShell() Self.shared = SystemShell()
} }

View File

@@ -11,11 +11,11 @@ import Foundation
public class TestableShell: Shellable { public class TestableShell: Shellable {
public typealias Input = String public typealias Input = String
init(expectations: [Input: OutputsToShell]) { init(expectations: [Input: BatchFakeShellOutput]) {
self.expectations = expectations self.expectations = expectations
} }
var expectations: [Input: OutputsToShell] = [:] var expectations: [Input: BatchFakeShellOutput] = [:]
func quiet(_ command: String) async { func quiet(_ command: String) async {
return return
@@ -37,9 +37,7 @@ public class TestableShell: Shellable {
guard let expectation = expectations[command] else { guard let expectation = expectations[command] else {
return .err("Unexpected Command") return .err("Unexpected Command")
} }
return ShellOutput(out: "", err: "")
let output = expectation.getOutputAsString()
return expectation.outputsToError() ? .err(output) : .out(output)
} }
} }
@@ -49,40 +47,57 @@ public class TestableShell: Shellable {
// 2. Delayed but then all at once: `.delay(300, "string")` // 2. Delayed but then all at once: `.delay(300, "string")`
// 3. A stream of data spread over multiple seconds: `.multiple([.delay(300, "hello"), .delay(300, "bye")])` // 3. A stream of data spread over multiple seconds: `.multiple([.delay(300, "hello"), .delay(300, "bye")])`
protocol OutputsToShell { struct FakeShellOutput {
func getOutputAsString() -> String let delay: TimeInterval
func getDuration() -> Int let output: ShellOutput
func outputsToError() -> Bool
}
struct FakeTerminalOutput: OutputsToShell { static func instant(_ stdOut: String, _ stdErr: String? = nil) -> FakeShellOutput {
var output: String return FakeShellOutput(delay: 0, output: ShellOutput(out: stdOut, err: stdErr ?? ""))
var duration: Int
var isError: Bool
func getOutputAsString() -> String {
return output
} }
func getDuration() -> Int { static func delayed(_ delay: TimeInterval, _ stdOut: String, _ stdErr: String? = nil) -> FakeShellOutput {
return duration return FakeShellOutput(delay: delay, output: ShellOutput(out: stdOut, err: stdErr ?? ""))
}
func outputsToError() -> Bool {
return isError
} }
} }
extension String: OutputsToShell { struct BatchFakeShellOutput {
func getOutputAsString() -> String { var items: [FakeShellOutput]
return self
/**
Outputs the fake shell output as expected.
*/
public func output(
didReceiveOutput: @escaping (ShellOutput) -> Void,
ignoreDelay: Bool = false
) async -> ShellOutput {
var allOut: String = ""
var allErr: String = ""
Task {
self.items.forEach { fakeShellOutput in
let delay = UInt64(fakeShellOutput.delay * 1_000_000_000)
try await Task.sleep(nanoseconds: delay)
allOut += fakeShellOutput.output.out
if fakeShellOutput.output.hasError {
allErr += fakeShellOutput.output.err
}
}
}
return ShellOutput(
out: allOut,
err: allErr
)
} }
func getDuration() -> Int { /**
return 100 For testing purposes (and speed) we may omit the delay, regardless of its timespan.
} */
public func outputInstantaneously(
func outputsToError() -> Bool { didReceiveOutput: @escaping (ShellOutput) -> Void
return false ) -> ShellOutput {
self.output(didReceiveOutput: didReceiveOutput, ignoreDelay: true)
} }
} }