Skip to content

chore: prebuild example/android; harden instrumented tests#10

Merged
achou11 merged 1 commit into
mainfrom
chore/android-test-cleanup
Apr 21, 2026
Merged

chore: prebuild example/android; harden instrumented tests#10
achou11 merged 1 commit into
mainfrom
chore/android-test-cleanup

Conversation

@gmaclennan

@gmaclennan gmaclennan commented Apr 21, 2026

Copy link
Copy Markdown
Member

Summary

Prebuild-driven example/android

  • Stop checking in example/android/ — it's now regenerated by npx expo prebuild --platform android.
  • New Expo config plugin at example/plugins/with-android-tests/index.js that copies Kotlin sources from example/tests/android/ into app/src/androidTest/... and patches app/build.gradle with the AndroidJUnitRunner and androidTestImplementation deps (idempotent via mergeContents, safe on re-prebuild).
  • Plugin registered in example/app.json.
  • Instrumented tests moved from the deleted example/android/app/src/androidTest/... tree to example/tests/android/ — the plugin source of truth: ServiceLifecycleTest.kt, ShutdownPathTest.kt, WaitForFileTest.kt.

Library module

  • android/build.gradle: hook installNodejsProjectDeps into preBuild instead of AGP-version-specific merge{Variant}Assets. More robust across AGP versions and between library/app modules.
  • android/src/main/assets/nodejs-project/index.js: replay started/ready events to state-IPC clients that connect after the one-shot broadcast by tracking a readinessPhase. The race is rarer on Android than iOS but the code is shared, so the fix lands on both.
  • Delete unused nodejs-project/lib/control-rpc.js.

Tests

CI — .github/workflows/android-tests.yml

  • Bump Node 18 → 20.
  • Run npm install in example/ and expo prebuild --platform android --no-install before Gradle.
  • Skip emulator-runner's input keyevent 82 (flaky after snapshot restore — it can crash the emulator if the input service hasn't published yet). Wait for sys.boot_completed ourselves and disable animations via adb shell settings put global ..._scale 0.0.
  • Add a 15-minute timeout on the instrumented step; pass -i so each test prints STARTED/PASSED/FAILED and a hang is identifiable.

Local runner — e2e/run-instrumented-tests.sh

  • Run ensure_deps + a new run_prebuild once in a shared setup block instead of duplicating across the unit-only and instrumented paths.

Test plan

  • Clean checkout: ./scripts/download-nodejs-mobile.sh, npm install, cd example && npm install && npx expo prebuild --platform android --no-install produces a buildable project.
  • e2e/run-instrumented-tests.sh --unit-only passes.
  • e2e/run-instrumented-tests.sh on an API 30 emulator runs ServiceLifecycleTest, ShutdownPathTest, WaitForFileTest to green.
  • Android Tests workflow green on CI.

@gmaclennan gmaclennan changed the title chore: clean up android tests chore: switch example/android to Expo prebuild; harden instrumented tests Apr 21, 2026
@gmaclennan gmaclennan changed the title chore: switch example/android to Expo prebuild; harden instrumented tests chore: prebuild example/android; harden instrumented tests Apr 21, 2026
@gmaclennan gmaclennan requested a review from achou11 April 21, 2026 14:13
@achou11 achou11 merged commit 20e3e13 into main Apr 21, 2026
4 checks passed
@achou11 achou11 deleted the chore/android-test-cleanup branch April 21, 2026 15:47
gmaclennan added a commit that referenced this pull request May 6, 2026
Addresses the high- and medium-priority issues from the
review subagent's pass over Phase 1+2a+2b:

