Skip to content

Commit

Permalink
feat(core): Introduce unit test extension to prodtest CLI [no changelog]
Browse files Browse the repository at this point in the history
  • Loading branch information
kopecdav committed Feb 23, 2025
1 parent 9f03e0c commit 74a4f5a
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/SConscript.prodtest
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ SOURCE_PRODTEST = [
'embed/projects/prodtest/cmd/prodtest_sdcard.c',
'embed/projects/prodtest/cmd/prodtest_sbu.c',
'embed/projects/prodtest/cmd/prodtest_touch.c',
'embed/projects/prodtest/cmd/prodtest_unit_test.c',
'embed/projects/prodtest/cmd/prodtest_wpc.c',
]

Expand Down
23 changes: 23 additions & 0 deletions core/embed/projects/prodtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -771,3 +771,26 @@ wpc-update
# Updating STWLC38...
# WPC update completed 800 ms
```

### unit-test-run
Prodtest have capability to verify the overall firmware functionality by running built-in unit tests which should excercise the basic
features of the firmware drivers. This command will run all registered unit tests and return 'OK' if all tests passed.

Example:
```
# Running all unit tests...
# ut-pmic-battery: PASSED
# ut-pmic-init-deinit: PASSED
OK
```

### unit-test-list
List all build-in unit tests

Example:
```
# List of all registered unit tests:
# ut-pmic-battery - Test PMIC battery connection
# ut-pmic-init-deinit - Test PMIC driver initialization and deinitialization
OK
```
71 changes: 71 additions & 0 deletions core/embed/projects/prodtest/cmd/prodtest_pmic.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,65 @@ static void prodtest_pmic_report(cli_t* cli) {
cli_ok(cli, "");
}

// ut-pmic-init-deinit
// This unit test verifies the PMIC driver initialization and deinitialization
// routine could be called repeatably witout failure. It should verify that all
// driver components are properly cleaned by deinit function.
static ut_status_t ut_pmic_init_deinit() {
ut_status_t ut_result = UT_PASSED;

for (uint8_t i = 0; i < 5; i++) {
// deinitilize the pmic driver
npm1300_deinit();
if (npm1300_init() == false) {
ut_result = UT_FAILED;
break;
}
}

npm1300_deinit();

return ut_result;
}

// ut-pmic-battery
// This unit test verifies the battery connection to NPM1300 PMIC.
// Firstly it initilize the PMIC driver and request the measurement
// report. From the measurement report it checks, if the battery voltage and
// NTC temperature are within the expeted range. At last, it checks if NTC
// temperature measurement is not too far away from the die temperarture.
static ut_status_t ut_pmic_battery() {
ut_status_t ut_result = UT_PASSED;
npm1300_report_t report;

if (npm1300_init() == false) {
ut_result = UT_FAILED;
} else {
// Request mesaurement report from PMIC
if (npm1300_measure_sync(&report) == false) {
ut_result = UT_FAILED;
} else {
// Battery voltage outside given range
if (report.vbat < 3.0 || report.vbat > 3.8) {
ut_result = UT_FAILED;
}

// Battery NTC outside given range
if (report.ntc_temp < -40.0 || report.ntc_temp > 50.0) {
ut_result = UT_FAILED;
}

// Battery NTC too far from die temp
if (abs(report.ntc_temp - report.die_temp) > 10.0) {
ut_result = UT_FAILED;
}
}
}

npm1300_deinit();
return ut_result;
}

// clang-format off

PRODTEST_CLI_CMD(
Expand Down Expand Up @@ -242,4 +301,16 @@ PRODTEST_CLI_CMD(
.args = "[<count>] [<period>]"
);

PRODTEST_CLI_UT(
.name = "ut-pmic-init-deinit",
.func = ut_pmic_init_deinit,
.info = "Test PMIC driver initialization and deinitialization",
)

PRODTEST_CLI_UT(
.name = "ut-pmic-battery",
.func = ut_pmic_battery,
.info = "Test PMIC battery connection",
)

#endif // USE_POWERCTL
74 changes: 74 additions & 0 deletions core/embed/projects/prodtest/cmd/prodtest_unit_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <rust_ui_prodtest.h>
#include <trezor_model.h>
#include <trezor_rtl.h>

#include <rtl/cli.h>

static void prodtest_unit_test_list(cli_t* cli) {
cli_trace(cli, "List of all registered unit tests:");

for (size_t i = 0; i < cli->unit_test_count; i++) {
cli_trace(cli, " %s - %s ", cli->unit_test_array[i].name,
cli->unit_test_array[i].info);
}

cli_ok(cli, "");
}

static void prodtest_unit_test_run(cli_t* cli) {
bool ut_passed = true;

cli_trace(cli, "Running all unit tests...");

for (size_t i = 0; i < cli->unit_test_count; i++) {
ut_status_t test_result = cli->unit_test_array[i].func(cli);

cli_trace(cli, "%s: %s", cli->unit_test_array[i].name,
test_result == UT_PASSED ? "PASSED" : "FAILED");

if (test_result == UT_FAILED) {
ut_passed = false;
}
}

if (ut_passed) {
cli_ok(cli, "");
} else {
cli_error(cli, CLI_ERROR, "Some of the unit tests Failed");
}
}

// clang-format off

PRODTEST_CLI_CMD(
.name = "unit-test-list",
.func = prodtest_unit_test_list,
.info = "Print list of all registered unit tests",
.args = ""
)

PRODTEST_CLI_CMD(
.name = "unit-test-run",
.func = prodtest_unit_test_run,
.info = "Run all registerd unit tests",
.args = ""
)
6 changes: 6 additions & 0 deletions core/embed/projects/prodtest/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,17 @@ int main(void) {

extern cli_command_t _prodtest_cli_cmd_section_start;
extern cli_command_t _prodtest_cli_cmd_section_end;
extern cli_unit_test_t _prodtest_cli_ut_section_start;
extern cli_unit_test_t _prodtest_cli_ut_section_end;

cli_set_commands(
&g_cli, &_prodtest_cli_cmd_section_start,
&_prodtest_cli_cmd_section_end - &_prodtest_cli_cmd_section_start);

cli_set_unit_tests(
&g_cli, &_prodtest_cli_ut_section_start,
&_prodtest_cli_ut_section_end - &_prodtest_cli_ut_section_start);

#ifdef USE_OPTIGA
optiga_init();
optiga_open_application();
Expand Down
5 changes: 5 additions & 0 deletions core/embed/rtl/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ void cli_set_commands(cli_t* cli, const cli_command_t* cmd_array,
cli->cmd_array = cmd_array;
cli->cmd_count = cmd_count;
}
void cli_set_unit_tests(cli_t* cli, const cli_unit_test_t* unit_test_array,
size_t unit_test_count) {
cli->unit_test_array = unit_test_array;
cli->unit_test_count = unit_test_count;
}

static void cli_vprintf(cli_t* cli, const char* format, va_list args) {
char buffer[CLI_LINE_BUFFER_SIZE];
Expand Down
33 changes: 33 additions & 0 deletions core/embed/rtl/inc/rtl/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,17 @@ typedef struct cli cli_t;
#define CLI_ERROR_LOCKED "locked"
#define CLI_ERROR_NODATA "no-data"

typedef enum {
UT_PASSED = 0,
UT_FAILED,
} ut_status_t;

// CLI command handler routine prototype
typedef void (*cli_cmd_handler_t)(cli_t* cli);

// CLI unit test handler routine prototype
typedef ut_status_t (*cli_ut_handler_t)(cli_t* cli);

// Structure describing the registration record for a CLI command handler
typedef struct {
// Command name
Expand All @@ -63,6 +71,16 @@ typedef struct {
const char* args;
} cli_command_t;

// Structure describing the registration record for a CLI unit test handler
typedef struct {
// Command name
const char* name;
// Command handler
cli_ut_handler_t func;
// Single line command description
const char* info;
} cli_unit_test_t;

#define CONCAT_INDIRECT(x, y) x##y
#define CONCAT(x, y) CONCAT_INDIRECT(x, y)

Expand All @@ -73,6 +91,13 @@ typedef struct {
section(".prodtest_cli_cmd"))) static const cli_command_t \
CONCAT(_cli_cmd_handler, __COUNTER__) = {__VA_ARGS__};

// Register a unit test handler by placing its registration structure
// into a specially designated linker script section
#define PRODTEST_CLI_UT(...) \
__attribute__((used, \
section(".prodtest_cli_ut"))) static const cli_unit_test_t \
CONCAT(_cli_ut_handler, __COUNTER__) = {__VA_ARGS__};

// Callback for writing characters to console output
typedef size_t (*cli_write_cb_t)(void* ctx, const char* buf, size_t len);
// Callback for reading characters from console input
Expand All @@ -88,6 +113,10 @@ struct cli {
const cli_command_t* cmd_array;
size_t cmd_count;

// Registered unit test command handlers
const cli_unit_test_t* unit_test_array;
size_t unit_test_count;

// Current line buffer
char line_buffer[CLI_LINE_BUFFER_SIZE];
// Command name (pointer to the line buffer)
Expand Down Expand Up @@ -123,6 +152,10 @@ bool cli_init(cli_t* cli, cli_read_cb_t read, cli_write_cb_t write,
void cli_set_commands(cli_t* cli, const cli_command_t* cmd_array,
size_t cmd_count);

// Register the unit test handlers
void cli_set_unit_tests(cli_t* cli, const cli_unit_test_t* unit_test_array,
size_t unit_test_count);

// Runs the CLI command loop
void cli_run_loop(cli_t* cli);

Expand Down
6 changes: 6 additions & 0 deletions core/embed/sys/linker/stm32f4/prodtest.ld
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ SECTIONS {
_prodtest_cli_cmd_section_start = .;
KEEP(*(.prodtest_cli_cmd))
_prodtest_cli_cmd_section_end = .;
. = ALIGN(4);

_prodtest_cli_ut_section_start = .;
KEEP(*(.prodtest_cli_ut))
_prodtest_cli_ut_section_end = .;


. = ALIGN(128K);
} >FLASH
Expand Down
5 changes: 5 additions & 0 deletions core/embed/sys/linker/stm32u58/prodtest.ld
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ SECTIONS {
_prodtest_cli_cmd_section_start = .;
KEEP(*(.prodtest_cli_cmd))
_prodtest_cli_cmd_section_end = .;
. = ALIGN(4);

_prodtest_cli_ut_section_start = .;
KEEP(*(.prodtest_cli_ut))
_prodtest_cli_ut_section_end = .;

. = ALIGN(512);
} >FLASH AT>FLASH
Expand Down
5 changes: 5 additions & 0 deletions core/embed/sys/linker/stm32u5g/prodtest.ld
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ SECTIONS {
_prodtest_cli_cmd_section_start = .;
KEEP(*(.prodtest_cli_cmd))
_prodtest_cli_cmd_section_end = .;
. = ALIGN(4);

_prodtest_cli_ut_section_start = .;
KEEP(*(.prodtest_cli_ut))
_prodtest_cli_ut_section_end = .;

. = ALIGN(512);
} >FLASH AT>FLASH
Expand Down

0 comments on commit 74a4f5a

Please sign in to comment.