Testing and release evidence

Test Local AI UI with a Fake Inference Engine

Inject deterministic streaming, success, failure, cancellation, and delay states behind the application interface.

Review status: Fact and editorial QA complete; owner review pending · 2026-10-06 · Fact IDs: COVE-LIFECYCLE-01, BENCHMARK-01, COVE-INFERENCE-CONTRACT-2026-10

Direct answer

A fake inference engine is a controllable test implementation of the app-facing contract, not a smaller copy of LiteRT-LM. In this repository, AiInferenceEngine exposes seven methods that return Flow<String>, and EngineProvider is a suspend function returning Result<AiInferenceEngine>. The fake should implement those exact methods and let a test choose the sequence of values or failure for each request. It should not invent initialize, close, cancel, engine-state, backend-selection, or native-resource methods on the interface. Those belong to concrete runtime ownership and are not part of the public fake contract. Start by checking the cold-flow boundary. Before collection, the Flow should not run its producer script, perform fake inference, or increment an execution/collection counter. The fake may record that an API method was invoked while constructing the Flow; keep that factory-call log separate from producer executions. Collection is the event that starts the fake behavior, matching the documented cold-flow contract. Make the behavior explicit: an immediate sequence, an empty flow, a delayed sequence, or a flow that throws after a known partial result. For each case, assert the collected values and terminal outcome. A test that only checks that a Flow object is non-null does not show that collection works, that the expected values arrived, or that an exception reaches the caller. Use the fake to make event order repeatable. A useful scripted result can emit a partial string, pause at a controlled point, emit a final segment, and complete; another can fail before the first value or after partial output. Let a test observe request count, input arguments, collection count, and completion. Keep each script small enough to answer one question. If a prompt or transcript is part of the fixture, use synthetic content and compare only the contract-relevant input, not a production user's text. Coroutine virtual time can make delayed flows deterministic. With kotlinx-coroutines-test, run the subject under runTest and use the shared TestCoroutineScheduler to advance through a fake delay. Keep the fake's delay on the test dispatcher supplied to it. A Flow moved to Dispatchers.Default by production code will not automatically use virtual time; either inject a dispatcher at the owning layer or keep that dispatcher behavior outside this fake-contract test. Do not add real sleeps to make an event likely to have happened. Wait for a named event or advance the scheduler to the point the test controls. Cancellation needs a precise claim. AiInferenceEngine has no explicit cancellation method; the app cancels the coroutine collecting a Flow. A cooperative fake can suspend between emissions and record that collection ended through cancellation. It can verify that a ViewModel stops consuming values, does not present a partial response as complete, and permits a later request. It cannot prove that a real JNI call or LiteRT-LM native generation stopped, released memory, or closed a conversation. Those require a separate real-runtime test and runtime-specific evidence. Avoid a fake that catches CancellationException and then emits more values, because that can hide a cancellation bug rather than model the contract. Test provider failure separately from inference failure. EngineProvider may return Result.failure before an engine exists; an engine method may instead throw while its flow is collected. Assert that the caller exposes the correct state for each boundary and does not call an unavailable engine. Also test an empty but successful flow, because no output is not identical to provider failure. If the UI accepts a second request, give each attempt a request identity and verify delayed events from an earlier fake script cannot overwrite the newer state. Concurrency expectations must be scoped to the interface contract and the component under test. The current InferenceEngine implementation serializes inference with a mutex, while AiInferenceEngine documentation says one engine instance is generally not safe for concurrent calls. A fake can record overlapping collections and help test that a ViewModel queues or rejects a second operation according to app policy. A fake that happens to serialize its flows does not prove the concrete engine does so. If serialization is a required production guarantee, test the real implementation or a separately specified adapter contract as well. Keep a small scripted fake rather than a broad test framework. Useful controls include per-method result scripts, a call log containing method and request ID, a gate that the test can release, and a count of active collectors. Avoid copying large image or audio buffers into the call log. The assertions should answer whether the app requested a method, observed the documented result sequence, handled termination, and reached the next state. They should not grade generated language quality or substitute fixed prose for a model benchmark. The repository's existing InferenceEngineTest creates flows for processText and transcribeAudio but only asserts that the Flow objects are non-null. That is useful evidence of construction, not evidence that collection emits a result. A new fake-engine test can close that contract gap for app-level callers. Keep the conclusion narrow: the fake proves how the tested consumer behaves for the scripted inputs and events. It does not prove the real runtime accepts the prompt, streams identically, honors cancellation natively, or produces correct translations, summaries, or transcriptions.

