Skip to content

refactor: extract the Steam client orchestrator into WhiskyKit for testability - #241

Merged
frankea merged 3 commits into
mainfrom
refactor/steam-orchestrator-whiskykit
Aug 29, 2026
Merged

refactor: extract the Steam client orchestrator into WhiskyKit for testability#241
frankea merged 3 commits into
mainfrom
refactor/steam-orchestrator-whiskykit

Conversation

@frankea

@frankea frankea commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Step 3 of #171, the last one: the Steam client orchestrator core moves into WhiskyKit, parameterized over a driver protocol so tests can drive it without Wine. No behavior change intended.

what moved

SteamClientOrchestrator now lives in WhiskyKit/Steam/. It keeps the three pieces that were the point of extracting it:

  • single-flight client startup (concurrent launches await one attempt)
  • per-game launch phases with the grace period
  • the shared process snapshot with its lifetime and coalesced concurrent reads

Side effects go through SteamClientDriver: host process names (ps), the bottle's process list (tasklist.exe), starting the client, applying launcher fixes, -applaunch, killing a process, and two lifecycle hooks (clientDidBecomeReady, shutdown). WineSteamClientDriver is the kit's production implementation and is open; the app's AppSteamClientDriver (30 lines) overrides only the two hooks to run SteamDownloadMonitor for the session, which stays app-target since nothing else needs it. LibraryModel constructs the kit type with that driver and is otherwise untouched.

The launcher-fix gate (skip in manual mode, skip when the bottle is already configured for Steam) is a public static on the orchestrator so the rule is testable on its own. Waits and intervals are a Timing value with production defaults, injectable for tests.

The three error strings (steam.client.timeout, steam.client.missing, steam.launch.timeout) are now referenced from the kit, so their catalog entries carry extractionState: manual.

tests

17 new Swift Testing cases against a scripted fake driver and a fake Steam layout on disk:

  • launch sequence: Steam missing, cold start end to end (phases observed at each step), concurrent launches share one startup, duplicate launch ignored, client timeout, launch-grace timeout, launch failure surfaced, client already running skips start and fixes
  • gating: manual mode never applies fixes; the gate's four cases
  • snapshot: shared inside the lifetime, re-read past it, concurrent reads coalesce into one list, empty host list skips tasklist
  • stop and tracking: per-game stop kills only that game's processes and drops the snapshot, stop-all cancels an in-flight launch and hands the driver its shutdown, tracking follows the process list

verified

  • WhiskyKit: 1294 XCTest plus 251 Swift Testing, 0 failures; the new suites pass three consecutive runs
  • Whisky app builds (the old file's project reference was renamed in place to the new driver file)
  • pinned SwiftFormat 0.58.7 and SwiftLint strict clean on every changed file

Closes #171.

…stability

Move SteamClientOrchestrator into the kit behind a SteamClientDriver
protocol: the orchestrator keeps the sequencing (single-flight client
startup, per-game phases, the shared process snapshot) and the driver
owns the side effects, so swift test can drive the whole launch flow
without Wine. WineSteamClientDriver is the production driver; the app
subclasses it only to run download monitoring for the session.

Part of #171; this is the last step.
@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.22754% with 23 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...it/Sources/WhiskyKit/Steam/SteamClientDriver.swift 60.00% 16 Missing ⚠️
Whisky/Utils/AppSteamClientDriver.swift 0.00% 6 Missing ⚠️
Whisky/View Models/LibraryModel.swift 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

The only logic in WineSteamClientDriver is turning a ps listing into
.exe image names; pull it out as a nonisolated static so it is testable
without spawning ps, and test the path, case and filtering rules.
@frankea

frankea commented Aug 29, 2026

Copy link
Copy Markdown
Owner Author

Review notes, since this one cannot be self-approved. Pure refactor, no changelog entry per convention.

The seam holds the split it promises: the orchestrator keeps every piece of sequencing that mattered (single-flight client startup, per-game phases, the shared snapshot with its lifetime and coalesced reads) and the driver is delegations plus one parser, which is now tested on its own. LibraryModel is a one-line change. The three error strings moved with the type and carry extractionState manual.

Coverage: the orchestrator file measures 98.9% of lines locally under swift test; the remaining uncovered lines in the driver are the four calls that need a running Wine plus two empty hooks, which is the point of the seam. Full kit suite 1294 XCTest plus 251 Swift Testing green, app target builds, both lints clean, CI green on the final head including the UI tests.

Merging; closes #171.

@frankea
frankea merged commit d1eb0a9 into main Aug 29, 2026
9 checks passed
@frankea
frankea deleted the refactor/steam-orchestrator-whiskykit branch August 29, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: extract app-target orchestration logic into WhiskyKit for testability

1 participant