Testing¶
./gradlew testDebugUnitTest lintDebug # unit and recorded-fixture tests, lint
./gradlew :demo:connectedDebugAndroidTest # UI end to end on an emulator or device
Recorded fixtures¶
Protocol behaviour is tested against responses recorded from real implementations (see
Recording fixtures): AI SDK and AG-UI streams from the
Pydantic AI adapters, MCP elicitation from the official mcp SDK, A2UI surfaces checked by the
official a2ui-core processor, and Agent Client Protocol sessions replayed to the official ACP
Kotlin SDK.
Live tests¶
Live tests run against real servers and are skipped unless you pass their address:
cd server && uv run uvicorn main:app --port 8788 # in another terminal
./gradlew :ai-elements-core:testDebugUnitTest --tests '*LiveHarnessServerTest*' -PliveAgentServer=http://localhost:8788
./gradlew :ai-elements-core:testDebugUnitTest --tests '*LiveMcpTest*' -PliveMcp=http://localhost:8788/mcp -PliveMcpLegacy=http://localhost:8790/mcp
./gradlew :ai-elements-core:testDebugUnitTest --tests '*LiveMcpOAuthTest*' -PliveMcpOAuth=http://127.0.0.1:8791/mcp
./gradlew :ai-elements-a2a:testDebugUnitTest -PliveA2a=http://localhost:8788
./gradlew :ai-elements-acp:testDebugUnitTest --tests '*LiveAcpTest*' \
-PliveAcpCommand="uv run --directory server python acp_agent.py" -PliveAcp=ws://localhost:8788/acp
End-to-end tests¶
demo/src/androidTest drives the demo app. Cases that need the reference server skip when it is
unreachable; pass its address with -e agentServer http://<host>:8788 (the default is the
emulator's 10.0.2.2). ScreenshotMatrixTest captures the same conversation in light and dark, in
every language (-e screenshots true); the screenshots on this site come from it.
Xiaomi / HyperOS devices
Instrumentation started in the background needs
adb shell appops set dev.ai.elements.demo 10021 allow (reset with … 10021 default), and the
device must stay awake and in portrait during the run.
Performance¶
Smoothness is measured with Macrobenchmark
on a release build, in the :benchmark module, over the chat's hot paths:
| Benchmark | What it measures |
|---|---|
startup |
cold start (StartupTimingMetric) |
scrollLongConversation |
flinging a long answer up and down |
streamLongAnswer |
a 24-section Markdown answer streaming in |
openHistoryAndSwitchConversation |
opening the history drawer and another long conversation |
Each runs twice: without ahead-of-time compilation (a fresh install) and with the Baseline Profile
(what users get). Read frameDurationCpuMs (P50 / P90 / P99) and frameOverrunMs (above 0 is a
missed frame) in the results. Run them on a real device; emulator numbers are not representative.
ANDROID_SERIAL=<device> ./gradlew :benchmark:connectedBenchmarkReleaseAndroidTest \
-Pandroid.testInstrumentationRunnerArguments.class=dev.ai.elements.benchmark.ChatBenchmarks
The demo ships a Baseline Profile
(demo/src/release/generated/baselineProfiles/), generated by BaselineProfileGenerator over the
same journeys. Regenerate it after large UI changes:
Apps using AI Elements should generate their own profile over their screens: the library's code on those paths is included.
API reference¶
First-release and upgrade checks¶
./gradlew apiCheck testDebugUnitTest lintDebug :demo:assembleRelease
tools/check-published-consumer.sh
tools/build-docs.sh
python3 -m unittest discover -s tools -p 'test_*.py'
The consumer build uses a separate Gradle settings file and an exclusive file Maven repository. Its exported component examples are compiled with the exact imports printed on the site. The site checker verifies that Dokka emitted symbol pages for every library, not just an index page. See API compatibility for reviewing intentional baseline changes.
When several devices are connected, set ANDROID_SERIAL=emulator-... to target just the emulator.
A successful Gradle task does not prove live tests ran: inspect skipped counts and start the
reference server before protocol E2E. Hosted-model tests remain opt-in and require separate evidence.
Bilingual landing screenshots¶
Use the emulator id from adb devices. The script runs ReleaseScreenshotsTest with curated
message data and the real Compose components, captures light/dark in English/Simplified Chinese,
and updates four WebP assets. It does not call a model or change system language/night settings.
It uses direct instrumentation so Gradle's post-test app uninstall cannot delete the screenshots
before collection. Inspect all four images before committing them.
Bilingual documentation¶
English lives in docs/, Chinese in docs/zh/. The two Zensical configurations build into site/
and site/zh/, sharing component images, compiled Kotlin examples and the English Dokka reference.
tools/check-docs.py verifies language tags, links, screenshots and Chinese counterparts for every
English guide. Component translations belong in tools/component-usage.zh.json, not generated pages.
SharedFoldersTest navigates the system picker from its current directory, including a direct
storage-root view without a device-name entry. It still selects the folder through the app,
reads and edits the real SAF files, and checks the resulting shared-storage bytes.
The picker test registers an emulator-only UI Automator watcher for the exact Pixel Launcher ANR dialog found in CI. It closes that unrelated launcher overlay and unregisters after the case; it does not dismiss the demo app's ANR or crash dialogs.
The English and Chinese installation examples share tools/doc-snippets/installation-*.gradle.kts.
tools/check-doc-parity.py rejects differing fenced examples in all 45 page pairs and runs before
the site build. Translate explanations outside those examples; update shared dependencies once.