rpc_test.c 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  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_i.h"
  7. #include "storage.pb.h"
  8. #include "storage/filesystem-api-defines.h"
  9. #include "storage/storage.h"
  10. #include <furi.h>
  11. #include "../minunit.h"
  12. #include <stdint.h>
  13. #include <stream_buffer.h>
  14. #include <pb.h>
  15. #include <pb_encode.h>
  16. #include <m-list.h>
  17. #include <lib/toolbox/md5.h>
  18. #include <cli/cli.h>
  19. #include <loader/loader.h>
  20. LIST_DEF(MsgList, PB_Main, M_POD_OPLIST)
  21. #define M_OPL_MsgList_t() LIST_OPLIST(MsgList)
  22. /* MinUnit test framework doesn't allow passing context into tests,
  23. * so we have to use global variables
  24. */
  25. static Rpc* rpc = NULL;
  26. static RpcSession* session = NULL;
  27. static StreamBufferHandle_t output_stream = NULL;
  28. static uint32_t command_id = 0;
  29. #define TEST_RPC_TAG "TEST_RPC"
  30. #define MAX_RECEIVE_OUTPUT_TIMEOUT 3000
  31. #define MAX_NAME_LENGTH 255
  32. #define MAX_DATA_SIZE 512 // have to be exact as in rpc_storage.c
  33. #define TEST_DIR TEST_DIR_NAME "/"
  34. #define TEST_DIR_NAME "/ext/unit_tests_tmp"
  35. #define MD5SUM_SIZE 16
  36. #define PING_REQUEST 0
  37. #define PING_RESPONSE 1
  38. #define WRITE_REQUEST 0
  39. #define READ_RESPONSE 1
  40. #define DEBUG_PRINT 0
  41. #define BYTES(x) (x), sizeof(x)
  42. static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size);
  43. static void clean_directory(Storage* fs_api, const char* clean_dir);
  44. static void
  45. test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id);
  46. static void test_rpc_encode_and_feed(MsgList_t msg_list);
  47. static void test_rpc_encode_and_feed_one(PB_Main* request);
  48. static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected);
  49. static void test_rpc_decode_and_compare(MsgList_t expected_msg_list);
  50. static void test_rpc_free_msg_list(MsgList_t msg_list);
  51. static void test_rpc_setup(void) {
  52. furi_assert(!rpc);
  53. furi_assert(!session);
  54. furi_assert(!output_stream);
  55. rpc = furi_record_open("rpc");
  56. for(int i = 0; !session && (i < 10000); ++i) {
  57. session = rpc_session_open(rpc);
  58. delay(1);
  59. }
  60. furi_assert(session);
  61. output_stream = xStreamBufferCreate(1000, 1);
  62. mu_assert(session, "failed to start session");
  63. rpc_session_set_send_bytes_callback(session, output_bytes_callback);
  64. rpc_session_set_context(session, output_stream);
  65. }
  66. static void test_rpc_teardown(void) {
  67. rpc_session_close(session);
  68. furi_record_close("rpc");
  69. vStreamBufferDelete(output_stream);
  70. ++command_id;
  71. output_stream = NULL;
  72. rpc = NULL;
  73. session = NULL;
  74. }
  75. static void test_rpc_storage_setup(void) {
  76. test_rpc_setup();
  77. Storage* fs_api = furi_record_open("storage");
  78. clean_directory(fs_api, TEST_DIR_NAME);
  79. furi_record_close("storage");
  80. }
  81. static void test_rpc_storage_teardown(void) {
  82. Storage* fs_api = furi_record_open("storage");
  83. clean_directory(fs_api, TEST_DIR_NAME);
  84. furi_record_close("storage");
  85. test_rpc_teardown();
  86. }
  87. static void clean_directory(Storage* fs_api, const char* clean_dir) {
  88. furi_assert(fs_api);
  89. furi_assert(clean_dir);
  90. File* dir = storage_file_alloc(fs_api);
  91. if(storage_dir_open(dir, clean_dir)) {
  92. FileInfo fileinfo;
  93. char* name = furi_alloc(MAX_NAME_LENGTH + 1);
  94. while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
  95. char* fullname = furi_alloc(strlen(clean_dir) + strlen(name) + 1 + 1);
  96. sprintf(fullname, "%s/%s", clean_dir, name);
  97. if(fileinfo.flags & FSF_DIRECTORY) {
  98. clean_directory(fs_api, fullname);
  99. }
  100. FS_Error error = storage_common_remove(fs_api, fullname);
  101. furi_assert(error == FSE_OK);
  102. free(fullname);
  103. }
  104. free(name);
  105. } else {
  106. FS_Error error = storage_common_mkdir(fs_api, clean_dir);
  107. (void)error;
  108. furi_assert(error == FSE_OK);
  109. }
  110. storage_dir_close(dir);
  111. storage_file_free(dir);
  112. }
  113. static void test_rpc_print_message_list(MsgList_t msg_list) {
  114. #if DEBUG_PRINT
  115. MsgList_reverse(msg_list);
  116. for
  117. M_EACH(msg, msg_list, MsgList_t) {
  118. rpc_print_message(msg);
  119. }
  120. MsgList_reverse(msg_list);
  121. #endif
  122. }
  123. static PB_CommandStatus test_rpc_storage_get_file_error(File* file) {
  124. FS_Error fs_error = storage_file_get_error(file);
  125. PB_CommandStatus pb_error;
  126. switch(fs_error) {
  127. case FSE_OK:
  128. pb_error = PB_CommandStatus_OK;
  129. break;
  130. case FSE_INVALID_NAME:
  131. pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;
  132. break;
  133. case FSE_INVALID_PARAMETER:
  134. pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;
  135. break;
  136. case FSE_INTERNAL:
  137. pb_error = PB_CommandStatus_ERROR_STORAGE_INTERNAL;
  138. break;
  139. case FSE_ALREADY_OPEN:
  140. pb_error = PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN;
  141. break;
  142. case FSE_DENIED:
  143. pb_error = PB_CommandStatus_ERROR_STORAGE_DENIED;
  144. break;
  145. case FSE_EXIST:
  146. pb_error = PB_CommandStatus_ERROR_STORAGE_EXIST;
  147. break;
  148. case FSE_NOT_EXIST:
  149. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_EXIST;
  150. break;
  151. case FSE_NOT_READY:
  152. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_READY;
  153. break;
  154. case FSE_NOT_IMPLEMENTED:
  155. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED;
  156. break;
  157. default:
  158. pb_error = PB_CommandStatus_ERROR;
  159. break;
  160. }
  161. return pb_error;
  162. }
  163. static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size) {
  164. StreamBufferHandle_t stream_buffer = ctx;
  165. size_t bytes_sent = xStreamBufferSend(stream_buffer, got_bytes, got_size, osWaitForever);
  166. (void)bytes_sent;
  167. furi_assert(bytes_sent == got_size);
  168. }
  169. static void test_rpc_add_ping_to_list(MsgList_t msg_list, bool request, uint32_t command_id) {
  170. PB_Main* response = MsgList_push_new(msg_list);
  171. response->command_id = command_id;
  172. response->command_status = PB_CommandStatus_OK;
  173. response->cb_content.funcs.encode = NULL;
  174. response->has_next = false;
  175. response->which_content = (request == PING_REQUEST) ? PB_Main_ping_request_tag :
  176. PB_Main_ping_response_tag;
  177. }
  178. static void test_rpc_create_simple_message(
  179. PB_Main* message,
  180. uint16_t tag,
  181. const char* str,
  182. uint32_t command_id) {
  183. furi_assert(message);
  184. char* str_copy = NULL;
  185. if(str) {
  186. str_copy = furi_alloc(strlen(str) + 1);
  187. strcpy(str_copy, str);
  188. }
  189. message->command_id = command_id;
  190. message->command_status = PB_CommandStatus_OK;
  191. message->cb_content.funcs.encode = NULL;
  192. message->which_content = tag;
  193. message->has_next = false;
  194. switch(tag) {
  195. case PB_Main_storage_list_request_tag:
  196. message->content.storage_list_request.path = str_copy;
  197. break;
  198. case PB_Main_storage_mkdir_request_tag:
  199. message->content.storage_mkdir_request.path = str_copy;
  200. break;
  201. case PB_Main_storage_read_request_tag:
  202. message->content.storage_read_request.path = str_copy;
  203. break;
  204. case PB_Main_storage_delete_request_tag:
  205. message->content.storage_delete_request.path = str_copy;
  206. break;
  207. case PB_Main_storage_md5sum_request_tag:
  208. message->content.storage_md5sum_request.path = str_copy;
  209. break;
  210. case PB_Main_storage_md5sum_response_tag: {
  211. char* md5sum = message->content.storage_md5sum_response.md5sum;
  212. size_t md5sum_size = sizeof(message->content.storage_md5sum_response.md5sum);
  213. furi_assert((strlen(str) + 1) <= md5sum_size);
  214. memcpy(md5sum, str_copy, md5sum_size);
  215. free(str_copy);
  216. break;
  217. }
  218. default:
  219. furi_assert(0);
  220. break;
  221. }
  222. }
  223. static void test_rpc_add_read_or_write_to_list(
  224. MsgList_t msg_list,
  225. bool write,
  226. const char* path,
  227. const uint8_t* pattern,
  228. size_t pattern_size,
  229. size_t pattern_repeats,
  230. uint32_t command_id) {
  231. furi_assert(pattern_repeats > 0);
  232. do {
  233. PB_Main* request = MsgList_push_new(msg_list);
  234. PB_Storage_File* msg_file = NULL;
  235. request->command_id = command_id;
  236. request->command_status = PB_CommandStatus_OK;
  237. if(write == WRITE_REQUEST) {
  238. size_t path_size = strlen(path) + 1;
  239. request->content.storage_write_request.path = furi_alloc(path_size);
  240. strncpy(request->content.storage_write_request.path, path, path_size);
  241. request->which_content = PB_Main_storage_write_request_tag;
  242. request->content.storage_write_request.has_file = true;
  243. msg_file = &request->content.storage_write_request.file;
  244. } else {
  245. request->which_content = PB_Main_storage_read_response_tag;
  246. request->content.storage_read_response.has_file = true;
  247. msg_file = &request->content.storage_read_response.file;
  248. }
  249. msg_file->data = furi_alloc(PB_BYTES_ARRAY_T_ALLOCSIZE(pattern_size));
  250. msg_file->data->size = pattern_size;
  251. memcpy(msg_file->data->bytes, pattern, pattern_size);
  252. --pattern_repeats;
  253. request->has_next = (pattern_repeats > 0);
  254. } while(pattern_repeats);
  255. }
  256. static void test_rpc_encode_and_feed_one(PB_Main* request) {
  257. furi_assert(request);
  258. pb_ostream_t ostream = PB_OSTREAM_SIZING;
  259. bool result = pb_encode_ex(&ostream, &PB_Main_msg, request, PB_ENCODE_DELIMITED);
  260. furi_check(result && ostream.bytes_written);
  261. uint8_t* buffer = furi_alloc(ostream.bytes_written);
  262. ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written);
  263. pb_encode_ex(&ostream, &PB_Main_msg, request, PB_ENCODE_DELIMITED);
  264. size_t bytes_left = ostream.bytes_written;
  265. uint8_t* buffer_ptr = buffer;
  266. do {
  267. size_t bytes_sent = rpc_session_feed(session, buffer_ptr, bytes_left, 1000);
  268. mu_check(bytes_sent > 0);
  269. bytes_left -= bytes_sent;
  270. buffer_ptr += bytes_sent;
  271. } while(bytes_left);
  272. free(buffer);
  273. pb_release(&PB_Main_msg, request);
  274. }
  275. static void test_rpc_encode_and_feed(MsgList_t msg_list) {
  276. MsgList_reverse(msg_list);
  277. for
  278. M_EACH(request, msg_list, MsgList_t) {
  279. test_rpc_encode_and_feed_one(request);
  280. }
  281. MsgList_reverse(msg_list);
  282. }
  283. static void
  284. test_rpc_compare_file(PB_Storage_File* result_msg_file, PB_Storage_File* expected_msg_file) {
  285. mu_check(!result_msg_file->name == !expected_msg_file->name);
  286. if(result_msg_file->name) {
  287. mu_check(!strcmp(result_msg_file->name, expected_msg_file->name));
  288. }
  289. mu_check(result_msg_file->size == expected_msg_file->size);
  290. mu_check(result_msg_file->type == expected_msg_file->type);
  291. mu_check(!result_msg_file->data == !expected_msg_file->data);
  292. mu_check(result_msg_file->data->size == expected_msg_file->data->size);
  293. for(int i = 0; i < result_msg_file->data->size; ++i) {
  294. mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]);
  295. }
  296. }
  297. static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) {
  298. mu_check(result->command_id == expected->command_id);
  299. mu_check(result->command_status == expected->command_status);
  300. mu_check(result->has_next == expected->has_next);
  301. mu_check(result->which_content == expected->which_content);
  302. if(result->command_status != PB_CommandStatus_OK) {
  303. mu_check(result->which_content == PB_Main_empty_tag);
  304. }
  305. switch(result->which_content) {
  306. case PB_Main_empty_tag:
  307. case PB_Main_ping_response_tag:
  308. /* nothing to check */
  309. break;
  310. case PB_Main_ping_request_tag:
  311. case PB_Main_storage_list_request_tag:
  312. case PB_Main_storage_read_request_tag:
  313. case PB_Main_storage_write_request_tag:
  314. case PB_Main_storage_delete_request_tag:
  315. case PB_Main_storage_mkdir_request_tag:
  316. case PB_Main_storage_md5sum_request_tag:
  317. /* rpc doesn't send it */
  318. mu_check(0);
  319. break;
  320. case PB_Main_app_lock_status_response_tag: {
  321. bool result_locked = result->content.app_lock_status_response.locked;
  322. bool expected_locked = expected->content.app_lock_status_response.locked;
  323. mu_check(result_locked == expected_locked);
  324. break;
  325. }
  326. case PB_Main_storage_read_response_tag: {
  327. bool result_has_msg_file = result->content.storage_read_response.has_file;
  328. bool expected_has_msg_file = expected->content.storage_read_response.has_file;
  329. mu_check(result_has_msg_file == expected_has_msg_file);
  330. if(result_has_msg_file) {
  331. PB_Storage_File* result_msg_file = &result->content.storage_read_response.file;
  332. PB_Storage_File* expected_msg_file = &expected->content.storage_read_response.file;
  333. test_rpc_compare_file(result_msg_file, expected_msg_file);
  334. } else {
  335. mu_check(0);
  336. }
  337. } break;
  338. case PB_Main_storage_list_response_tag: {
  339. size_t expected_msg_files = expected->content.storage_list_response.file_count;
  340. size_t result_msg_files = result->content.storage_list_response.file_count;
  341. mu_check(result_msg_files == expected_msg_files);
  342. for(int i = 0; i < expected_msg_files; ++i) {
  343. PB_Storage_File* result_msg_file = &result->content.storage_list_response.file[i];
  344. PB_Storage_File* expected_msg_file = &expected->content.storage_list_response.file[i];
  345. test_rpc_compare_file(result_msg_file, expected_msg_file);
  346. }
  347. break;
  348. }
  349. case PB_Main_storage_md5sum_response_tag: {
  350. char* result_md5sum = result->content.storage_md5sum_response.md5sum;
  351. char* expected_md5sum = expected->content.storage_md5sum_response.md5sum;
  352. mu_check(!strcmp(result_md5sum, expected_md5sum));
  353. break;
  354. }
  355. default:
  356. furi_assert(0);
  357. break;
  358. }
  359. }
  360. static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
  361. StreamBufferHandle_t stream_buffer = istream->state;
  362. size_t bytes_received = 0;
  363. bytes_received = xStreamBufferReceive(stream_buffer, buf, count, MAX_RECEIVE_OUTPUT_TIMEOUT);
  364. return (count == bytes_received);
  365. }
  366. static void
  367. test_rpc_storage_list_create_expected_list_root(MsgList_t msg_list, uint32_t command_id) {
  368. PB_Main* message = MsgList_push_new(msg_list);
  369. message->has_next = false;
  370. message->cb_content.funcs.encode = NULL;
  371. message->command_id = command_id;
  372. message->which_content = PB_Main_storage_list_response_tag;
  373. message->content.storage_list_response.file_count = 3;
  374. message->content.storage_list_response.file[0].data = NULL;
  375. message->content.storage_list_response.file[1].data = NULL;
  376. message->content.storage_list_response.file[2].data = NULL;
  377. message->content.storage_list_response.file[0].size = 0;
  378. message->content.storage_list_response.file[1].size = 0;
  379. message->content.storage_list_response.file[2].size = 0;
  380. message->content.storage_list_response.file[0].type = PB_Storage_File_FileType_DIR;
  381. message->content.storage_list_response.file[1].type = PB_Storage_File_FileType_DIR;
  382. message->content.storage_list_response.file[2].type = PB_Storage_File_FileType_DIR;
  383. char* str = furi_alloc(4);
  384. strcpy(str, "any");
  385. message->content.storage_list_response.file[0].name = str;
  386. str = furi_alloc(4);
  387. strcpy(str, "int");
  388. message->content.storage_list_response.file[1].name = str;
  389. str = furi_alloc(4);
  390. strcpy(str, "ext");
  391. message->content.storage_list_response.file[2].name = str;
  392. }
  393. static void test_rpc_storage_list_create_expected_list(
  394. MsgList_t msg_list,
  395. const char* path,
  396. uint32_t command_id) {
  397. Storage* fs_api = furi_record_open("storage");
  398. File* dir = storage_file_alloc(fs_api);
  399. PB_Main response = {
  400. .command_id = command_id,
  401. .has_next = false,
  402. .which_content = PB_Main_storage_list_request_tag,
  403. /* other fields (e.g. msg_files ptrs) explicitly initialized by 0 */
  404. };
  405. PB_Storage_ListResponse* list = &response.content.storage_list_response;
  406. response.which_content = PB_Main_storage_list_response_tag;
  407. bool finish = false;
  408. int i = 0;
  409. if(storage_dir_open(dir, path)) {
  410. response.command_status = PB_CommandStatus_OK;
  411. } else {
  412. response.command_status = test_rpc_storage_get_file_error(dir);
  413. response.which_content = PB_Main_empty_tag;
  414. finish = true;
  415. }
  416. while(!finish) {
  417. FileInfo fileinfo;
  418. char* name = furi_alloc(MAX_NAME_LENGTH + 1);
  419. if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
  420. if(i == COUNT_OF(list->file)) {
  421. list->file_count = i;
  422. response.has_next = true;
  423. MsgList_push_back(msg_list, response);
  424. i = 0;
  425. }
  426. list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR :
  427. PB_Storage_File_FileType_FILE;
  428. list->file[i].size = fileinfo.size;
  429. list->file[i].data = NULL;
  430. /* memory free inside rpc_encode_and_send() -> pb_release() */
  431. list->file[i].name = name;
  432. ++i;
  433. } else {
  434. finish = true;
  435. free(name);
  436. }
  437. }
  438. list->file_count = i;
  439. response.has_next = false;
  440. MsgList_push_back(msg_list, response);
  441. storage_dir_close(dir);
  442. storage_file_free(dir);
  443. furi_record_close("storage");
  444. }
  445. static void test_rpc_decode_and_compare(MsgList_t expected_msg_list) {
  446. furi_assert(!MsgList_empty_p(expected_msg_list));
  447. pb_istream_t istream = {
  448. .callback = test_rpc_pb_stream_read,
  449. .state = output_stream,
  450. .errmsg = NULL,
  451. .bytes_left = 0x7FFFFFFF,
  452. };
  453. /* other fields explicitly initialized by 0 */
  454. PB_Main result = {.cb_content.funcs.decode = NULL};
  455. /* mlib adds msg_files into start of list, so reverse it */
  456. MsgList_reverse(expected_msg_list);
  457. for
  458. M_EACH(expected_msg, expected_msg_list, MsgList_t) {
  459. if(!pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) {
  460. mu_assert(
  461. 0,
  462. "not all expected messages decoded (maybe increase MAX_RECEIVE_OUTPUT_TIMEOUT)");
  463. break;
  464. }
  465. test_rpc_compare_messages(&result, expected_msg);
  466. pb_release(&PB_Main_msg, &result);
  467. }
  468. MsgList_reverse(expected_msg_list);
  469. }
  470. static void test_rpc_free_msg_list(MsgList_t msg_list) {
  471. for
  472. M_EACH(it, msg_list, MsgList_t) {
  473. pb_release(&PB_Main_msg, it);
  474. }
  475. MsgList_clear(msg_list);
  476. }
  477. static void test_rpc_storage_list_run(const char* path, uint32_t command_id) {
  478. PB_Main request;
  479. MsgList_t expected_msg_list;
  480. MsgList_init(expected_msg_list);
  481. test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id);
  482. if(!strcmp(path, "/")) {
  483. test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id);
  484. } else {
  485. test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id);
  486. }
  487. test_rpc_encode_and_feed_one(&request);
  488. test_rpc_decode_and_compare(expected_msg_list);
  489. pb_release(&PB_Main_msg, &request);
  490. test_rpc_free_msg_list(expected_msg_list);
  491. }
  492. MU_TEST(test_storage_list) {
  493. test_rpc_storage_list_run("/", ++command_id);
  494. test_rpc_storage_list_run("/ext/nfc", ++command_id);
  495. test_rpc_storage_list_run("/int", ++command_id);
  496. test_rpc_storage_list_run("/ext", ++command_id);
  497. test_rpc_storage_list_run("/ext/irda", ++command_id);
  498. test_rpc_storage_list_run("/ext/ibutton", ++command_id);
  499. test_rpc_storage_list_run("/ext/lfrfid", ++command_id);
  500. test_rpc_storage_list_run("error_path", ++command_id);
  501. }
  502. static void
  503. test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id) {
  504. PB_Main* response = MsgList_push_new(msg_list);
  505. response->command_id = command_id;
  506. response->command_status = status;
  507. response->cb_content.funcs.encode = NULL;
  508. response->has_next = false;
  509. response->which_content = PB_Main_empty_tag;
  510. }
  511. static void test_rpc_add_read_to_list_by_reading_real_file(
  512. MsgList_t msg_list,
  513. const char* path,
  514. uint32_t command_id) {
  515. furi_assert(MsgList_empty_p(msg_list));
  516. Storage* fs_api = furi_record_open("storage");
  517. File* file = storage_file_alloc(fs_api);
  518. bool result = false;
  519. if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  520. size_t size_left = storage_file_size(file);
  521. do {
  522. PB_Main* response = MsgList_push_new(msg_list);
  523. response->command_id = command_id;
  524. response->command_status = PB_CommandStatus_OK;
  525. response->has_next = false;
  526. response->which_content = PB_Main_storage_read_response_tag;
  527. response->content.storage_read_response.has_file = true;
  528. response->content.storage_read_response.file.data =
  529. furi_alloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE)));
  530. uint8_t* buffer = response->content.storage_read_response.file.data->bytes;
  531. uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;
  532. size_t read_size = MIN(size_left, MAX_DATA_SIZE);
  533. *read_size_msg = storage_file_read(file, buffer, read_size);
  534. size_left -= read_size;
  535. result = (*read_size_msg == read_size);
  536. if(result) {
  537. response->has_next = (size_left > 0);
  538. }
  539. } while((size_left != 0) && result);
  540. if(!result) {
  541. test_rpc_add_empty_to_list(
  542. msg_list, test_rpc_storage_get_file_error(file), command_id);
  543. }
  544. } else {
  545. test_rpc_add_empty_to_list(msg_list, test_rpc_storage_get_file_error(file), command_id);
  546. }
  547. storage_file_close(file);
  548. storage_file_free(file);
  549. furi_record_close("storage");
  550. }
  551. static void test_storage_read_run(const char* path, uint32_t command_id) {
  552. PB_Main request;
  553. MsgList_t expected_msg_list;
  554. MsgList_init(expected_msg_list);
  555. test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id);
  556. test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id);
  557. test_rpc_encode_and_feed_one(&request);
  558. test_rpc_decode_and_compare(expected_msg_list);
  559. pb_release(&PB_Main_msg, &request);
  560. test_rpc_free_msg_list(expected_msg_list);
  561. }
  562. static bool test_is_exists(const char* path) {
  563. Storage* fs_api = furi_record_open("storage");
  564. FileInfo fileinfo;
  565. FS_Error result = storage_common_stat(fs_api, path, &fileinfo);
  566. furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST));
  567. return result == FSE_OK;
  568. }
  569. static void test_create_dir(const char* path) {
  570. Storage* fs_api = furi_record_open("storage");
  571. FS_Error error = storage_common_mkdir(fs_api, path);
  572. (void)error;
  573. furi_assert((error == FSE_OK) || (error == FSE_EXIST));
  574. furi_record_close("storage");
  575. furi_check(test_is_exists(path));
  576. }
  577. static void test_create_file(const char* path, size_t size) {
  578. Storage* fs_api = furi_record_open("storage");
  579. File* file = storage_file_alloc(fs_api);
  580. if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  581. uint8_t buf[128] = {0};
  582. for(int i = 0; i < sizeof(buf); ++i) {
  583. buf[i] = '0' + (i % 10);
  584. }
  585. while(size) {
  586. size_t written = storage_file_write(file, buf, MIN(size, sizeof(buf)));
  587. furi_assert(written);
  588. size -= written;
  589. }
  590. }
  591. storage_file_close(file);
  592. storage_file_free(file);
  593. furi_record_close("storage");
  594. furi_check(test_is_exists(path));
  595. }
  596. MU_TEST(test_storage_read) {
  597. test_create_file(TEST_DIR "empty.txt", 0);
  598. test_create_file(TEST_DIR "file1.txt", 1);
  599. test_create_file(TEST_DIR "file2.txt", MAX_DATA_SIZE);
  600. test_create_file(TEST_DIR "file3.txt", MAX_DATA_SIZE + 1);
  601. test_create_file(TEST_DIR "file4.txt", (MAX_DATA_SIZE * 2) + 1);
  602. test_storage_read_run(TEST_DIR "empty.txt", ++command_id);
  603. test_storage_read_run(TEST_DIR "file1.txt", ++command_id);
  604. test_storage_read_run(TEST_DIR "file2.txt", ++command_id);
  605. test_storage_read_run(TEST_DIR "file3.txt", ++command_id);
  606. test_storage_read_run(TEST_DIR "file4.txt", ++command_id);
  607. }
  608. static void test_storage_write_run(
  609. const char* path,
  610. size_t write_size,
  611. size_t write_count,
  612. uint32_t command_id,
  613. PB_CommandStatus status) {
  614. MsgList_t input_msg_list;
  615. MsgList_init(input_msg_list);
  616. MsgList_t expected_msg_list;
  617. MsgList_init(expected_msg_list);
  618. uint8_t* buf = furi_alloc(write_size);
  619. for(int i = 0; i < write_size; ++i) {
  620. buf[i] = '0' + (i % 10);
  621. }
  622. test_rpc_add_read_or_write_to_list(
  623. input_msg_list, WRITE_REQUEST, path, buf, write_size, write_count, command_id);
  624. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  625. test_rpc_encode_and_feed(input_msg_list);
  626. test_rpc_decode_and_compare(expected_msg_list);
  627. test_rpc_free_msg_list(input_msg_list);
  628. test_rpc_free_msg_list(expected_msg_list);
  629. free(buf);
  630. }
  631. static void test_storage_write_read_run(
  632. const char* path,
  633. const uint8_t* pattern,
  634. size_t pattern_size,
  635. size_t pattern_repeats,
  636. uint32_t* command_id) {
  637. MsgList_t input_msg_list;
  638. MsgList_init(input_msg_list);
  639. MsgList_t expected_msg_list;
  640. MsgList_init(expected_msg_list);
  641. test_rpc_add_read_or_write_to_list(
  642. input_msg_list, WRITE_REQUEST, path, pattern, pattern_size, pattern_repeats, ++*command_id);
  643. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id);
  644. test_rpc_create_simple_message(
  645. MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id);
  646. test_rpc_add_read_or_write_to_list(
  647. expected_msg_list,
  648. READ_RESPONSE,
  649. path,
  650. pattern,
  651. pattern_size,
  652. pattern_repeats,
  653. *command_id);
  654. test_rpc_print_message_list(input_msg_list);
  655. test_rpc_print_message_list(expected_msg_list);
  656. test_rpc_encode_and_feed(input_msg_list);
  657. test_rpc_decode_and_compare(expected_msg_list);
  658. test_rpc_free_msg_list(input_msg_list);
  659. test_rpc_free_msg_list(expected_msg_list);
  660. }
  661. MU_TEST(test_storage_write_read) {
  662. uint8_t pattern1[] = "abcdefgh";
  663. test_storage_write_read_run(TEST_DIR "test1.txt", pattern1, sizeof(pattern1), 1, &command_id);
  664. test_storage_write_read_run(TEST_DIR "test2.txt", pattern1, 1, 1, &command_id);
  665. test_storage_write_read_run(TEST_DIR "test3.txt", pattern1, 0, 1, &command_id);
  666. }
  667. MU_TEST(test_storage_write) {
  668. test_storage_write_run(
  669. TEST_DIR "afaefo/aefaef/aef/aef/test1.txt",
  670. 1,
  671. 1,
  672. ++command_id,
  673. PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
  674. test_storage_write_run(TEST_DIR "test1.txt", 100, 1, ++command_id, PB_CommandStatus_OK);
  675. test_storage_write_run(TEST_DIR "test2.txt", 100, 3, ++command_id, PB_CommandStatus_OK);
  676. test_storage_write_run(TEST_DIR "test1.txt", 100, 3, ++command_id, PB_CommandStatus_OK);
  677. test_storage_write_run(TEST_DIR "test2.txt", 100, 3, ++command_id, PB_CommandStatus_OK);
  678. test_storage_write_run(
  679. TEST_DIR "afaefo/aefaef/aef/aef/test1.txt",
  680. 1,
  681. 1,
  682. ++command_id,
  683. PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
  684. test_storage_write_run(TEST_DIR "test2.txt", 1, 50, ++command_id, PB_CommandStatus_OK);
  685. test_storage_write_run(TEST_DIR "test2.txt", 512, 3, ++command_id, PB_CommandStatus_OK);
  686. }
  687. MU_TEST(test_storage_interrupt_continuous_same_system) {
  688. MsgList_t input_msg_list;
  689. MsgList_init(input_msg_list);
  690. MsgList_t expected_msg_list;
  691. MsgList_init(expected_msg_list);
  692. uint8_t pattern[16] = {0};
  693. test_rpc_add_read_or_write_to_list(
  694. input_msg_list,
  695. WRITE_REQUEST,
  696. TEST_DIR "test1.txt",
  697. pattern,
  698. sizeof(pattern),
  699. 3,
  700. command_id);
  701. /* replace last packet (has_next == false) with another command */
  702. PB_Main message_to_remove;
  703. MsgList_pop_back(&message_to_remove, input_msg_list);
  704. pb_release(&PB_Main_msg, &message_to_remove);
  705. test_rpc_create_simple_message(
  706. MsgList_push_new(input_msg_list),
  707. PB_Main_storage_mkdir_request_tag,
  708. TEST_DIR "dir1",
  709. command_id + 1);
  710. test_rpc_add_read_or_write_to_list(
  711. input_msg_list,
  712. WRITE_REQUEST,
  713. TEST_DIR "test2.txt",
  714. pattern,
  715. sizeof(pattern),
  716. 3,
  717. command_id);
  718. test_rpc_add_empty_to_list(
  719. expected_msg_list, PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED, command_id);
  720. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id + 1);
  721. test_rpc_encode_and_feed(input_msg_list);
  722. test_rpc_decode_and_compare(expected_msg_list);
  723. test_rpc_free_msg_list(input_msg_list);
  724. test_rpc_free_msg_list(expected_msg_list);
  725. }
  726. MU_TEST(test_storage_interrupt_continuous_another_system) {
  727. MsgList_t input_msg_list;
  728. MsgList_init(input_msg_list);
  729. MsgList_t expected_msg_list;
  730. MsgList_init(expected_msg_list);
  731. uint8_t pattern[16] = {0};
  732. test_rpc_add_read_or_write_to_list(
  733. input_msg_list,
  734. WRITE_REQUEST,
  735. TEST_DIR "test1.txt",
  736. pattern,
  737. sizeof(pattern),
  738. 3,
  739. command_id);
  740. PB_Main message = {
  741. .command_id = command_id + 1,
  742. .command_status = PB_CommandStatus_OK,
  743. .cb_content.funcs.encode = NULL,
  744. .has_next = false,
  745. .which_content = PB_Main_ping_request_tag,
  746. };
  747. MsgList_it_t it;
  748. MsgList_it(it, input_msg_list);
  749. MsgList_next(it);
  750. MsgList_insert(input_msg_list, it, message);
  751. test_rpc_add_read_or_write_to_list(
  752. input_msg_list,
  753. WRITE_REQUEST,
  754. TEST_DIR "test2.txt",
  755. pattern,
  756. sizeof(pattern),
  757. 3,
  758. command_id + 2);
  759. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, command_id + 1);
  760. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id);
  761. test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, command_id + 2);
  762. test_rpc_encode_and_feed(input_msg_list);
  763. test_rpc_decode_and_compare(expected_msg_list);
  764. test_rpc_free_msg_list(input_msg_list);
  765. test_rpc_free_msg_list(expected_msg_list);
  766. }
  767. static void test_storage_delete_run(
  768. const char* path,
  769. size_t command_id,
  770. PB_CommandStatus status,
  771. bool recursive) {
  772. PB_Main request;
  773. MsgList_t expected_msg_list;
  774. MsgList_init(expected_msg_list);
  775. test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
  776. request.content.storage_delete_request.recursive = recursive;
  777. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  778. test_rpc_encode_and_feed_one(&request);
  779. test_rpc_decode_and_compare(expected_msg_list);
  780. pb_release(&PB_Main_msg, &request);
  781. test_rpc_free_msg_list(expected_msg_list);
  782. }
  783. #define TEST_DIR_RMRF_NAME TEST_DIR "rmrf_test"
  784. #define TEST_DIR_RMRF TEST_DIR_RMRF_NAME "/"
  785. MU_TEST(test_storage_delete_recursive) {
  786. test_create_dir(TEST_DIR_RMRF_NAME);
  787. test_create_dir(TEST_DIR_RMRF "dir1");
  788. test_create_file(TEST_DIR_RMRF "dir1/file1", 1);
  789. test_create_dir(TEST_DIR_RMRF "dir1/dir1");
  790. test_create_dir(TEST_DIR_RMRF "dir1/dir2");
  791. test_create_file(TEST_DIR_RMRF "dir1/dir2/file1", 1);
  792. test_create_file(TEST_DIR_RMRF "dir1/dir2/file2", 1);
  793. test_create_dir(TEST_DIR_RMRF "dir1/dir3");
  794. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1");
  795. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1");
  796. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1");
  797. test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1/dir1");
  798. test_create_dir(TEST_DIR_RMRF "dir2");
  799. test_create_dir(TEST_DIR_RMRF "dir2/dir1");
  800. test_create_dir(TEST_DIR_RMRF "dir2/dir2");
  801. test_create_file(TEST_DIR_RMRF "dir2/dir2/file1", 1);
  802. test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1");
  803. test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1");
  804. test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1");
  805. test_create_file(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1/file1", 1);
  806. test_storage_delete_run(
  807. TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY, false);
  808. mu_check(test_is_exists(TEST_DIR_RMRF_NAME));
  809. test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);
  810. mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
  811. test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, false);
  812. mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
  813. test_create_dir(TEST_DIR_RMRF_NAME);
  814. test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true);
  815. mu_check(!test_is_exists(TEST_DIR_RMRF_NAME));
  816. test_create_dir(TEST_DIR "file1");
  817. test_storage_delete_run(TEST_DIR "file1", ++command_id, PB_CommandStatus_OK, true);
  818. mu_check(!test_is_exists(TEST_DIR "file1"));
  819. }
  820. MU_TEST(test_storage_delete) {
  821. test_storage_delete_run(NULL, ++command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS, false);
  822. furi_check(!test_is_exists(TEST_DIR "empty.txt"));
  823. test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false);
  824. mu_check(!test_is_exists(TEST_DIR "empty.txt"));
  825. test_create_file(TEST_DIR "empty.txt", 0);
  826. test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false);
  827. mu_check(!test_is_exists(TEST_DIR "empty.txt"));
  828. furi_check(!test_is_exists(TEST_DIR "dir1"));
  829. test_create_dir(TEST_DIR "dir1");
  830. test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false);
  831. mu_check(!test_is_exists(TEST_DIR "dir1"));
  832. test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false);
  833. mu_check(!test_is_exists(TEST_DIR "dir1"));
  834. }
  835. static void test_storage_mkdir_run(const char* path, size_t command_id, PB_CommandStatus status) {
  836. PB_Main request;
  837. MsgList_t expected_msg_list;
  838. MsgList_init(expected_msg_list);
  839. test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id);
  840. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  841. test_rpc_encode_and_feed_one(&request);
  842. test_rpc_decode_and_compare(expected_msg_list);
  843. pb_release(&PB_Main_msg, &request);
  844. test_rpc_free_msg_list(expected_msg_list);
  845. }
  846. MU_TEST(test_storage_mkdir) {
  847. furi_check(!test_is_exists(TEST_DIR "dir1"));
  848. test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK);
  849. mu_check(test_is_exists(TEST_DIR "dir1"));
  850. test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
  851. mu_check(test_is_exists(TEST_DIR "dir1"));
  852. furi_check(!test_is_exists(TEST_DIR "dir2"));
  853. test_create_dir(TEST_DIR "dir2");
  854. test_storage_mkdir_run(TEST_DIR "dir2", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST);
  855. mu_check(test_is_exists(TEST_DIR "dir2"));
  856. }
  857. static void test_storage_calculate_md5sum(const char* path, char* md5sum) {
  858. Storage* api = furi_record_open("storage");
  859. File* file = storage_file_alloc(api);
  860. if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  861. const uint16_t once_read_size = 512;
  862. const uint8_t hash_size = MD5SUM_SIZE;
  863. uint8_t* data = malloc(once_read_size);
  864. uint8_t* hash = malloc(sizeof(uint8_t) * hash_size);
  865. md5_context* md5_ctx = malloc(sizeof(md5_context));
  866. md5_starts(md5_ctx);
  867. while(true) {
  868. uint16_t read_size = storage_file_read(file, data, once_read_size);
  869. if(read_size == 0) break;
  870. md5_update(md5_ctx, data, read_size);
  871. }
  872. md5_finish(md5_ctx, hash);
  873. free(md5_ctx);
  874. for(uint8_t i = 0; i < hash_size; i++) {
  875. md5sum += sprintf(md5sum, "%02x", hash[i]);
  876. }
  877. free(hash);
  878. free(data);
  879. } else {
  880. furi_assert(0);
  881. }
  882. storage_file_close(file);
  883. storage_file_free(file);
  884. furi_record_close("storage");
  885. }
  886. static void test_storage_md5sum_run(
  887. const char* path,
  888. uint32_t command_id,
  889. const char* md5sum,
  890. PB_CommandStatus status) {
  891. PB_Main request;
  892. MsgList_t expected_msg_list;
  893. MsgList_init(expected_msg_list);
  894. test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id);
  895. if(status == PB_CommandStatus_OK) {
  896. PB_Main* response = MsgList_push_new(expected_msg_list);
  897. test_rpc_create_simple_message(
  898. response, PB_Main_storage_md5sum_response_tag, md5sum, command_id);
  899. response->command_status = status;
  900. } else {
  901. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  902. }
  903. test_rpc_encode_and_feed_one(&request);
  904. test_rpc_decode_and_compare(expected_msg_list);
  905. pb_release(&PB_Main_msg, &request);
  906. test_rpc_free_msg_list(expected_msg_list);
  907. }
  908. MU_TEST(test_storage_md5sum) {
  909. char md5sum1[MD5SUM_SIZE * 2 + 1] = {0};
  910. char md5sum2[MD5SUM_SIZE * 2 + 1] = {0};
  911. char md5sum3[MD5SUM_SIZE * 2 + 1] = {0};
  912. test_storage_md5sum_run(
  913. TEST_DIR "test1.txt", ++command_id, "", PB_CommandStatus_ERROR_STORAGE_NOT_EXIST);
  914. test_create_file(TEST_DIR "file1.txt", 0);
  915. test_create_file(TEST_DIR "file2.txt", 1);
  916. test_create_file(TEST_DIR "file3.txt", 512);
  917. test_storage_calculate_md5sum(TEST_DIR "file1.txt", md5sum1);
  918. test_storage_calculate_md5sum(TEST_DIR "file2.txt", md5sum2);
  919. test_storage_calculate_md5sum(TEST_DIR "file3.txt", md5sum3);
  920. test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK);
  921. test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK);
  922. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  923. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  924. test_storage_md5sum_run(TEST_DIR "file3.txt", ++command_id, md5sum3, PB_CommandStatus_OK);
  925. test_storage_md5sum_run(TEST_DIR "file3.txt", ++command_id, md5sum3, PB_CommandStatus_OK);
  926. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  927. test_storage_md5sum_run(TEST_DIR "file3.txt", ++command_id, md5sum3, PB_CommandStatus_OK);
  928. test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK);
  929. test_storage_md5sum_run(TEST_DIR "file2.txt", ++command_id, md5sum2, PB_CommandStatus_OK);
  930. }
  931. MU_TEST(test_ping) {
  932. MsgList_t input_msg_list;
  933. MsgList_init(input_msg_list);
  934. MsgList_t expected_msg_list;
  935. MsgList_init(expected_msg_list);
  936. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 0);
  937. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 1);
  938. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 0);
  939. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 500);
  940. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, (uint32_t)-1);
  941. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 700);
  942. test_rpc_add_ping_to_list(input_msg_list, PING_REQUEST, 1);
  943. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 0);
  944. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 1);
  945. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 0);
  946. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 500);
  947. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, (uint32_t)-1);
  948. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 700);
  949. test_rpc_add_ping_to_list(expected_msg_list, PING_RESPONSE, 1);
  950. test_rpc_encode_and_feed(input_msg_list);
  951. test_rpc_decode_and_compare(expected_msg_list);
  952. test_rpc_free_msg_list(input_msg_list);
  953. test_rpc_free_msg_list(expected_msg_list);
  954. }
  955. // TODO: 1) test for rubbish data
  956. // 2) test for unexpected end of packet
  957. // 3) test for one push of several packets
  958. // 4) test for fill buffer till end (great varint) and close connection
  959. MU_TEST_SUITE(test_rpc_status) {
  960. MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);
  961. MU_RUN_TEST(test_ping);
  962. }
  963. MU_TEST_SUITE(test_rpc_storage) {
  964. MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown);
  965. MU_RUN_TEST(test_storage_list);
  966. MU_RUN_TEST(test_storage_read);
  967. MU_RUN_TEST(test_storage_write_read);
  968. MU_RUN_TEST(test_storage_write);
  969. MU_RUN_TEST(test_storage_delete);
  970. MU_RUN_TEST(test_storage_delete_recursive);
  971. MU_RUN_TEST(test_storage_mkdir);
  972. MU_RUN_TEST(test_storage_md5sum);
  973. MU_RUN_TEST(test_storage_interrupt_continuous_same_system);
  974. MU_RUN_TEST(test_storage_interrupt_continuous_another_system);
  975. }
  976. static void test_app_create_request(
  977. PB_Main* request,
  978. const char* app_name,
  979. const char* app_args,
  980. uint32_t command_id) {
  981. request->command_id = command_id;
  982. request->command_status = PB_CommandStatus_OK;
  983. request->cb_content.funcs.encode = NULL;
  984. request->which_content = PB_Main_app_start_tag;
  985. request->has_next = false;
  986. if(app_name) {
  987. char* msg_app_name = furi_alloc(strlen(app_name) + 1);
  988. strcpy(msg_app_name, app_name);
  989. request->content.app_start.name = msg_app_name;
  990. } else {
  991. request->content.app_start.name = NULL;
  992. }
  993. if(app_args) {
  994. char* msg_app_args = furi_alloc(strlen(app_args) + 1);
  995. strcpy(msg_app_args, app_args);
  996. request->content.app_start.args = msg_app_args;
  997. } else {
  998. request->content.app_start.args = NULL;
  999. }
  1000. }
  1001. static void test_app_start_run(
  1002. const char* app_name,
  1003. const char* app_args,
  1004. PB_CommandStatus status,
  1005. uint32_t command_id) {
  1006. PB_Main request;
  1007. MsgList_t expected_msg_list;
  1008. MsgList_init(expected_msg_list);
  1009. test_app_create_request(&request, app_name, app_args, command_id);
  1010. test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
  1011. test_rpc_encode_and_feed_one(&request);
  1012. test_rpc_decode_and_compare(expected_msg_list);
  1013. pb_release(&PB_Main_msg, &request);
  1014. test_rpc_free_msg_list(expected_msg_list);
  1015. }
  1016. static void test_app_get_status_lock_run(bool locked_expected, uint32_t command_id) {
  1017. PB_Main request = {
  1018. .command_id = command_id,
  1019. .command_status = PB_CommandStatus_OK,
  1020. .which_content = PB_Main_app_lock_status_request_tag,
  1021. .has_next = false,
  1022. };
  1023. MsgList_t expected_msg_list;
  1024. MsgList_init(expected_msg_list);
  1025. PB_Main* response = MsgList_push_new(expected_msg_list);
  1026. response->command_id = command_id;
  1027. response->command_status = PB_CommandStatus_OK;
  1028. response->which_content = PB_Main_app_lock_status_response_tag;
  1029. response->has_next = false;
  1030. response->content.app_lock_status_response.locked = locked_expected;
  1031. test_rpc_encode_and_feed_one(&request);
  1032. test_rpc_decode_and_compare(expected_msg_list);
  1033. pb_release(&PB_Main_msg, &request);
  1034. test_rpc_free_msg_list(expected_msg_list);
  1035. }
  1036. MU_TEST(test_app_start_and_lock_status) {
  1037. test_app_get_status_lock_run(false, ++command_id);
  1038. test_app_start_run(NULL, "/ext/file", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1039. test_app_start_run(NULL, NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1040. test_app_get_status_lock_run(false, ++command_id);
  1041. test_app_start_run(
  1042. "skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1043. test_app_get_status_lock_run(false, ++command_id);
  1044. test_app_start_run("Delay Test", "0", PB_CommandStatus_OK, ++command_id);
  1045. delay(100);
  1046. test_app_get_status_lock_run(false, ++command_id);
  1047. test_app_start_run("Delay Test", "200", PB_CommandStatus_OK, ++command_id);
  1048. test_app_get_status_lock_run(true, ++command_id);
  1049. delay(100);
  1050. test_app_get_status_lock_run(true, ++command_id);
  1051. test_app_start_run("Delay Test", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
  1052. delay(200);
  1053. test_app_get_status_lock_run(false, ++command_id);
  1054. test_app_start_run("Delay Test", "500", PB_CommandStatus_OK, ++command_id);
  1055. delay(100);
  1056. test_app_get_status_lock_run(true, ++command_id);
  1057. test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id);
  1058. delay(100);
  1059. test_app_get_status_lock_run(true, ++command_id);
  1060. test_app_start_run(
  1061. "2_girls_1_app", "0", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id);
  1062. delay(100);
  1063. test_app_get_status_lock_run(true, ++command_id);
  1064. delay(500);
  1065. test_app_get_status_lock_run(false, ++command_id);
  1066. }
  1067. MU_TEST_SUITE(test_rpc_app) {
  1068. MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown);
  1069. MU_RUN_TEST(test_app_start_and_lock_status);
  1070. }
  1071. int run_minunit_test_rpc() {
  1072. Storage* storage = furi_record_open("storage");
  1073. furi_record_close("storage");
  1074. if(storage_sd_status(storage) != FSE_OK) {
  1075. FURI_LOG_E("UNIT_TESTS", "SD card not mounted - skip storage tests");
  1076. } else {
  1077. MU_RUN_SUITE(test_rpc_storage);
  1078. }
  1079. MU_RUN_SUITE(test_rpc_status);
  1080. MU_RUN_SUITE(test_rpc_app);
  1081. return MU_EXIT_CODE;
  1082. }
  1083. int32_t delay_test_app(void* p) {
  1084. int timeout = atoi((const char*)p);
  1085. if(timeout > 0) {
  1086. delay(timeout);
  1087. }
  1088. return 0;
  1089. }