summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-11-03 08:29:32 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-11-03 08:29:32 +0000
commit913ba0e829ca20ccbb1a8db213541760dfc42a4b (patch)
treeb56b62b4739e0f4c8ed5b36cae5404936048dc22 /docs
parent87b8cfa942b995d1c013827ec7014cf6ef4138ca (diff)
Measure and test the view models the ui exclusion was hiding
The coverage gate excluded `**/ui/**` wholesale, justified as "Compose screens are impractical to unit-test". It also hid six view models -- plain Kotlin classes holding real decisions, every one at 0%, not one of them a screen -- behind a justification that only ever applied to screens. PlayerViewModel and ListenViewModel were 150 lines of untested logic the gate could not see. Done by a subagent; its coverage figure, test count and detekt result were re-derived here from the XML rather than taken on trust, and its two findings checked against the source. The exclusion now names each screen file instead of the package. All six view models go from 0% to 92-99%, the measured `ui` package to 96.6%, and the project to **95.17% against a 94.44% baseline** -- the gate passes with the exclusion narrowed and no COVERAGE_EXCEPTION. 96 new tests, suite 951 -> 1048. The tests drive the real SubscriptionRepository, PlaybackAdvancer, QueueRepository, DripReleaser and FeedResolver against the existing FakeDaos and MockWebServer, so the banners asserted are the app's own wording rather than a stub agreeing with itself. Four production seams, all behaviour-preserving: `UiGraph` and `ViewPreferences` over the graph, `PlaybackControls` over PlayerConnection (the same shape as the `Playhead` precedent), and a second constructor per view model with the `(Application)` one kept as a delegate. That last is load-bearing in a way nothing would catch: `by viewModels()` finds a constructor reflectively, so losing it throws on screen open with a green build and no failing test. ViewModelConstructionTest is the guard. Its first version checked for the literal ": AndroidViewModel" and so missed TranscriptViewModel, which wraps its supertype onto the next line -- a check that would have passed while leaving a seventh view model unguarded in silence. It now cross-checks the two ways of finding them against each other. The action and state objects are excluded on a different ground from the screens, and the comment says so explicitly so the list does not grow by analogy: they construct fine off a device, but every line jacoco counts in them is one Kotlin generated -- constructor, componentN, copy, equals, hashCode, toString. Measuring them measures the compiler. A data class that gains a method or an init block comes back off the list. Also, found while reviewing: detekt's `UnusedImports` rule is off by default, which is how 71 dead imports accumulated across nine files. EpisodeActions.kt was eleven lines of data class under forty of them -- Scaffold, SnackbarHost, LazyColumn and the rest, left behind when the screens were split per file, which made it read like a screen. The rule is on and the 71 are gone. Two in that file needed removing by hand: detekt whitelists `getValue`/`setValue` unconditionally because it cannot tell a `by` delegation from none, and that file has no delegation at all. 1048 tests pass, detekt 0 findings on a fresh report, lint clean, coverage 95.17%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'docs')
0 files changed, 0 insertions, 0 deletions