mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-10-02 16:44:33 -04:00
76 lines
2.7 KiB
Python
Executable File
76 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Check that every module with tests is wired into a reusable-check.yml test shard.
|
|
|
|
The shard task lists are hand-maintained. Every module in settings.gradle.kts must
|
|
have a test task in the shard matrix or be exempted here, and an exempt test-less
|
|
module that gains test sources fails until it is wired into a shard. Exits non-zero
|
|
on drift.
|
|
"""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
# Modules whose tests run in a dedicated job or only on-device --
|
|
# exempt unconditionally.
|
|
COVERED_ELSEWHERE = {
|
|
':screenshot-tests', # dedicated screenshot-check job
|
|
':docs-screenshots', # doc-screenshot generation (screenshot tooling)
|
|
':baselineprofile', # benchmark module, instrumented-only
|
|
':store-screenshots', # store-listing screenshots from the real app, instrumented-only
|
|
}
|
|
# Modules with no unit-test sources yet. One of these gaining test
|
|
# sources fails the guard: move it into a shard in reusable-check.yml
|
|
# and remove it from this list.
|
|
NO_TESTS_YET = {
|
|
':core:di',
|
|
':core:nfc',
|
|
':core:resources',
|
|
}
|
|
|
|
|
|
def has_test_sources(module: str) -> bool:
|
|
root = REPO_ROOT / module.lstrip(':').replace(':', '/')
|
|
return any(
|
|
f.suffix == '.kt'
|
|
for d in root.glob('src/*')
|
|
if 'test' in d.name.lower()
|
|
for f in d.rglob('*.kt')
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
settings = (REPO_ROOT / 'settings.gradle.kts').read_text()
|
|
check = (REPO_ROOT / '.github/workflows/reusable-check.yml').read_text()
|
|
|
|
modules = set(re.findall(r'"(:[^"]+)"', settings))
|
|
|
|
shards = check.split('# ── Sharded Unit Tests')[1].split('# ── Android Build')[0]
|
|
# A commented-out task runs nothing, so it must not count as coverage.
|
|
shards = '\n'.join(re.sub(r'(^|\s)#.*$', '', line) for line in shards.splitlines())
|
|
|
|
problems = []
|
|
for m in sorted(modules):
|
|
if m in COVERED_ELSEWHERE:
|
|
continue
|
|
if m in NO_TESTS_YET:
|
|
if has_test_sources(m):
|
|
problems.append(f'{m} is exempt as test-less but has test sources -- wire it into a shard')
|
|
# Require an actual test task (allTests / test / test<Variant>UnitTest),
|
|
# not just any reference -- a lone kover entry must not satisfy this.
|
|
elif not re.search(rf'{re.escape(m)}:(allTests|test)', shards):
|
|
problems.append(f'{m} has no test task in any reusable-check.yml test shard')
|
|
|
|
if problems:
|
|
print('CI shard coverage drift detected:')
|
|
for p in problems:
|
|
print(' -', p)
|
|
raise SystemExit(1)
|
|
exempt = COVERED_ELSEWHERE | NO_TESTS_YET
|
|
print(f'{len(modules) - len(modules & exempt)} modules verified against the shard matrix.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|