SDK
AO_Autotest
The Active Object that runs the self-test rows of the Kode Dot in order: the groups all, main and user, how a verdict is read, the hold and the kept report.
What it is
A self-test row is one test of one part of the board, such as pmic or sd. AO_Autotest holds the table of rows and runs them in order. It owns no hardware: a row belongs to the Active Object that owns the part it judges. AO_Autotest decides only when each row is asked, how long its owner has to answer, and how the answer is read.
Whatever asks for a run is a client. There are three: the console command test (AUTOTEST_CLIENT_CONSOLE), the Self-test page of the settings screen (AUTOTEST_CLIENT_GUI) and AO_TestDot (AUTOTEST_CLIENT_TEST_DOT), which asks for one row at a time in the E5 final test (sdk/kaos/test-dot). A client posts one request that carries its id, then reads what AO_Autotest publishes: the answer to the request, each row as it starts and ends, the wait for the client, and the end of the run with a tally. Every event carries the id of its client, and a client reads only its own. One run is in flight at a time, whoever asks.
Two words select a group of rows. main is what a bare main board answers. user is the profile for the owner of a unit. The console's test component=all is not a group of this AO: it is the E5 final test, which AO_TestDot runs, asking this AO for its rows by name.
How it is connected
sequenceDiagram autonumber participant C as Client box "kodeOS" participant A as AO_Autotest participant O as The row's owner end C->>A: AUTOTEST_RUN_REQ_SIG A-->>C: AUTOTEST_BEGIN_SIG, rows A->>O: the row's request A-->>C: AUTOTEST_ROW_SIG, running O-->>A: TEST_RESULT_SIG A-->>C: AUTOTEST_ROW_SIG, result A-->>C: AUTOTEST_END_SIG, tally
AO_Autotest in numbers:
| Thing | Value |
|---|---|
| Priority | 10, right under AO_Console (11), its first client |
| Queue | 16 events |
| Stack | 4096 bytes |
| Claims | Nothing |
| Compiled under | KODEOS_AUTOTEST, off by default and selected by KODEOS_CONSOLE |
| States | One |
Waiting for an owner costs nothing: it posts one request, arms one timer and sleeps. Because it runs below the console, the console prints a row's record before the sequencer asks the next row, so the log lines of one row never land above the verdict of the row before.
The table
TESTS[] has one entry per row: name, a permanent id (AUTOTEST_ID_*, never reused because the kept report stores it), the function that runs or asks, a kind, its group bits, budget_ms, an optional answer name (several rows of one owner answer under the owner's name), flags, a label and a problem sentence for a person. autotest_row_label(), autotest_row_problem() and autotest_row_name() read them by id. A group runs every row whose mask has its bit, in table order.
Two kinds of row
A row is synchronous or asynchronous.
| Kind | How it runs | Rows |
|---|---|---|
TEST_SYNC | No owner. AO_Autotest runs it in its own step and publishes the verdict at once | screenshot, jpeg, clocks, mem, boot, apps, kfw, id, ble_console |
TEST_ASYNC | It belongs to another AO. AO_Autotest asks the owner and arms the budget, and the owner answers later with TEST_RESULT_SIG or TEST_SKIP_SIG | fs, sd, pmic, gauge, ioexp, imu, mag, haptic, c5, ir, nfc, rfid, audio, buttons, led, display, tp, net |
The clocks row holds the core for tens of milliseconds while it counts the crystal. psram is of the asynchronous kind with AO_Autotest as its own owner: it takes the free PSRAM less a reserve, walks it on the psram_step timer, publishes PSRAM_TEST_SIG with what it found and gives the blocks back when it ends or runs out of budget.
The run
The rows run one at a time, because several need a person at the board and two rows asking at once would each judge the other's answers. For each row AO_Autotest publishes AUTOTEST_ROW_SIG twice, with AUTOTEST_ROW_RUNNING and the budget, then with the result. A row whose owner stays silent for its whole budget ends as a timeout and the run goes on. While a run is in flight AO_Autotest subscribes to the verdicts and the step events of the guided rows; while idle it hears nothing but requests. It compares the name a verdict arrives under with the row in flight and ignores any other, with one warning, such as a row answering after its budget.
The user profile
user is not a mask. order_build() has its own list, USER_ORDER, and looks each id up in the table. A row this build lacks is not in the run. Rows that need nobody come first, then the hold, then the rows that need hands.
The table lists the order of a user run.
| Place | Row | Owner |
|---|---|---|
| 1 to 4 | ioexp, clocks, imu, mag | AO_IoExp, AO_Autotest, AO_Imu |
| 5 | haptic | AO_Haptic |
| 6 | sd_user: the card row, answering as sd; a slot with no card is skipped | AO_Storage |
| 7 | c5_user: the link, a Wi-Fi sweep that completes and the BLE controller, no floor on what the scans hear | AO_Radio |
| 8 | ir | AO_Ir |
| 9, 10 | nfc_field, rfid_field: the part names itself and its field stands for one window | AO_Nfc, AO_Rfid |
| 11 | audio | AO_Audio |
| hold | The run waits for its client | |
| 12 | power_plug: the charger plugged in, both power chips judged, the charger taken out | AO_Power |
| 13 to 16 | buttons, led, display, tp | AO_Buttons, AO_LedMgr, AO_Display, AO_Touch |
| 17, 18 | nfc_card, rfid_card: any tag held to the back passes | AO_Nfc, AO_Rfid |
| 19, 20 | psram, mem | AO_Autotest |
A run of user holds 20 rows, or 19 without the radio; the hold is not a row. A run holds at most AUTOTEST_RUN_MAX (24) entries.
How a verdict is read
An owner answers pass or fail. AO_Autotest reads that together with what the steps of the row said (verdict_state()):
- A pass is a pass.
- A fail of a row flagged
ROW_F_SKIP_NO_CARD, when the first step of the card found no card, is skipped. - A fail when a step went unanswered and none was answered "no" is a timeout: nobody was there to press, or the row had no witness.
- Any other fail is a fail.
TEST_SKIP_SIG from an owner ends the row as skipped whatever it says, and a budget that ends with no answer is a timeout.
The step events that set the flags are BTN_TEST_SIG, TOUCH_TEST_SIG, HAPTIC_TEST_SIG, DISPLAY_TEST_SIG, LED_TEST_SIG, POWER_TEST_SIG and STORAGE_TEST_SIG. The owners publish them, and AO_Autotest does not forward them.
Whose run it is. The two guided scripts, buttons and touch, can also be started by another client (buttons_test_request_from(), touch_test_request_from()). Their steps and their verdict carry an origin, and AO_Autotest counts only those of TEST_ORIGIN_AUTOTEST: a step of another client's run does not mark the row now running, and a verdict of another origin is ignored (the log says so at debug level). The steps of the other owners have no origin, because only AO_Autotest asks for them.
The hold
In the user profile the walk publishes AUTOTEST_HOLD_SIG, arms the budget timer for HOLD_MS (300000 ms, 5 min) and waits for autotest_continue() from the client that owns the run.
The console does not wait for a person: it prints >|hold=continue and continues at once. With no answer in 5 min the run ends with cancelled set. If the pool has no event for the hold, nobody could answer, so the run goes on.
The cancel
autotest_cancel_request() ends the run after the row in flight and at once during a hold. A row is never torn down halfway.
The kept report
When a user run ends and was not cancelled, AO_Autotest keeps one blob in NVS: namespace ktest, key last, version 1. It holds each row's id and final state, and the UTC time of the end (0 when the clock was not set).
A cancelled run keeps nothing. all and main keep nothing. A run of one row by name replaces that row's state in the report and leaves the rest. autotest_report_request() publishes AUTOTEST_REPORT_SIG once per row and a closing marker with the row count, the problem count (fail or timeout) and the time.
The budget timer
QTimeEvt_disarm() returns false both for a timer never armed and for one that has expired with its event already queued. Without a guard that late event would time out the next row. AO_Autotest counts each such case when it stops the timer and drops that many AUTOTEST_BUDGET_SIG events. The hold uses the same timer and guard.
Resting with the board
With CONFIG_KODE_LOWPOWER, AO_Autotest joins the rounds of AO_LowPower as autotest (sdk/kaos/lowpower). It owns no device, so it has nothing to put away or to restore. It answers a round busy with test row while a run is in flight, a hold included, because the operator at the pad would find the board gone in the middle of a row. It answers ready otherwise.
Specifications
Signals in
| Signal | Event | From |
|---|---|---|
AUTOTEST_RUN_REQ_SIG | AutotestRunReqEvt (client, group, name) | autotest_run_request() |
AUTOTEST_CANCEL_REQ_SIG, AUTOTEST_CONTINUE_SIG, AUTOTEST_REPORT_REQ_SIG | AutotestClientEvt (client) | autotest_cancel_request(), autotest_continue(), autotest_report_request() |
TEST_RESULT_SIG, TEST_SKIP_SIG | TestResultEvt (name, ok) | The owner of the row, during a run only |
BTN_TEST_SIG, TOUCH_TEST_SIG, HAPTIC_TEST_SIG, DISPLAY_TEST_SIG, LED_TEST_SIG, POWER_TEST_SIG, STORAGE_TEST_SIG | The owners' step events | The owners, during a run only |
AUTOTEST_BUDGET_SIG, AUTOTEST_PSRAM_STEP_SIG | none (timers) | Itself |
LOWPOWER_ROUND_SIG | LowPowerRoundEvt | AO_LowPower, with CONFIG_KODE_LOWPOWER: answered busy test row during a run |
Signals out
| Signal | Event | When |
|---|---|---|
AUTOTEST_BEGIN_SIG | AutotestBeginEvt (client, status, rows) | The answer to a run request: AUTOTEST_TAKEN (0), AUTOTEST_BUSY (1) or AUTOTEST_UNKNOWN (2). A hold is not a row |
AUTOTEST_ROW_SIG | AutotestRowEvt (client, id, pos, state, name, budget_ms) | A row starts and ends. budget_ms is 0 for a row judged on the spot and in every state but running |
AUTOTEST_HOLD_SIG | AutotestHoldEvt (client) | A user run waits for its client |
AUTOTEST_END_SIG | AutotestEndEvt (client, tally[5], cancelled) | The run is over. The tally has one count per state, capped at 255 |
AUTOTEST_REPORT_SIG | AutotestReportEvt (client, last, id, state, rows, problems, when) | The answer to a report request |
PSRAM_TEST_SIG | PsramTestEvt | What the PSRAM row found, once per run, before its verdict |
If the pool has no event for AUTOTEST_BEGIN_SIG, AO_Autotest logs an error and does not start the run. A client counts the rows that end against the number in that event.
The groups and states
| Group | Constant | Rows |
|---|---|---|
main | AUTOTEST_G_MAIN | pmic, gauge, ioexp, clocks, imu, mag |
user | AUTOTEST_G_USER | The ordered list above |
| By name | AUTOTEST_G_NONE | Any row, including the ones in no group: haptic, sd, c5, ir, nfc, rfid, audio, buttons, led, display, tp, psram, mem, fs, net, boot, apps, kfw, id, screenshot, jpeg, ble_console and the user rows |
A row ends in one of five states: AUTOTEST_ROW_RUNNING (0), _PASS (1), _FAIL (2), _TIMEOUT (3) or _SKIPPED (4). The default budget of a row is KODE_TEST_ROW_BUDGET_MS, 5000 ms, and the slower rows have budgets of their own in kqp_config.h.
The calls
| Call | What it does |
|---|---|
autotest_run_request(client, group, name) | Asks for a run. group is AUTOTEST_G_MAIN or _USER, or _NONE with a row name |
autotest_cancel_request(client) | Ends the run after the row in flight. Only the run's client may |
autotest_continue(client) | Answers a hold |
autotest_report_request(client) | Publishes the kept report. Allowed during a run |
autotest_row_label(id), autotest_row_problem(id), autotest_row_name(id) | Constant strings; empty for an id this build lacks |
The console verbs are test [component=…] and test_report. The only stored data is the report blob. The option is KODEOS_AUTOTEST.
Gotchas
Example code
A run answers with events and has no return value, so the console is where to see one. Each step ends in something you can read.
- Open the console on the cable and type
test component=main. Six records arrive, one>|test=<row> result=<state>per row (pmic,gauge,ioexp,clocks,imu,mag). - Read the last two lines:
>|summary pass= fail= timeout=and the bare>ok. A verdict block withALL PASSEDor the failing rows precedes them in the log. - Type
test component=userwith the unit on a table. The console prints>|hold=continueafter the rows that need nobody. The rows after it ask for presses on the pad and taps on the glass. - Type
test_report. One>|row="<name>" result=<state>record per row arrives, then>|when=<utc> rows=<n> problems=<n>.
In the SDK
components/kaos/autotest:autotest_ao.c(the table, the walk, the report) andautotest_psram.c(the PSRAM walk).kqp_signals.h: the events, the group bits, the row ids and the states.kaos.h: the five calls.