mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-11-08 05:30:05 +01:00
🏗 WIP
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user