Files
reelbox/tool/native_player_timing_test.py
T

92 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""Run real Android activity timing/EOF handlers with fixture events, without JNI.
Uses the existing lean Robolectric resolver/runner. This is control-flow evidence,
not decoded-media, network, or physical-device verification.
"""
import argparse
import fcntl
import hashlib
import json
import os
from pathlib import Path
import sys
import zipfile
import player_overlay_android_test as harness
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / 'android/app/src/main/kotlin/org/reelbox/reelbox'
WORK = Path('/root/.hermes/cache/scratch/reelbox-native-timing')
def aar_classes(artifact, version):
matches = sorted((harness.CACHE / artifact / version).glob('*/*.aar'))
if not matches:
raise RuntimeError(f'Missing cached AAR: {artifact}')
target = WORK / (matches[-1].stem + '-classes.jar')
with zipfile.ZipFile(matches[-1]) as archive:
target.write_bytes(archive.read('classes.jar'))
return target
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--test')
args = parser.parse_args()
WORK.mkdir(parents=True, exist_ok=True)
(WORK / 'tmp').mkdir(exist_ok=True)
harness.WORK = WORK
sdk = Path(os.environ.get('ANDROID_HOME', os.environ.get('ANDROID_SDK_ROOT', str(Path.home() / 'Android/Sdk'))))
android = sdk / 'platforms/android-36/android.jar'
if not android.is_file():
raise RuntimeError(f'Android 36 jar missing: {android}')
with (ROOT / '.tooling.lock').open('a') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
harness.LOCK_FD = lock.fileno()
deps = harness.resolve()
stdlib = harness.cached_jar('org.jetbrains.kotlin/kotlin-stdlib', '2.4.0')
vlc = aar_classes('org.videolan.android/libvlc-all', '3.7.7')
core = aar_classes('androidx.core/core', '1.16.0')
classes = WORK / 'classes'
classes.mkdir(exist_ok=True)
cp = os.pathsep.join(map(str, [classes, android, stdlib, vlc, core, *deps]))
compiler = [harness.cached_jar('org.jetbrains.kotlin/kotlin-compiler-embeddable', '2.4.0'), stdlib,
harness.cached_jar('org.jetbrains.kotlin/kotlin-script-runtime', '2.4.0'),
harness.cached_jar('org.jetbrains.kotlin/kotlin-reflect'),
harness.cached_jar('org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm'),
harness.cached_jar('org.jetbrains/annotations')]
compiler += sorted((harness.CACHE / 'org.jetbrains.intellij.deps/trove4j').glob('*/*/*.jar'))[-1:]
sources = [SRC / name for name in ('NativeUrlPolicy.kt', 'NativePlayerPolicy.kt', 'NativeFileIntents.kt',
'PlayerControlsState.kt', 'PlayerOverlayView.kt', 'VlcPlayerActivity.kt')]
test = ROOT / 'android/app/src/test/java/org/reelbox/reelbox/NativePlayerTimingAndroidTest.java'
hashes = {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in [*sources, test]}
(WORK / 'tested-inputs.json').write_text(json.dumps(hashes, indent=2) + '\n')
harness.run([str(harness.JDK / 'bin/java'), '-Xmx256m', '-XX:+UseSerialGC', f'-Djava.io.tmpdir={WORK / "tmp"}',
'-cp', os.pathsep.join(map(str, compiler)), 'org.jetbrains.kotlin.cli.jvm.K2JVMCompiler',
'-jvm-target', '17', '-no-stdlib', '-no-reflect', '-classpath', cp,
'-d', str(classes), *map(str, sources)], log='kotlin-compile.log')
(WORK / 'OverlayHostRunner.java').write_text(harness.RUNNER)
(WORK / 'OverlayHarnessSmokeTest.java').write_text(harness.SMOKE)
harness.run([str(harness.JDK / 'bin/javac'), '-J-Xmx192m', '--release', '17', '-cp', cp, '-d', str(classes),
str(WORK / 'OverlayHostRunner.java'), str(WORK / 'OverlayHarnessSmokeTest.java'), str(test)], log='javac-timing.log')
opens = ['java.base/java.lang', 'java.base/java.util', 'java.base/java.io', 'java.base/java.net',
'java.base/java.security', 'java.base/java.text', 'java.base/jdk.internal.access',
'java.desktop/java.awt.font', 'jdk.compiler/com.sun.tools.javac.api']
java = [str(harness.JDK / 'bin/java'), '-Xmx320m', '-XX:MaxMetaspaceSize=192m', '-XX:+UseSerialGC',
f'-Djava.io.tmpdir={WORK / "tmp"}', '-Drobolectric.dependency.repo.url=https://repo.maven.apache.org/maven2',
f'-Dmaven.repo.local={WORK / "m2"}', '-Drobolectric.graphicsMode=LEGACY',
*['--add-opens=' + p + '=ALL-UNNAMED' for p in opens], '-cp', cp,
'org.reelbox.reelbox.OverlayHostRunner']
harness.run([*java, 'org.reelbox.reelbox.OverlayHarnessSmokeTest', str(WORK / 'smoke-results.xml')], log='smoke.log')
command = [*java, 'org.reelbox.reelbox.NativePlayerTimingAndroidTest', str(WORK / 'timing-results.xml')]
if args.test:
command.append(args.test)
harness.run(command, log='timing.log')
assert all(hashlib.sha256(Path(p).read_bytes()).hexdigest() == digest for p, digest in hashes.items())
print('PASS: actual activity event handlers and result gate; fixture events, no decoder/device claims')
if __name__ == '__main__':
main()