Skip to content

Commit

Permalink
Fix ProcessTests on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
stevapple committed Dec 7, 2021
1 parent dab60f0 commit 8f521a0
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 33 deletions.
158 changes: 127 additions & 31 deletions Tests/TSCBasicTests/ProcessTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,55 +25,66 @@ class ProcessTests: XCTestCase {

func testBasics() throws {
do {
#if os(Windows)
let process = Process(args: "cmd", "/c", "echo", "hello")
#else
let process = Process(args: "echo", "hello")
#endif
try process.launch()
let result = try process.waitUntilExit()
XCTAssertEqual(try result.utf8Output(), "hello\n")
XCTAssertEqual(result.exitStatus, .terminated(code: 0))
XCTAssertEqual(result.arguments, process.arguments)
}

do {
#if os(Windows)
let process = Process(args: "cmd.exe", "/c", "exit", "4")
#else
let process = Process(args: script("exit4"))
#endif
try process.launch()
let result = try process.waitUntilExit()
XCTAssertEqual(result.exitStatus, .terminated(code: 4))
}
}

func testPopen() throws {
#if os(Windows)
let echo = "echo.exe"
#else
let echo = "echo"
#endif
#if os(Windows)
let echo = ["cmd.exe", "/c", "echo"]
#else
let echo = ["echo"]
#endif
// Test basic echo.
XCTAssertEqual(try Process.popen(arguments: [echo, "hello"]).utf8Output(), "hello\n")
XCTAssertEqual(try Process.popen(arguments: echo + ["hello"]).utf8Output(), "hello\n")

// Test buffer larger than that allocated.
try withTemporaryFile { file in
let count = 10_000
let stream = BufferedOutputByteStream()
stream <<< Format.asRepeating(string: "a", count: count)
try localFileSystem.writeFileContents(file.path, bytes: stream.bytes)
#if os(Windows)
let cat = "cat.exe"
#else
let cat = "cat"
#endif
let outputCount = try Process.popen(args: cat, file.path.pathString).utf8Output().count
#if os(Windows)
let process = try Process.popen(args: "cmd.exe", "/c", "type", file.path.pathString)
#else
let process = try Process.popen(args: "cat", file.path.pathString)
#endif
let outputCount = try process.utf8Output().count
XCTAssert(outputCount == count)
}
}

func testPopenAsync() throws {
#if os(Windows)
#if os(Windows)
let args = ["where.exe", "where"]
let answer = "C:\\Windows\\System32\\where.exe"
#else
var buffer = Array<WCHAR>(repeating: 0, count: Int(MAX_PATH + 1))
guard GetSystemDirectoryW(&buffer, .init(buffer.count)) > 0 else {
return XCTFail()
}
let answer = String(decodingCString: buffer, as: UTF16.self) + "\\where.exe"
#else
let args = ["whoami"]
let answer = NSUserName()
#endif
#endif
var popenResult: Result<ProcessResult, Error>?
let group = DispatchGroup()
group.enter()
Expand All @@ -95,25 +106,34 @@ class ProcessTests: XCTestCase {

func testCheckNonZeroExit() throws {
do {
#if os(Windows)
let output = try Process.checkNonZeroExit(args: "cmd.exe", "/c", "echo", "hello")
#else
let output = try Process.checkNonZeroExit(args: "echo", "hello")
#endif
XCTAssertEqual(output, "hello\n")
}

do {
#if os(Windows)
let output = try Process.checkNonZeroExit(args: "cmd.exe", "/c", "exit", "4")
#else
let output = try Process.checkNonZeroExit(args: script("exit4"))
#endif
XCTFail("Unexpected success \(output)")
} catch ProcessResult.Error.nonZeroExit(let result) {
XCTAssertEqual(result.exitStatus, .terminated(code: 4))
}
}

func testFindExecutable() throws {
#if !os(Windows)
try testWithTemporaryDirectory { tmpdir in
// This process should always work.
XCTAssertTrue(Process.findExecutable("ls") != nil)
XCTAssertNotNil(Process.findExecutable("ls"))

XCTAssertEqual(Process.findExecutable("nonExistantProgram"), nil)
XCTAssertEqual(Process.findExecutable(""), nil)
XCTAssertNil(Process.findExecutable("nonExistantProgram"))
XCTAssertNil(Process.findExecutable(""))

// Create a local nonexecutable file to test.
let tempExecutable = tmpdir.appending(component: "nonExecutableProgram")
Expand All @@ -124,9 +144,40 @@ class ProcessTests: XCTestCase {
""")

try withCustomEnv(["PATH": tmpdir.pathString]) {
XCTAssertEqual(Process.findExecutable("nonExecutableProgram"), nil)
XCTAssertNil(Process.findExecutable("nonExecutableProgram"))
}
}
#else
try testWithTemporaryDirectory { tmpdir in
// Test System32 without .exe suffix.
XCTAssertNotNil(Process.findExecutable("cmd"))

// Test Windows with .exe suffix.
XCTAssertNotNil(Process.findExecutable("explorer.exe"))

// Test non-existant programs.
XCTAssertNil(Process.findExecutable("nonExistantProgram"))
XCTAssertNil(Process.findExecutable(""))

// Copy an executable file to test.
let tempExecutable = tmpdir.appending(component: "executableProgram.exe")
try localFileSystem.copy(from: Process.findExecutable("cmd")!, to: tempExecutable)

// Create a non-executable file to test.
let tempNonExecutable = tmpdir.appending(component: "program.bat")
try localFileSystem.writeFileContents(tempNonExecutable, bytes: """
@echo off
exit
""")

try withCustomEnv(["Path": tmpdir.pathString]) {
XCTAssertNotNil(Process.findExecutable("executableProgram.exe"))
XCTAssertNotNil(Process.findExecutable("executableProgram"))
XCTAssertNil(Process.findExecutable("program.bat"))
}
}
#endif
}

func testNonExecutableLaunch() throws {
Expand All @@ -144,7 +195,7 @@ class ProcessTests: XCTestCase {
let process = Process(args: "nonExecutableProgram")
try process.launch()
XCTFail("Should have failed to validate nonExecutableProgram")
} catch Process.Error.missingExecutableProgram (let program){
} catch Process.Error.missingExecutableProgram(let program) {
XCTAssert(program == "nonExecutableProgram")
}
}
Expand Down Expand Up @@ -230,7 +281,11 @@ class ProcessTests: XCTestCase {
#endif

func testThreadSafetyOnWaitUntilExit() throws {
#if os(Windows)
let process = Process(args: "cmd", "/c", "echo", "hello")
#else
let process = Process(args: "echo", "hello")
#endif
try process.launch()

var result1: String = ""
Expand All @@ -255,7 +310,12 @@ class ProcessTests: XCTestCase {

func testStdin() throws {
var stdout = [UInt8]()
let process = Process(args: script("in-to-out"), outputRedirection: .stream(stdout: { stdoutBytes in
#if os(Windows)
let inToOut = ["python.exe", script("in-to-out")]
#else
let inToOut = [script("in-to-out")]
#endif
let process = Process(arguments: inToOut, outputRedirection: .stream(stdout: { stdoutBytes in
stdout += stdoutBytes
}, stderr: { _ in }))
let stdinStream = try process.launch()
Expand All @@ -273,14 +333,24 @@ class ProcessTests: XCTestCase {
func testStdoutStdErr() throws {
// A simple script to check that stdout and stderr are captured separatly.
do {
let result = try Process.popen(args: script("simple-stdout-stderr"))
#if os(Windows)
let simpleStdoutStdErr = ["python.exe", script("simple-stdout-stderr")]
#else
let simpleStdoutStdErr = [script("simple-stdout-stderr")]
#endif
let result = try Process.popen(arguments: simpleStdoutStdErr)
XCTAssertEqual(try result.utf8Output(), "simple output\n")
XCTAssertEqual(try result.utf8stderrOutput(), "simple error\n")
}

// A long stdout and stderr output.
do {
let result = try Process.popen(args: script("long-stdout-stderr"))
#if os(Windows)
let longStdoutStdErr = ["python.exe", script("long-stdout-stderr")]
#else
let longStdoutStdErr = [script("long-stdout-stderr")]
#endif
let result = try Process.popen(arguments: longStdoutStdErr)
let count = 16 * 1024
XCTAssertEqual(try result.utf8Output(), String(repeating: "1", count: count))
XCTAssertEqual(try result.utf8stderrOutput(), String(repeating: "2", count: count))
Expand All @@ -298,7 +368,12 @@ class ProcessTests: XCTestCase {
func testStdoutStdErrRedirected() throws {
// A simple script to check that stdout and stderr are captured in the same location.
do {
let process = Process(args: script("simple-stdout-stderr"), outputRedirection: .collect(redirectStderr: true))
#if os(Windows)
let simpleStdoutStdErr = ["python.exe", script("simple-stdout-stderr")]
#else
let simpleStdoutStdErr = [script("simple-stdout-stderr")]
#endif
let process = Process(arguments: simpleStdoutStdErr, outputRedirection: .collect(redirectStderr: true))
try process.launch()
let result = try process.waitUntilExit()
XCTAssertEqual(try result.utf8Output(), "simple error\nsimple output\n")
Expand All @@ -307,7 +382,12 @@ class ProcessTests: XCTestCase {

// A long stdout and stderr output.
do {
let process = Process(args: script("long-stdout-stderr"), outputRedirection: .collect(redirectStderr: true))
#if os(Windows)
let longStdoutStdErr = ["python.exe", script("long-stdout-stderr")]
#else
let longStdoutStdErr = [script("long-stdout-stderr")]
#endif
let process = Process(arguments: longStdoutStdErr, outputRedirection: .collect(redirectStderr: true))
try process.launch()
let result = try process.waitUntilExit()

Expand All @@ -320,7 +400,12 @@ class ProcessTests: XCTestCase {
func testStdoutStdErrStreaming() throws {
var stdout = [UInt8]()
var stderr = [UInt8]()
let process = Process(args: script("long-stdout-stderr"), outputRedirection: .stream(stdout: { stdoutBytes in
#if os(Windows)
let longStdoutStdErr = ["python.exe", script("long-stdout-stderr")]
#else
let longStdoutStdErr = [script("long-stdout-stderr")]
#endif
let process = Process(arguments: longStdoutStdErr, outputRedirection: .stream(stdout: { stdoutBytes in
stdout += stdoutBytes
}, stderr: { stderrBytes in
stderr += stderrBytes
Expand All @@ -336,7 +421,12 @@ class ProcessTests: XCTestCase {
func testStdoutStdErrStreamingRedirected() throws {
var stdout = [UInt8]()
var stderr = [UInt8]()
let process = Process(args: script("long-stdout-stderr"), outputRedirection: .stream(stdout: { stdoutBytes in
#if os(Windows)
let longStdoutStdErr = ["python.exe", script("long-stdout-stderr")]
#else
let longStdoutStdErr = [script("long-stdout-stderr")]
#endif
let process = Process(arguments: longStdoutStdErr, outputRedirection: .stream(stdout: { stdoutBytes in
stdout += stdoutBytes
}, stderr: { stderrBytes in
stderr += stderrBytes
Expand Down Expand Up @@ -370,15 +460,21 @@ class ProcessTests: XCTestCase {
try localFileSystem.createDirectory(childPath.parentDirectory, recursive: true)
try localFileSystem.writeFileContents(childPath, bytes: ByteString("child"))

#if os(Windows)
let args = ["cmd.exe", "/c", "type", "file"]
#else
let args = ["cat", "file"]
#endif

do {
let process = Process(arguments: ["cat", "file"], workingDirectory: tempDirPath)
let process = Process(arguments: args, workingDirectory: tempDirPath)
try process.launch()
let result = try process.waitUntilExit()
XCTAssertEqual(try result.utf8Output(), "parent")
}

do {
let process = Process(arguments: ["cat", "file"], workingDirectory: childPath.parentDirectory)
let process = Process(arguments: args, workingDirectory: childPath.parentDirectory)
try process.launch()
let result = try process.waitUntilExit()
XCTAssertEqual(try result.utf8Output(), "child")
Expand Down
4 changes: 2 additions & 2 deletions Tests/TSCBasicTests/TemporaryFileTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,18 @@ class TemporaryFileTests: XCTestCase {
}

/// Check that the temporary file doesn't leak file descriptors.
#if !os(Windows) // `fileDescriptor` is currently unavailable in Windows
func testLeaks() throws {
// We check this by testing that we get back the same FD after a
// sequence of creating and destroying TemporaryFile objects. I don't
// believe that this is guaranteed by POSIX, but it is true on all
// platforms I know of.
#if !os(Windows)
let initialFD = try Int(withTemporaryFile { return $0.fileHandle.fileDescriptor })
for _ in 0..<10 {
_ = try withTemporaryFile { return $0.fileHandle }
}
let endFD = try Int(withTemporaryFile { return $0.fileHandle.fileDescriptor })
XCTAssertEqual(initialFD, endFD)
#endif
}
#endif
}

0 comments on commit 8f521a0

Please sign in to comment.