ReviewOS

also looking at this

craft-native/craft

test(mobile-e2e): prove Android reports approximate location as granted

#214
Merged glennmichael123 wants to merge fix/android-location-granted into main
5 files +85 -5
scripts/mobile-e2e/android.tsmodified+32-1
Changes to scripts/mobile-e2e/android.ts
@@ -2,7 +2,7 @@ import type { LegOutcome, RunnerOptions } from './types'
22import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
33import { join } from 'node:path'
44import { init } from '../../packages/android/src/index'
5import { androidDeclines, awaitedNeeds, DISMISS_SHARE_MENU, evaluateRun, hasTerminated, shareMenuInFront } from './protocol'
5import { androidDeclines, awaitedNeeds, DISMISS_SHARE_MENU, evaluateRun, hasTerminated, runtimePermissionGranted, shareMenuInFront } from './protocol'
66import { command, driverPage, waitForFile } from './support'
77
88/**
@@ -15,11 +15,29 @@ import { command, driverPage, waitForFile } from './support'
1515 * Clipboard is not a configurable capability on Android - CraftBridge serves
1616 * clipboardRead/clipboardWrite unconditionally - so the success case needs no
1717 * flag, and asking for one would imply a gate that is not there.
18 *
19 * Geolocation on, because that is what puts the location permissions in the
20 * manifest, and `pm grant` refuses a permission the manifest does not declare.
21 * The Kotlin does not otherwise read the flag (#209).
1822 */
1923const CONFIG = {
2024 enablePushNotifications: false,
25 enableGeolocation: true,
2126}
2227
28/**
29 * The one location permission the harness grants: approximate, and not
30 * precise.
31 *
32 * #192 was a device with location granted that still reported something
33 * else. Granting both permissions would pass against a bridge that required
34 * either one, and that is not the contract. Android 12 onwards lets a person
35 * choose approximate only, and CraftPermissionPolicy counts that as `granted`
36 * (780e952). Coarse alone is the stricter of the two tests: anything this
37 * passes, a device with both permissions granted passes too.
38 */
39const GRANTED_LOCATION_PERMISSION = 'android.permission.ACCESS_COARSE_LOCATION'
40
2341const PACKAGE = 'dev.craft.e2e.probe'
2442const APP_NAME = 'CraftE2EProbe'
2543
@@ -142,6 +160,19 @@ async function runLeg(leg: Leg, options: RunnerOptions): Promise<LegOutcome> {
142160 await adb(['uninstall', PACKAGE], { serial, allowFailure: true })
143161 await adb(['install', '-r', apk], { serial, logPath: join(evidence, 'adb.log') })
144162
163 // Granted from the host before launch, so no dialog waits on a person.
164 await adb(['shell', 'pm', 'grant', PACKAGE, GRANTED_LOCATION_PERMISSION], { serial, logPath: join(evidence, 'adb.log') })
165
166 // And read back, because the case is only as strict as the device state it
167 // ran against: approximate granted, precise not. A device that had precise
168 // granted as well would pass against a bridge requiring precise.
169 const packageState = (await adb(['shell', 'dumpsys', 'package', PACKAGE], { serial })).stdout
170 writeFileSync(join(evidence, 'package-permissions.txt'), packageState)
171 if (runtimePermissionGranted(packageState, GRANTED_LOCATION_PERMISSION) !== true)
172 failures.push(`${GRANTED_LOCATION_PERMISSION} is not granted after pm grant; see ${label}/package-permissions.txt`)
173 if (runtimePermissionGranted(packageState, 'android.permission.ACCESS_FINE_LOCATION') === true)
174 failures.push('ACCESS_FINE_LOCATION is granted too, so the location case cannot tell approximate-only from precise')
175
145176 // Not best-effort: the ring buffer outlives an uninstall, and a transcript
146177 // left in it would otherwise be read as this run's. The plan event carries
147178 // this run's nonce as a second guard.