test(arch): the stopwatch stays silent, and tabular figures stay in the theme

Two new rules and two extended. The stopwatch may not name a player, a
vibrator, an audio manager — or a wake lock: it is the one timekeeper in the
app that never makes a sound and never holds the CPU awake, and that is now
a build failure rather than a promise in a comment.

`fontFeatureSettings` may appear only under `ui/theme/`, so the figure
settings stay in one place instead of spreading to call sites. The
Android-free list gains the engine; the screen's needle list gains the
service and its notifications.
This commit is contained in:
2026-09-22 10:24:08 +02:00
parent 5e73e81dc3
commit 83995c39db
@@ -136,6 +136,7 @@ class ArchitectureRulesTest {
path.contains("de/jeanlucmakiola/clockula/domain/alarm/") ||
path.endsWith("de/jeanlucmakiola/clockula/alarm/AlarmEngine.kt") ||
path.endsWith("de/jeanlucmakiola/clockula/timer/TimerEngine.kt") ||
path.endsWith("de/jeanlucmakiola/clockula/stopwatch/StopwatchEngine.kt") ||
path.endsWith("de/jeanlucmakiola/clockula/alarm/AlarmResolver.kt") ||
path.contains("de/jeanlucmakiola/clockula/system/")
}
@@ -194,7 +195,15 @@ class ArchitectureRulesTest {
val ui = kotlinSources
.filter { it.invariantSeparatorsPath.contains("de/jeanlucmakiola/clockula/ui/") }
val offenders = ui.usingInCode("TimerScheduler", "TimerService", "TimerRingPolicy", "AlarmManager")
val offenders = ui.usingInCode(
"TimerScheduler",
"TimerService",
"TimerRingPolicy",
"AlarmManager",
// M7 D23: the screen posts no notification and starts no service.
"StopwatchService",
"StopwatchNotifications",
)
assertThat(offenders).isEmpty()
}
@@ -213,4 +222,42 @@ class ArchitectureRulesTest {
assertThat(offenders).isEmpty()
}
/**
* M7 D19: the stopwatch is silent. It never opens audio, never vibrates and
* never takes a wake lock — which is the one thing a future "make the lap
* button click" change would quietly break.
*/
@Test
fun `the stopwatch makes no sound`() {
val stopwatch = kotlinSources
.filter { it.invariantSeparatorsPath.contains("de/jeanlucmakiola/clockula/stopwatch/") }
val offenders = stopwatch.usingInCode(
"MediaPlayer",
"RingAudioPlayer",
"RingVibrator",
"Vibrator",
"AudioManager",
// D3's "no wake lock, ever" is half of the promise this rule makes,
// so the needle list has to name it too.
"PowerManager",
"WakeLock",
)
assertThat(offenders).isEmpty()
}
/**
* M7 D12: tabular figures are configured **once**, in the theme, so no call
* site ever re-specifies them and no readout can be left proportional.
*/
@Test
fun `tabular figures are configured only in the theme`() {
val offenders = kotlinSources.containing("fontFeatureSettings").filterNot {
it.contains("de/jeanlucmakiola/clockula/ui/theme/")
}
assertThat(offenders).isEmpty()
}
}