test(strings): guard each strings file against another game's words

PLAN.md M20 step 3. StringVocabularyTest reads strings.xml, strings_uo.xml
and strings_rust.xml in every values* directory, so translations are checked
too. It fails, naming the key and the word, when the core file holds either
game's word, the UO file holds a Rust word, or the Rust file holds a UO word.
The word list is runicgateway.com's checkTerms.mjs list, word for word. The
app never shows Discord's "guild ID", so that allowlist entry isn't carried
over.

Three more tests keep the guard honest:
- a fourth strings file with no owner is refused, because Android would read
  it and the guard would skip it;
- the three default files must exist and hold strings, so a wrong path can't
  pass on nothing;
- the matcher is checked against known hits and misses.

Run against the core file as it was before the rewording, it names exactly
the 14 strings that commit changed.

build.gradle.kts declares src/main/res as an input of the Test tasks. A
reworded string changes no class, so without that input Gradle left the test
task UP-TO-DATE and the guard did not run again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-23 01:35:59 -05:00
parent 5e28989adc
commit 3048582ce0
2 changed files with 175 additions and 0 deletions

View File

@@ -181,6 +181,13 @@ dependencies {
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
}
// StringVocabularyTest (PLAN.md M20) reads the string resources from disk. A reworded
// string changes no class, so without this input the test task would stay UP-TO-DATE
// and the guard would not run again.
tasks.withType<Test>().configureEach {
inputs.dir("src/main/res").withPathSensitivity(PathSensitivity.RELATIVE)
}
// JaCoCo XML coverage from the JVM unit tests, consumed by SonarQube (§12.1). Generated,
// DI (Hilt), and Compose-scaffold classes are excluded so they don't dilute the number;
// pure-@Composable UI is excluded on the Sonar side (sonar.coverage.exclusions) because