also looking at this
fix(android): make every Zig decline speak, and prove Zig answers
#205
8 files
+616
-255
| @@ -1,5 +1,7 @@ | ||
| 1 | 1 | import { describe, expect, it } from 'bun:test' |
| 2 | import { evaluateRun, hasTerminated, parseDriverOutput, REQUIRED_CASES, requiredCaseProblems, ZIG_REFUSED_ACTION, ZIG_TESTED_ACTIONS, zigDispatchedActions, zigRefusals } from './protocol' | |
| 2 | import { readFileSync } from 'node:fs' | |
| 3 | import { join } from 'node:path' | |
| 4 | import { ANDROID_DECLINE_PHRASES, androidDeclines, evaluateRun, hasTerminated, parseDriverOutput, REQUIRED_CASES, requiredCaseProblems, ZIG_REFUSED_ACTION, ZIG_TESTED_ACTIONS, zigDispatchedActions, zigRefusals } from './protocol' | |
| 3 | 5 | |
| 4 | 6 | const ESC = String.fromCharCode(27) |
| 5 | 7 | |
| @@ -276,3 +278,44 @@ describe('zig attribution', () => { | ||
| 276 | 278 | expect(ZIG_TESTED_ACTIONS).toContain(ZIG_REFUSED_ACTION) |
| 277 | 279 | }) |
| 278 | 280 | }) |
| 281 | ||
| 282 | describe('android declines', () => { | |
| 283 | // The lines the first Android runtime run actually produced, plus the two | |
| 284 | // decline kinds it did not hit, so all three are read the way they are | |
| 285 | // written. | |
| 286 | const logcat = [ | |
| 287 | '09-16 11:29:24.000 2611 2611 I CraftNative: craft: libcraft.so loaded', | |
| 288 | '09-16 11:29:24.001 2611 2611 I CraftNative: craft: registered 103 natives on com/craft/runtime/CraftNative', | |
| 289 | '09-16 11:29:24.535 2611 2705 W System.err: java.lang.NoSuchFieldError: no "J" field "longVersionCode"', | |
| 290 | '09-16 11:29:24.535 2611 2705 W CraftNative: craft: getDeviceInfo fell through to the shim (JavaException)', | |
| 291 | '09-16 11:29:25.000 2611 2705 E CraftNative: craft: locationResult failed with no fallback (OutOfMemory)', | |
| 292 | '09-16 11:29:26.000 2611 2705 E CraftNative: craft: reviewError could not reach the page (NoWebView)', | |
| 293 | '09-16 11:29:26.100 2611 2705 D CraftBridge: CRAFT-E2E {"event":"done","passed":4,"failed":0}', | |
| 294 | ].join('\n') | |
| 295 | ||
| 296 | it('reads every kind of decline, and names the action and the error', () => { | |
| 297 | expect(androidDeclines(logcat)).toEqual([ | |
| 298 | 'getDeviceInfo: fell through to the shim (JavaException)', | |
| 299 | 'locationResult: failed with no fallback (OutOfMemory)', | |
| 300 | 'reviewError: could not reach the page (NoWebView)', | |
| 301 | ]) | |
| 302 | }) | |
| 303 | ||
| 304 | // The case that shipped: bound, every page case passing, and Kotlin | |
| 305 | // answering. Registration is not a decline and must not be read as one — | |
| 306 | // and a run with nothing but registration is the clean result. | |
| 307 | it('does not mistake loading or registration for a decline', () => { | |
| 308 | const clean = logcat.split('\n').filter(line => !/fell through|no fallback|reach the page/.test(line)).join('\n') | |
| 309 | expect(androidDeclines(clean)).toEqual([]) | |
| 310 | }) | |
| 311 | ||
| 312 | it('matches the wording android_dispatch.zig actually writes', () => { | |
| 313 | // Read from the Zig source rather than trusted, because each side alone | |
| 314 | // would let a reworded helper turn the runtime leg blind: Zig would log a | |
| 315 | // phrase this suite no longer matched, and every decline would pass. | |
| 316 | const dispatch = readFileSync(join(import.meta.dir, '../../packages/zig/src/android_dispatch.zig'), 'utf8') | |
| 317 | for (const phrase of ANDROID_DECLINE_PHRASES) | |
| 318 | expect(dispatch).toContain(`" ${phrase} ({s})"`) | |
| 319 | }) | |
| 320 | }) | |
| 321 | ||