From 1db29eaea85efed70bf8a249bdd9c883e8d44b31 Mon Sep 17 00:00:00 2001 From: Albert Kharisov Date: Wed, 13 Oct 2021 19:39:37 +0400 Subject: [PATCH] [FL-1946] RPC App launch (#758) * RPC: Add App start, lock status - Add RPC commands Application start, lock status acquiring. - Write tests for RPC App system. - Replace Unit Tests application with CLI command. This is for CI needs and for tests that run application. * Fix NDEBUG build * Update PB submodule * Fix RPC print (ENABLE DEBUG PRINT!) * snprintf -> string_t * Fix Hard Fault (early mutex free) * printf -> string_t, format, enable tests * Update submodule: protobuf * Applications: rollback unit test naming scheme. Co-authored-by: Aleksandr Kutuzov --- applications/applications.c | 15 +- applications/applications.mk | 3 +- applications/loader/loader.c | 19 ++- applications/loader/loader.h | 12 +- applications/rpc/rpc.c | 140 +++++++++++------- applications/rpc/rpc_app.c | 78 ++++++++++ applications/rpc/rpc_i.h | 2 + applications/rpc/rpc_storage.c | 1 + .../subghz_frequency_analyzer_worker.c | 2 +- applications/tests/rpc/rpc_test.c | 138 ++++++++++++++++- applications/tests/test_index.c | 52 ++++--- assets/compiled/application.pb.c | 18 +++ assets/compiled/application.pb.h | 79 ++++++++++ assets/compiled/flipper.pb.h | 27 +++- assets/protobuf | 2 +- .../protocols/subghz_protocol_came_twee.c | 2 +- 16 files changed, 494 insertions(+), 96 deletions(-) create mode 100644 applications/rpc/rpc_app.c create mode 100644 assets/compiled/application.pb.c create mode 100644 assets/compiled/application.pb.h diff --git a/applications/applications.c b/applications/applications.c index 1e1469c4..761dd8fc 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -20,7 +20,7 @@ extern int32_t desktop_srv(void* p); extern int32_t accessor_app(void* p); extern int32_t archive_app(void* p); extern int32_t blink_test_app(void* p); -extern int32_t flipper_test_app(void* p); +extern int32_t delay_test_app(void* p); extern int32_t gpio_app(void* p); extern int32_t ibutton_app(void* p); extern int32_t irda_app(void* p); @@ -49,6 +49,7 @@ extern void nfc_cli_init(); extern void storage_cli_init(); extern void subghz_cli_init(); extern void power_cli_init(); +extern void unit_tests_cli_init(); // Settings extern int32_t notification_settings_app(void* p); @@ -183,6 +184,10 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { #ifdef SRV_STORAGE storage_cli_init, #endif + +#ifdef APP_UNIT_TESTS + unit_tests_cli_init, +#endif }; const size_t FLIPPER_ON_SYSTEM_START_COUNT = @@ -220,10 +225,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { {.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14}, #endif -#ifdef APP_UNIT_TESTS - {.app = flipper_test_app, .name = "Unit Tests", .stack_size = 1024 * 8, .icon = &A_Plugins_14}, -#endif - #ifdef APP_IRDA_MONITOR {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14}, #endif @@ -239,6 +240,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { #ifdef SRV_BT {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL}, #endif + +#ifdef APP_UNIT_TESTS + {.app = delay_test_app, .name = "Delay Test App", .stack_size = 1024, .icon = &A_Plugins_14}, +#endif }; const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); diff --git a/applications/applications.mk b/applications/applications.mk index 5fb8a511..4e9f6d56 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -42,7 +42,6 @@ APP_BLINK = 1 APP_IRDA_MONITOR = 1 APP_KEYPAD_TEST = 1 APP_SD_TEST = 1 -APP_UNIT_TESTS = 0 APP_VIBRO_DEMO = 1 APP_USB_TEST = 1 endif @@ -60,7 +59,7 @@ SRV_GUI = 1 endif -APP_UNIT_TESTS ?= 0 +APP_UNIT_TESTS ?= 0 ifeq ($(APP_UNIT_TESTS), 1) CFLAGS += -DAPP_UNIT_TESTS endif diff --git a/applications/loader/loader.c b/applications/loader/loader.c index d121d139..d52eaa18 100755 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -1,3 +1,4 @@ +#include "loader/loader.h" #include "loader_i.h" #define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) @@ -56,7 +57,7 @@ static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) { furi_thread_start(loader_instance->thread); } -bool loader_start(Loader* instance, const char* name, const char* args) { +LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { furi_assert(name); const FlipperApplication* flipper_app = NULL; @@ -79,14 +80,15 @@ bool loader_start(Loader* instance, const char* name, const char* args) { if(!flipper_app) { FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name); - return false; + return LoaderStatusErrorUnknownApp; } - loader_lock(instance); + bool locked = loader_lock(instance); - if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) { + if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) { FURI_LOG_E(LOADER_LOG_TAG, "Can't start app. %s is running", instance->current_app->name); - return false; + /* no need to call loader_unlock() - it is called as soon as application stops */ + return LoaderStatusErrorAppStarted; } instance->current_app = flipper_app; @@ -106,7 +108,8 @@ bool loader_start(Loader* instance, const char* name, const char* args) { furi_thread_set_context(instance->thread, thread_args); furi_thread_set_callback(instance->thread, flipper_app->app); - return furi_thread_start(instance->thread); + bool thread_started = furi_thread_start(instance->thread); + return thread_started ? LoaderStatusOk : LoaderStatusErrorInternal; } bool loader_lock(Loader* instance) { @@ -127,6 +130,10 @@ void loader_unlock(Loader* instance) { furi_check(osMutexRelease(instance->mutex) == osOK); } +bool loader_is_locked(Loader* instance) { + return (instance->lock_semaphore > 0); +} + static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { furi_assert(context); diff --git a/applications/loader/loader.h b/applications/loader/loader.h index 568cfaa3..6c231b7a 100644 --- a/applications/loader/loader.h +++ b/applications/loader/loader.h @@ -4,12 +4,19 @@ typedef struct Loader Loader; +typedef enum { + LoaderStatusOk, + LoaderStatusErrorAppStarted, + LoaderStatusErrorUnknownApp, + LoaderStatusErrorInternal, +} LoaderStatus; + /** Start application * @param name - application name * @param args - application arguments * @retval true on success */ -bool loader_start(Loader* instance, const char* name, const char* args); +LoaderStatus loader_start(Loader* instance, const char* name, const char* args); /** Lock application start * @retval true on success @@ -19,5 +26,8 @@ bool loader_lock(Loader* instance); /** Unlock application start */ void loader_unlock(Loader* instance); +/** Get loader lock status */ +bool loader_is_locked(Loader* instance); + /** Show primary loader */ void loader_show_menu(); diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index 493e4f65..adb23e78 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -4,6 +4,7 @@ #include "furi-hal-delay.h" #include "furi/check.h" #include "furi/log.h" +#include #include "pb.h" #include "pb_decode.h" #include "pb_encode.h" @@ -42,6 +43,10 @@ static RpcSystemCallbacks rpc_systems[] = { .alloc = rpc_system_storage_alloc, .free = rpc_system_storage_free, }, + { + .alloc = rpc_system_app_alloc, + .free = NULL, + }, }; struct RpcSession { @@ -65,31 +70,28 @@ struct Rpc { static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); -static size_t rpc_sprint_msg_file( - char* str, - size_t str_size, +static size_t rpc_sprintf_msg_file( + string_t str, const char* prefix, const PB_Storage_File* msg_file, size_t msg_files_size) { size_t cnt = 0; for(int i = 0; i < msg_files_size; ++i, ++msg_file) { - cnt += snprintf( - str + cnt, - str_size - cnt, + string_cat_printf( + str, "%s[%c] size: %5ld", prefix, msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', msg_file->size); if(msg_file->name) { - cnt += snprintf(str + cnt, str_size - cnt, " \'%s\'", msg_file->name); + string_cat_printf(str, " \'%s\'", msg_file->name); } if(msg_file->data && msg_file->data->size) { - cnt += snprintf( - str + cnt, - str_size - cnt, + string_cat_printf( + str, " (%d):\'%.*s%s\'", msg_file->data->size, MIN(msg_file->data->size, 30), @@ -97,23 +99,18 @@ static size_t rpc_sprint_msg_file( msg_file->data->size > 30 ? "..." : ""); } - cnt += snprintf(str + cnt, str_size - cnt, "\r\n"); + string_cat_printf(str, "\r\n"); } return cnt; } -#define ADD_STR(s, c, ...) snprintf(s + c, sizeof(s) - c, ##__VA_ARGS__); - -#define ADD_STR_ELEMENT(s, c, ...) rpc_sprint_msg_file(s + c, sizeof(s) - c, ##__VA_ARGS__); - void rpc_print_message(const PB_Main* message) { - char str[500]; - size_t cnt = 0; + string_t str; + string_init(str); - cnt += snprintf( - str + cnt, - sizeof(str) - cnt, + string_cat_printf( + str, "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", message->command_status, message->command_id, @@ -121,88 +118,112 @@ void rpc_print_message(const PB_Main* message) { switch(message->which_content) { default: /* not implemented yet */ - cnt += ADD_STR(str, cnt, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); + string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); break; + case PB_Main_app_start_tag: { + string_cat_printf(str, "\tapp_start {\r\n"); + const char* name = message->content.app_start.name; + const char* args = message->content.app_start.args; + if(name) { + string_cat_printf(str, "\t\tname: %s\r\n", name); + } + if(args) { + string_cat_printf(str, "\t\targs: %s\r\n", args); + } + break; + } + case PB_Main_app_lock_status_request_tag: { + string_cat_printf(str, "\tapp_lock_status_request {\r\n"); + break; + } + case PB_Main_app_lock_status_response_tag: { + string_cat_printf(str, "\tapp_lock_status_response {\r\n"); + bool lock_status = message->content.app_lock_status_response.locked; + string_cat_printf(str, "\t\tlocked: %s\r\n", lock_status ? "true" : "false"); + break; + } case PB_Main_storage_md5sum_request_tag: { - cnt += ADD_STR(str, cnt, "\tmd5sum_request {\r\n"); + string_cat_printf(str, "\tmd5sum_request {\r\n"); const char* path = message->content.storage_md5sum_request.path; if(path) { - cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); + string_cat_printf(str, "\t\tpath: %s\r\n", path); } break; } case PB_Main_storage_md5sum_response_tag: { - cnt += ADD_STR(str, cnt, "\tmd5sum_response {\r\n"); + string_cat_printf(str, "\tmd5sum_response {\r\n"); const char* path = message->content.storage_md5sum_response.md5sum; if(path) { - cnt += ADD_STR(str, cnt, "\t\tmd5sum: %s\r\n", path); + string_cat_printf(str, "\t\tmd5sum: %s\r\n", path); } break; } case PB_Main_ping_request_tag: - cnt += ADD_STR(str, cnt, "\tping_request {\r\n"); + string_cat_printf(str, "\tping_request {\r\n"); break; case PB_Main_ping_response_tag: - cnt += ADD_STR(str, cnt, "\tping_response {\r\n"); + string_cat_printf(str, "\tping_response {\r\n"); break; case PB_Main_storage_mkdir_request_tag: - cnt += ADD_STR(str, cnt, "\tmkdir {\r\n"); + string_cat_printf(str, "\tmkdir {\r\n"); break; case PB_Main_storage_delete_request_tag: { - cnt += ADD_STR(str, cnt, "\tdelete {\r\n"); + string_cat_printf(str, "\tdelete {\r\n"); const char* path = message->content.storage_delete_request.path; if(path) { - cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); + string_cat_printf(str, "\t\tpath: %s\r\n", path); } break; } case PB_Main_empty_tag: - cnt += ADD_STR(str, cnt, "\tempty {\r\n"); + string_cat_printf(str, "\tempty {\r\n"); break; case PB_Main_storage_list_request_tag: { - cnt += ADD_STR(str, cnt, "\tlist_request {\r\n"); + string_cat_printf(str, "\tlist_request {\r\n"); const char* path = message->content.storage_list_request.path; if(path) { - cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); + string_cat_printf(str, "\t\tpath: %s\r\n", path); } break; } case PB_Main_storage_read_request_tag: { - cnt += ADD_STR(str, cnt, "\tread_request {\r\n"); + string_cat_printf(str, "\tread_request {\r\n"); const char* path = message->content.storage_read_request.path; if(path) { - cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); + string_cat_printf(str, "\t\tpath: %s\r\n", path); } break; } case PB_Main_storage_write_request_tag: { - cnt += ADD_STR(str, cnt, "\twrite_request {\r\n"); + string_cat_printf(str, "\twrite_request {\r\n"); const char* path = message->content.storage_write_request.path; if(path) { - cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); + string_cat_printf(str, "\t\tpath: %s\r\n", path); } if(message->content.storage_write_request.has_file) { const PB_Storage_File* msg_file = &message->content.storage_write_request.file; - cnt += ADD_STR_ELEMENT(str, cnt, "\t\t\t", msg_file, 1); + rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); } break; } case PB_Main_storage_read_response_tag: - cnt += ADD_STR(str, cnt, "\tread_response {\r\n"); + string_cat_printf(str, "\tread_response {\r\n"); if(message->content.storage_read_response.has_file) { const PB_Storage_File* msg_file = &message->content.storage_read_response.file; - cnt += ADD_STR_ELEMENT(str, cnt, "\t\t\t", msg_file, 1); + rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); } break; case PB_Main_storage_list_response_tag: { const PB_Storage_File* msg_file = message->content.storage_list_response.file; size_t msg_file_count = message->content.storage_list_response.file_count; - cnt += ADD_STR(str, cnt, "\tlist_response {\r\n"); - cnt += ADD_STR_ELEMENT(str, cnt, "\t\t", msg_file, msg_file_count); + string_cat_printf(str, "\tlist_response {\r\n"); + rpc_sprintf_msg_file(str, "\t\t", msg_file, msg_file_count); } } - cnt += ADD_STR(str, cnt, "\t}\r\n}\r\n"); - printf("%s", str); + string_cat_printf(str, "\t}\r\n}\r\n"); + printf("%s", string_get_cstr(str)); + + string_clear(str); } static Rpc* rpc_alloc(void) { @@ -253,7 +274,6 @@ void rpc_close_session(RpcSession* session) { furi_assert(session->rpc); furi_assert(session->rpc->busy); - osMutexDelete(session->send_bytes_mutex); rpc_set_send_bytes_callback(session, NULL, NULL); osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT); } @@ -328,22 +348,31 @@ void rpc_encode_and_send(Rpc* rpc, PB_Main* main_message) { pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED); { - osMutexAcquire(session->send_bytes_mutex, osWaitForever); - #if DEBUG_PRINT - printf("\r\nREPONSE DEC(%d): {", ostream.bytes_written); - for(int i = 0; i < ostream.bytes_written; ++i) { - printf("%d, ", buffer[i]); - } - printf("}\r\n"); + string_t str; + string_init(str); + string_reserve(str, 100 + ostream.bytes_written * 5); - printf("REPONSE HEX(%d): {", ostream.bytes_written); + string_cat_printf(str, "\r\nREPONSE DEC(%d): {", ostream.bytes_written); for(int i = 0; i < ostream.bytes_written; ++i) { - printf("%02X", buffer[i]); + string_cat_printf(str, "%d, ", buffer[i]); } - printf("}\r\n\r\n"); + string_cat_printf(str, "}\r\n"); + + printf("%s", string_get_cstr(str)); + string_clean(str); + string_reserve(str, 100 + ostream.bytes_written * 3); + + string_cat_printf(str, "REPONSE HEX(%d): {", ostream.bytes_written); + for(int i = 0; i < ostream.bytes_written; ++i) { + string_cat_printf(str, "%02X", buffer[i]); + } + string_cat_printf(str, "}\r\n\r\n"); + + printf("%s", string_get_cstr(str)); #endif // DEBUG_PRINT + osMutexAcquire(session->send_bytes_mutex, osWaitForever); if(session->send_bytes_callback) { session->send_bytes_callback( session->send_bytes_context, buffer, ostream.bytes_written); @@ -408,6 +437,7 @@ int32_t rpc_srv(void* p) { } } free(session->system_contexts); + osMutexDelete(session->send_bytes_mutex); RpcHandlerDict_clean(rpc->handlers); rpc->busy = false; } else { diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c new file mode 100644 index 00000000..db4e5567 --- /dev/null +++ b/applications/rpc/rpc_app.c @@ -0,0 +1,78 @@ +#include "flipper.pb.h" +#include "furi/record.h" +#include "status.pb.h" +#include "rpc_i.h" +#include +#include + +void rpc_system_app_start_process(const PB_Main* request, void* context) { + Rpc* rpc = context; + furi_assert(rpc); + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_start_tag); + PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; + + Loader* loader = furi_record_open("loader"); + const char* app_name = request->content.app_start.name; + if(app_name) { + const char* app_args = request->content.app_start.args; + LoaderStatus status = loader_start(loader, app_name, app_args); + if(status == LoaderStatusErrorAppStarted) { + result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; + } else if(status == LoaderStatusErrorInternal) { + result = PB_CommandStatus_ERROR_APP_CANT_START; + } else if(status == LoaderStatusErrorUnknownApp) { + result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; + } else if(status == LoaderStatusOk) { + result = PB_CommandStatus_OK; + } else { + furi_assert(0); + } + } else { + result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; + } + + furi_record_close("loader"); + + rpc_encode_and_send_empty(rpc, request->command_id, result); +} + +void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { + Rpc* rpc = context; + furi_assert(rpc); + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); + + Loader* loader = furi_record_open("loader"); + + PB_Main response = { + .has_next = false, + .command_status = PB_CommandStatus_OK, + .command_id = request->command_id, + .which_content = PB_Main_app_lock_status_response_tag, + }; + + response.content.app_lock_status_response.locked = loader_is_locked(loader); + + furi_record_close("loader"); + + rpc_encode_and_send(rpc, &response); +} + +void* rpc_system_app_alloc(Rpc* rpc) { + furi_assert(rpc); + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = rpc, + }; + + rpc_handler.message_handler = rpc_system_app_start_process; + rpc_add_handler(rpc, PB_Main_app_start_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_lock_status_process; + rpc_add_handler(rpc, PB_Main_app_lock_status_request_tag, &rpc_handler); + + return NULL; +} diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index e84021a5..5bcb9d9b 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -22,4 +22,6 @@ void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler); void* rpc_system_status_alloc(Rpc* rpc); void* rpc_system_storage_alloc(Rpc* rpc); void rpc_system_storage_free(void* ctx); +void* rpc_system_app_alloc(Rpc* rpc); + void rpc_print_message(const PB_Main* message); diff --git a/applications/rpc/rpc_storage.c b/applications/rpc/rpc_storage.c index 22bb2e1c..96842cba 100644 --- a/applications/rpc/rpc_storage.c +++ b/applications/rpc/rpc_storage.c @@ -340,6 +340,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); + (void)md5sum_size; furi_assert(hash_size <= ((md5sum_size - 1) / 2)); for(uint8_t i = 0; i < hash_size; i++) { md5sum += sprintf(md5sum, "%02x", hash[i]); diff --git a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/subghz/helpers/subghz_frequency_analyzer_worker.c index a720006d..83f09e04 100644 --- a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -41,7 +41,7 @@ static uint32_t subghz_frequency_analyzer_worker_expRunningAverageAdaptive( static int32_t subghz_frequency_analyzer_worker_thread(void* context) { SubGhzFrequencyAnalyzerWorker* instance = context; - FrequencyRSSI frequency_rssi; + FrequencyRSSI frequency_rssi = {.frequency = 0, .rssi = 0}; float rssi; uint32_t frequency; uint32_t frequency_start; diff --git a/applications/tests/rpc/rpc_test.c b/applications/tests/rpc/rpc_test.c index 84fef1de..1ac2b490 100644 --- a/applications/tests/rpc/rpc_test.c +++ b/applications/tests/rpc/rpc_test.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include LIST_DEF(MsgList, PB_Main, M_POD_OPLIST) #define M_OPL_MsgList_t() LIST_OPLIST(MsgList) @@ -108,6 +110,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { free(name); } else { FS_Error error = storage_common_mkdir(fs_api, clean_dir); + (void)error; furi_assert(error == FSE_OK); } @@ -172,6 +175,7 @@ static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size StreamBufferHandle_t stream_buffer = ctx; size_t bytes_sent = xStreamBufferSend(stream_buffer, got_bytes, got_size, osWaitForever); + (void)bytes_sent; furi_assert(bytes_sent == got_size); } @@ -346,6 +350,12 @@ static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { /* rpc doesn't send it */ mu_check(0); break; + case PB_Main_app_lock_status_response_tag: { + bool result_locked = result->content.app_lock_status_response.locked; + bool expected_locked = expected->content.app_lock_status_response.locked; + mu_check(result_locked == expected_locked); + break; + } case PB_Main_storage_read_response_tag: { bool result_has_msg_file = result->content.storage_read_response.has_file; bool expected_has_msg_file = expected->content.storage_read_response.has_file; @@ -588,6 +598,7 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { static void test_create_dir(const char* path) { Storage* fs_api = furi_record_open("storage"); FS_Error error = storage_common_mkdir(fs_api, path); + (void)error; furi_assert((error == FSE_OK) || (error == FSE_EXIST)); furi_record_close("storage"); } @@ -717,7 +728,7 @@ MU_TEST(test_storage_write) { ++command_id, PB_CommandStatus_ERROR_STORAGE_NOT_EXIST); test_storage_write_run(TEST_DIR "test2.txt", 1, 50, ++command_id, PB_CommandStatus_OK); - test_storage_write_run(TEST_DIR "test2.txt", 4096, 1, ++command_id, PB_CommandStatus_OK); + test_storage_write_run(TEST_DIR "test2.txt", 512, 3, ++command_id, PB_CommandStatus_OK); } MU_TEST(test_storage_interrupt_continuous_same_system) { @@ -866,6 +877,7 @@ MU_TEST(test_storage_mkdir) { Storage* fs_api = furi_record_open("storage"); FS_Error error = storage_common_remove(fs_api, TEST_DIR "dir1"); + (void)error; furi_assert(error == FSE_OK); furi_record_close("storage"); @@ -1018,10 +1030,134 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_interrupt_continuous_another_system); } +static void test_app_create_request( + PB_Main* request, + const char* app_name, + const char* app_args, + uint32_t command_id) { + request->command_id = command_id; + request->command_status = PB_CommandStatus_OK; + request->cb_content.funcs.encode = NULL; + request->which_content = PB_Main_app_start_tag; + request->has_next = false; + + if(app_name) { + char* msg_app_name = furi_alloc(strlen(app_name) + 1); + strcpy(msg_app_name, app_name); + request->content.app_start.name = msg_app_name; + } else { + request->content.app_start.name = NULL; + } + + if(app_args) { + char* msg_app_args = furi_alloc(strlen(app_args) + 1); + strcpy(msg_app_args, app_args); + request->content.app_start.args = msg_app_args; + } else { + request->content.app_start.args = NULL; + } +} + +static void test_app_start_run( + const char* app_name, + const char* app_args, + PB_CommandStatus status, + uint32_t command_id) { + PB_Main request; + MsgList_t expected_msg_list; + MsgList_init(expected_msg_list); + + test_app_create_request(&request, app_name, app_args, command_id); + test_rpc_add_empty_to_list(expected_msg_list, status, command_id); + + test_rpc_encode_and_feed_one(&request); + test_rpc_decode_and_compare(expected_msg_list); + + pb_release(&PB_Main_msg, &request); + test_rpc_free_msg_list(expected_msg_list); +} + +static void test_app_get_status_lock_run(bool locked_expected, uint32_t command_id) { + PB_Main request = { + .command_id = command_id, + .command_status = PB_CommandStatus_OK, + .which_content = PB_Main_app_lock_status_request_tag, + .has_next = false, + }; + + MsgList_t expected_msg_list; + MsgList_init(expected_msg_list); + PB_Main* response = MsgList_push_new(expected_msg_list); + response->command_id = command_id; + response->command_status = PB_CommandStatus_OK; + response->which_content = PB_Main_app_lock_status_response_tag; + response->has_next = false; + response->content.app_lock_status_response.locked = locked_expected; + + test_rpc_encode_and_feed_one(&request); + test_rpc_decode_and_compare(expected_msg_list); + + pb_release(&PB_Main_msg, &request); + test_rpc_free_msg_list(expected_msg_list); +} + +MU_TEST(test_app_start_and_lock_status) { + test_app_get_status_lock_run(false, ++command_id); + test_app_start_run(NULL, "/ext/file", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); + test_app_start_run(NULL, NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); + test_app_get_status_lock_run(false, ++command_id); + test_app_start_run( + "skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); + test_app_get_status_lock_run(false, ++command_id); + + test_app_start_run("Delay Test App", "0", PB_CommandStatus_OK, ++command_id); + delay(100); + test_app_get_status_lock_run(false, ++command_id); + + test_app_start_run("Delay Test App", "200", PB_CommandStatus_OK, ++command_id); + test_app_get_status_lock_run(true, ++command_id); + delay(100); + test_app_get_status_lock_run(true, ++command_id); + test_app_start_run( + "Delay Test App", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); + delay(200); + test_app_get_status_lock_run(false, ++command_id); + + test_app_start_run("Delay Test App", "500", PB_CommandStatus_OK, ++command_id); + delay(100); + test_app_get_status_lock_run(true, ++command_id); + test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); + delay(100); + test_app_get_status_lock_run(true, ++command_id); + test_app_start_run( + "2_girls_1_app", "0", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); + delay(100); + test_app_get_status_lock_run(true, ++command_id); + delay(500); + test_app_get_status_lock_run(false, ++command_id); +} + +MU_TEST_SUITE(test_rpc_app) { + MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown); + + MU_RUN_TEST(test_app_start_and_lock_status); +} + int run_minunit_test_rpc() { MU_RUN_SUITE(test_rpc_storage); MU_RUN_SUITE(test_rpc_status); + MU_RUN_SUITE(test_rpc_app); MU_REPORT(); return MU_EXIT_CODE; } + +int32_t delay_test_app(void* p) { + int timeout = atoi((const char*)p); + + if(timeout > 0) { + delay(timeout); + } + + return 0; +} diff --git a/applications/tests/test_index.c b/applications/tests/test_index.c index 7bb775f3..7158b304 100644 --- a/applications/tests/test_index.c +++ b/applications/tests/test_index.c @@ -1,33 +1,51 @@ +#include "m-string.h" #include #include #include #include "minunit_vars.h" #include +#include +#include int run_minunit(); int run_minunit_test_irda_decoder_encoder(); int run_minunit_test_rpc(); -int32_t flipper_test_app(void* p) { +void unit_tests_cli(Cli* cli, string_t args, void* context) { uint32_t test_result = 0; + minunit_run = 0; + minunit_assert = 0; + minunit_fail = 0; + minunit_status = 0; + + Loader* loader = furi_record_open("loader"); + furi_record_close("loader"); NotificationApp* notification = furi_record_open("notification"); - - notification_message_block(notification, &sequence_set_only_blue_255); - - // test_result |= run_minunit(); // disabled as it fails randomly - test_result |= run_minunit_test_irda_decoder_encoder(); - test_result |= run_minunit_test_rpc(); - - if(test_result == 0) { - // test passed - notification_message(notification, &sequence_success); - } else { - // test failed - notification_message(notification, &sequence_error); - } - furi_record_close("notification"); - return 0; + if(loader_is_locked(loader)) { + FURI_LOG_E("UNIT_TESTS", "RPC: stop all applications to run tests"); + notification_message(notification, &sequence_blink_magenta_100); + } else { + notification_message_block(notification, &sequence_set_only_blue_255); + + test_result |= run_minunit(); + test_result |= run_minunit_test_irda_decoder_encoder(); + test_result |= run_minunit_test_rpc(); + + if(test_result == 0) { + notification_message(notification, &sequence_success); + FURI_LOG_I("UNIT_TESTS", "PASSED"); + } else { + notification_message(notification, &sequence_error); + FURI_LOG_E("UNIT_TESTS", "FAILED"); + } + } +} + +void unit_tests_cli_init() { + Cli* cli = furi_record_open("cli"); + cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); + furi_record_close("cli"); } diff --git a/assets/compiled/application.pb.c b/assets/compiled/application.pb.c new file mode 100644 index 00000000..097a57b0 --- /dev/null +++ b/assets/compiled/application.pb.c @@ -0,0 +1,18 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.5 */ + +#include "application.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(PB_App_Start, PB_App_Start, AUTO) + + +PB_BIND(PB_App_LockStatusRequest, PB_App_LockStatusRequest, AUTO) + + +PB_BIND(PB_App_LockStatusResponse, PB_App_LockStatusResponse, AUTO) + + + diff --git a/assets/compiled/application.pb.h b/assets/compiled/application.pb.h new file mode 100644 index 00000000..b7a053f3 --- /dev/null +++ b/assets/compiled/application.pb.h @@ -0,0 +1,79 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.5 */ + +#ifndef PB_PB_APP_APPLICATION_PB_H_INCLUDED +#define PB_PB_APP_APPLICATION_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _PB_App_LockStatusRequest { + char dummy_field; +} PB_App_LockStatusRequest; + +typedef struct _PB_App_Start { + char *name; + char *args; +} PB_App_Start; + +typedef struct _PB_App_LockStatusResponse { + bool locked; +} PB_App_LockStatusResponse; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define PB_App_Start_init_default {NULL, NULL} +#define PB_App_LockStatusRequest_init_default {0} +#define PB_App_LockStatusResponse_init_default {0} +#define PB_App_Start_init_zero {NULL, NULL} +#define PB_App_LockStatusRequest_init_zero {0} +#define PB_App_LockStatusResponse_init_zero {0} + +/* Field tags (for use in manual encoding/decoding) */ +#define PB_App_Start_name_tag 1 +#define PB_App_Start_args_tag 2 +#define PB_App_LockStatusResponse_locked_tag 1 + +/* Struct field encoding specification for nanopb */ +#define PB_App_Start_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, name, 1) \ +X(a, POINTER, SINGULAR, STRING, args, 2) +#define PB_App_Start_CALLBACK NULL +#define PB_App_Start_DEFAULT NULL + +#define PB_App_LockStatusRequest_FIELDLIST(X, a) \ + +#define PB_App_LockStatusRequest_CALLBACK NULL +#define PB_App_LockStatusRequest_DEFAULT NULL + +#define PB_App_LockStatusResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, locked, 1) +#define PB_App_LockStatusResponse_CALLBACK NULL +#define PB_App_LockStatusResponse_DEFAULT NULL + +extern const pb_msgdesc_t PB_App_Start_msg; +extern const pb_msgdesc_t PB_App_LockStatusRequest_msg; +extern const pb_msgdesc_t PB_App_LockStatusResponse_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define PB_App_Start_fields &PB_App_Start_msg +#define PB_App_LockStatusRequest_fields &PB_App_LockStatusRequest_msg +#define PB_App_LockStatusResponse_fields &PB_App_LockStatusResponse_msg + +/* Maximum encoded size of messages (where known) */ +/* PB_App_Start_size depends on runtime parameters */ +#define PB_App_LockStatusRequest_size 0 +#define PB_App_LockStatusResponse_size 2 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/assets/compiled/flipper.pb.h b/assets/compiled/flipper.pb.h index f78393e6..4af72479 100644 --- a/assets/compiled/flipper.pb.h +++ b/assets/compiled/flipper.pb.h @@ -6,6 +6,7 @@ #include #include "storage.pb.h" #include "status.pb.h" +#include "application.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -28,7 +29,9 @@ typedef enum _PB_CommandStatus { PB_CommandStatus_ERROR_STORAGE_INVALID_NAME = 10, /* *< Invalid name/path */ PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */ PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */ - PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13 /* *< File/Dir already opened */ + PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13, /* *< File/Dir already opened */ + PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - either wrong name, or internal error */ + PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *< Another app is running */ } PB_CommandStatus; /* Struct definitions */ @@ -58,14 +61,17 @@ typedef struct _PB_Main { PB_Storage_MkdirRequest storage_mkdir_request; PB_Storage_Md5sumRequest storage_md5sum_request; PB_Storage_Md5sumResponse storage_md5sum_response; + PB_App_Start app_start; + PB_App_LockStatusRequest app_lock_status_request; + PB_App_LockStatusResponse app_lock_status_response; } content; } PB_Main; /* Helper constants for enums */ #define _PB_CommandStatus_MIN PB_CommandStatus_OK -#define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_INVALID_PARAMETERS -#define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_INVALID_PARAMETERS+1)) +#define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED +#define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED+1)) #ifdef __cplusplus @@ -94,6 +100,9 @@ extern "C" { #define PB_Main_storage_mkdir_request_tag 13 #define PB_Main_storage_md5sum_request_tag 14 #define PB_Main_storage_md5sum_response_tag 15 +#define PB_Main_app_start_tag 16 +#define PB_Main_app_lock_status_request_tag 17 +#define PB_Main_app_lock_status_response_tag 18 /* Struct field encoding specification for nanopb */ #define PB_Empty_FIELDLIST(X, a) \ @@ -116,7 +125,10 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_write_request,content.storag X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_delete_request,content.storage_delete_request), 12) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_mkdir_request,content.storage_mkdir_request), 13) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_request,content.storage_md5sum_request), 14) \ -X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response), 15) +X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response), 15) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,app_start,content.app_start), 16) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_request,content.app_lock_status_request), 17) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app_lock_status_response), 18) #define PB_Main_CALLBACK NULL #define PB_Main_DEFAULT NULL #define PB_Main_content_empty_MSGTYPE PB_Empty @@ -131,6 +143,9 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_response,content.stor #define PB_Main_content_storage_mkdir_request_MSGTYPE PB_Storage_MkdirRequest #define PB_Main_content_storage_md5sum_request_MSGTYPE PB_Storage_Md5sumRequest #define PB_Main_content_storage_md5sum_response_MSGTYPE PB_Storage_Md5sumResponse +#define PB_Main_content_app_start_MSGTYPE PB_App_Start +#define PB_Main_content_app_lock_status_request_MSGTYPE PB_App_LockStatusRequest +#define PB_Main_content_app_lock_status_response_MSGTYPE PB_App_LockStatusResponse extern const pb_msgdesc_t PB_Empty_msg; extern const pb_msgdesc_t PB_Main_msg; @@ -141,9 +156,9 @@ extern const pb_msgdesc_t PB_Main_msg; /* Maximum encoded size of messages (where known) */ #define PB_Empty_size 0 -#if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) +#if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_Start_size) #define PB_Main_size (10 + sizeof(union PB_Main_content_size_union)) -union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f0[36];}; +union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_Start_size)]; char f0[36];}; #endif #ifdef __cplusplus diff --git a/assets/protobuf b/assets/protobuf index 41599b8e..8e6db414 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 41599b8e6a6b33a229e8f5fa58de1a2cfcc8184a +Subproject commit 8e6db414beed5aff0902f2cca2f4146a0dffb7a1 diff --git a/lib/subghz/protocols/subghz_protocol_came_twee.c b/lib/subghz/protocols/subghz_protocol_came_twee.c index 5dbfa81a..7a7274e8 100644 --- a/lib/subghz/protocols/subghz_protocol_came_twee.c +++ b/lib/subghz/protocols/subghz_protocol_came_twee.c @@ -56,7 +56,7 @@ void subghz_protocol_came_twee_free(SubGhzProtocolCameTwee* instance) { LevelDuration subghz_protocol_came_twee_add_duration_to_upload( SubGhzProtocolCameTwee* instance, ManchesterEncoderResult result) { - LevelDuration data; + LevelDuration data = {.duration = 0, .level = 0}; switch(result) { case ManchesterEncoderResultShortLow: data.duration = instance->common.te_short;