Tests/KeycaskCLITests/CLI.swift
94 lines · 3375 bytes
1import Foundation
2import Testing
3
4enum Binary {
5 static let url: URL = {
6 #if os(macOS)
7 if let index = CommandLine.arguments.firstIndex(of: "--test-bundle-path"),
8 CommandLine.arguments.count > index + 1
9 {
10 return URL(fileURLWithPath: CommandLine.arguments[index + 1])
11 .deletingLastPathComponent() // MacOS
12 .deletingLastPathComponent() // Contents
13 .deletingLastPathComponent() // *.xctest
14 .deletingLastPathComponent() // Products/Debug
15 .appendingPathComponent("keycask")
16 }
17 for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
18 return bundle.bundleURL.deletingLastPathComponent().appendingPathComponent(
19 "keycask")
20 }
21 fatalError("test bundle not found")
22 #elseif os(Windows)
23 return Bundle.main.bundleURL.appendingPathComponent("keycask.exe")
24 #else
25 return Bundle.main.bundleURL.appendingPathComponent("keycask")
26 #endif
27 }()
28}
29
30struct CLI {
31 struct Result {
32 let status: Int32
33 let stdout: String
34 let stderr: String
35 var lines: [String] { stdout.split(separator: "\n").map(String.init) }
36 }
37
38 static let passphrase = "correct horse battery"
39
40 let dir: URL
41 let vault: URL
42
43 init() throws {
44 dir = FileManager.default.temporaryDirectory
45 .appendingPathComponent("keycask-tests-\(UUID().uuidString)")
46 try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
47 vault = dir.appendingPathComponent("vault.kc")
48 }
49
50 @discardableResult
51 func run(
52 _ args: [String],
53 stdin: String? = nil,
54 passphrase: String? = CLI.passphrase,
55 extraEnvironment: [String: String] = [:]
56 ) throws -> Result {
57 let process = Process()
58 process.executableURL = Binary.url
59 process.arguments = args
60 var env = ProcessInfo.processInfo.environment
61 env["KEYCASK_VAULT"] = vault.path
62 env.removeValue(forKey: "KEYCASK_PASSPHRASE")
63 if let passphrase { env["KEYCASK_PASSPHRASE"] = passphrase }
64 for (k, v) in extraEnvironment { env[k] = v }
65 process.environment = env
66
67 let out = Pipe()
68 let err = Pipe()
69 let input = Pipe()
70 process.standardOutput = out
71 process.standardError = err
72 process.standardInput = input
73 try process.run()
74 if let stdin {
75 input.fileHandleForWriting.write(Data(stdin.utf8))
76 }
77 try input.fileHandleForWriting.close()
78 let outData = out.fileHandleForReading.readDataToEndOfFile()
79 let errData = err.fileHandleForReading.readDataToEndOfFile()
80 process.waitUntilExit()
81 return Result(
82 status: process.terminationStatus,
83 stdout: String(decoding: outData, as: UTF8.self),
84 stderr: String(decoding: errData, as: UTF8.self))
85 }
86
87 /// Runs `init` and returns the harness, for tests that need a vault.
88 static func initialized() throws -> CLI {
89 let cli = try CLI()
90 let r = try cli.run(["init"])
91 precondition(r.status == 0, "init failed: \(r.stderr)")
92 return cli
93 }
94}