Tomacco

Docs Operation reference testing operations

testing operations#

16 operations. Generated from assets/operations.json; all areas.

On this page (16)

testing.find#

Find Android element

Wait for native UI elements using semantic properties; return bounds and attributes. The virtual iPhone (device virtual-iphone) reports no UI hierarchy, so a selector with only text or text_contains (and index) there is read off the screenshot with Tesseract instead: the matches carry source ocr, pixel_based true and a confidence, and the text can be misread or missed. Any other selector property answers ui_hierarchy_unavailable, whose details.testing lists what works there.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty
selectorobjectyes
selector.textstring
selector.text_containsstring
selector.resource_idstring
selector.descriptionstring
selector.classstring
selector.packagestring
selector.clickableboolean
selector.enabledboolean
selector.checkedboolean
selector.selectedboolean
selector.indexinteger0 to 10000
timeoutinteger100 to 60
statestring"present"one of present, absent
tomaccoctl call testing.find '{"device":"<device>","selector":{"text":"<text>"}}'

testing.action#

Operate Android element

Click, long-click or fill a uniquely matched native Android element. The virtual iPhone (device virtual-iphone) reports no UI hierarchy, so there a selector with only text or text_contains (and index) clicks or long-clicks the middle of the text read off the screenshot with Tesseract (source ocr, pixel_based true); fill and any other selector property answer an error, and ui_hierarchy_unavailable lists in details.testing what works.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty
selectorobjectyes
selector.textstring
selector.text_containsstring
selector.resource_idstring
selector.descriptionstring
selector.classstring
selector.packagestring
selector.clickableboolean
selector.enabledboolean
selector.checkedboolean
selector.selectedboolean
selector.indexinteger0 to 10000
timeoutinteger100 to 60
actionstringyesone of click, long_click, fill
textstringup to 10000 characters
durationinteger800100 to 10000
tomaccoctl call testing.action '{"device":"<device>","selector":{"text":"<text>"},"action":"click"}'

testing.run#

Run native tests

Run JSON native UI scenarios (one file, or a directory of them) across devices with device locks, JUnit, JSON reports, failure screenshots and a self-contained report.html; each run's outcomes are added to the test history (testing.history). A device is an Android serial or virtual-iphone, the running virtual iPhone. By default every device runs every scenario; shard splits the scenarios across the devices and merges their results into one report. With retries, a failed scenario runs again; each result lists its attempts and flaky marks a pass after a failure. Besides selector steps, a scenario can tap and swipe at pixel coordinates, type text, send a key, pause, take a screenshot, assert_screen (compare the screen with a baseline PNG), wait_screen (until it matches), wait_text (until text is on the screen, or with state absent gone; from the UI hierarchy on Android, read with Tesseract where there is none or source is ocr) and wait_stable (until the screen has not changed for duration milliseconds). On Android, a measure step records startup, frames, memory or usage metrics of an app, and a budget fails the scenario as budget_exceeded when a metric is outside it. On the virtual iPhone, steps go through ios.emulator.control at pixels of ios.virtual.screenshot, launch opens an app through the guest, and stop, clear and measure fail, and so do selector steps other than a text or text_contains selector, which is read off the screenshot with Tesseract (pixel-based; wait and assert, click and long_click, not fill): the guest reports no UI hierarchy. Its crash reports are not read after a scenario; use testing.crashes. With platform ios, it runs the same scenarios on USB iPhones through the WebDriverAgent session runtime.start keeps for each device (start it first): selectors are read against WDA's XML source, visible elements only, with text as the label (or the value of an element without one), resource_id as the accessibility identifier, description as the label and class as the element type; tap and swipe coordinates are in points; keys are home, enter, backspace, delete or tab. clickable, checked and package selectors and the clear step are refused there, and crashes are not read.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstringyesAbsolute path of a scenario JSON, or of a directory whose .json files are scenarios, run in name order
directorystringyes
devicesarray of stringyes1 to 32 items; Android serials from device.list, or virtual-iphone for the running virtual iPhone
platformstring"android"one of android, ios; android: adb devices; ios: USB iPhones, each with a connected runtime.start session
shardbooleanfalseSplit the scenarios across the devices, each scenario once, on the next free device, instead of running every scenario on every device. At most parallelism devices are used.
parallelisminteger21 to 8
timeoutinteger360010 to 86400
retriesinteger00 to 5; Run a failed scenario again up to this many times per device; a scenario's own retries field takes precedence. A pass after a failure is flaky in report.json, with every attempt listed.
budgetstringAbsolute path of a budget JSON file (as for profile.budget) checked against each scenario's measure steps; a scenario's own budget takes precedence
budgetsobjectThe budget inline instead of a file
baselinesstringAbsolute path of a baselines directory: an assert_screen or wait_screen step's relative baseline is a file in it, and a step without one gets SCENARIO/LABEL.png (or step-NNN.png)
record_baselinesbooleanfalseA screen step whose baseline is missing keeps its screenshot and the scenario goes on; the scenario then fails with baselines_missing. Approve the screenshots with testing.baselines
tomaccoctl call testing.run '{"path":"<path>","directory":"<directory>","devices":["<device>"]}'

