[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 <alleteam@gmail.com>
This commit is contained in:
Albert Kharisov 2021-10-13 19:39:37 +04:00 committed by GitHub
parent a643bd14be
commit 1db29eaea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 494 additions and 96 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -4,6 +4,7 @@
#include "furi-hal-delay.h"
#include "furi/check.h"
#include "furi/log.h"
#include <m-string.h>
#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 {

View File

@ -0,0 +1,78 @@
#include "flipper.pb.h"
#include "furi/record.h"
#include "status.pb.h"
#include "rpc_i.h"
#include <furi.h>
#include <loader/loader.h>
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;
}

View File

@ -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);

View File

@ -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]);

View File

@ -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;

View File

@ -14,6 +14,8 @@
#include <pb_encode.h>
#include <m-list.h>
#include <lib/toolbox/md5.h>
#include <cli/cli.h>
#include <loader/loader.h>
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;
}

View File

@ -1,33 +1,51 @@
#include "m-string.h"
#include <stdio.h>
#include <furi.h>
#include <furi-hal.h>
#include "minunit_vars.h"
#include <notification/notification-messages.h>
#include <cli/cli.h>
#include <loader/loader.h>
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");
}

View File

@ -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)

View File

@ -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 <pb.h>
#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

View File

@ -6,6 +6,7 @@
#include <pb.h>
#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

@ -1 +1 @@
Subproject commit 41599b8e6a6b33a229e8f5fa58de1a2cfcc8184a
Subproject commit 8e6db414beed5aff0902f2cca2f4146a0dffb7a1

View File

@ -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;