diff --git a/applications/debug/unit_tests/test_runner.c b/applications/debug/unit_tests/test_runner.c index 6af807086..299a943c0 100644 --- a/applications/debug/unit_tests/test_runner.c +++ b/applications/debug/unit_tests/test_runner.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,9 @@ struct TestRunner { int minunit_assert; int minunit_fail; int minunit_status; + + // Summary + size_t total_failed; }; TestRunner* test_runner_alloc(Cli* cli, FuriString* args) { @@ -112,6 +116,8 @@ static bool test_runner_run_plugin(TestRunner* instance, const char* path) { instance->minunit_status += test->get_minunit_status(); result = (instance->minunit_fail == 0); + + instance->total_failed += instance->minunit_fail; } while(false); flipper_application_free(lib); @@ -134,7 +140,7 @@ static void test_runner_run_internal(TestRunner* instance) { } while(true) { - if(cli_cmd_interrupt_received(instance->cli)) { + if(instance->cli && cli_cmd_interrupt_received(instance->cli)) { break; } @@ -165,7 +171,23 @@ static void test_runner_run_internal(TestRunner* instance) { if(!result) { printf("Failed to execute test: %s\r\n", file_basename_cstr); - break; + + if(!instance->cli) { + notification_message(instance->notification, &sequence_error); + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header( + message, "Failed Test:", 64, 30, AlignCenter, AlignBottom); + dialog_message_set_text( + message, file_basename_cstr, 64, 34, AlignCenter, AlignTop); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + + notification_message_block( + instance->notification, &sequence_set_only_blue_255); + } } } } while(false); @@ -192,7 +214,7 @@ void test_runner_run(TestRunner* instance) { test_runner_run_internal(instance); if(instance->minunit_run != 0) { - printf("\r\nFailed tests: %d\r\n", instance->minunit_fail); + printf("\r\nFailed tests: %d\r\n", instance->total_failed); // Time report cycle_counter = (furi_get_tick() - cycle_counter); @@ -204,13 +226,39 @@ void test_runner_run(TestRunner* instance) { printf("Leaked: %ld\r\n", heap_before - heap_after); // Final Report - if(instance->minunit_fail == 0) { + if(instance->total_failed == 0) { notification_message(instance->notification, &sequence_success); printf("Status: PASSED\r\n"); } else { notification_message(instance->notification, &sequence_error); printf("Status: FAILED\r\n"); } + + if(!instance->cli) { + char text[70]; + snprintf( + text, + sizeof(text), + "Failed tests: %d\n" + "Consumed: %lu ms\n" + "Leaked: %ld", + instance->total_failed, + cycle_counter, + heap_before - heap_after); + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header( + message, + instance->total_failed == 0 ? "PASSED" : "FAILED", + 64, + 8, + AlignCenter, + AlignTop); + dialog_message_set_text(message, text, 64, 38, AlignCenter, AlignCenter); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } } } } diff --git a/applications/debug/unit_tests/unit_tests.c b/applications/debug/unit_tests/unit_tests.c index 237cb9080..2b3468cbc 100644 --- a/applications/debug/unit_tests/unit_tests.c +++ b/applications/debug/unit_tests/unit_tests.c @@ -1,4 +1,5 @@ #include +#include #include #include "test_runner.h" @@ -12,10 +13,33 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) { test_runner_free(test_runner); } +static void unit_tests_pending(void* context, uint32_t arg) { + UNUSED(arg); + FuriThread* thread = context; + furi_thread_join(thread); + furi_thread_free(thread); +} + +static int32_t unit_tests_thread(void* context) { + furi_delay_ms(5000); + FuriString* args = furi_string_alloc(); + TestRunner* test_runner = test_runner_alloc(NULL, args); + test_runner_run(test_runner); + test_runner_free(test_runner); + furi_string_free(args); + furi_timer_pending_callback(unit_tests_pending, context, 0); + return 0; +} + void unit_tests_on_system_start(void) { #ifdef SRV_CLI Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); furi_record_close(RECORD_CLI); #endif + if(furi_hal_is_normal_boot()) { + FuriThread* thread = furi_thread_alloc_ex("UnitTests", 4 * 1024, unit_tests_thread, NULL); + furi_thread_set_context(thread, thread); + furi_thread_start(thread); + } } diff --git a/fbt_options.py b/fbt_options.py index dd6fb2790..3da9c78bc 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -98,6 +98,17 @@ FIRMWARE_APPS = { "settings_apps", ], "unit_tests": [ + # Svc + "basic_services", + # Apps + "main_apps", + "system_apps", + # Settings + "settings_apps", + # Tests + "unit_tests", + ], + "unit_tests_min": [ "basic_services", "updater_app", "radio_device_cc1101_ext", diff --git a/firmware.scons b/firmware.scons index a2e72b087..c38d8b23c 100644 --- a/firmware.scons +++ b/firmware.scons @@ -95,6 +95,9 @@ else: ) env.AppendUnique(CPPDEFINES=["FW_CFG_${FIRMWARE_APP_SET}"]) +if env.subst("$FIRMWARE_APP_SET").startswith("unit_tests"): + env.Replace(SKIP_EXTERNAL=True) + env.ConfigureForTarget(env.subst("${TARGET_HW}")) Export("env")