diff --git a/Foundation/Process.swift b/Foundation/Process.swift index c89a244a92..c02d6ae915 100644 --- a/Foundation/Process.swift +++ b/Foundation/Process.swift @@ -232,8 +232,28 @@ open class Process: NSObject { } // These properties can only be set before a launch. - open var executableURL: URL? - open var currentDirectoryURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true) + private var _executable: URL? + open var executableURL: URL? { + get { _executable } + set { + guard let url = newValue, url.isFileURL else { + fatalError("must provide a launch path") + } + _executable = url + } + } + + private var _currentDirectoryURL: URL? = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true) + open var currentDirectoryURL: URL? { + get { _currentDirectoryURL } + set { + guard let url = newValue, url.isFileURL else { + fatalError("non-file URL argument") + } + _currentDirectoryURL = url + } + } + open var arguments: [String]? open var environment: [String : String]? // if not set, use current @@ -245,7 +265,7 @@ open class Process: NSObject { @available(*, deprecated, renamed: "currentDirectoryURL") open var currentDirectoryPath: String { - get { return currentDirectoryURL.path } + get { return currentDirectoryURL!.path } set { currentDirectoryURL = URL(fileURLWithPath: newValue) } } @@ -478,8 +498,7 @@ open class Process: NSObject { if let env = self.environment { environment = env } else { - environment = ProcessInfo.processInfo.environment - environment["PWD"] = currentDirectoryURL.path + environment = ProcessInfo.processInfo.environment } // On Windows, the PATH is required in order to locate dlls needed by @@ -658,7 +677,6 @@ open class Process: NSObject { env = e } else { env = ProcessInfo.processInfo.environment - env["PWD"] = currentDirectoryURL.path } let nenv = env.count @@ -839,7 +857,7 @@ open class Process: NSObject { let fileManager = FileManager() let previousDirectoryPath = fileManager.currentDirectoryPath - if !fileManager.changeCurrentDirectoryPath(currentDirectoryURL.path) { + if let dir = currentDirectoryURL?.path, !fileManager.changeCurrentDirectoryPath(dir) { throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL) } diff --git a/TestFoundation/TestFileManager.swift b/TestFoundation/TestFileManager.swift index cafca1ea6d..df1613b760 100644 --- a/TestFoundation/TestFileManager.swift +++ b/TestFoundation/TestFileManager.swift @@ -99,6 +99,7 @@ class TestFileManager : XCTestCase { func test_creatingDirectoryWithShortIntermediatePath() { let fileManager = FileManager.default + let cwd = fileManager.currentDirectoryPath fileManager.changeCurrentDirectoryPath(NSTemporaryDirectory()) let relativePath = NSUUID().uuidString @@ -109,6 +110,7 @@ class TestFileManager : XCTestCase { } catch { XCTFail("Failed to create and clean up directory") } + fileManager.changeCurrentDirectoryPath(cwd) } func test_moveFile() { diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index 935a7c3735..e25c4e5177 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -255,17 +255,18 @@ class TestProcess : XCTestCase { if (dir.hasSuffix("/") && dir != "/") || dir.hasSuffix("\\") { dir.removeLast() } - return dir + return dir.standardizePath() }() let fm = FileManager.default let previousWorkingDirectory = fm.currentDirectoryPath + XCTAssertNotEqual(previousWorkingDirectory.standardizePath(), tmpDir) // Test that getcwd() returns the currentDirectoryPath do { let (pwd, _) = try runTask([xdgTestHelperURL().path, "--getcwd"], currentDirectoryPath: tmpDir) // Check the sub-process used the correct directory - XCTAssertEqual(pwd.trimmingCharacters(in: .newlines), tmpDir) + XCTAssertEqual(pwd.trimmingCharacters(in: .newlines).standardizePath(), tmpDir) } catch { XCTFail("Test failed: \(error)") } @@ -274,7 +275,9 @@ class TestProcess : XCTestCase { do { let (pwd, _) = try runTask([xdgTestHelperURL().path, "--echo-PWD"], currentDirectoryPath: tmpDir) // Check the sub-process used the correct directory - XCTAssertEqual(pwd.trimmingCharacters(in: .newlines), tmpDir) + let cwd = FileManager.default.currentDirectoryPath.standardizePath() + XCTAssertNotEqual(cwd, tmpDir) + XCTAssertNotEqual(pwd.trimmingCharacters(in: .newlines).standardizePath(), tmpDir) } catch { XCTFail("Test failed: \(error)") } @@ -566,6 +569,42 @@ class TestProcess : XCTestCase { } + func test_currentDirectory() throws { + + let process = Process() + XCTAssertNil(process.executableURL) + XCTAssertNotNil(process.currentDirectoryURL) + process.executableURL = URL(fileURLWithPath: "/some_file_that_doesnt_exist", isDirectory: false) + XCTAssertThrowsError(try process.run()) { + let code = CocoaError.Code(rawValue: ($0 as? NSError)!.code) + XCTAssertEqual(code, .fileReadNoSuchFile) + } + + do { + let (stdout, _) = try runTask([xdgTestHelperURL().path, "--getcwd"], currentDirectoryPath: "/") + XCTAssertEqual(stdout.trimmingCharacters(in: CharacterSet(["\n", "\r"])), "/") + } + + do { + XCTAssertNotEqual("/", FileManager.default.currentDirectoryPath) + XCTAssertNotEqual(FileManager.default.currentDirectoryPath, "/") + let (stdout, _) = try runTask([xdgTestHelperURL().path, "--echo-PWD"], currentDirectoryPath: "/") + let directory = stdout.trimmingCharacters(in: CharacterSet(["\n", "\r"])) + XCTAssertEqual(directory, ProcessInfo.processInfo.environment["PWD"]) + XCTAssertNotEqual(directory, "/") + } + + do { + try runTask([xdgTestHelperURL().path, "--getcwd"], currentDirectoryPath: "/some_directory_that_doesnt_exsit") + } catch { + let code = CocoaError.Code(rawValue: (error as? NSError)!.code) + XCTAssertEqual(code, .fileReadNoSuchFile) + return + } + XCTFail("Failed to catch error") + } + + static var allTests: [(String, (TestProcess) -> () throws -> Void)] { var tests = [ ("test_exit0" , test_exit0), @@ -592,6 +631,7 @@ class TestProcess : XCTestCase { ("test_redirect_all_using_null", test_redirect_all_using_null), ("test_redirect_all_using_nil", test_redirect_all_using_nil), ("test_plutil", test_plutil), + ("test_currentDirectory", test_currentDirectory), ] #if !os(Windows) @@ -690,6 +730,7 @@ class _SignalHelperRunner { } } +@discardableResult internal func runTask(_ arguments: [String], environment: [String: String]? = nil, currentDirectoryPath: String? = nil) throws -> (String, String) { let process = Process() diff --git a/TestFoundation/Utilities.swift b/TestFoundation/Utilities.swift index 64b476e946..7ac04ce2ed 100644 --- a/TestFoundation/Utilities.swift +++ b/TestFoundation/Utilities.swift @@ -621,6 +621,12 @@ extension XCTest { } } +extension String { + public func standardizePath() -> String { + URL(fileURLWithPath: self).resolvingSymlinksInPath().path + } +} + extension FileHandle: TextOutputStream { public func write(_ string: String) { write(Data(string.utf8))