Use Portrait when no valid orientation preference is saved; preserve all explicit user choices, including System default. Cover missing, invalid, explicit and platform orientation behavior in four focused tests. Focused tests, debug lint (zero errors/warnings) and debug assembly pass. A matching-signer in-place TC22 update preserved login/enrollment and Android reports the normal activity requested portrait. Record exact physical Code128 evidence and fully green prior native CI separately. Co-Authored-By: OpenAI Codex <noreply@openai.com> Signed-off-by: James Maes <james@kof22.com>
7.5 KiB
Isolated TC22 acceptance
The emulator-only CI entry point continues to refuse physical devices. The explicit physical entry point is limited to an attached Zebra TC22 and requires its serial and one named scenario:
$env:ANDROID_HOME = "$env:LOCALAPPDATA\Android\Sdk"
node scripts/ci/scanner-device-e2e.mjs --serial TC22_SERIAL --scenario session-monitor ../qbit-mobile-scanners
node scripts/ci/scanner-device-e2e.mjs --serial TC22_SERIAL --scenario navigation
node scripts/ci/scanner-device-e2e.mjs --serial TC22_SERIAL --scenario licenses
node scripts/ci/scanner-device-e2e.mjs --serial TC22_SERIAL --scenario orientation
Build the matching APKs first, using Java 21 and one bounded Gradle invocation (--max-workers=2 --no-parallel). Scanner scenarios need :scanner-host:assembleE2e :scanner-host:assembleE2eAndroidTest and the companion demo's installed artifact/classes/test classpath. The navigation scenario needs :qqqsampleandroidmobileapp:assembleDebug :qqqsampleandroidmobileapp:assembleDebugAndroidTest.
The handheld must already be unlocked. This runner does not change power, lock, animation, developer, DataWedge or ordinary scanner settings. It refuses existing disposable test packages, installs only separate test applications, and removes only applications installed by that run. The ordinary io.qrun.mobile.scanner installation and its data are never cleared or replaced. Scanner scenarios use a temporary local server/database and one new non-rebinding USB reverse, which is removed on cleanup. Existing reverses, including the operator's ordinary server connection, remain in place.
session-monitor exercises real login/enrollment/approval, process activity, recreation, actual PixelCopy upload, background/paused-frame clearing, administrator session termination and fresh login. navigation runs the shared native navigation, input and account suites with synthetic repository data and verifies request counts/draft preservation. It installs the sample app temporarily and refuses to replace any existing sample installation or retained app data. licenses opens the bundled notice list and renders the Google artwork license; orientation checks the app's orientation policy. These two scenarios use only the isolated scanner APK/test APK and need no demo server or USB reverse. Other scanner scenario names are the same as EMULATOR.md.
Evidence is written under scanner-host/build/native-device-e2e. Request audits contain operation IDs/status/timing, not credentials or bodies. Do not capture the normal app's screens or customer data as test evidence. Automatic camera transport tests must use synthetic originals; a real camera shot requires the operator to aim at an agreed non-sensitive target. The current implementation and test outcomes are recorded separately in SESSION-STATE.md.
The standalone orientation and licenses scenarios need only the isolated scanner APKs and no server. Orientation acceptance checks saved policy, actual portrait/landscape configuration, recreation, and a fresh Activity launch for each choice. The session and photo scenarios separately verify that a rotation retains process input and a pending original.
Physical session-monitor now uses the disposable io.qrun.mobile.scanner.qa variant so a retained .e2e installation is preserved. Build it with -PscannerTestBuildType=qa :scanner-host:assembleQa :scanner-host:assembleQaAndroidTest; hosted/emulator sessions continue using E2E. Its rotation check waits for the requested landscape configuration and resumed lifecycle, verifies the draft, then explicitly recreates and verifies the draft again. Brief physical-test focus transitions wait up to five seconds within the existing stage deadline; actual resumed/unlocked/focused requirements remain enforced. Physical installs use ADB's non-streaming path.
Explicit operator-consented device-screen acceptance
This manual-only test is excluded from hosted/default scenarios. Compile QA packages as above, then run:
node scripts/ci/scanner-screen-device.mjs --serial TC22_SERIAL --fixture PRIVATE_FIXTURE.json --operator-consent
The private file must identify purpose: "device-screen-qa", a disposable loopback baseUrl, username: "scanner", adminUsername: "admin" and fresh private password/adminPassword values. Use the qualified core/API1.9 and mobile-scanners1.2 runtime; the runner refuses the normal57832 and retained51631 ports. Keep this file owner-only and outside Git. It installs only absent QA/QA.test packages and owns only its new reverse/proxy resources.
Named stages request notification permission, three fresh Android projection approvals, Home, Stop and a return through Recents. The operator handles every Android permission dialog; the test never grants them. It checks real app/Home pixels, Stop, viewer-lease expiry, administrator session end and fresh login without resumed projection. Each operator wait is bounded to120seconds. Server JPEGs are compared with the physical display in memory using bounded spatial RGB error and blank/wrong-layout negative controls. Neither screenshot source is retained as evidence or substituted for the application's uploaded frame.
Compilation and runner guard checks pass; actual system consent/display acceptance remains unexecuted. A failed run records FAILED rather than leaving a successful-looking proof, preserves sanitized stage/operation evidence and removes only its disposable resources. Successful execution requires every completion stage and successful native semantic screen uploads; a decodable JPEG alone is insufficient.
Photo acceptance requires the companion optional Record Files integration's successfully verified isolated fixture. Start integrations/record-files/Start-ConsumerFixture.ps1 in that repository, keep its returned credential file private, and run:
node scripts/ci/scanner-device-photo-e2e.mjs --serial TC22_SERIAL --fixture PRIVATE_CREDENTIAL_FILE
This runner accepts only that loopback fixture's expected account/process/parent contract. It does not start, stop or delete the external fixture. Its test intercepts Android camera/picker activity results with a generated image, verifies automatic camera upload and retained server association/hash, then checks pending gallery-original retention across rotation and cleanup on administrator-forced logout. No surroundings are photographed.
Device Settings includes Orientation: System default, Portrait, Landscape, Reverse portrait and Reverse landscape. Portrait is the default when no choice has been saved or the saved value is invalid. An existing explicit choice, including System default, is preserved. The choice is local to this installation and survives sign-out, server changes and restarts; it does not modify Android's global auto-rotate preference. Android can recreate the Activity when applying a choice. The host retains process ViewModels and saves unsubmitted manual scanner text across that recreation. Pending photo originals remain with the process's retained ViewModel.
The TC22 test target runs Android 14. Android's requested-orientation API permits manufacturer overrides; for apps targeting API36 or later, Android16 ignores orientation restrictions on displays whose smallest width is at least 600dp. The setting requests the platform policy and does not force transforms or work around those system restrictions.