From 8f521a0216a1967faef64881535951db99d4cbae Mon Sep 17 00:00:00 2001 From: YR Chen Date: Wed, 8 Dec 2021 07:47:50 +0800 Subject: [PATCH] Fix `ProcessTests` on Windows --- Tests/TSCBasicTests/ProcessTests.swift | 158 +++++++++++++++---- Tests/TSCBasicTests/TemporaryFileTests.swift | 4 +- 2 files changed, 129 insertions(+), 33 deletions(-) diff --git a/Tests/TSCBasicTests/ProcessTests.swift b/Tests/TSCBasicTests/ProcessTests.swift index 1ff03416..2cf1c8cb 100644 --- a/Tests/TSCBasicTests/ProcessTests.swift +++ b/Tests/TSCBasicTests/ProcessTests.swift @@ -25,16 +25,23 @@ 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)) @@ -42,13 +49,13 @@ class ProcessTests: XCTestCase { } 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 @@ -56,24 +63,28 @@ class ProcessTests: XCTestCase { 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(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? let group = DispatchGroup() group.enter() @@ -95,12 +106,20 @@ 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)) @@ -108,12 +127,13 @@ class ProcessTests: XCTestCase { } 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") @@ -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 { @@ -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") } } @@ -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 = "" @@ -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() @@ -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)) @@ -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") @@ -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() @@ -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 @@ -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 @@ -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") diff --git a/Tests/TSCBasicTests/TemporaryFileTests.swift b/Tests/TSCBasicTests/TemporaryFileTests.swift index 2d724673..1dc97599 100644 --- a/Tests/TSCBasicTests/TemporaryFileTests.swift +++ b/Tests/TSCBasicTests/TemporaryFileTests.swift @@ -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 }