Skip to content

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:

ANDROID_SERIAL=<device> ./gradlew :demo:generateReleaseBaselineProfile

Apps using AI Elements should generate their own profile over their screens: the library's code on those paths is included.

API reference

./gradlew :dokkaGenerate      # build/dokka/html

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

tools/capture-release-screenshots.sh emulator-5582

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.