168 lines
4.1 KiB
Swift
168 lines
4.1 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import Testing
|
|
|
|
@testable import phbar
|
|
|
|
// MARK: - Loading
|
|
|
|
@MainActor
|
|
@Test func loadBlocksFromTOML() throws {
|
|
let toml = """
|
|
[[block]]
|
|
command = "echo hello"
|
|
name = "greeting"
|
|
refresh = 5.0
|
|
|
|
[[block]]
|
|
command = "echo world"
|
|
"""
|
|
|
|
let blocks = try PHBlock.load(from: toml)
|
|
|
|
#expect(blocks.count == 2)
|
|
#expect(blocks[0].command == "echo hello")
|
|
#expect(blocks[0].name == "greeting")
|
|
#expect(blocks[0].refresh == 5.0)
|
|
#expect(blocks[0].label == nil)
|
|
#expect(blocks[1].name == nil)
|
|
#expect(blocks[1].refresh == nil)
|
|
}
|
|
|
|
@MainActor
|
|
@Test func loadBlocksRejectsInvalidTOML() {
|
|
#expect(throws: (any Error).self) {
|
|
try PHBlock.load(from: "not = valid = toml = =")
|
|
}
|
|
}
|
|
|
|
// MARK: - Named set loading
|
|
|
|
private func makeBlocksConfigDir() throws -> URL {
|
|
let dir = FileManager.default.temporaryDirectory.appending(path: "phbar_blocks_\(UUID().uuidString)")
|
|
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
|
return dir
|
|
}
|
|
|
|
@MainActor
|
|
@Test func loadSetReadsNamedFile() throws {
|
|
let dir = try makeBlocksConfigDir()
|
|
defer { try? FileManager.default.removeItem(at: dir) }
|
|
|
|
let blocksDir = dir.appending(path: "blocks")
|
|
try FileManager.default.createDirectory(at: blocksDir, withIntermediateDirectories: true)
|
|
try Data("""
|
|
[[block]]
|
|
command = "echo hi"
|
|
name = "greeting"
|
|
""".utf8).write(to: blocksDir.appending(path: "laptop.toml"))
|
|
|
|
let blocks = try PHBlock.load("laptop", in: dir)
|
|
|
|
#expect(blocks.count == 1)
|
|
#expect(blocks[0].name == "greeting")
|
|
}
|
|
|
|
@MainActor
|
|
@Test func loadSetThrowsForMissingSet() {
|
|
let dir = FileManager.default.temporaryDirectory.appending(path: "phbar_missing_\(UUID().uuidString)")
|
|
#expect(throws: (any Error).self) {
|
|
try PHBlock.load("nope", in: dir)
|
|
}
|
|
}
|
|
|
|
// MARK: - Compute
|
|
|
|
@MainActor
|
|
@Test func computeReturnsCommandStdout() async throws {
|
|
let blocks = try PHBlock.load(from: """
|
|
[[block]]
|
|
command = "printf panini"
|
|
""")
|
|
|
|
let output = await blocks[0].compute()
|
|
|
|
#expect(output == "panini")
|
|
}
|
|
|
|
@MainActor
|
|
@Test func computeRunsInConfigDirectory() async throws {
|
|
// Block commands execute with the resolved config root (see `PHPaths`) as
|
|
// their working directory, so a block from any set resolves relative paths
|
|
// the same way. Guards against the config dir being absent in sandboxed CIs.
|
|
guard FileManager.default.fileExists(atPath: PHPaths.configDirectory.path) else { return }
|
|
|
|
let blocks = try PHBlock.load(from: """
|
|
[[block]]
|
|
command = "pwd"
|
|
""")
|
|
|
|
let output = await blocks[0].compute()
|
|
|
|
// Resolve symlinks on both sides: the config dir is commonly a symlink into
|
|
// a dotfiles repo, and `pwd` reports the physical path.
|
|
let expected = PHPaths.configDirectory.resolvingSymlinksInPath().path
|
|
#expect(output == expected)
|
|
}
|
|
|
|
// MARK: - Auto-refresh
|
|
|
|
@MainActor
|
|
@Test func updateSetsLabel() async throws {
|
|
let block = try PHBlock.load(from: """
|
|
[[block]]
|
|
command = "printf panini"
|
|
""")[0]
|
|
|
|
#expect(block.label == nil)
|
|
|
|
await block.update()
|
|
|
|
#expect(block.label == "panini")
|
|
}
|
|
|
|
@MainActor
|
|
@Test func startAutoRefreshWithoutIntervalComputesOnce() async throws {
|
|
let block = try PHBlock.load(from: """
|
|
[[block]]
|
|
command = "printf hi"
|
|
""")[0]
|
|
|
|
block.startAutoRefresh()
|
|
|
|
// Allow the one-shot update task to run.
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
|
|
#expect(block.label == "hi")
|
|
|
|
block.stopAutoRefresh()
|
|
}
|
|
|
|
@MainActor
|
|
@Test func startAutoRefreshRepeatsAtInterval() async throws {
|
|
// A counter file lets us observe how many times the command ran.
|
|
let counter = FileManager.default.temporaryDirectory
|
|
.appending(path: "phbar_test_\(UUID().uuidString)")
|
|
defer { try? FileManager.default.removeItem(at: counter) }
|
|
|
|
let path = counter.path
|
|
let toml = """
|
|
[[block]]
|
|
command = "n=$(cat \(path) 2>/dev/null || echo 0); n=$((n+1)); echo $n > \(path); echo $n"
|
|
refresh = 0.05
|
|
"""
|
|
|
|
let block = try PHBlock.load(from: toml)[0]
|
|
|
|
block.startAutoRefresh()
|
|
|
|
try await Task.sleep(for: .milliseconds(250))
|
|
|
|
block.stopAutoRefresh()
|
|
|
|
let count = Int(block.label!.trimmingCharacters(in: .whitespacesAndNewlines)) ?? 0
|
|
|
|
// At ~20Hz over 250ms the command should have run more than once.
|
|
#expect(count >= 2)
|
|
}
|