rpc_test.c 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650
  1. #include "flipper.pb.h"
  2. #include "furi_hal_delay.h"
  3. #include "furi/check.h"
  4. #include "furi/record.h"
  5. #include "pb_decode.h"
  6. #include <rpc/rpc.h>
  7. #include "rpc/rpc_i.h"
  8. #include "storage.pb.h"
  9. #include "storage/filesystem_api_defines.h"
  10. #include "storage/storage.h"
  11. #include <furi.h>
  12. #include "../minunit.h"
  13. #include <stdint.h>
  14. #include <stream_buffer.h>
  15. #include <pb.h>
  16. #include <pb_encode.h>
  17. #include <m-list.h>
  18. #include <lib/toolbox/md5.h>
  19. #include <cli/cli.h>
  20. #include <loader/loader.h>
  21. #include <protobuf_version.h>
  22. #include <semphr.h>
  23. LIST_DEF(MsgList, PB_Main, M_POD_OPLIST)
  24. #define M_OPL_MsgList_t() LIST_OPLIST(MsgList)
  25. /* MinUnit test framework doesn't allow passing context into tests,
  26. * so we have to use global variables
  27. */
  28. static Rpc* rpc = NULL;
  29. static RpcSession* session = NULL;
  30. static uint32_t command_id = 0;
  31. typedef struct {
  32. StreamBufferHandle_t output_stream;
  33. SemaphoreHandle_t close_session_semaphore;
  34. SemaphoreHandle_t terminate_semaphore;
  35. TickType_t timeout;
  36. } RpcSessionContext;
  37. static RpcSessionContext rpc_session_context;
  38. #define TAG "UnitTestsRpc"
  39. #define MAX_RECEIVE_OUTPUT_TIMEOUT 3000
  40. #define MAX_NAME_LENGTH 255
  41. #define MAX_DATA_SIZE 512 // have to be exact as in rpc_storage.c
  42. #define TEST_DIR TEST_DIR_NAME "/"
  43. #define TEST_DIR_NAME "/ext/unit_tests_tmp"
  44. #define MD5SUM_SIZE 16
  45. #define PING_REQUEST 0
  46. #define PING_RESPONSE 1
  47. #define WRITE_REQUEST 0
  48. #define READ_RESPONSE 1
  49. #define DEBUG_PRINT 0
  50. #define BYTES(x) (x), sizeof(x)
  51. #define DISABLE_TEST(code) \
  52. do { \
  53. volatile int a = 0; \
  54. if(a) { \
  55. code \
  56. } \
  57. } while(0)
  58. static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size);
  59. static void clean_directory(Storage* fs_api, const char* clean_dir);
  60. static void
  61. test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id);
  62. static void test_rpc_encode_and_feed(MsgList_t msg_list);
  63. static void test_rpc_encode_and_feed_one(PB_Main* request);
  64. static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected);
  65. static void test_rpc_decode_and_compare(MsgList_t expected_msg_list);
  66. static void test_rpc_free_msg_list(MsgList_t msg_list);
  67. static void test_rpc_session_close_callback(void* context);
  68. static void test_rpc_session_terminated_callback(void* context);
  69. static void test_rpc_setup(void) {
  70. furi_check(!rpc);
  71. furi_check(!session);
  72. rpc = furi_record_open("rpc");
  73. for(int i = 0; !session && (i < 10000); ++i) {
  74. session = rpc_session_open(rpc);
  75. delay(1);
  76. }
  77. furi_check(session);
  78. rpc_session_context.output_stream = xStreamBufferCreate(1000, 1);
  79. rpc_session_set_send_bytes_callback(session, output_bytes_callback);
  80. rpc_session_context.close_session_semaphore = xSemaphoreCreateBinary();
  81. rpc_session_context.terminate_semaphore = xSemaphoreCreateBinary();
  82. rpc_session_set_close_callback(session, test_rpc_session_close_callback);
  83. rpc_session_set_terminated_callback(session, test_rpc_session_terminated_callback);
  84. rpc_session_set_context(session, &rpc_session_context);
  85. }
  86. static void test_rpc_teardown(void) {
  87. furi_check(rpc_session_context.close_session_semaphore);
  88. xSemaphoreTake(rpc_session_context.terminate_semaphore, 0);
  89. rpc_session_close(session);
  90. furi_check(xSemaphoreTake(rpc_session_context.terminate_semaphore, portMAX_DELAY));
  91. furi_record_close("rpc");
  92. vStreamBufferDelete(rpc_session_context.output_stream);
  93. vSemaphoreDelete(rpc_session_context.close_session_semaphore);
  94. vSemaphoreDelete(rpc_session_context.terminate_semaphore);
  95. ++command_id;
  96. rpc_session_context.output_stream = NULL;
  97. rpc_session_context.close_session_semaphore = NULL;
  98. rpc = NULL;
  99. session = NULL;
  100. }
  101. static void test_rpc_storage_setup(void) {
  102. test_rpc_setup();
  103. Storage* fs_api = furi_record_open("storage");
  104. clean_directory(fs_api, TEST_DIR_NAME);
  105. furi_record_close("storage");
  106. }
  107. static void test_rpc_storage_teardown(void) {
  108. test_rpc_teardown();
  109. Storage* fs_api = furi_record_open("storage");
  110. clean_directory(fs_api, TEST_DIR_NAME);
  111. furi_record_close("storage");
  112. }
  113. static void test_rpc_session_close_callback(void* context) {
  114. furi_check(context);
  115. RpcSessionContext* callbacks_context = context;
  116. xSemaphoreGive(callbacks_context->close_session_semaphore);
  117. }
  118. static void test_rpc_session_terminated_callback(void* context) {
  119. furi_check(context);
  120. RpcSessionContext* callbacks_context = context;
  121. xSemaphoreGive(callbacks_context->terminate_semaphore);
  122. }
  123. static void clean_directory(Storage* fs_api, const char* clean_dir) {
  124. furi_check(fs_api);
  125. furi_check(clean_dir);
  126. File* dir = storage_file_alloc(fs_api);
  127. if(storage_dir_open(dir, clean_dir)) {
  128. FileInfo fileinfo;
  129. char* name = malloc(MAX_NAME_LENGTH + 1);
  130. while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
  131. char* fullname = malloc(strlen(clean_dir) + strlen(name) + 1 + 1);
  132. sprintf(fullname, "%s/%s", clean_dir, name);
  133. if(fileinfo.flags & FSF_DIRECTORY) {
  134. clean_directory(fs_api, fullname);
  135. }
  136. FS_Error error = storage_common_remove(fs_api, fullname);
  137. furi_check(error == FSE_OK);
  138. free(fullname);
  139. }
  140. free(name);
  141. } else {
  142. FS_Error error = storage_common_mkdir(fs_api, clean_dir);
  143. (void)error;
  144. furi_check(error == FSE_OK);
  145. }
  146. storage_dir_close(dir);
  147. storage_file_free(dir);
  148. }
  149. static void test_rpc_print_message_list(MsgList_t msg_list) {
  150. #if DEBUG_PRINT
  151. MsgList_reverse(msg_list);
  152. for
  153. M_EACH(msg, msg_list, MsgList_t) {
  154. rpc_print_message(msg);
  155. }
  156. MsgList_reverse(msg_list);
  157. #endif
  158. }
  159. static PB_CommandStatus test_rpc_storage_get_file_error(File* file) {
  160. FS_Error fs_error = storage_file_get_error(file);
  161. PB_CommandStatus pb_error;
  162. switch(fs_error) {
  163. case FSE_OK:
  164. pb_error = PB_CommandStatus_OK;
  165. break;
  166. case FSE_INVALID_NAME:
  167. pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;
  168. break;
  169. case FSE_INVALID_PARAMETER:
  170. pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;
  171. break;
  172. case FSE_INTERNAL:
  173. pb_error = PB_CommandStatus_ERROR_STORAGE_INTERNAL;
  174. break;
  175. case FSE_ALREADY_OPEN:
  176. pb_error = PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN;
  177. break;
  178. case FSE_DENIED:
  179. pb_error = PB_CommandStatus_ERROR_STORAGE_DENIED;
  180. break;
  181. case FSE_EXIST:
  182. pb_error = PB_CommandStatus_ERROR_STORAGE_EXIST;
  183. break;
  184. case FSE_NOT_EXIST:
  185. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_EXIST;
  186. break;
  187. case FSE_NOT_READY:
  188. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_READY;
  189. break;
  190. case FSE_NOT_IMPLEMENTED:
  191. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED;
  192. break;
  193. default:
  194. pb_error = PB_CommandStatus_ERROR;
  195. break;
  196. }
  197. return pb_error;
  198. }
  199. static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size) {
  200. RpcSessionContext* callbacks_context = ctx;
  201. size_t bytes_sent =
  202. xStreamBufferSend(callbacks_context->output_stream, got_bytes, got_size, osWaitForever);
  203. (void)bytes_sent;
  204. furi_check(bytes_sent == got_size);
  205. }
  206. static void test_rpc_add_ping_to_list(MsgList_t msg_list, bool request, uint32_t command_id) {
  207. PB_Main* response = MsgList_push_new(msg_list);
  208. response->command_id = command_id;
  209. response->command_status = PB_CommandStatus_OK;
  210. response->cb_content.funcs.encode = NULL;
  211. response->has_next = false;
  212. response->which_content = (request == PING_REQUEST) ? PB_Main_system_ping_request_tag :
  213. PB_Main_system_ping_response_tag;
  214. }
  215. static void test_rpc_create_simple_message(
  216. PB_Main* message,
  217. uint16_t tag,
  218. const char* str,
  219. uint32_t command_id) {
  220. furi_check(message);
  221. char* str_copy = NULL;
  222. if(str) {
  223. str_copy = strdup(str);
  224. }
  225. message->command_id = command_id;
  226. message->command_status = PB_CommandStatus_OK;
  227. message->cb_content.funcs.encode = NULL;
  228. message->which_content = tag;
  229. message->has_next = false;
  230. switch(tag) {
  231. case PB_Main_storage_info_request_tag:
  232. message->content.storage_info_request.path = str_copy;
  233. break;
  234. case PB_Main_storage_stat_request_tag:
  235. message->content.storage_stat_request.path = str_copy;
  236. break;
  237. case PB_Main_storage_list_request_tag:
  238. message->content.storage_list_request.path = str_copy;
  239. break;
  240. case PB_Main_storage_mkdir_request_tag:
  241. message->content.storage_mkdir_request.path = str_copy;
  242. break;
  243. case PB_Main_storage_read_request_tag:
  244. message->content.storage_read_request.path = str_copy;
  245. break;
  246. case PB_Main_storage_delete_request_tag:
  247. message->content.storage_delete_request.path = str_copy;
  248. break;
  249. case PB_Main_storage_md5sum_request_tag:
  250. message->content.storage_md5sum_request.path = str_copy;
  251. break;
  252. case PB_Main_storage_md5sum_response_tag: {
  253. char* md5sum = message->content.storage_md5sum_response.md5sum;
  254. size_t md5sum_size = sizeof(message->content.storage_md5sum_response.md5sum);
  255. furi_check((strlen(str) + 1) <= md5sum_size);
  256. memcpy(md5sum, str_copy, md5sum_size);
  257. free(str_copy);
  258. break;
  259. }
  260. default:
  261. furi_check(0);
  262. break;
  263. }
  264. }
  265. static void test_rpc_add_read_or_write_to_list(
  266. MsgList_t msg_list,
  267. bool write,
  268. const char* path,
  269. const uint8_t* pattern,
  270. size_t pattern_size,
  271. size_t pattern_repeats,
  272. uint32_t command_id) {
  273. furi_check(pattern_repeats > 0);
  274. do {
  275. PB_Main* request = MsgList_push_new(msg_list);
  276. PB_Storage_File* msg_file = NULL;
  277. request->command_id = command_id;
  278. request->command_status = PB_CommandStatus_OK;
  279. if(write == WRITE_REQUEST) {
  280. request->content.storage_write_request.path = strdup(path);
  281. request->which_content = PB_Main_storage_write_request_tag;
  282. request->content.storage_write_request.has_file = true;
  283. msg_file = &request->content.storage_write_request.file;
  284. } else {
  285. request->which_content = PB_Main_storage_read_response_tag;
  286. request->content.storage_read_response.has_file = true;
  287. msg_file = &request->content.storage_read_response.file;
  288. }
  289. msg_file->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(pattern_size));
  290. msg_file->data->size = pattern_size;
  291. memcpy(msg_file->data->bytes, pattern, pattern_size);
  292. --pattern_repeats;
  293. request->has_next = (pattern_repeats > 0);
  294. } while(pattern_repeats);
  295. }
  296. static void test_rpc_encode_and_feed_one(PB_Main* request) {
  297. furi_check(request);
  298. pb_ostream_t ostream = PB_OSTREAM_SIZING;
  299. bool result = pb_encode_ex(&ostream, &PB_Main_msg, request, PB_ENCODE_DELIMITED);
  300. furi_check(result && ostream.bytes_written);
  301. uint8_t* buffer = malloc(ostream.bytes_written);
  302. ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written);
  303. pb_encode_ex(&ostream, &PB_Main_msg, request, PB_ENCODE_DELIMITED);
  304. size_t bytes_left = ostream.bytes_written;
  305. uint8_t* buffer_ptr = buffer;
  306. do {
  307. size_t bytes_sent = rpc_session_feed(session, buffer_ptr, bytes_left, 1000);
  308. mu_check(bytes_sent > 0);
  309. bytes_left -= bytes_sent;
  310. buffer_ptr += bytes_sent;
  311. } while(bytes_left);
  312. free(buffer);
  313. pb_release(&PB_Main_msg, request);
  314. }
  315. static void test_rpc_encode_and_feed(MsgList_t msg_list) {
  316. MsgList_reverse(msg_list);
  317. for
  318. M_EACH(request, msg_list, MsgList_t) {
  319. test_rpc_encode_and_feed_one(request);
  320. }
  321. MsgList_reverse(msg_list);
  322. }
  323. static void
  324. test_rpc_compare_file(PB_Storage_File* result_msg_file, PB_Storage_File* expected_msg_file) {
  325. mu_check(!result_msg_file->name == !expected_msg_file->name);
  326. if(result_msg_file->name) {
  327. mu_check(!strcmp(result_msg_file->name, expected_msg_file->name));
  328. }
  329. mu_check(result_msg_file->size == expected_msg_file->size);
  330. mu_check(result_msg_file->type == expected_msg_file->type);
  331. mu_check(!result_msg_file->data == !expected_msg_file->data);
  332. mu_check(result_msg_file->data->size == expected_msg_file->data->size);
  333. for(int i = 0; i < result_msg_file->data->size; ++i) {
  334. mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]);
  335. }
  336. }
  337. static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) {
  338. mu_check(result->command_id == expected->command_id);
  339. mu_check(result->command_status == expected->command_status);
  340. mu_check(result->has_next == expected->has_next);
  341. mu_check(result->which_content == expected->which_content);
  342. if(result->command_status != PB_CommandStatus_OK) {
  343. mu_check(result->which_content == PB_Main_empty_tag);
  344. }
  345. switch(result->which_content) {
  346. case PB_Main_empty_tag:
  347. case PB_Main_system_ping_response_tag:
  348. /* nothing to check */
  349. break;
  350. case PB_Main_system_ping_request_tag:
  351. case PB_Main_storage_list_request_tag:
  352. case PB_Main_storage_read_request_tag:
  353. case PB_Main_storage_write_request_tag:
  354. case PB_Main_storage_delete_request_tag:
  355. case PB_Main_storage_mkdir_request_tag:
  356. case PB_Main_storage_md5sum_request_tag:
  357. /* rpc doesn't send it */
  358. mu_check(0);
  359. break;
  360. case PB_Main_app_lock_status_response_tag: {
  361. bool result_locked = result->content.app_lock_status_response.locked;
  362. bool expected_locked = expected->content.app_lock_status_response.locked;
  363. mu_check(result_locked == expected_locked);
  364. break;
  365. }
  366. case PB_Main_storage_info_response_tag: {
  367. uint64_t result_total_space = result->content.storage_info_response.total_space;
  368. uint64_t expected_total_space = expected->content.storage_info_response.total_space;
  369. mu_check(result_total_space == expected_total_space);
  370. uint64_t result_free_space = result->content.storage_info_response.free_space;
  371. uint64_t expected_free_space = expected->content.storage_info_response.free_space;
  372. mu_check(result_free_space == expected_free_space);
  373. } break;
  374. case PB_Main_storage_stat_response_tag: {
  375. bool result_has_msg_file = result->content.storage_stat_response.has_file;
  376. bool expected_has_msg_file = expected->content.storage_stat_response.has_file;
  377. mu_check(result_has_msg_file == expected_has_msg_file);
  378. if(result_has_msg_file) {
  379. PB_Storage_File* result_msg_file = &result->content.storage_stat_response.file;
  380. PB_Storage_File* expected_msg_file = &expected->content.storage_stat_response.file;
  381. test_rpc_compare_file(result_msg_file, expected_msg_file);
  382. } else {
  383. mu_check(0);
  384. }
  385. } break;
  386. case PB_Main_storage_read_response_tag: {
  387. bool result_has_msg_file = result->content.storage_read_response.has_file;
  388. bool expected_has_msg_file = expected->content.storage_read_response.has_file;
  389. mu_check(result_has_msg_file == expected_has_msg_file);
  390. if(result_has_msg_file) {
  391. PB_Storage_File* result_msg_file = &result->content.storage_read_response.file;
  392. PB_Storage_File* expected_msg_file = &expected->content.storage_read_response.file;
  393. test_rpc_compare_file(result_msg_file, expected_msg_file);
  394. } else {
  395. mu_check(0);
  396. }
  397. } break;
  398. case PB_Main_storage_list_response_tag: {
  399. size_t expected_msg_files = expected->content.storage_list_response.file_count;
  400. size_t result_msg_files = result->content.storage_list_response.file_count;
  401. mu_check(result_msg_files == expected_msg_files);
  402. for(int i = 0; i < expected_msg_files; ++i) {
  403. PB_Storage_File* result_msg_file = &result->content.storage_list_response.file[i];
  404. PB_Storage_File* expected_msg_file = &expected->content.storage_list_response.file[i];
  405. test_rpc_compare_file(result_msg_file, expected_msg_file);
  406. }
  407. break;
  408. }
  409. case PB_Main_storage_md5sum_response_tag: {
  410. char* result_md5sum = result->content.storage_md5sum_response.md5sum;
  411. char* expected_md5sum = expected->content.storage_md5sum_response.md5sum;
  412. mu_check(!strcmp(result_md5sum, expected_md5sum));
  413. break;
  414. }
  415. case PB_Main_system_protobuf_version_response_tag: {
  416. uint32_t major_version_expected = expected->content.system_protobuf_version_response.major;
  417. uint32_t minor_version_expected = expected->content.system_protobuf_version_response.minor;
  418. uint32_t major_version_result = result->content.system_protobuf_version_response.major;
  419. uint32_t minor_version_result = result->content.system_protobuf_version_response.minor;
  420. mu_check(major_version_expected == major_version_result);
  421. mu_check(minor_version_expected == minor_version_result);
  422. break;
  423. }
  424. default:
  425. furi_check(0);
  426. break;
  427. }
  428. }
  429. static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
  430. RpcSessionContext* session_context = istream->state;
  431. size_t bytes_received = 0;
  432. TickType_t now = xTaskGetTickCount();
  433. int32_t time_left = session_context->timeout - now;
  434. time_left = MAX(time_left, 0);
  435. bytes_received = xStreamBufferReceive(session_context->output_stream, buf, count, time_left);
  436. return (count == bytes_received);
  437. }
  438. static void
  439. test_rpc_storage_list_create_expected_list_root(MsgList_t msg_list, uint32_t command_id) {
  440. PB_Main* message = MsgList_push_new(msg_list);
  441. message->has_next = false;
  442. message->cb_content.funcs.encode = NULL;
  443. message->command_id = command_id;
  444. message->which_content = PB_Main_storage_list_response_tag;
  445. message->content.storage_list_response.file_count = 3;
  446. message->content.storage_list_response.file[0].data = NULL;
  447. message->content.storage_list_response.file[1].data = NULL;
  448. message->content.storage_list_response.file[2].data = NULL;
  449. message->content.storage_list_response.file[0].size = 0;
  450. message->content.storage_list_response.file[1].size = 0;
  451. message->content.storage_list_response.file[2].size = 0;
  452. message->content.storage_list_response.file[0].type = PB_Storage_File_FileType_DIR;
  453. message->content.storage_list_response.file[1].type = PB_Storage_File_FileType_DIR;
  454. message->content.storage_list_response.file[2].type = PB_Storage_File_FileType_DIR;
  455. char* str = malloc(4);
  456. strcpy(str, "any");
  457. message->content.storage_list_response.file[0].name = str;
  458. str = malloc(4);
  459. strcpy(str, "int");
  460. message->content.storage_list_response.file[1].name = str;
  461. str = malloc(4);
  462. strcpy(str, "ext");
  463. message->content.storage_list_response.file[2].name = str;
  464. }
  465. static void test_rpc_storage_list_create_expected_list(
  466. MsgList_t msg_list,
  467. const char* path,
  468. uint32_t command_id) {
  469. Storage* fs_api = furi_record_open("storage");
  470. File* dir = storage_file_alloc(fs_api);
  471. PB_Main response = {
  472. .command_id = command_id,
  473. .has_next = false,
  474. .which_content = PB_Main_storage_list_response_tag,
  475. /* other fields (e.g. msg_files ptrs) explicitly initialized by 0 */
  476. };
  477. PB_Storage_ListResponse* list = &response.content.storage_list_response;
  478. bool finish = false;
  479. int i = 0;
  480. if(storage_dir_open(dir, path)) {
  481. response.command_status = PB_CommandStatus_OK;
  482. } else {
  483. response.command_status = test_rpc_storage_get_file_error(dir);
  484. response.which_content = PB_Main_empty_tag;
  485. finish = true;
  486. }
  487. while(!finish) {
  488. FileInfo fileinfo;
  489. char* name = malloc(MAX_NAME_LENGTH + 1);
  490. if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
  491. if(i == COUNT_OF(list->file)) {
  492. list->file_count = i;
  493. response.has_next = true;
  494. MsgList_push_back(msg_list, response);
  495. i = 0;
  496. }
  497. list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR :
  498. PB_Storage_File_FileType_FILE;
  499. list->file[i].size = fileinfo.size;
  500. list->file[i].data = NULL;
  501. /* memory free inside rpc_encode_and_send() -> pb_release() */
  502. list->file[i].name = name;
  503. ++i;
  504. } else {
  505. finish = true;
  506. free(name);
  507. }
  508. }
  509. list->file_count = i;
  510. response.has_next = false;
  511. MsgList_push_back(msg_list, response);
  512. storage_dir_close(dir);
  513. storage_file_free(dir);
  514. furi_record_close("storage");
  515. }
  516. static void test_rpc_decode_and_compare(MsgList_t expected_msg_list) {
  517. furi_check(!MsgList_empty_p(expected_msg_list));
  518. rpc_session_context.timeout = xTaskGetTickCount() + MAX_RECEIVE_OUTPUT_TIMEOUT;
  519. pb_istream_t istream = {
  520. .callback = test_rpc_pb_stream_read,
  521. .state = &rpc_session_context,
  522. .errmsg = NULL,
  523. .bytes_left = 0x7FFFFFFF,
  524. };
  525. /* other fields explicitly initialized by 0 */
  526. PB_Main result = {.cb_content.funcs.decode = NULL};
  527. /* mlib adds msg_files into start of list, so reverse it */
  528. MsgList_reverse(expected_msg_list);
  529. for
  530. M_EACH(expected_msg, expected_msg_list, MsgList_t) {
  531. if(!pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) {
  532. mu_fail(
  533. "not all expected messages decoded (maybe increase MAX_RECEIVE_OUTPUT_TIMEOUT)");
  534. break;
  535. }
  536. test_rpc_compare_messages(&result, expected_msg);
  537. pb_release(&PB_Main_msg, &result);
  538. }
  539. rpc_session_context.timeout = xTaskGetTickCount() + 50;
  540. if(pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) {
  541. mu_fail("decoded more than expected");
  542. }
  543. MsgList_reverse(expected_msg_list);
  544. }
  545. static void test_rpc_free_msg_list(MsgList_t msg_list) {
  546. for
  547. M_EACH(it, msg_list, MsgList_t) {
  548. pb_release(&PB_Main_msg, it);
  549. }
  550. MsgList_clear(msg_list);
  551. }
  552. static void test_rpc_storage_list_run(const char* path, uint32_t command_id) {
  553. PB_Main request;
  554. MsgList_t expected_msg_list;
  555. MsgList_init(expected_msg_list);
  556. test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id);
  557. if(!strcmp(path, "/")) {
  558. test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id);
  559. } else {
  560. test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id);
  561. }
  562. test_rpc_encode_and_feed_one(&request);
  563. test_rpc_decode_and_compare(expected_msg_list);
  564. pb_release(&PB_Main_msg, &request);
  565. test_rpc_free_msg_list(expected_msg_list);
  566. }
  567. MU_TEST(test_storage_list) {
  568. test_rpc_storage_list_run("/", ++command_id);
  569. test_rpc_storage_list_run("/ext/nfc", ++command_id);
  570. test_rpc_storage_list_run("/int", ++command_id);
  571. test_rpc_storage_list_run("/ext", ++command_id);
  572. test_rpc_storage_list_run("/ext/infrared", ++command_id);
  573. test_rpc_storage_list_run("/ext/ibutton", ++command_id);
  574. test_rpc_storage_list_run("/ext/lfrfid", ++command_id);
  575. test_rpc_storage_list_run("error_path", ++command_id);
  576. }
  577. static void
  578. test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id) {
  579. PB_Main* response = MsgList_push_new(msg_list);
  580. response->command_id = command_id;
  581. response->command_status = status;
  582. response->cb_content.funcs.encode = NULL;
  583. response->has_next = false;
  584. response->which_content = PB_Main_empty_tag;
  585. }
  586. static void test_rpc_add_read_to_list_by_reading_real_file(
  587. MsgList_t msg_list,
  588. const char* path,
  589. uint32_t command_id) {
  590. furi_check(MsgList_empty_p(msg_list));
  591. Storage* fs_api = furi_record_open("storage");
  592. File* file = storage_file_alloc(fs_api);
  593. bool result = false;
  594. if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  595. size_t size_left = storage_file_size(file);
  596. do {
  597. PB_Main* response = MsgList_push_new(msg_list);
  598. response->command_id = command_id;
  599. response->command_status = PB_CommandStatus_OK;
  600. response->has_next = false;
  601. response->which_content = PB_Main_storage_read_response_tag;
  602. response->content.storage_read_response.has_file = true;
  603. response->content.storage_read_response.file.data =
  604. malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE)));
  605. uint8_t* buffer = response->content.storage_read_response.file.data->bytes;
  606. uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;
  607. size_t read_size = MIN(size_left, MAX_DATA_SIZE);
  608. *read_size_msg = storage_file_read(file, buffer, read_size);
  609. size_left -= read_size;
  610. result = (*read_size_msg == read_size);
  611. if(result) {
  612. response->has_next = (size_left > 0);
  613. }
  614. } while((size_left != 0) && result);
  615. if(!result) {
  616. test_rpc_add_empty_to_list(
  617. msg_list, test_rpc_storage_get_file_error(file), command_id);
  618. }
  619. } else {
  620. test_rpc_add_empty_to_list(msg_list, test_rpc_storage_get_file_error(file), command_id);
  621. }
  622. storage_file_close(file);
  623. storage_file_free(file);
  624. furi_record_close("storage");
  625. }
  626. static void test_storage_read_run(const char* path, uint32_t command_id) {
  627. PB_Main request;
  628. MsgList_t expected_msg_list;
  629. MsgList_init(expected_msg_list);
  630. test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id);
  631. test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id);
  632. test_rpc_encode_and_feed_one(&request);
  633. test_rpc_decode_and_compare(expected_msg_list);
  634. pb_release(&PB_Main_msg, &request);
  635. test_rpc_free_msg_list(expected_msg_list);
  636. }
  637. static bool test_is_exists(const char* path) {
  638. Storage* fs_api = furi_record_open("storage");
  639. FileInfo fileinfo;
  640. FS_Error result = storage_common_stat(fs_api, path, &fileinfo);
  641. furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST));
  642. furi_record_close("storage");
  643. return result == FSE_OK;
  644. }
  645. static void test_create_dir(const char* path) {
  646. Storage* fs_api = furi_record_open("storage");
  647. FS_Error error = storage_common_mkdir(fs_api, path);
  648. (void)error;
  649. furi_check((error == FSE_OK) || (error == FSE_EXIST));
  650. furi_record_close("storage");
  651. furi_check(test_is_exists(path));
  652. }
  653. static void test_create_file(const char* path, size_t size) {
  654. Storage* fs_api = furi_record_open("storage");
  655. File* file = storage_file_alloc(fs_api);
  656. if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  657. uint8_t buf[128] = {0};
  658. for(int i = 0; i < sizeof(buf); ++i) {
  659. buf[i] = '0' + (i % 10);
  660. }
  661. while(size) {
  662. size_t written = storage_file_write(file, buf, MIN(size, sizeof(buf)));
  663. furi_check(written);
  664. size -= written;
  665. }
  666. }
  667. storage_file_close(file);
  668. storage_file_free(file);
  669. furi_record_close("storage");
  670. furi_check(test_is_exists(path));
  671. }
  672. static void test_rpc_storage_info_run(const char* path, uint32_t command_id) {
  673. PB_Main request;
  674. MsgList_t expected_msg_list;
  675. MsgList_init(expected_msg_list);
  676. test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id);
  677. PB_Main* response = MsgList_push_new(expected_msg_list);
  678. response->command_id = command_id;
  679. Storage* fs_api = furi_record_open("storage");
  680. FS_Error error = storage_common_fs_info(
  681. fs_api,
  682. path,
  683. &response->content.storage_info_response.total_space,
  684. &response->content.storage_info_response.free_space);
  685. response->command_status = rpc_system_storage_get_error(error);
  686. if(error == FSE_OK) {
  687. response->which_content = PB_Main_storage_info_response_tag;
  688. } else {
  689. response->which_content = PB_Main_empty_tag;
  690. }
  691. test_rpc_encode_and_feed_one(&request);
  692. test_rpc_decode_and_compare(expected_msg_list);
  693. pb_release(&PB_Main_msg, &request);
  694. test_rpc_free_msg_list(expected_msg_list);
  695. }
  696. static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
  697. PB_Main request;
  698. MsgList_t expected_msg_list;
  699. MsgList_init(expected_msg_list);
  700. test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id);
  701. Storage* fs_api = furi_record_open("storage");
  702. FileInfo fileinfo;
  703. FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
  704. furi_record_close("storage");
  705. PB_Main* response = MsgList_push_new(expected_msg_list);
  706. response->command_id = command_id;
  707. response->command_status = rpc_system_storage_get_error(error);
  708. response->has_next = false;
  709. response->which_content = PB_Main_empty_tag;
  710. if(error == FSE_OK) {
  711. response->which_content = PB_Main_storage_stat_response_tag;
  712. response->content.storage_stat_response.has_file = true;
  713. response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
  714. PB_Storage_File_FileType_DIR :
  715. PB_Storage_File_FileType_FILE;
  716. response->content.storage_stat_response.file.size = fileinfo.size;
  717. }
  718. test_rpc_encode_and_feed_one(&request);
  719. test_rpc_decode_and_compare(expected_msg_list);
  720. pb_release(&PB_Main_msg, &request);
  721. test_rpc_free_msg_list(expected_msg_list);
  722. }
  723. MU_TEST(test_storage_info) {
  724. test_rpc_storage_info_run("/any", ++command_id);
  725. test_rpc_storage_info_run("/int", ++command_id);
  726. test_rpc_storage_info_run("/ext", ++command_id);
  727. }
  728. #define TEST_DIR_STAT_NAME TEST_DIR "stat_dir"
  729. #define TEST_DIR_STAT TEST_DIR_STAT_NAME "/"
  730. MU_TEST(test_storage_stat) {
  731. test_create_dir(TEST_DIR_STAT_NAME);
  732. test_create_file(TEST_DIR_STAT "empty.txt", 0);
  733. test_create_file(TEST_DIR_STAT "l33t.txt", 1337);
  734. test_rpc_storage_stat_run("/", ++command_id);
  735. test_rpc_storage_stat_run("/int", ++command_id);
  736. test_rpc_storage_stat_run("/ext", ++command_id);
  737. test_rpc_storage_stat_run(TEST_DIR_STAT "empty.txt", ++command_id);
  738. test_rpc_storage_stat_run(TEST_DIR_STAT "l33t.txt", ++command_id);
  739. test_rpc_storage_stat_run(TEST_DIR_STAT "missing", ++command_id);
  740. test_rpc_storage_stat_run(TEST_DIR_STAT_NAME, ++command_id);
  741. test_rpc_storage_stat_run(TEST_DIR_STAT, ++command_id);
  742. }
  743. MU_TEST(test_storage_read) {
  744. test_create_file(TEST_DIR "empty.txt", 0);
  745. test_create_file(TEST_DIR "file1.txt", 1);
  746. test_create_file(TEST_DIR "file2.txt", MAX_DATA_SIZE);
  747. test_create_file(TEST_DIR "file3.txt", MAX_DATA_SIZE + 1);
  748. test_create_file(TEST_DIR "file4.txt", (MAX_DATA_SIZE * 2) + 1);
  749. test_storage_read_run(TEST_DIR "empty.txt", ++command_id);
  750. test_storage_read_run(TEST_DIR "file1.txt", ++command_id);
  751. test_storage_read_run(TEST_DIR "file2.txt", ++command_id);
  752. test_storage_read_run(TEST_DIR "file3.txt", ++command_id);
  753. test_storage_read_run(TEST_DIR "file4.txt", ++command_id);
  754. }
  755. static void test_storage_write_run(
  756. const char* path,
  757. size_t write_size,
  758. size_t write_count,
  759. uint32_t command_id,
  760. PB_CommandStatus status) {
  761. MsgList_t input_msg_list;
  762. MsgList_init(input_msg_list);
  763. MsgList_t expected_msg_list;
  764. MsgList_init(expected_msg_list);
  765. uint8_t* buf = malloc(write_size);
  766. for(int i = 0; i < write_size; ++i) {
  767. buf[i] = '0' + (i % 10);
  768. }
  769. test_rpc_add_read_or_write_to_list(
  770. input_msg_list, WRITE_REQUEST, path, buf, write_size, write_count, command_id);
  771. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  772. test_rpc_encode_and_feed(input_msg_list);
  773. test_rpc_decode_and_compare(expected_msg_list);
  774. test_rpc_free_msg_list(input_msg_list);
  775. test_rpc_free_msg_list(expected_msg_list);
  776. free(buf);
  777. }
  778. static void test_storage_write_read_run(
  779. const char* path,
  780. const uint8_t* pattern,
  781. size_t pattern_size,
  782. size_t pattern_repeats,
  783. uint32_t* command_id) {
  784. MsgList_t input_msg_list;
  785. MsgList_init(input_msg_list);
  786. MsgList_t expected_msg_list;
  787. MsgList_init(expected_msg_list);
  788. test_rpc_add_read_or_write_to_list(
  789. input_msg_list, WRITE_REQUEST, path, pattern, pattern_size, pattern_repeats, ++*command_id);
  790. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id);
  791. test_rpc_create_simple_message(
  792. MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id);
  793. test_rpc_add_read_or_write_to_list(
  794. expected_msg_list,
  795. READ_RESPONSE,
  796. path,
  797. pattern,
  798. pattern_size,
  799. pattern_repeats,
  800. *command_id);
  801. test_rpc_print_message_list(input_msg_list);
  802. test_rpc_print_message_list(expected_msg_list);
  803. test_rpc_encode_and_feed(input_msg_list);
  804. test_rpc_decode_and_compare(expected_msg_list);
  805. test_rpc_free_msg_list(input_msg_list);
  806. test_rpc_free_msg_list(expected_msg_list);
  807. }
  808. MU_TEST(test_storage_write_read) {
  809. uint8_t pattern1[] = "abcdefgh";
  810. test_storage_write_read_run(TEST_DIR "test1.txt", pattern1, sizeof(pattern1), 1, &command_id);
  811. test_storage_write_read_run(TEST_DIR "test2.txt", pattern1, 1, 1, &command_id);
  812. test_storage_write_read_run(TEST_DIR "test3.txt", pattern1, 0, 1, &command_id);
  813. }
  814. MU_TEST(test_storage_write) {
  815. test_storage_write_run(
  816. TEST_DIR "afaefo/aefaef/aef/aef/test1.txt",
  817. 1,
  818. 1,
  819. ++command_id,
  820. PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
  821. test_storage_write_run(TEST_DIR "test1.txt", 100, 1, ++command_id, PB_CommandStatus_OK);
  822. test_storage_write_run(TEST_DIR "test2.txt", 100, 3, ++command_id, PB_CommandStatus_OK);
  823. test_storage_write_run(TEST_DIR "test1.txt", 100, 3, ++command_id, PB_CommandStatus_OK);
  824. test_storage_write_run(TEST_DIR "test2.txt", 100, 3, ++command_id, PB_CommandStatus_OK);
  825. test_storage_write_run(
  826. TEST_DIR "afaefo/aefaef/aef/aef/test1.txt",
  827. 1,
  828. 1,
  829. ++command_id,
  830. PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
  831. test_storage_write_run(TEST_DIR "test2.txt", 1, 50, ++command_id, PB_CommandStatus_OK);
  832. test_storage_write_run(TEST_DIR "test2.txt", 512, 3, ++command_id, PB_CommandStatus_OK);
  833. }
  834. MU_TEST(test_storage_interrupt_continuous_same_system) {
  835. MsgList_t input_msg_list;
  836. MsgList_init(input_msg_list);
  837. MsgList_t expected_msg_list;
  838. MsgList_init(expected_msg_list);
  839. uint8_t pattern[16] = {0};
  840. test_rpc_add_read_or_write_to_list(
  841. input_msg_list,
  842. WRITE_REQUEST,
  843. TEST_DIR "test1.txt",
  844. pattern,
  845. sizeof(pattern),
  846. 3,
  847. command_id);
  848. /* replace last packet (has_next == false) with another command */
  849. PB_Main message_to_remove;
  850. MsgList_pop_back(&message_to_remove, input_msg_list);
  851. pb_release(&PB_Main_msg, &message_to_remove);
  852. test_rpc_create_simple_message(
  853. MsgList_push_new(input_msg_list),
  854. PB_Main_storage_mkdir_request_tag,
  855. TEST_DIR "dir1",
  856. command_id + 1);
  857. test_rpc_add_read_or_write_to_list(
  858. input_msg_list,
  859. WRITE_REQUEST,
  860. TEST_DIR "test2.txt",
  861. pattern,
  862. sizeof(pattern),
  863. 3,
  864. command_id);
  865. test_rpc_add_empty_to_list(
  866. expected_msg_list, PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED, command_id);
  867. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id + 1);
  868. test_rpc_encode_and_feed(input_msg_list);
  869. test_rpc_decode_and_compare(expected_msg_list);
  870. test_rpc_free_msg_list(input_msg_list);
  871. test_rpc_free_msg_list(expected_msg_list);
  872. }
  873. MU_TEST(test_storage_interrupt_continuous_another_system) {
  874. MsgList_t input_msg_list;
  875. MsgList_init(input_msg_list);
  876. MsgList_t expected_msg_list;
  877. MsgList_init(expected_msg_list);
  878. uint8_t pattern[16] = {0};
  879. test_rpc_add_read_or_write_to_list(
  880. input_msg_list,
  881. WRITE_REQUEST,
  882. TEST_DIR "test1.txt",
  883. pattern,
  884. sizeof(pattern),
  885. 3,
  886. command_id);
  887. PB_Main message = {
  888. .command_id = command_id + 1,
  889. .command_status = PB_CommandStatus_OK,
  890. .cb_content.funcs.encode = NULL,
  891. .has_next = false,
  892. .which_content = PB_Main_system_ping_request_tag,
  893. };
  894. MsgList_it_t it;
  895. MsgList_it(it, input_msg_list);
  896. MsgList_next(it);
  897. MsgList_insert(input_msg_list, it, message);
  898. test_rpc_add_read_or_write_to_list(
  899. input_msg_list,
  900. WRITE_REQUEST,
  901. TEST_DIR "test2.txt",
  902. pattern,
  903. sizeof(pattern),
  904. 3,
  905. command_id + 2);
  906. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, command_id + 1);
  907. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id);
  908. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id + 2);
  909. test_rpc_encode_and_feed(input_msg_list);
  910. test_rpc_decode_and_compare(expected_msg_list);
  911. test_rpc_free_msg_list(input_msg_list);
  912. test_rpc_free_msg_list(expected_msg_list);
  913. }
  914. static void test_storage_delete_run(
  915. const char* path,
  916. size_t command_id,
  917. PB_CommandStatus status,
  918. bool recursive) {
  919. PB_Main request;
  920. MsgList_t expected_msg_list;
  921. MsgList_init(expected_msg_list);
  922. test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
  923. request.content.storage_delete_request.recursive = recursive;
  924. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  925. test_rpc_encode_and_feed_one(&request);
  926. test_rpc_decode_and_compare(expected_msg_list);
  927. pb_release(&PB_Main_msg, &request);
  928. test_rpc_free_msg_list(expected_msg_list);
  929. }
  930. #define TEST_DIR_RMRF_NAME TEST_DIR "rmrf_test"
  931. #define TEST_DIR_RMRF TEST_DIR_RMRF_NAME "/"
  932. MU_TEST(test_storage_delete_recursive) {
  933. test_create_dir(TEST_DIR_RMRF_NAME);
  934. test_create_dir(TEST_DIR_RMRF "dir1");
  935. test_create_file(TEST_DIR_RMRF "dir1/file1", 1);
  936. test_create_dir(TEST_DIR_RMRF "dir1/dir1");
  937. test_create_dir(TEST_DIR_RMRF "dir1/dir2");
  938. test_create_file(TEST_DIR_RMRF "dir1/dir2/file1", 1);
  939. test_create_file(TEST_DIR_RMRF "dir1/dir2/file2", 1);
  940. test_create_dir(TEST_DIR_RMRF "dir1/dir3");
  941. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1");
  942. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1");
  943. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1");
  944. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1/dir1");
  945. test_create_dir(TEST_DIR_RMRF "dir2");
  946. test_create_dir(TEST_DIR_RMRF "dir2/dir1");
  947. test_create_dir(TEST_DIR_RMRF "dir2/dir2");
  948. test_create_file(TEST_DIR_RMRF "dir2/dir2/file1", 1);
  949. test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1");
  950. test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1");
  951. test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1");
  952. test_create_file(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1/file1", 1);
  953. test_storage_delete_run(
  954. TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY, false);
  955. mu_check(test_is_exists(TEST_DIR_RMRF_NAME));
  956. test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);
  957. mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
  958. test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, false);
  959. mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
  960. test_create_dir(TEST_DIR_RMRF_NAME);
  961. test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);
  962. mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
  963. test_create_dir(TEST_DIR "file1");
  964. test_storage_delete_run(TEST_DIR "file1", ++command_id, PB_CommandStatus_OK, true);
  965. mu_check(!test_is_exists(TEST_DIR "file1"));
  966. }
  967. MU_TEST(test_storage_delete) {
  968. test_storage_delete_run(NULL, ++command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS, false);
  969. furi_check(!test_is_exists(TEST_DIR "empty.txt"));
  970. test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false);
  971. mu_check(!test_is_exists(TEST_DIR "empty.txt"));
  972. test_create_file(TEST_DIR "empty.txt", 0);
  973. test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false);
  974. mu_check(!test_is_exists(TEST_DIR "empty.txt"));
  975. furi_check(!test_is_exists(TEST_DIR "dir1"));
  976. test_create_dir(TEST_DIR "dir1");
  977. test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false);
  978. mu_check(!test_is_exists(TEST_DIR "dir1"));
  979. test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false);
  980. mu_check(!test_is_exists(TEST_DIR "dir1"));
  981. }
  982. static void test_storage_mkdir_run(const char* path, size_t command_id, PB_CommandStatus status) {
  983. PB_Main request;
  984. MsgList_t expected_msg_list;
  985. MsgList_init(expected_msg_list);
  986. test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id);
  987. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  988. test_rpc_encode_and_feed_one(&request);
  989. test_rpc_decode_and_compare(expected_msg_list);
  990. pb_release(&PB_Main_msg, &request);
  991. test_rpc_free_msg_list(expected_msg_list);
  992. }
  993. MU_TEST(test_storage_mkdir) {
  994. furi_check(!test_is_exists(TEST_DIR "dir1"));
  995. test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK);
  996. mu_check(test_is_exists(TEST_DIR "dir1"));
  997. test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
  998. mu_check(test_is_exists(TEST_DIR "dir1"));
  999. furi_check(!test_is_exists(TEST_DIR "dir2"));
  1000. test_create_dir(TEST_DIR "dir2");
  1001. test_storage_mkdir_run(TEST_DIR "dir2", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
  1002. mu_check(test_is_exists(TEST_DIR "dir2"));
  1003. }
  1004. static void test_storage_calculate_md5sum(const char* path, char* md5sum) {
  1005. Storage* api = furi_record_open("storage");
  1006. File* file = storage_file_alloc(api);
  1007. if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  1008. const uint16_t once_read_size = 512;
  1009. const uint8_t hash_size = MD5SUM_SIZE;
  1010. uint8_t* data = malloc(once_read_size);
  1011. uint8_t* hash = malloc(sizeof(uint8_t) * hash_size);
  1012. md5_context* md5_ctx = malloc(sizeof(md5_context));
  1013. md5_starts(md5_ctx);
  1014. while(true) {
  1015. uint16_t read_size = storage_file_read(file, data, once_read_size);
  1016. if(read_size == 0) break;
  1017. md5_update(md5_ctx, data, read_size);
  1018. }
  1019. md5_finish(md5_ctx, hash);
  1020. free(md5_ctx);
  1021. for(uint8_t i = 0; i < hash_size; i++) {
  1022. md5sum += sprintf(md5sum, "%02x", hash[i]);
  1023. }
  1024. free(hash);
  1025. free(data);
  1026. } else {
  1027. furi_check(0);
  1028. }
  1029. storage_file_close(file);
  1030. storage_file_free(file);
  1031. furi_record_close("storage");
  1032. }
  1033. static void test_storage_md5sum_run(
  1034. const char* path,
  1035. uint32_t command_id,
  1036. const char* md5sum,
  1037. PB_CommandStatus status) {
  1038. PB_Main request;
  1039. MsgList_t expected_msg_list;
  1040. MsgList_init(expected_msg_list);
  1041. test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id);
  1042. if(status == PB_CommandStatus_OK) {
  1043. PB_Main* response = MsgList_push_new(expected_msg_list);
  1044. test_rpc_create_simple_message(
  1045. response, PB_Main_storage_md5sum_response_tag, md5sum, command_id);
  1046. response->command_status = status;
  1047. } else {
  1048. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  1049. }
  1050. test_rpc_encode_and_feed_one(&request);
  1051. test_rpc_decode_and_compare(expected_msg_list);
  1052. pb_release(&PB_Main_msg, &request);
  1053. test_rpc_free_msg_list(expected_msg_list);
  1054. }
  1055. MU_TEST(test_storage_md5sum) {
  1056. char md5sum1[MD5SUM_SIZE * 2 + 1] = {0};
  1057. char md5sum2[MD5SUM_SIZE * 2 + 1] = {0};
  1058. char md5sum3[MD5SUM_SIZE * 2 + 1] = {0};
  1059. test_storage_md5sum_run(
  1060. TEST_DIR "test1.txt", ++command_id, "", PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
  1061. test_create_file(TEST_DIR "file1.txt", 0);
  1062. test_create_file(TEST_DIR "file2.txt", 1);
  1063. test_create_file(TEST_DIR "file3.txt", 512);
  1064. test_storage_calculate_md5sum(TEST_DIR "file1.txt", md5sum1);
  1065. test_storage_calculate_md5sum(TEST_DIR "file2.txt", md5sum2);
  1066. test_storage_calculate_md5sum(TEST_DIR "file3.txt", md5sum3);
  1067. test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK);
  1068. test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK);
  1069. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  1070. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  1071. test_storage_md5sum_run(TEST_DIR "file3.txt", ++command_id, md5sum3, PB_CommandStatus_OK);
  1072. test_storage_md5sum_run(TEST_DIR "file3.txt", ++command_id, md5sum3, PB_CommandStatus_OK);
  1073. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  1074. test_storage_md5sum_run(TEST_DIR "file3.txt", ++command_id, md5sum3, PB_CommandStatus_OK);
  1075. test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK);
  1076. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  1077. }
  1078. static void test_rpc_storage_rename_run(
  1079. const char* old_path,
  1080. const char* new_path,
  1081. uint32_t command_id,
  1082. PB_CommandStatus status) {
  1083. PB_Main request;
  1084. MsgList_t expected_msg_list;
  1085. MsgList_init(expected_msg_list);
  1086. char* str_old_path = strdup(old_path);
  1087. char* str_new_path = strdup(new_path);
  1088. request.command_id = command_id;
  1089. request.command_status = PB_CommandStatus_OK;
  1090. request.cb_content.funcs.encode = NULL;
  1091. request.which_content = PB_Main_storage_rename_request_tag;
  1092. request.has_next = false;
  1093. request.content.storage_rename_request.old_path = str_old_path;
  1094. request.content.storage_rename_request.new_path = str_new_path;
  1095. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  1096. test_rpc_encode_and_feed_one(&request);
  1097. test_rpc_decode_and_compare(expected_msg_list);
  1098. pb_release(&PB_Main_msg, &request);
  1099. test_rpc_free_msg_list(expected_msg_list);
  1100. }
  1101. MU_TEST(test_storage_rename) {
  1102. test_rpc_storage_rename_run(
  1103. NULL, NULL, ++command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME);
  1104. furi_check(!test_is_exists(TEST_DIR "empty.txt"));
  1105. test_create_file(TEST_DIR "empty.txt", 0);
  1106. test_rpc_storage_rename_run(
  1107. TEST_DIR "empty.txt", TEST_DIR "empty2.txt", ++command_id, PB_CommandStatus_OK);
  1108. mu_check(!test_is_exists(TEST_DIR "empty.txt"));
  1109. mu_check(test_is_exists(TEST_DIR "empty2.txt"));
  1110. furi_check(!test_is_exists(TEST_DIR "dir1"));
  1111. test_create_dir(TEST_DIR "dir1");
  1112. test_rpc_storage_rename_run(
  1113. TEST_DIR "dir1", TEST_DIR "dir2", ++command_id, PB_CommandStatus_OK);
  1114. mu_check(!test_is_exists(TEST_DIR "dir1"));
  1115. mu_check(test_is_exists(TEST_DIR "dir2"));
  1116. }
  1117. MU_TEST(test_ping) {
  1118. MsgList_t input_msg_list;
  1119. MsgList_init(input_msg_list);
  1120. MsgList_t expected_msg_list;
  1121. MsgList_init(expected_msg_list);
  1122. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 0);
  1123. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 1);
  1124. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 0);
  1125. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 500);
  1126. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, (uint32_t)-1);
  1127. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 700);
  1128. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 1);
  1129. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 0);
  1130. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 1);
  1131. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 0);
  1132. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 500);
  1133. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, (uint32_t)-1);
  1134. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 700);
  1135. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 1);
  1136. test_rpc_encode_and_feed(input_msg_list);
  1137. test_rpc_decode_and_compare(expected_msg_list);
  1138. test_rpc_free_msg_list(input_msg_list);
  1139. test_rpc_free_msg_list(expected_msg_list);
  1140. }
  1141. MU_TEST(test_system_protobuf_version) {
  1142. MsgList_t expected_msg_list;
  1143. MsgList_init(expected_msg_list);
  1144. PB_Main request;
  1145. request.command_id = ++command_id;
  1146. request.command_status = PB_CommandStatus_OK;
  1147. request.cb_content.funcs.decode = NULL;
  1148. request.has_next = false;
  1149. request.which_content = PB_Main_system_protobuf_version_request_tag;
  1150. PB_Main* response = MsgList_push_new(expected_msg_list);
  1151. response->command_id = command_id;
  1152. response->command_status = PB_CommandStatus_OK;
  1153. response->cb_content.funcs.encode = NULL;
  1154. response->has_next = false;
  1155. response->which_content = PB_Main_system_protobuf_version_response_tag;
  1156. response->content.system_protobuf_version_response.major = PROTOBUF_MAJOR_VERSION;
  1157. response->content.system_protobuf_version_response.minor = PROTOBUF_MINOR_VERSION;
  1158. test_rpc_encode_and_feed_one(&request);
  1159. test_rpc_decode_and_compare(expected_msg_list);
  1160. test_rpc_free_msg_list(expected_msg_list);
  1161. }
  1162. MU_TEST_SUITE(test_rpc_system) {
  1163. MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);
  1164. MU_RUN_TEST(test_ping);
  1165. MU_RUN_TEST(test_system_protobuf_version);
  1166. }
  1167. MU_TEST_SUITE(test_rpc_storage) {
  1168. MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown);
  1169. MU_RUN_TEST(test_storage_info);
  1170. MU_RUN_TEST(test_storage_stat);
  1171. MU_RUN_TEST(test_storage_list);
  1172. MU_RUN_TEST(test_storage_read);
  1173. MU_RUN_TEST(test_storage_write_read);
  1174. MU_RUN_TEST(test_storage_write);
  1175. MU_RUN_TEST(test_storage_delete);
  1176. MU_RUN_TEST(test_storage_delete_recursive);
  1177. MU_RUN_TEST(test_storage_mkdir);
  1178. MU_RUN_TEST(test_storage_md5sum);
  1179. MU_RUN_TEST(test_storage_rename);
  1180. // TODO: repair test
  1181. DISABLE_TEST(MU_RUN_TEST(test_storage_interrupt_continuous_same_system););
  1182. MU_RUN_TEST(test_storage_interrupt_continuous_another_system);
  1183. }
  1184. static void test_app_create_request(
  1185. PB_Main* request,
  1186. const char* app_name,
  1187. const char* app_args,
  1188. uint32_t command_id) {
  1189. request->command_id = command_id;
  1190. request->command_status = PB_CommandStatus_OK;
  1191. request->cb_content.funcs.encode = NULL;
  1192. request->which_content = PB_Main_app_start_request_tag;
  1193. request->has_next = false;
  1194. if(app_name) {
  1195. char* msg_app_name = strdup(app_name);
  1196. request->content.app_start_request.name = msg_app_name;
  1197. } else {
  1198. request->content.app_start_request.name = NULL;
  1199. }
  1200. if(app_args) {
  1201. char* msg_app_args = strdup(app_args);
  1202. request->content.app_start_request.args = msg_app_args;
  1203. } else {
  1204. request->content.app_start_request.args = NULL;
  1205. }
  1206. }
  1207. static void test_app_start_run(
  1208. const char* app_name,
  1209. const char* app_args,
  1210. PB_CommandStatus status,
  1211. uint32_t command_id) {
  1212. PB_Main request;
  1213. MsgList_t expected_msg_list;
  1214. MsgList_init(expected_msg_list);
  1215. test_app_create_request(&request, app_name, app_args, command_id);
  1216. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  1217. test_rpc_encode_and_feed_one(&request);
  1218. test_rpc_decode_and_compare(expected_msg_list);
  1219. pb_release(&PB_Main_msg, &request);
  1220. test_rpc_free_msg_list(expected_msg_list);
  1221. }
  1222. static void test_app_get_status_lock_run(bool locked_expected, uint32_t command_id) {
  1223. PB_Main request = {
  1224. .command_id = command_id,
  1225. .command_status = PB_CommandStatus_OK,
  1226. .which_content = PB_Main_app_lock_status_request_tag,
  1227. .has_next = false,
  1228. };
  1229. MsgList_t expected_msg_list;
  1230. MsgList_init(expected_msg_list);
  1231. PB_Main* response = MsgList_push_new(expected_msg_list);
  1232. response->command_id = command_id;
  1233. response->command_status = PB_CommandStatus_OK;
  1234. response->which_content = PB_Main_app_lock_status_response_tag;
  1235. response->has_next = false;
  1236. response->content.app_lock_status_response.locked = locked_expected;
  1237. test_rpc_encode_and_feed_one(&request);
  1238. test_rpc_decode_and_compare(expected_msg_list);
  1239. pb_release(&PB_Main_msg, &request);
  1240. test_rpc_free_msg_list(expected_msg_list);
  1241. }
  1242. MU_TEST(test_app_start_and_lock_status) {
  1243. test_app_get_status_lock_run(false, ++command_id);
  1244. test_app_start_run(NULL, "/ext/file", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1245. test_app_start_run(NULL, NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1246. test_app_get_status_lock_run(false, ++command_id);
  1247. test_app_start_run(
  1248. "skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1249. test_app_get_status_lock_run(false, ++command_id);
  1250. test_app_start_run("Delay Test", "0", PB_CommandStatus_OK, ++command_id);
  1251. delay(100);
  1252. test_app_get_status_lock_run(false, ++command_id);
  1253. test_app_start_run("Delay Test", "200", PB_CommandStatus_OK, ++command_id);
  1254. test_app_get_status_lock_run(true, ++command_id);
  1255. delay(100);
  1256. test_app_get_status_lock_run(true, ++command_id);
  1257. test_app_start_run("Delay Test", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
  1258. delay(200);
  1259. test_app_get_status_lock_run(false, ++command_id);
  1260. test_app_start_run("Delay Test", "500", PB_CommandStatus_OK, ++command_id);
  1261. delay(100);
  1262. test_app_get_status_lock_run(true, ++command_id);
  1263. test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
  1264. delay(100);
  1265. test_app_get_status_lock_run(true, ++command_id);
  1266. test_app_start_run(
  1267. "2_girls_1_app", "0", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1268. delay(100);
  1269. test_app_get_status_lock_run(true, ++command_id);
  1270. delay(500);
  1271. test_app_get_status_lock_run(false, ++command_id);
  1272. }
  1273. MU_TEST_SUITE(test_rpc_app) {
  1274. MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);
  1275. DISABLE_TEST(MU_RUN_TEST(test_app_start_and_lock_status););
  1276. }
  1277. static void
  1278. test_send_rubbish(RpcSession* session, const char* pattern, size_t pattern_size, size_t size) {
  1279. uint8_t* buf = malloc(size);
  1280. for(int i = 0; i < size; ++i) {
  1281. buf[i] = pattern[i % pattern_size];
  1282. }
  1283. size_t bytes_sent = rpc_session_feed(session, buf, size, 1000);
  1284. furi_check(bytes_sent == size);
  1285. free(buf);
  1286. }
  1287. static void test_rpc_feed_rubbish_run(
  1288. MsgList_t input_before,
  1289. MsgList_t input_after,
  1290. MsgList_t expected,
  1291. const char* pattern,
  1292. size_t pattern_size,
  1293. size_t size) {
  1294. test_rpc_setup();
  1295. test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_DECODE, 0);
  1296. furi_check(!xSemaphoreTake(rpc_session_context.close_session_semaphore, 0));
  1297. test_rpc_encode_and_feed(input_before);
  1298. test_send_rubbish(session, pattern, pattern_size, size);
  1299. test_rpc_encode_and_feed(input_after);
  1300. test_rpc_decode_and_compare(expected);
  1301. test_rpc_teardown();
  1302. }
  1303. #define RUN_TEST_RPC_FEED_RUBBISH(ib, ia, e, b, c) \
  1304. test_rpc_feed_rubbish_run(ib, ia, e, b, sizeof(b), c)
  1305. #define INIT_LISTS() \
  1306. MsgList_init(input_before); \
  1307. MsgList_init(input_after); \
  1308. MsgList_init(expected);
  1309. #define FREE_LISTS() \
  1310. test_rpc_free_msg_list(input_before); \
  1311. test_rpc_free_msg_list(input_after); \
  1312. test_rpc_free_msg_list(expected);
  1313. MU_TEST(test_rpc_feed_rubbish) {
  1314. MsgList_t input_before;
  1315. MsgList_t input_after;
  1316. MsgList_t expected;
  1317. INIT_LISTS();
  1318. // input is empty
  1319. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x12\x30rubbi\x42sh", 50);
  1320. FREE_LISTS();
  1321. INIT_LISTS();
  1322. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1323. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1324. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x2\x2\x2\x5\x99\x1", 30);
  1325. FREE_LISTS();
  1326. INIT_LISTS();
  1327. test_rpc_add_ping_to_list(input_after, PING_REQUEST, ++command_id);
  1328. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x12\x30rubbi\x42sh", 50);
  1329. FREE_LISTS();
  1330. INIT_LISTS();
  1331. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1332. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1333. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1334. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1335. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1336. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1337. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1338. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1339. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1340. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1341. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1342. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1343. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1344. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1345. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1346. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1347. test_rpc_add_ping_to_list(input_after, PING_REQUEST, command_id);
  1348. test_rpc_add_ping_to_list(input_after, PING_REQUEST, command_id);
  1349. test_rpc_add_ping_to_list(input_after, PING_REQUEST, command_id);
  1350. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x99\x2\x2\x5\x99\x1", 300);
  1351. FREE_LISTS();
  1352. INIT_LISTS();
  1353. test_rpc_add_ping_to_list(input_after, PING_REQUEST, ++command_id);
  1354. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x1\x99\x2\x5\x99\x1", 300);
  1355. FREE_LISTS();
  1356. INIT_LISTS();
  1357. test_rpc_add_ping_to_list(input_before, PING_REQUEST, ++command_id);
  1358. test_rpc_add_ping_to_list(expected, PING_RESPONSE, command_id);
  1359. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x2\x2\x2\x5\x99\x1", 30);
  1360. FREE_LISTS();
  1361. INIT_LISTS();
  1362. test_rpc_add_ping_to_list(input_before, PING_RESPONSE, ++command_id);
  1363. test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_NOT_IMPLEMENTED, command_id);
  1364. test_rpc_add_ping_to_list(input_before, PING_RESPONSE, ++command_id);
  1365. test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_NOT_IMPLEMENTED, command_id);
  1366. RUN_TEST_RPC_FEED_RUBBISH(input_before, input_after, expected, "\x12\x30rubbi\x42sh", 50);
  1367. FREE_LISTS();
  1368. }
  1369. MU_TEST_SUITE(test_rpc_session) {
  1370. MU_RUN_TEST(test_rpc_feed_rubbish);
  1371. }
  1372. int run_minunit_test_rpc() {
  1373. Storage* storage = furi_record_open("storage");
  1374. if(storage_sd_status(storage) != FSE_OK) {
  1375. FURI_LOG_E(TAG, "SD card not mounted - skip storage tests");
  1376. } else {
  1377. MU_RUN_SUITE(test_rpc_storage);
  1378. }
  1379. furi_record_close("storage");
  1380. MU_RUN_SUITE(test_rpc_system);
  1381. MU_RUN_SUITE(test_rpc_app);
  1382. MU_RUN_SUITE(test_rpc_session);
  1383. return MU_EXIT_CODE;
  1384. }
  1385. int32_t delay_test_app(void* p) {
  1386. int timeout = atoi((const char*)p);
  1387. if(timeout > 0) {
  1388. delay(timeout);
  1389. }
  1390. return 0;
  1391. }