A ViewModel must stop presenting an old stream after a new request

A user submits a local summary request and then edits the input before the first response completes. The team needs to check event ownership and cancellation without loading a model, and must not mistake a cancelled fake collector for proof that native generation stopped.

Decision

Inject a scripted AiInferenceEngine that pauses after a partial value, start a second request, cancel or supersede the first according to product policy, and assert that only current-request events can update state.

Expected outcome

The ViewModel test proves request-state behavior for the controlled fake and leaves real native cancellation, resource release, and output quality to separate tests.

Evidence to collect

The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:

  • Interface contract
  • Deterministic state fixtures
  • UI and recovery assertions

A release claim is valid only for the recorded configuration and acceptance set. Generalization requires additional records.

Implementation workflow

  1. Implement the exact interface. Provide all seven AiInferenceEngine Flow methods and an EngineProvider result path. Do not add native lifecycle methods that the interface does not define; record calls and test-controlled scripts at the fake boundary.
  2. Prove cold collection. Separate any API-method factory-call log from execution and collection counters. Assert the producer script does not run before collection, then check its emissions and terminal event.
  3. Script success and failure edges. Cover multiple values, empty success, provider Result.failure, immediate flow exception, and failure after partial output. Verify each produces the intended app state without logging private fixture text.
  4. Control delays and cancellation. Use runTest and a shared scheduler for fake delays. Cancel at a known point, assert no later values are consumed, and keep the conclusion limited to cooperative Flow collection.
  5. Check competing request identity. Use a gate to release old and new request events in a deliberate order. Confirm the active request owns the visible result and that one fake run does not claim real-engine concurrency safety.
  6. Name the evidence boundary. Report fake implementation, fixture script, collector and dispatcher setup, assertions, and untested runtime behavior. Keep native initialization, true engine close, hardware backend, and task quality out of the fake-only conclusion.

Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.

Acceptance criteria

  • Tests collect the flows and assert emitted values, empty success, provider failure, and collection-time exceptions rather than only checking that a Flow exists.
  • Cancellation is triggered at a deterministic event boundary and is reported as cooperative collector behavior, not proof of native cancellation.
  • The fake implements the declared interface without adding methods or claims about native initialization, close, backend selection, or model quality.

Decision table

DimensionChoice or evidenceHow to interpret it
Fake AiInferenceEngineScript Flow values, timing, and errorsFast deterministic evidence about the consumer and interface contract; it does not execute the real runtime.
InferenceEngine unit or integration testCollect a real implementation with a real compatible artifactCan observe adapter/runtime behavior, but requires native dependencies and artifact-specific setup.
Device task evaluationRun a versioned task fixture on a named build/deviceMay support scoped task or resource findings; neither a fake nor one output proves general quality.

Failure patterns to prevent

  • Embedding runtime types in UI
  • Testing only successful completion

Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.

Minimum reproducibility record

LayerRecord
DeviceManufacturer, model, chipset, RAM class, operating-system build
SoftwareApplication version and git commit
ModelFamily, variant, revision, format, file length, hash, quantization
RuntimeName, revision, requested backend, observed backend evidence
WorkloadFixture revision, input hash, prompt hash, output policy
OutcomeCompleted, failed, cancelled, fallback, and privacy-safe diagnostics

Release checklist

  • ☐ The primary query is answered without an unsupported number.
  • ☐ Every artifact and runtime is pinned.
  • ☐ The representative task and failure policy are explicit.
  • ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
  • ☐ User-content and network boundaries are documented.
  • ☐ Result wording applies only to the recorded configuration.
  • ☐ The page links to raw method or evidence when results are added.

Sources and related evidence

Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.

Frequently asked questions

Does cancelling the fake flow prove LiteRT-LM stopped generating?

No. It shows that the collector and test double responded to coroutine cancellation. Native work, memory release, and conversation close require a real-runtime test and evidence from the concrete adapter.

Should the fake include initialize and close methods?

Not on AiInferenceEngine, because its declared contract has seven inference methods and no initialize or close operation. Test ModelManager or a concrete runtime lifecycle at the owner that actually exposes those operations.

Is checking that a returned Flow is non-null enough?

No. Collect it and assert the values, terminal condition, error mapping, and state effect that matter. Construction alone says nothing about emission or caller behavior.