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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
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"])
}
@Test func parsingEmptyOutputProducesNoCells() {
#expect(Runner.parse("") == [])
}
@Test func parsingRemovesOneFinalNewline() {
#expect(Runner.parse("cell\n") == ["cell"])
#expect(Runner.parse("cell") == ["cell"])
}
@Test func parsingPreservesInternalBlankLines() {
#expect(Runner.parse("first\n\n\nlast\n") == ["first", "", "", "last"])
#expect(Runner.parse("\nfirst") == ["", "first"])
}
@Test func parsingPreservesAdditionalTrailingBlankLines() {
#expect(Runner.parse("cell\n\n\n") == ["cell", "", ""])
#expect(Runner.parse("\n") == [""])
#expect(Runner.parse("\n\n") == ["", ""])
}
@Test func parsingPreservesLineOrder() {
#expect(Runner.parse("third\nfirst\nsecond\n") == ["third", "first", "second"])
}
@Test func parsingDoesNotTrimWhitespace() {
#expect(Runner.parse(" leading\ntrailing \n\t \t\n") == [" leading", "trailing ", "\t \t"])
}
|