testing.record#

Record UI test

Record a UI test on an Android device or the virtual iPhone. Between start and stop, the taps, swipes, text and keys sent to the device through android.input, runtime.input (the desktop's live display included) and testing.action become testing.run scenario steps. A tap becomes a click on the element under it, named by resource ID, description or text (with index when that is shared), when the UI hierarchy read just before the tap names one; otherwise a tap at pixel coordinates. Reading the hierarchy makes each tap slower while recording. stop writes the scenario to path; status counts the steps; discard throws the recording away. Input a scenario cannot express, such as a rotation, is listed under skipped. With device virtual-iphone (while it runs), start notes the screen's size and the ios.emulator.control input sent to its guest job becomes steps at pixel coordinates: click a tap, drag a swipe, a streamed touch_down to touch_up a tap, long press or swipe, and text, key and home their steps; a key combination and power_button are skipped.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Android serial from device.list, or virtual-iphone
actionstringyesone of start, status, stop, discard
namestringup to 150 characters; The scenario's name, set at start
pathstringAbsolute path of the scenario JSON that stop writes
overwritebooleanfalseLet stop replace an existing file at path
tomaccoctl call testing.record '{"device":"<device>","action":"start"}'

testing.lint#

Check UI test scenario

Check a testing.run scenario without acting on the device: its format, and each step against the current Android screen (or a saved android.hierarchy XML). findings name the step and a code: not_found (no element here matches; a warning, since an earlier step may open the screen it is on), ambiguous (several match: an error for click, long_click and fill, which need one), disabled, present (an element expected absent is there), off_screen (a tap or swipe point outside the screen) and invalid_key. steps lists each step's match count. A scenario that does not parse answers valid false with the error. passed is false when any finding is an error. The virtual iPhone (device virtual-iphone) reports no UI hierarchy: it answers ui_hierarchy_unavailable, whose details.testing lists what works there.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of the scenario JSON
devicestringnon-empty; Android serial from device.list; reads the current screen
hierarchystringnon-empty; Absolute path of a saved android.hierarchy XML instead of a device
tomaccoctl call testing.lint '{"path":"<path>"}'

testing.visual_diff#

Compare with baseline image

Compare a PNG screenshot with a baseline PNG pixel by pixel. Reports changed_fraction, changed_bounds and passed (changed_fraction &lt;= threshold); writes a diff image with changes in red when diff is given. update_baseline keeps the actual image as the new baseline instead of comparing.

Annotations: writes, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
actualstringyesnon-empty; Absolute path of the PNG to check, such as a device.capture result
baselinestringyesnon-empty; Absolute path of the approved PNG
diffstringAbsolute path for a difference image: the baseline faded, changed pixels red, ignored regions blue
thresholdnumber00 to 1; Largest changed fraction of the compared pixels that still passes
toleranceinteger00 to 255; A pixel counts as changed when a channel differs by more than this
ignorearray of objectup to 100 items; Regions in baseline pixels that are not compared, such as the status bar clock
ignore[].xintegeryesat least 0
ignore[].yintegeryesat least 0
ignore[].widthintegeryesat least 1
ignore[].heightintegeryesat least 1
update_baselinebooleanfalseCopy actual to baseline and compare nothing; only after you have checked the image
tomaccoctl call testing.visual_diff '{"actual":"<actual>","baseline":"<baseline>"}'

testing.accessibility#

Check Android accessibility

Audit an Android screen's UI hierarchy: clickable elements without a label (missing_label), touch targets under min_target_dp (small_target) and text shorter than min_text_height_dp (small_text). Returns structured findings with bounds in pixels and sizes in dp. Heuristics, not a certificate: no contrast, font size or focus order. The virtual iPhone (device virtual-iphone) reports no UI hierarchy: it answers ui_hierarchy_unavailable, whose details.testing lists what works there.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringnon-empty; Android serial from device.list; reads the current screen
hierarchystringnon-empty; Absolute path of a saved android.hierarchy XML instead of a device
densitynumber50 to 1000; Screen density in dpi; read from the device when omitted, required with hierarchy
bundle_idstringAudit only this app's elements, leaving out the status bar and other apps
min_target_dpnumber481 to 200
min_text_height_dpnumber141 to 100
reportstringAbsolute path to also write the result as JSON
tomaccoctl call testing.accessibility

testing.crashes#

Find app crashes

Read logcat's crash, system and main buffers and return Java crashes (FATAL EXCEPTION), ANRs and native crashes (Fatal signal) as structured events with package, pid, exception, message, stack and reason. Window: since (epoch seconds on the device clock), the life of job_id, or the last lines lines. With device virtual-iphone, reads the running virtual iPhone's crash reports whose names start with process through the guest service, as ios.virtual.crash_reports does, and returns each one with an exception as a crash event: package (bundle identifier), process, pid, thread, exception, signal, message, termination and the crashed thread's frames as stack, timed by the report's capture time; unparsed lists the rest with the reason. The guest returns at most three reports per directory, so collection_complete false means some were left out, and an empty list never proves that nothing crashed.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Android serial from device.list, or virtual-iphone
bundle_idstringOnly events of this app and its processes
processstringnon-empty, up to 255 characters; virtual-iphone only, and required there: the report name prefix, usually the app's executable name
sincenumberat least 0; Only events from this time on, in seconds since 1970 on the device clock
job_idstringOnly events while this job ran, such as a project.run or testing.run job; uses the computer's clock, which an emulator shares
linesinteger200001 to 200000; Without since or job_id, how many recent log lines to read
reportstringAbsolute path to also write the result as JSON
tomaccoctl call testing.crashes '{"device":"<device>"}'

testing.results#

Read test results

Read the JUnit XML results of a project's tests, or the JSON reporter output of Dart and Flutter tests, into one summary: totals, suites and each failure with its message and stack. Finds Gradle unit (build/test-results) and instrumented (build/outputs/androidTest-results) results and the Flutter reports project.execute writes to build/tomacco, or reads a given directory or file such as testing.run's junit.xml or a flutter test --machine output. golden_failures lists each failed Flutter golden-file comparison with its message and the masterImage, testImage, isolatedDiff and maskedDiff images flutter_test wrote. since or job_id leaves out results older than the run.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of the project, a results directory, or one JUnit XML or Dart JSON reporter file
sincenumberat least 0; Leave out files modified before this time, in seconds since 1970
job_idstringLeave out files older than this job, such as the project.execute test job
reportstringAbsolute path to also write the summary as JSON
tomaccoctl call testing.results '{"path":"<path>"}'

testing.coverage#

Read code coverage

Read the JaCoCo XML coverage reports of a project's tests into totals (instruction, branch, line, complexity, method, class: covered, missed, percent), each source file with its missed lines, and each class, least covered first. Finds the reports AGP writes for unit tests (enableUnitTestCoverage) and instrumented tests (enableAndroidTestCoverage) under build/reports/coverage, the jacoco plugin's under build/reports/jacoco and Kover's XML under build/reports/kover; project.execute with coverage:true runs the tasks. Several reports are merged line by line: a line is covered when any report covers it, and each class comes from the report that covers it most. baseline, a JSON report an earlier call wrote or JaCoCo XML, adds delta: the change of each total and each file whose line coverage moved, appeared or went away. minimum and max_drop are checks on line coverage that set passed.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of the project, a reports directory, or one JaCoCo or Kover XML report
sincenumberat least 0; Leave out files modified before this time, in seconds since 1970
job_idstringLeave out files older than this job, such as the project.execute test job
baselinestringnon-empty; Absolute path of a JSON report an earlier testing.coverage wrote with report, or of JaCoCo XML (a file or directory), to compare with
minimumnumber0 to 100; Least line coverage in percent that passes
max_dropnumber0 to 100; With baseline: the most line coverage may fall, in percentage points, and still pass
limitinteger501 to 5000; Files and classes listed in the answer, least covered first; report keeps them all
reportstringAbsolute path to also write the full result as JSON, usable as a later baseline
tomaccoctl call testing.coverage '{"path":"<path>"}'

testing.report#

Write HTML test report

Write one self-contained HTML file for a test run: for a testing.run report directory, each scenario and device with its outcome, attempts, steps and their timings, screenshots, baselines and difference images, crashes and metrics; for a project, a results directory or file, the JUnit and Dart JSON results with totals, suites, every test case and each failure with its stack and Flutter golden images. Styles are inline, images embedded as data URIs (up to 48 MiB in all, beyond that linked by path) and there are no scripts. testing.run and project.execute test jobs write theirs when they end; call this for another place or an earlier run. Returns the file as html with what it holds.

Annotations: writes, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of a testing.run report directory, a project, a results directory or one result file
sincenumberat least 0; Leave out files modified before this time, in seconds since 1970
job_idstringLeave out files older than this job, such as the project.execute test job
outputstringAbsolute path of the HTML file; by default report.html in a testing.run directory, build/tomacco/test-report.html in a project, or test-report.html beside a file
tomaccoctl call testing.report '{"path":"<path>"}'

testing.history#

Test pass-rate history

Pass rates of tests and UI scenarios across runs, to find flaky ones. testing.run adds each scenario's outcome per device when it ends and a project.execute test or instrumented-test job each test case, to a store in Tomacco's data directory; record adds a testing.run directory, a project's or directory's current JUnit or Dart JSON results, or a file, once per file version. summary lists each test with runs, passed, failed, skipped, passed_after_retry, pass_rate (percent of runs that passed, retried passes included), flips (changes between pass and fail), recent (P passed, R passed after a retry, F failed, S skipped; newest last), last_failure and flaky (both outcomes in the window, or a retried pass), flaky tests first, the least reliable first. last limits each test to its newest runs.

Annotations: writes, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
actionstring"summary"one of summary, record
pathstringnon-empty; record: absolute path of a testing.run report directory, a project, a results directory or one result file
sincenumberat least 0; Leave out files modified before this time, in seconds since 1970
job_idstringLeave out files older than this job, such as the project.execute test job
suitestringup to 500 characters; record: the name the entries are grouped under (the path by default); summary: only entries whose suite contains this
namestringup to 500 characters; summary: only tests whose name contains this, ignoring case
by_devicebooleanfalsesummary: one line per test and device
flaky_onlybooleanfalse
lastinteger501 to 10000; summary: each test's newest runs counted
min_runsinteger11 to 10000; summary: leave out tests with fewer runs
limitinteger501 to 5000
storestringAbsolute path of another store file (JSON lines) instead of the default one
tomaccoctl call testing.history

testing.impact#

Select affected tests

Pick the tests a change likely affects in a Gradle build, from the changed files: git diff against base (HEAD by default, so uncommitted and untracked changes) or files. A changed test class is selected itself; a changed Kotlin or Java source selects the unit and instrumented test classes of its module and of the modules that depend on it (project(...) and projects.x in their build files) that are named after it (NameTest, NameTests, NameIT, NameSpec, TestName) or use its name in the same package or with an import, and otherwise the tests of its package in its module. Resources or the manifest select the module's instrumented tests, a module's build file all its tests and its dependents', and the settings, versions catalog, gradle.properties, gradle/, buildSrc or build-logic everything (run_all). scenarios, a testing.run scenario file or directory, adds the scenarios that launch an app whose module a change touches. commands are the Gradle calls with --tests and the instrumentation runner's class argument; run unit or instrumented starts them as a job. The answer lists unmapped files. Heuristics over the files: Gradle is not run, and a test reached through reflection, dependency injection or generated code can be missed.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of the project
basestringup to 200 characters; Git revision to compare the working tree with; HEAD by default
filesarray of stringup to 5000 items; Changed files, relative to the project or absolute, instead of asking git
scenariosstringnon-empty; Absolute path of a testing.run scenario file or directory to select from
configurationstring"debug"one of debug, release
runstring"none"one of none, unit, instrumented; none lists only; unit or instrumented starts the selected tests as a job
devicestringrun instrumented: the device serial ANDROID_SERIAL selects
timeoutinteger18001 to 86400
tomaccoctl call testing.impact '{"path":"<path>"}'

testing.baselines#

Review screenshot baselines

Manage the baselines of assert_screen and wait_screen steps. review lists each screen step of a testing.run report directory per device as new (no baseline yet), changed, size_changed, passed, failed or not_reached, with its id, baseline, screenshot (actual), difference image and changed_fraction; by default only the screens to review (new, changed, size_changed), all_screens lists every one. approve copies the screenshots of the named screens, or with all of every new and changed screen, over their baselines, and refuses before writing when two devices would write different images to one baseline. list shows the PNG baselines in a directory with their sizes. Pair it with testing.run's baselines directory and record_baselines.

Annotations: destructive, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
actionstring"review"one of review, approve, list
runstringnon-empty; review, approve: absolute path of the testing.run report directory
screensarray of stringup to 1000 items; approve: the ids review lists
allbooleanfalseapprove: every new, changed and size_changed screen
all_screensbooleanfalsereview: list passed, failed and unreached screens too
directorystringnon-empty; list: absolute path of the baselines directory
tomaccoctl call testing.baselines

testing.setup#

Set up Android text input

Build and install the native Android instrumentation for Unicode text replacement. Returns a durable job.

Annotations: writes, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty
tomaccoctl call testing.setup '{"device":"<device>"}'

testing.instrumentation#

Check Android text input

Check the installed native Android instrumentation protocol.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty
tomaccoctl call testing.instrumentation '{"device":"<device>"}'

View this page as Markdown on GitHub