ReviewOS

also looking at this

craft-native/craft

fix(native): retain keyed controls and correct map and tab chrome

#368
Merged chris wants to merge codex/native-repeated-selector-identity into main
32 files +449 -100

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/typescript/src/__tests__/android-emulator.test.tsmodified+23-1
Changes to packages/typescript/src/__tests__/android-emulator.test.ts
@@ -5,7 +5,7 @@ import { join } from 'node:path'
55import { createAndroidEmulator } from '../../../android/src/emulator'
66import type { AndroidEmulatorOptions } from '../../../android/src/emulator'
77
8function fixture(hardware: boolean, delayedSettings = false, delayedEvents = false): { root: string, options: AndroidEmulatorOptions } {
8function fixture(hardware: boolean, delayedSettings = false, delayedEvents = false, delayedHome = false): { root: string, options: AndroidEmulatorOptions } {
99 const root = mkdtempSync(join(tmpdir(), 'craft emulator fixture '))
1010 const sdk = join(root, 'managed sdk')
1111 for (const directory of ['bin', 'platform-tools', 'emulator']) mkdirSync(join(sdk, directory), { recursive: true })
@@ -30,6 +30,13 @@ else
3030 printf '1\\n' ;;
3131 *'pm path android') printf 'package:/system/framework/framework-res.apk\\n' ;;
3232 *'pidof com.android.systemui') printf '1234\\n' ;;
33 *'cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.HOME')
34 if [ '${delayedHome ? 'yes' : 'no'}' = yes ] && [ ! -f "$ANDROID_HOME/home-ready" ]; then
35 touch "$ANDROID_HOME/home-ready"
36 printf 'com.android.sdksetup/.DefaultActivity\\n'
37 else
38 printf 'com.android.launcher3/.Launcher\\n'
39 fi ;;
3340 *'logcat -b events -d -t 1')
3441 if [ '${delayedEvents ? 'yes' : 'no'}' = yes ] && [ ! -f "$ANDROID_HOME/events-ready" ]; then
3542 touch "$ANDROID_HOME/events-ready"
@@ -113,6 +120,21 @@ test('boot and System UI do not admit a device before its event log is readable'
113120 finally { await session?.dispose(); rmSync(root, { recursive: true, force: true }) }
114121}, 10_000)
115122
123test('boot completion waits for the automatic SDK setup activity to release HOME', async () => {
124 const { root, options } = fixture(true, false, false, true)
125 let session: Awaited<ReturnType<typeof createAndroidEmulator>> | undefined
126 try {
127 session = await createAndroidEmulator(options)
128 const trace = readFileSync(join(options.artifactsDirectory, 'device-commands.jsonl'), 'utf8').trim().split('\n').map(line => JSON.parse(line))
129 const home = trace.filter(entry => entry.args.includes('resolve-activity'))
130 expect(home.map(entry => entry.stdout.trim())).toEqual(['com.android.sdksetup/.DefaultActivity', 'com.android.launcher3/.Launcher'])
131 const identity = JSON.parse(readFileSync(join(options.artifactsDirectory, 'android-emulator.json'), 'utf8'))
132 expect(Date.parse(home[1].finishedAt)).toBeLessThanOrEqual(Date.parse(identity.bootedAt))
133 expect(identity.bootTimeoutMs).toBe(600_000)
134 }
135 finally { await session?.dispose(); rmSync(root, { recursive: true, force: true }) }
136}, 10_000)
137
116138test('the SDK process fixture records hardware mode and reaps only its owned process', async () => {
117139 const { root, options } = fixture(true)
118140 options.bootTimeoutMs = 42_000