Merge nucleic/mellow-dewy-falcon-rjhr into main
build / build (push) Canceled after 0s

This commit is contained in:
2026-08-07 04:14:34 -07:00
parent 042bd813a8
commit af0369d443
5 changed files with 450 additions and 32 deletions
@@ -0,0 +1,116 @@
import Foundation
import Testing
@testable import RunnerCore
/// Tests for ``XcodeInstall`` — the decidable parts of installing Xcode.
///
/// The bug these exist for: the installer assumed the archive expanded to
/// `Xcode.app`. A beta expands to `Xcode-beta.app`, so a 12 GB upload and a
/// half-hour expansion both succeeded and then the final `mv` failed with
/// "No such file or directory". Nothing about that is worth discovering from a
/// forty-minute live run twice.
@Suite("XcodeInstall")
struct XcodeInstallTests {
// MARK: - Discovering the expanded app
@Test("a release archive's Xcode.app is found")
func findsReleaseApp() throws {
let path = try XcodeInstall.expandedAppPath(
fromListing: "/tmp/xcode-expand/Xcode.app\n", staging: "/tmp/xcode-expand")
#expect(path == "/tmp/xcode-expand/Xcode.app")
}
/// The reported failure, in one line.
@Test("a beta archive's Xcode-beta.app is found")
func findsBetaApp() throws {
let path = try XcodeInstall.expandedAppPath(
fromListing: "/tmp/xcode-expand/Xcode-beta.app", staging: "/tmp/xcode-expand")
#expect(path == "/tmp/xcode-expand/Xcode-beta.app")
}
@Test("a version-qualified name is found")
func findsVersionQualifiedApp() throws {
let path = try XcodeInstall.expandedAppPath(
fromListing: " /tmp/xcode-expand/Xcode_16.2.app \n\n", staging: "/tmp/xcode-expand")
#expect(path == "/tmp/xcode-expand/Xcode_16.2.app")
}
/// `/bin/sh` echoes an unmatched glob back verbatim, so "no match" arrives
/// as a plausible-looking path rather than as empty output.
@Test("an unmatched glob reads as no match, not as a path")
func unmatchedGlobIsNotAPath() {
#expect(throws: CoreError.self) {
try XcodeInstall.expandedAppPath(
fromListing: "/tmp/xcode-expand/*.app\n", staging: "/tmp/xcode-expand")
}
}
@Test("empty output is an error naming the staging directory")
func emptyListingThrows() {
do {
_ = try XcodeInstall.expandedAppPath(fromListing: "\n \n", staging: "/tmp/xcode-expand")
Issue.record("expected a failure")
} catch {
#expect("\(error)".contains("/tmp/xcode-expand"))
}
}
/// Guessing between two candidates would install something the operator did
/// not ask for, so this stops instead.
@Test("several apps is an error listing them")
func ambiguousListingThrows() {
do {
_ = try XcodeInstall.expandedAppPath(
fromListing: "/tmp/x/Xcode.app\n/tmp/x/Xcode-beta.app\n", staging: "/tmp/x")
Issue.record("expected a failure")
} catch {
let text = "\(error)"
#expect(text.contains("Xcode.app"))
#expect(text.contains("Xcode-beta.app"))
}
}
// MARK: - Disk arithmetic
@Test("the requirement covers the archive and its expansion")
func requiredFreeSpaceCoversBoth() {
let xip = 12_000_000_000
#expect(XcodeInstall.expansionEstimateBytes(xipBytes: xip) == 42_000_000_000)
#expect(XcodeInstall.requiredFreeBytes(xipBytes: xip) == 54_000_000_000)
}
@Test("df -Pk output yields available bytes")
func parsesDF() {
let output = """
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/disk3s5 488245288 120000000 62914560 66% /
"""
#expect(XcodeInstall.availableBytes(dfOutput: output) == 62_914_560 * 1024)
}
/// Unparseable output means "could not check", not "no space" — the caller
/// must not refuse to install because `df` printed something unexpected.
@Test("unparseable df output yields nil rather than zero")
func unparseableDFIsNil() {
#expect(XcodeInstall.availableBytes(dfOutput: "df: /nope: No such file or directory") == nil)
#expect(XcodeInstall.availableBytes(dfOutput: "") == nil)
}
@Test("the shortfall message names every number and a remedy")
func messageNamesTheNumbers() {
let text = XcodeInstall.insufficientDiskMessage(
xipBytes: 12_000_000_000, availableBytes: 20_000_000_000)
#expect(text.contains("20.0 GB")) // available
#expect(text.contains("54.0 GB")) // needed
#expect(text.contains("12.0 GB")) // the archive
#expect(text.contains("--disk-gb"))
}
@Test("byte counts render as decimal gigabytes")
func formatsGigabytes() {
#expect(XcodeInstall.formatGB(12_400_000_000) == "12.4 GB")
#expect(XcodeInstall.formatGB(0) == "0.0 GB")
}
}