diff --git a/Foundation/Process.swift b/Foundation/Process.swift index 201aaaf6ac..3f87d6caf4 100644 --- a/Foundation/Process.swift +++ b/Foundation/Process.swift @@ -860,7 +860,7 @@ open class Process: NSObject { for (new, old) in adddup2 { posix(_CFPosixSpawnFileActionsAddDup2(fileActions, old, new)) } - for fd in addclose { + for fd in addclose.filter({ $0 >= 0 }) { posix(_CFPosixSpawnFileActionsAddClose(fileActions, fd)) } diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index e33c056710..c203c5abde 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -696,6 +696,73 @@ class TestProcess : XCTestCase { } } + func test_pipeCloseBeforeLaunch() { + let process = Process() + let stdInput = Pipe() + let stdOutput = Pipe() + + process.executableURL = xdgTestHelperURL() + process.arguments = ["--cat"] + process.standardInput = stdInput + process.standardOutput = stdOutput + + let string = "Hello, World" + let stdInputPipe = stdInput.fileHandleForWriting + XCTAssertNoThrow(try stdInputPipe.write(XCTUnwrap(string.data(using: .utf8)))) + stdInputPipe.closeFile() + + XCTAssertNoThrow(try process.run()) + process.waitUntilExit() + + let stdOutputPipe = stdOutput.fileHandleForReading + do { + let readData = try XCTUnwrap(stdOutputPipe.readToEnd()) + let readString = String(data: readData, encoding: .utf8) + XCTAssertEqual(string, readString) + } catch { + XCTFail("\(error)") + } + } + + func test_multiProcesses() { + let source = Process() + source.executableURL = xdgTestHelperURL() + source.arguments = [ "--getcwd" ] + + let cat1 = Process() + cat1.executableURL = xdgTestHelperURL() + cat1.arguments = [ "--cat" ] + + let cat2 = Process() + cat2.executableURL = xdgTestHelperURL() + cat2.arguments = [ "--cat" ] + + let pipe1 = Pipe() + source.standardOutput = pipe1 + cat1.standardInput = pipe1 + + let pipe2 = Pipe() + cat1.standardOutput = pipe2 + cat2.standardInput = pipe2 + + let pipe3 = Pipe() + cat2.standardOutput = pipe3 + + XCTAssertNoThrow(try source.run()) + XCTAssertNoThrow(try cat1.run()) + XCTAssertNoThrow(try cat2.run()) + cat2.waitUntilExit() + cat1.waitUntilExit() + source.waitUntilExit() + + do { + let data = try XCTUnwrap(pipe3.fileHandleForReading.readToEnd()) + let pwd = String.init(decoding: data, as: UTF8.self).trimmingCharacters(in: CharacterSet(["\n", "\r"])) + XCTAssertEqual(pwd, FileManager.default.currentDirectoryPath.standardizePath()) + } catch { + XCTFail("\(error)") + } + } static var allTests: [(String, (TestProcess) -> () throws -> Void)] { var tests = [ @@ -724,6 +791,8 @@ class TestProcess : XCTestCase { ("test_redirect_all_using_nil", test_redirect_all_using_nil), ("test_plutil", test_plutil), ("test_currentDirectory", test_currentDirectory), + ("test_pipeCloseBeforeLaunch", test_pipeCloseBeforeLaunch), + ("test_multiProcesses", test_multiProcesses), ] #if !os(Windows)