blob: 982e7282e330f7f6bbc1c10a66bca712d755adc7 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
import Foundation
import Synchronization
import Testing
@testable import bar
/// Blocking Runner work stays off the cooperative executor. The outer deadline
/// also fails promptly if a regression prevents Runner from returning.
private func runCommand(_ command: String, env: [String: String] = [:]) throws -> (lines: [String], diagnostics: Data) {
let url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
FileManager.default.createFile(atPath: url.path, contents: nil)
let output = try FileHandle(forWritingTo: url)
defer { try? FileManager.default.removeItem(at: url) }
let result = Mutex<[String]?>(nil)
let finished = DispatchSemaphore(value: 0)
DispatchQueue.global(qos: .utility).async {
let lines = Runner.run(command, env: env, errorOutput: output)
try? output.close()
result.withLock { $0 = lines }
finished.signal()
}
let completed = finished.wait(timeout: .now() + 5) == .success
try #require(completed, "Runner exceeded the five-second outer deadline")
return (try #require(result.withLock { $0 }), try Data(contentsOf: url))
}
@Test func drainsLargeStderrBeforeStdout() throws {
let result = try runCommand("/usr/bin/head -c 1048576 /dev/zero >&2; printf ' ready \\n'")
#expect(result.lines == [" ready "])
#expect(result.diagnostics == Data(repeating: 0, count: 1048576))
}
@Test func drainsSimultaneousLargeStreams() throws {
let result = try runCommand("/usr/bin/head -c 1048576 /dev/zero >&2 & /usr/bin/head -c 1048576 /dev/zero; wait")
#expect(result.lines == [String(repeating: "\0", count: 1048576)])
#expect(result.diagnostics == Data(repeating: 0, count: 1048576))
}
@Test func emptyCommandOutput() throws {
let result = try runCommand(":")
#expect(result.lines.isEmpty)
#expect(result.diagnostics.isEmpty)
}
@Test func failedCommandForwardsDiagnostics() throws {
let result = try runCommand("printf 'ignored'; printf 'failure\\n' >&2; exit 7")
#expect(result.lines == ["ERROR"])
#expect(result.diagnostics == Data("failure\n".utf8))
}
@Test func shellEnvironmentAndWhitespaceArePreserved() throws {
let result = try runCommand("printf ' %s \\n\\nlast\\n' \"$BAR_RUNNER_TEST\"; test -n \"$PATH\"", env: ["BAR_RUNNER_TEST": "value"])
#expect(result.lines == [" value ", "", "last"])
}
|