Skip to content

Latest commit

 

History

History
124 lines (89 loc) · 5.82 KB

File metadata and controls

124 lines (89 loc) · 5.82 KB

LivingTTS Cleanup Verification

Branch under test: verify/backfill-2026-06-30 (tip = main = f5282a0) Pre-cleanup baseline: main~1 (cdc6585) Date: 2026-06-30 Verdict: EQUIVALENT — no behavior divergence found


1. A/B Full Suite (pre-cleanup vs cleaned)

Branch Commit Tests run Passed Failed
pre-cleanup (main~1) cdc6585 5 5 0
cleaned (main) f5282a0 5 5 0

Both suites ran via git worktree add ../livingtts-pre main~1 + swift test in each tree. Same 5 tests, same results — full parity confirmed.


2. Backfill Tests for Uncovered Refactors

New file: Tests/LivingTTSTests/CleanupCharacterizationTests.swift (10 tests, all passing).

REDUNDANT-01 — Removed window.setContentSize(NSSize(760,560)) in LivingTTSApp.swift

Characterization: NSWindow(contentRect: NSRect(0,0,760,560), ...) already establishes content size 760×560. The subsequent setContentSize call in main~1 was a no-op.

A/B status: Automated (both branches can run the same tests).

Tests:

  • testWindowContentSizeIsSetByContentRect — creates NSWindow with the same contentRect: parameters as the app and asserts contentRect(forFrameRect:) returns 760×560. Passes.
  • testRedundantSetContentSizeIsNoOp — additionally calls setContentSize(760,560) and asserts the content rect is still 760×560. Passes.

Why not testing LivingTTSApp.controlPanelWindow directly: LivingTTSApp is @main (executable entry point) and controlPanelWindow is private. The struct cannot be instantiated from the test target. The AppKit invariant is tested in isolation with the same init parameters used in app code.


COMPLEX-02 — preferredEnglishVoiceNames changed from computed instance var to private static let

Characterization: The set contents are identical in both revisions: {ava, allison, samantha, tom, daniel, serena, jamie, zoe} (8 names, lowercase).

Changing to static let means the set is allocated once at class load time rather than reallocated on every voiceRank(_:) call. The membership — and therefore voice sort order — is identical.

A/B status: Automated (behavioral) + Reasoned-from-source (set contents).

Test:

  • testSystemTTSBackendAvailableVoicesIsCallable — calls SystemTTSBackend().availableVoices() (the public API that exercises preferredEnglishVoiceNames internally) and asserts all returned VoiceOption values have non-empty id and displayName. Passes.

Why direct set comparison is not possible: preferredEnglishVoiceNames is private in both revisions (Swift access control; @testable import exposes internal but not private). Set-content equivalence is confirmed by source-level inspection of both main~1 and f5282a0.


DUP-01 — Badge text/color logic deduped into ControlPanelTheme.badgeDescriptor(for:)

Characterization: Both MenuBarMenuView and MiniPlayerPanelView in main~1 (cdc6585) contained identical inline badgeText and badgePalette computed vars. The cleaned version replaces both with calls to the new ControlPanelTheme.badgeDescriptor(for:) static helper.

Pre-cleanup inline mapping (source-verified at cdc6585):

PlaybackState text fill foreground
.idle "Ready" accentGreenFill accentGreenText
.capturing / .cleaning / .preparingSpeech "Working" accentYellowFill accentYellowText
.speaking "Reading" accentBlueFill accentBlueText
.paused "Paused" accentBlueFill accentBlueText
.error(_) "Needs Attention" accentRedFill accentRedText

A/B status: Automated on cleaned branch (badgeDescriptor is new in this commit and only exists post-cleanup). Pre-cleanup comparison: reasoned-from-pre-cleanup-source (inline logic read directly from main~1 source; both view files agreed on all 5 cases).

Tests (all on cleaned branch, all passing):

  • testBadgeDescriptorIdle.idle → ("Ready", green, green) ✓
  • testBadgeDescriptorCapturing.capturing → ("Working", yellow, yellow) ✓
  • testBadgeDescriptorCleaning.cleaning → ("Working", yellow, yellow) ✓
  • testBadgeDescriptorPreparingSpeech.preparingSpeech → ("Working", yellow, yellow) ✓
  • testBadgeDescriptorSpeaking.speaking → ("Reading", blue, blue) ✓
  • testBadgeDescriptorPaused.paused → ("Paused", blue, blue) ✓
  • testBadgeDescriptorError.error("any message") → ("Needs Attention", red, red) ✓

3. Smoke Test

Step Result
swift build Build complete (0.1s), exit 0
swift run (8s timeout) No crash output, no console errors; process killed by timeout (exit 124) — expected for a persistent menu-bar GUI app

Note on headless menu-bar observation: LivingTTS is an NSApplication-based menu-bar extra. Visual confirmation (menu-bar icon appearance, floating panel rendering) requires an interactive display session. Launch success + absence of console errors is the observable signal in a headless environment.


Summary

Refactor Tests A/B Method Result
REDUNDANT-01 (window size) 2 new tests Automated (AppKit invariant) EQUIVALENT
COMPLEX-02 (voice names set) 1 new test Automated (behavioral) + Reasoned-from-source (set contents) EQUIVALENT
DUP-01 (badge descriptor) 7 new tests Automated on cleaned; Reasoned-from-source for pre-cleanup EQUIVALENT
Full suite parity 5 existing Automated A/B (both worktrees) PARITY — 5/5 on both
Smoke build swift build exit 0 PASS
Smoke launch swift run 8s no errors PASS

Verdict: EQUIVALENT. All three refactors are behavior-preserving. No divergence found.