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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
/*
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import func XCTest.XCTFail
import class Foundation.NSDate
import class Foundation.Thread
import TSCBasic
import TSCUtility
#if canImport(Darwin)
import class Foundation.Bundle
#endif
public enum Configuration {
case Debug
case Release
}
/// Test helper utility for executing a block with a temporary directory.
public func testWithTemporaryDirectory(
function: StaticString = #function,
body: (AbsolutePath) throws -> Void
) throws {
let cleanedFunction = function.description
.replacingOccurrences(of: "(", with: "")
.replacingOccurrences(of: ")", with: "")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: ":", with: "_")
try withTemporaryDirectory(prefix: "spm-tests-\(cleanedFunction)") { tmpDirPath in
defer {
// Unblock and remove the tmp dir on deinit.
try? localFileSystem.chmod(.userWritable, path: tmpDirPath, options: [.recursive])
try? localFileSystem.removeFileTree(tmpDirPath)
}
try body(tmpDirPath)
}
}
public func systemQuietly(_ args: [String]) throws {
// Discard the output, by default.
//
// FIXME: Find a better default behavior here.
try Process.checkNonZeroExit(arguments: args)
}
public func systemQuietly(_ args: String...) throws {
try systemQuietly(args)
}
/// Temporary override environment variables
///
/// WARNING! This method is not thread-safe. POSIX environments are shared
/// between threads. This means that when this method is called simultaneously
/// from different threads, the environment will neither be setup nor restored
/// correctly.
public func withCustomEnv(_ env: [String: String], body: () throws -> Void) throws {
let state = Array(env.keys).map({ ($0, ProcessEnv.vars[$0]) })
let restore = {
for (key, value) in state {
if let value = value {
try ProcessEnv.setVar(key, value: value)
} else {
try ProcessEnv.unsetVar(key)
}
}
}
do {
for (key, value) in env {
try ProcessEnv.setVar(key, value: value)
}
try body()
} catch {
try? restore()
throw error
}
try restore()
}
/// Waits for a file to appear for around 1 second.
/// Returns true if found, false otherwise.
public func waitForFile(_ path: AbsolutePath) -> Bool {
// setting timeout for 30s as on busy systems (such as CI) this could take a while
let endTime = NSDate().timeIntervalSince1970 + 30
while NSDate().timeIntervalSince1970 < endTime {
// Sleep for a bit so we don't burn a lot of CPU.
Thread.sleep(forTimeInterval: 0.01)
if localFileSystem.exists(path) {
return true
}
}
return false
}
extension Process {
/// If the given pid is running or not.
///
/// - Parameters:
/// - pid: The pid to check.
/// - orDefunct: If set to true, the method will also check if pid is defunct and return false.
/// - Returns: True if the given pid is running.
public static func running(_ pid: ProcessID, orDefunct: Bool = false) throws -> Bool {
// Shell out to `ps -s` instead of using getpgid() as that is more deterministic on linux.
let result = try Process.popen(arguments: ["ps", "-p", String(pid)])
// If ps -p exited with return code 1, it means there is no entry for the process.
var exited = result.exitStatus == .terminated(code: 1)
if orDefunct {
// Check if the process became defunct.
exited = try exited || result.utf8Output().contains("defunct")
}
return !exited
}
}
|