Boot transaction lifecycle (was: review #1, #15)
- applyAndEmit now closes bootTx and drains in-flight phase
  spans on STOPPING / STOPPED transitions too — not just
  STARTED / ERROR. stop()-from-STARTING transitions to
  STOPPING (rule 3 of deriveLifecycleState) and bypassed
  both terminals; destroy() forcing STOPPED-via-stopRequested
  did the same. Status mapped: STARTED→ok, ERROR→
  internal_error, STOPPING/STOPPED→cancelled.
- startBootTransaction now passes a TransactionContext
  carrying TracesSamplingDecision(true, 1.0). The previous
  TransactionOptions-only setup didn't actually force
  sampling, so with the SDK default tracesSampleRate=0.0 the
  boot transaction was dropped before reaching the wire.

SentryConfig misconfig handling (was: review #3)
- Both Kotlin and Swift readers used to crash on (DSN-set,
  environment-missing) — meant to be "fail loud" but a stale
  prebuild from before the validation was added would crash
  every cold start with no recovery. Now log loud (System.err
  on Android since android.util.Log isn't mocked on JVM
  tests; NSLog on iOS) and return null (Sentry off). Updated
  test renamed to assert "returns null, doesn't throw".

Span op/description ordering (was: review #19)
- transaction.startChild(op, description) — op is the indexed
  dashboard column. Was passing ("boot", "boot.<phase>"),
  swapped to ("boot.<phase>", human-readable description) so
  the dashboard groups by the phase taxonomy that matches
  the bench backend's boot-spans.js helper.

Plugin idempotency (was: review #7)
- Previously, dropping `props.sentry` from the plugin
  registration left stale meta-data / plist entries from a
  previous prebuild (with `expo prebuild --no-clean`). Plugin
  now passes through a no-Sentry cleanup mod that strips
  every key it owns; consumer-owned keys (e.g. io.sentry.* set
  by @sentry/react-native's plugin) are untouched.

messageerror payload truncation (was: review #8)
- src/sentry.ts now truncates the wrapped error message to
  256 chars before forwarding to captureException. The
  control-frame parser surfaces offending input verbatim,
  which can include arbitrary bytes from a corrupted frame —
  truncating keeps Sentry events small and readable.

IPC + SEND_ERROR_NATIVE breadcrumbs/events (was: review #9, #10)
- NodeJSIPC's onConnectionStateChange callback wired in the
  FGS-side controlIpc construction; emits comapeo.ipc
  breadcrumbs at info (warning on Error). Per §7.4.5.
- SEND_ERROR_NATIVE_TIMEOUT_MS firing now captures a
  level=warning event with timeout:errorNativeForward tag.
  Per §7.4.4.

Logging swallowed surprises (was: review low-priority)
- SentryFgsBridge's empty `catch (t: Throwable) {}` blocks
  now Log.w so debug builds notice swallowed bridge / SDK
  bugs.

Post-init bridge tests (was: review #6)
- New SentryFgsBridgeImplTest spins up a real Sentry hub via
  the cross-platform Sentry.init(SentryOptions) path with an
  in-memory ITransport. Covers: addBreadcrumb (no envelope on
  its own), captureException + captureMessage (envelope
  enqueued), startBootTransaction with global
  tracesSampleRate=0.0 (must still reach transport thanks
  to the TracesSamplingDecision override — regression test
  for the §15 bug above), boot span lifecycle, finishSpan
  with cancelled status, unknown level fallback to INFO.

All Sentry-related tests pass: 25 cases across
SentryConfigTest (8), SentryFgsBridgeTest (10),
SentryFgsBridgeImplTest (7).

Verified locally:
- npm run lint clean
- npx tsc --noEmit clean
- ./gradlew :comapeo-core-react-native:testDebugUnitTest
  passes
- ./gradlew :comapeo-core-react-native:compileDebugKotlin
  succeeds with sentry-android on the compile classpath
gmaclennan added a commit that referenced this pull request Jun 22, 2026
## Optic Release Automation

This **draft** PR is opened by Github action
[optic-release-automation-action](https://github.com/nearform-actions/optic-release-automation-action).

A new **draft** GitHub release
[v1.0.0-pre.2](https://github.com/digidem/comapeo-core-react-native/releases/tag/untagged-c499977757c9745e56b2)
has been created.

Release author: @gmaclennan

#### If you want to go ahead with the release, please merge this PR.
When you merge:

- The GitHub release will be published

- The npm package with tag pre will be published according to the
publishing rules you have configured



- No major or minor tags will be updated as configured


#### If you close the PR

- The new draft release will be deleted and nothing will change

## What's Changed
* Android Testing Infrastructure & Bug Fixes by @gmaclennan in
#3
* chore: prebuild example/android; harden instrumented tests by
@gmaclennan in
#10
* Integrate @comapeo/core via IPC over Unix sockets by @gmaclennan in
#5
* chore: adjust repo setup by @achou11 in
#12
* chore: minor fixes based on expo-doctor by @achou11 in
#13
* Add iOS support & test infrastructure by @gmaclennan in
#6
* chore: add architecture docs & plans by @gmaclennan in
#11
* update some native deps used in backend by @achou11 in
#14
* iOS Phase 1: unified JS bundle + smoke test (simulator-only) by
@gmaclennan in
#15
* iOS Phase 2: xcframework Embed & Sign for native addons by @gmaclennan
in #16
* Phase 2 Android: jniLibs packaging + unified rollup loader plugin by
@gmaclennan in
#17
* chore: post-Phase-2 cleanup — comments, plan docs, agents.md by
@gmaclennan in
#33
* android: read abiFilters from reactNativeArchitectures (#30) by
@gmaclennan in
#35
* refactor: simplify build-backend.ts; rollup writes directly to native
asset trees by @gmaclennan in
#34
* chore: fix eslint configuration by @achou11 in
#41
* android: audit 16 KB page alignment on every shipped .so by
@gmaclennan in
#43
* Add rootkey persistence and lifecycle state management by @gmaclennan
in #36
* chore: move example app into apps directory by @achou11 in
#18
* refactor: per-component lifecycle state with derived ComapeoState by
@gmaclennan in
#47
* android: fold waitForFile into connect retry loop by @gmaclennan in
#52
* chore: add e2e testing app by @achou11 in
#49
* fix(android): drop setUnlockedDeviceRequired from rootkey wrapper key
by @gmaclennan in
#57
* fix(backend): cache stopping/error frames for late joiners by
@gmaclennan in
#58
* fix(ios-tests): wait for STOPPING before signalling node exit by
@gmaclennan in
#59
* fix(android): drain JNI stdio pumps before returning from node::Start
by @gmaclennan in
#60
* Sentry integration: Phase 1 + Phase 2a + Phase 2b by @gmaclennan in
#54
* feat(backend): polywasm-backed undici on iOS, re-enable maps plugin by
@gmaclennan in
#62
* ci: drop unreliable Android emulator snapshot caching by @gmaclennan
in #64
* feat(sentry): land Phase 3 — backend loader + RPC tracing by
@gmaclennan in
#63
* fix(ios-tests): serialise STOPPING/STOPPED observers in
testFullLifecycleStateTransitions by @gmaclennan in
#71
* use npm list instead of custom traversal to get native module versions
by @achou11 in
#70
* feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS
MetricKit app-exit telemetry by @gmaclennan in
#72
* fix(sentry): make exit telemetry lossless and stop cross-process
clobbering by @gmaclennan in
#84
* chore(e2e): add e2e tests on browserstack via Maestro by @achou11 in
#56
* feat(sentry): migrate to @sentry/react-native v8; exit telemetry as
Application Metrics by @gmaclennan in
#73
* Map server integration by @gmaclennan in
#86
* chore(deps): upgrade to Expo SDK 56 (React Native 0.85) by @gmaclennan
in #87
* chore(ci): add release workflow by @gmaclennan in
#90
* chore: fix npm script and release build script by @gmaclennan in
#91
* chore(pack): don't try to package build files by @gmaclennan in
#92
* fix: start fastify listening by @gmaclennan in
#93
* perf(backend): switch bundler from rollup to rolldown by @gmaclennan
in #94
* fix(ci): ignore-scripts in ios npm installs by @gmaclennan in
#96
* fix(ci): replace --ignore-scripts with npm strict-allow-scripts
allowlist by @gmaclennan in
#106
* feat(config): let the consuming app supply the default project config
by @gmaclennan in
#95
* chore(release): merge prerelease branch. by @gmaclennan in
#110

## New Contributors
* @achou11 made their first contribution in
#12

**Full Changelog**:
https://github.com/digidem/comapeo-core-react-native/commits/v1.0.0-pre.2

<!--

<release-meta>{"id":342868678,"version":"v1.0.0-pre.2","npmTag":"pre","opticUrl":"https://optic-zf3votdk5a-ew.a.run.app/api/generate/"}</release-meta>
-->
@gmaclennan gmaclennan added the maintenance Refactor / test / chore / ci / build (changelog) label Jun 22, 2026
gmaclennan added a commit that referenced this pull request Jun 22, 2026
## Optic Release Automation

This **draft** PR is opened by Github action
[optic-release-automation-action](https://github.com/nearform-actions/optic-release-automation-action).

A new **draft** GitHub release
[v1.0.0-pre.2](https://github.com/digidem/comapeo-core-react-native/releases/tag/untagged-352a6c41c12fd02dec37)
has been created.

Release author: @gmaclennan

#### If you want to go ahead with the release, please merge this PR.
When you merge:

- The GitHub release will be published

- The npm package with tag pre will be published according to the
publishing rules you have configured



- No major or minor tags will be updated as configured


#### If you close the PR

- The new draft release will be deleted and nothing will change

<!-- Release notes generated using configuration in .github/release.yml
at 7fe80b4 -->

## What's Changed
### 🚀 Features
* Integrate @comapeo/core via IPC over Unix sockets by @gmaclennan in
#5
* Add iOS support & test infrastructure by @gmaclennan in
#6
* iOS Phase 1: unified JS bundle + smoke test (simulator-only) by
@gmaclennan in
#15
* iOS Phase 2: xcframework Embed & Sign for native addons by @gmaclennan
in #16
* Phase 2 Android: jniLibs packaging + unified rollup loader plugin by
@gmaclennan in
#17
* android: read abiFilters from reactNativeArchitectures (#30) by
@gmaclennan in
#35
* Add rootkey persistence and lifecycle state management by @gmaclennan
in #36
* Sentry integration: Phase 1 + Phase 2a + Phase 2b by @gmaclennan in
#54
* feat(backend): polywasm-backed undici on iOS, re-enable maps plugin by
@gmaclennan in
#62
* feat(sentry): land Phase 3 — backend loader + RPC tracing by
@gmaclennan in
#63
* feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS
MetricKit app-exit telemetry by @gmaclennan in
#72
* feat(sentry): migrate to @sentry/react-native v8; exit telemetry as
Application Metrics by @gmaclennan in
#73
* Map server integration by @gmaclennan in
#86
* feat(config): let the consuming app supply the default project config
by @gmaclennan in
#95
### 🐛 Bug Fixes
* fix(android): drop setUnlockedDeviceRequired from rootkey wrapper key
by @gmaclennan in
#57
* fix(backend): cache stopping/error frames for late joiners by
@gmaclennan in
#58
* fix(ios-tests): wait for STOPPING before signalling node exit by
@gmaclennan in
#59
* fix(android): drain JNI stdio pumps before returning from node::Start
by @gmaclennan in
#60
* fix(ios-tests): serialise STOPPING/STOPPED observers in
testFullLifecycleStateTransitions by @gmaclennan in
#71
* fix(sentry): make exit telemetry lossless and stop cross-process
clobbering by @gmaclennan in
#84
* fix: start fastify listening by @gmaclennan in
#93
* fix(ci): ignore-scripts in ios npm installs by @gmaclennan in
#96
* fix(ci): replace --ignore-scripts with npm strict-allow-scripts
allowlist by @gmaclennan in
#106
* fix(release): stop `npm pack --dry-run` leaking dry-run into backend
install by @gmaclennan in
#129
### ⚡ Performance
* perf(backend): switch bundler from rollup to rolldown by @gmaclennan
in #94
### ⬆️ Dependencies
* update some native deps used in backend by @achou11 in
#14
* chore(deps): upgrade to Expo SDK 56 (React Native 0.85) by @gmaclennan
in #87
### 🏗️ Maintenance
* Android Testing Infrastructure & Bug Fixes by @gmaclennan in
#3
* chore: prebuild example/android; harden instrumented tests by
@gmaclennan in
#10
* chore: adjust repo setup by @achou11 in
#12
* chore: minor fixes based on expo-doctor by @achou11 in
#13
* chore: add architecture docs & plans by @gmaclennan in
#11
* chore: post-Phase-2 cleanup — comments, plan docs, agents.md by
@gmaclennan in
#33
* refactor: simplify build-backend.ts; rollup writes directly to native
asset trees by @gmaclennan in
#34
* chore: fix eslint configuration by @achou11 in
#41
* android: audit 16 KB page alignment on every shipped .so by
@gmaclennan in
#43
* chore: move example app into apps directory by @achou11 in
#18
* refactor: per-component lifecycle state with derived ComapeoState by
@gmaclennan in
#47
* android: fold waitForFile into connect retry loop by @gmaclennan in
#52
* chore: add e2e testing app by @achou11 in
#49
* ci: drop unreliable Android emulator snapshot caching by @gmaclennan
in #64
* use npm list instead of custom traversal to get native module versions
by @achou11 in
#70
* chore(e2e): add e2e tests on browserstack via Maestro by @achou11 in
#56
* chore(ci): add release workflow by @gmaclennan in
#90
* chore: fix npm script and release build script by @gmaclennan in
#91
* chore(pack): don't try to package build files by @gmaclennan in
#92
* chore(release): merge prerelease branch. by @gmaclennan in
#110
* ci(e2e): retry BrowserStack builds on infra-class flakes by
@gmaclennan in
#113
### Other Changes
* ci: derive changelog labels from PR titles + add Dependabot by
@gmaclennan in
#114

## New Contributors
* @achou11 made their first contribution in
#12
* @optic-release-automation[bot] made their first contribution in
#112

**Full Changelog**:
https://github.com/digidem/comapeo-core-react-native/commits/v1.0.0-pre.2

<!--

<release-meta>{"id":342970724,"version":"v1.0.0-pre.2","npmTag":"pre","opticUrl":"https://optic-zf3votdk5a-ew.a.run.app/api/generate/"}</release-meta>
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance Refactor / test / chore / ci / build (changelog)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants