test(arch): ICU, MaterialShapes and the ambient zone each stay where they belong
Three new rules. `android.icu` may be named only under `data/zones/`, so the platform's name database stays behind its seam. `MaterialShapes` and `androidx.graphics.shapes` may be named only under `ui/worldclock/`, so the showpiece stays the one place that morphs. And nothing under `domain/` or `ui/` may read `ZoneId.systemDefault()` or `TimeZone.getDefault()` — the device's zone is an argument, or the tests cannot pin a single one. All three hold retroactively for M0 through M7.
This commit is contained in:
@@ -260,4 +260,52 @@ class ArchitectureRulesTest {
|
|||||||
|
|
||||||
assertThat(offenders).isEmpty()
|
assertThat(offenders).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M8 D24: ICU is a seam, not an ambient API. Every localised zone name the
|
||||||
|
* app shows comes through `data/zones/`, so an OEM build with a broken ICU
|
||||||
|
* degrades one caption instead of crashing a tab.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `ICU is named only under the zone-names seam`() {
|
||||||
|
val offenders = kotlinSources.usingInCode("android.icu").filterNot {
|
||||||
|
it.contains("de/jeanlucmakiola/clockula/data/zones/")
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(offenders).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M8 D24: `docs/PLAN.md` §8's "not sprinkled everywhere", made mechanical.
|
||||||
|
* The shape morph is the world clock's one showpiece; the ring screen and
|
||||||
|
* timer progress are sanctioned too, so widening this allowlist is a
|
||||||
|
* deliberate edit rather than a drift.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `MaterialShapes is drawn only on the world clock face`() {
|
||||||
|
val offenders = kotlinSources
|
||||||
|
.usingInCode("MaterialShapes", "androidx.graphics.shapes")
|
||||||
|
.filterNot { it.contains("de/jeanlucmakiola/clockula/ui/worldclock/") }
|
||||||
|
|
||||||
|
assertThat(offenders).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M8 D24: the zone is a parameter, from `ZoneProvider` — never an ambient
|
||||||
|
* read at a call site. Scoped to `domain/` and `ui/` on purpose:
|
||||||
|
* `alarm/ring/RingNotifications.kt` formats a snooze time with
|
||||||
|
* `systemDefault()` today, and rewriting it is not M8's business.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `the domain and the ui read no ambient time zone`() {
|
||||||
|
val domainAndUi = kotlinSources.filter {
|
||||||
|
val path = it.invariantSeparatorsPath
|
||||||
|
path.contains("de/jeanlucmakiola/clockula/domain/") ||
|
||||||
|
path.contains("de/jeanlucmakiola/clockula/ui/")
|
||||||
|
}
|
||||||
|
|
||||||
|
val offenders = domainAndUi.usingInCode("ZoneId.systemDefault()", "TimeZone.getDefault()")
|
||||||
|
|
||||||
|
assertThat(offenders).isEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,8 +138,13 @@ class FakeAlarmCapabilities(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The device's zone, which a test can change under a scheduled alarm. */
|
/** The device's zone, which a test can change under a scheduled alarm. */
|
||||||
class FakeZoneProvider(var zone: ZoneId = BERLIN) : ZoneProvider {
|
class FakeZoneProvider(
|
||||||
|
var zone: ZoneId = BERLIN,
|
||||||
|
var availableIds: Set<String> = ZoneId.getAvailableZoneIds(),
|
||||||
|
) : ZoneProvider {
|
||||||
override fun current(): ZoneId = zone
|
override fun current(): ZoneId = zone
|
||||||
|
|
||||||
|
override fun available(): Set<String> = availableIds
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeBootIdProvider(
|
class FakeBootIdProvider(
|
||||||
|
|||||||
Reference in New Issue
Block a user