Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Foundation

enum InstallerCheckState: String, Equatable {
case passed = "pass"
case warning = "warn"
case failed = "fail"
case info
}

struct InstallerEvent: Equatable {
var phase: String
var code: String
var state: InstallerCheckState
var message: String

static func parse(line: String) -> InstallerEvent? {
guard line.hasPrefix("TB_EVENT|") else { return nil }
let fields = line.split(separator: "|", maxSplits: 4, omittingEmptySubsequences: false)
guard fields.count == 5,
let state = InstallerCheckState(rawValue: String(fields[3])) else { return nil }
return InstallerEvent(
phase: String(fields[1]),
code: String(fields[2]),
state: state,
message: String(fields[4])
)
}

static func summary(
from events: [InstallerEvent],
priorityCodes: [String],
limit: Int = 5
) -> [InstallerEvent] {
guard limit > 0 else { return [] }
var latestByCode: [String: InstallerEvent] = [:]
events.forEach { latestByCode[$0.code] = $0 }

var selected: [InstallerEvent] = []
var includedCodes = Set<String>()
func appendOnce(_ event: InstallerEvent) {
guard includedCodes.insert(event.code).inserted else { return }
selected.append(event)
}

// A failed or warning check must never be hidden by earlier successful
// rows. Show the most recent actionable result first, then fill the
// remaining space with the normal high-level summary.
for state in [InstallerCheckState.failed, .warning] {
for event in events.reversed()
where event.state == state && latestByCode[event.code] == event {
appendOnce(event)
}
}
for code in priorityCodes {
if let event = latestByCode[code] { appendOnce(event) }
}
for event in events.reversed() where latestByCode[event.code] == event {
appendOnce(event)
}
return Array(selected.prefix(limit))
}
}
54 changes: 54 additions & 0 deletions packaging/monitor-mode/tests/InstallerEventTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation

@main
struct InstallerEventTests {
private static var checks = 0
private static var failures = 0

private static func check(_ label: String, _ condition: @autoclosure () -> Bool) {
checks += 1
if !condition() {
failures += 1
fputs("FAIL: \(label)\n", stderr)
}
}

static func main() {
let parsed = InstallerEvent.parse(line: "TB_EVENT|preflight|hardware|pass|iMac18,2")
check("event phase parsed", parsed?.phase == "preflight")
check("event code parsed", parsed?.code == "hardware")
check("event state parsed", parsed?.state == .passed)
check("event message parsed", parsed?.message == "iMac18,2")
check(
"event message preserves separators",
InstallerEvent.parse(line: "TB_EVENT|phase|code|info|one|two")?.message == "one|two"
)
check("ordinary output ignored", InstallerEvent.parse(line: "ordinary output") == nil)
check("malformed event ignored", InstallerEvent.parse(line: "TB_EVENT|short") == nil)
check("unknown state ignored", InstallerEvent.parse(line: "TB_EVENT|phase|code|maybe|message") == nil)

let events = [
InstallerEvent(phase: "preflight", code: "hardware", state: .passed, message: "hardware ok"),
InstallerEvent(phase: "preflight", code: "package", state: .warning, message: "old warning"),
InstallerEvent(phase: "preflight", code: "package", state: .passed, message: "package ok"),
InstallerEvent(phase: "preflight", code: "architecture", state: .failed, message: "wrong architecture"),
InstallerEvent(phase: "readiness", code: "link", state: .warning, message: "using Wi-Fi")
]
let summary = InstallerEvent.summary(
from: events,
priorityCodes: ["hardware", "package", "link"]
)
check("failure appears first", summary.first?.code == "architecture")
check("warning remains visible", summary.dropFirst().first?.code == "link")
check("resolved warning is not prioritized", summary.dropFirst(2).first?.code == "hardware")
check("summary has no duplicate codes", Set(summary.map(\.code)).count == summary.count)
check(
"summary limit is enforced",
InstallerEvent.summary(from: events, priorityCodes: ["hardware"], limit: 2).count == 2
)
check("zero limit returns no rows", InstallerEvent.summary(from: events, priorityCodes: [], limit: 0).isEmpty)

print("installer event tests: \(checks) checks, \(failures) failures")
exit(failures == 0 ? 0 : 1)
}
}
15 changes: 15 additions & 0 deletions packaging/monitor-mode/tests/test_installer_events.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/zsh
set -euo pipefail

TEST_DIR="${0:A:h}"
SOURCE_DIR="${TEST_DIR:h}/installer-app"
BUILD_DIR="$(/usr/bin/mktemp -d /private/tmp/targetbridge-installer-event-tests.XXXXXX)"
trap '/bin/rm -R "${BUILD_DIR}"' EXIT

/usr/bin/xcrun swiftc \
-module-cache-path "${BUILD_DIR}/ModuleCache" \
"${SOURCE_DIR}/TargetBridgeInstallerEvent.swift" \
"${TEST_DIR}/InstallerEventTests.swift" \
-o "${BUILD_DIR}/InstallerEventTests"

"${BUILD_DIR}/InstallerEventTests"
Loading