stream_test.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #include <furi.h>
  2. #include <toolbox/stream/stream.h>
  3. #include <toolbox/stream/string_stream.h>
  4. #include <toolbox/stream/file_stream.h>
  5. #include <toolbox/stream/buffered_file_stream.h>
  6. #include <storage/storage.h>
  7. #include "../minunit.h"
  8. static const char* stream_test_data = "I write differently from what I speak, "
  9. "I speak differently from what I think, "
  10. "I think differently from the way I ought to think, "
  11. "and so it all proceeds into deepest darkness.";
  12. static const char* stream_test_left_data = "There are two cardinal human sins ";
  13. static const char* stream_test_right_data =
  14. "from which all others derive: impatience and indolence.";
  15. MU_TEST_1(stream_composite_subtest, Stream* stream) {
  16. const size_t data_size = 128;
  17. uint8_t data[data_size];
  18. FuriString* string_lee;
  19. string_lee = furi_string_alloc_set("lee");
  20. // test that stream is empty
  21. // "" -> ""
  22. mu_check(stream_size(stream) == 0);
  23. mu_check(stream_eof(stream));
  24. mu_check(stream_tell(stream) == 0);
  25. mu_check(stream_read(stream, data, data_size) == 0);
  26. mu_check(stream_eof(stream));
  27. mu_check(stream_tell(stream) == 0);
  28. // write char
  29. // "" -> "2"
  30. mu_check(stream_write_char(stream, '2') == 1);
  31. mu_check(stream_size(stream) == 1);
  32. mu_check(stream_tell(stream) == 1);
  33. mu_check(stream_eof(stream));
  34. // test rewind and eof
  35. stream_rewind(stream);
  36. mu_check(stream_size(stream) == 1);
  37. mu_check(stream_tell(stream) == 0);
  38. mu_check(!stream_eof(stream));
  39. // add another char with replacement
  40. // "2" -> "1"
  41. mu_check(stream_write_char(stream, '1') == 1);
  42. mu_check(stream_size(stream) == 1);
  43. mu_check(stream_tell(stream) == 1);
  44. mu_check(stream_eof(stream));
  45. // write string
  46. // "1" -> "1337_69"
  47. mu_check(stream_write_cstring(stream, "337_69") == 6);
  48. mu_check(stream_size(stream) == 7);
  49. mu_check(stream_tell(stream) == 7);
  50. mu_check(stream_eof(stream));
  51. // read data
  52. memset(data, 0, data_size);
  53. stream_rewind(stream);
  54. mu_check(stream_read(stream, data, data_size) == 7);
  55. mu_check(strcmp((char*)data, "1337_69") == 0);
  56. // test misc seeks
  57. mu_check(stream_seek(stream, 2, StreamOffsetFromStart));
  58. mu_check(stream_tell(stream) == 2);
  59. mu_check(!stream_seek(stream, 9000, StreamOffsetFromStart));
  60. mu_check(stream_tell(stream) == 7);
  61. mu_check(stream_eof(stream));
  62. mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));
  63. mu_check(stream_tell(stream) == 4);
  64. // write string with replacemet
  65. // "1337_69" -> "1337lee"
  66. mu_check(stream_write_string(stream, string_lee) == 3);
  67. mu_check(stream_size(stream) == 7);
  68. mu_check(stream_tell(stream) == 7);
  69. mu_check(stream_eof(stream));
  70. // append char
  71. // "1337lee" -> "1337leet"
  72. mu_check(stream_write(stream, (uint8_t*)"t", 1) == 1);
  73. mu_check(stream_size(stream) == 8);
  74. mu_check(stream_tell(stream) == 8);
  75. mu_check(stream_eof(stream));
  76. // read data
  77. memset(data, 0, data_size);
  78. stream_rewind(stream);
  79. mu_check(stream_read(stream, data, data_size) == 8);
  80. mu_check(strcmp((char*)data, "1337leet") == 0);
  81. mu_check(stream_tell(stream) == 8);
  82. mu_check(stream_eof(stream));
  83. // negative seek from current position -> clamp to 0
  84. mu_check(!stream_seek(stream, -9000, StreamOffsetFromCurrent));
  85. mu_check(stream_tell(stream) == 0);
  86. // negative seek from start position -> clamp to 0
  87. stream_rewind(stream);
  88. mu_check(!stream_seek(stream, -3, StreamOffsetFromStart));
  89. mu_check(stream_tell(stream) == 0);
  90. // zero seek from current position -> clamp to stream size
  91. mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
  92. mu_check(stream_tell(stream) == 8);
  93. // negative seek from end position -> clamp to 0
  94. mu_check(!stream_seek(stream, -9000, StreamOffsetFromEnd));
  95. mu_check(stream_tell(stream) == 0);
  96. // clean stream
  97. stream_clean(stream);
  98. mu_check(stream_size(stream) == 0);
  99. mu_check(stream_eof(stream));
  100. mu_check(stream_tell(stream) == 0);
  101. // write format
  102. // "" -> "dio666"
  103. mu_check(stream_write_format(stream, "%s%d", "dio", 666) == 6);
  104. mu_check(stream_size(stream) == 6);
  105. mu_check(stream_eof(stream));
  106. mu_check(stream_tell(stream) == 6);
  107. // read data
  108. memset(data, 0, data_size);
  109. stream_rewind(stream);
  110. mu_check(stream_read(stream, data, data_size) == 6);
  111. mu_check(strcmp((char*)data, "dio666") == 0);
  112. // clean and write cstring
  113. // "dio666" -> "" -> "1234567890"
  114. stream_clean(stream);
  115. mu_check(stream_write_cstring(stream, "1234567890") == 10);
  116. // delete 4 bytes from 1 pos
  117. // "1xxxx67890" -> "167890"
  118. mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
  119. mu_check(stream_delete(stream, 4));
  120. mu_assert_int_eq(6, stream_size(stream));
  121. // read data
  122. memset(data, 0, data_size);
  123. stream_rewind(stream);
  124. mu_assert_int_eq(6, stream_read(stream, data, data_size));
  125. mu_check(strcmp((char*)data, "167890") == 0);
  126. // write cstring
  127. // "167890" -> "167890It Was Me, Dio!"
  128. mu_check(stream_write_cstring(stream, "It Was Me, Dio!") == 15);
  129. // delete 1337 bytes from 1 pos
  130. // and check that we can delete only 20 bytes
  131. // "1xxxxxxxxxxxxxxxxxxxx" -> "1"
  132. mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
  133. mu_check(stream_delete(stream, 1337));
  134. mu_assert_int_eq(1, stream_size(stream));
  135. // read data
  136. memset(data, 0, data_size);
  137. stream_rewind(stream);
  138. mu_check(stream_read(stream, data, data_size) == 1);
  139. mu_check(strcmp((char*)data, "1") == 0);
  140. // write cstring from 0 pos, replacing 1 byte
  141. // "1" -> "Oh? You're roaching me?"
  142. mu_check(stream_rewind(stream));
  143. mu_assert_int_eq(23, stream_write_cstring(stream, "Oh? You're roaching me?"));
  144. // insert 11 bytes to 0 pos
  145. // "Oh? You're roaching me?" -> "Za Warudo! Oh? You're roaching me?"
  146. mu_check(stream_rewind(stream));
  147. mu_check(stream_insert(stream, (uint8_t*)"Za Warudo! ", 11));
  148. mu_assert_int_eq(34, stream_size(stream));
  149. // read data
  150. memset(data, 0, data_size);
  151. stream_rewind(stream);
  152. mu_assert_int_eq(34, stream_read(stream, data, data_size));
  153. mu_assert_string_eq("Za Warudo! Oh? You're roaching me?", (char*)data);
  154. // insert cstring to 22 pos
  155. // "Za Warudo! Oh? You're roaching me?" -> "Za Warudo! Oh? You're approaching me?"
  156. mu_check(stream_seek(stream, 22, StreamOffsetFromStart));
  157. mu_check(stream_insert_cstring(stream, "app"));
  158. mu_assert_int_eq(37, stream_size(stream));
  159. // read data
  160. memset(data, 0, data_size);
  161. stream_rewind(stream);
  162. mu_assert_int_eq(37, stream_read(stream, data, data_size));
  163. mu_assert_string_eq("Za Warudo! Oh? You're approaching me?", (char*)data);
  164. // insert cstring to the end of the stream
  165. // "Za Warudo! Oh? You're approaching me?" -> "Za Warudo! Oh? You're approaching me? It was me, Dio!"
  166. mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
  167. mu_check(stream_insert_cstring(stream, " It was me, Dio!"));
  168. mu_assert_int_eq(53, stream_size(stream));
  169. // read data
  170. memset(data, 0, data_size);
  171. stream_rewind(stream);
  172. mu_assert_int_eq(53, stream_read(stream, data, data_size));
  173. mu_assert_string_eq("Za Warudo! Oh? You're approaching me? It was me, Dio!", (char*)data);
  174. // delete 168430090 bytes from stream
  175. // and test that we can delete only 53
  176. mu_check(stream_rewind(stream));
  177. mu_check(stream_delete(stream, 0x0A0A0A0A));
  178. mu_assert_int_eq(0, stream_size(stream));
  179. mu_check(stream_eof(stream));
  180. mu_assert_int_eq(0, stream_tell(stream));
  181. // clean stream
  182. stream_clean(stream);
  183. mu_assert_int_eq(0, stream_size(stream));
  184. mu_check(stream_eof(stream));
  185. mu_assert_int_eq(0, stream_tell(stream));
  186. // insert formatted string at the end of stream
  187. // "" -> "dio666"
  188. mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
  189. mu_assert_int_eq(6, stream_size(stream));
  190. mu_check(stream_eof(stream));
  191. mu_assert_int_eq(6, stream_tell(stream));
  192. // insert formatted string at the end of stream
  193. // "dio666" -> "dio666zlo555"
  194. mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
  195. mu_assert_int_eq(12, stream_size(stream));
  196. mu_check(stream_eof(stream));
  197. mu_assert_int_eq(12, stream_tell(stream));
  198. // insert formatted string at the 6 pos
  199. // "dio666" -> "dio666baba13zlo555"
  200. mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
  201. mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
  202. mu_assert_int_eq(18, stream_size(stream));
  203. mu_assert_int_eq(12, stream_tell(stream));
  204. // read data
  205. memset(data, 0, data_size);
  206. stream_rewind(stream);
  207. mu_assert_int_eq(18, stream_read(stream, data, data_size));
  208. mu_assert_string_eq("dio666baba13zlo555", (char*)data);
  209. // delete 6 chars from pos 6 and insert 1 chars
  210. // "dio666baba13zlo555" -> "dio666xzlo555"
  211. mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
  212. mu_check(stream_delete_and_insert_char(stream, 6, 'x'));
  213. mu_assert_int_eq(13, stream_size(stream));
  214. mu_assert_int_eq(7, stream_tell(stream));
  215. // read data
  216. memset(data, 0, data_size);
  217. stream_rewind(stream);
  218. mu_check(stream_read(stream, data, data_size) == 13);
  219. mu_assert_string_eq("dio666xzlo555", (char*)data);
  220. // delete 9000 chars from pos 6 and insert 3 chars from string
  221. // "dio666xzlo555" -> "dio666777"
  222. mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
  223. mu_check(stream_delete_and_insert_cstring(stream, 9000, "777"));
  224. mu_assert_int_eq(9, stream_size(stream));
  225. mu_assert_int_eq(9, stream_tell(stream));
  226. mu_check(stream_eof(stream));
  227. furi_string_free(string_lee);
  228. }
  229. MU_TEST(stream_composite_test) {
  230. // test string stream
  231. Stream* stream;
  232. stream = string_stream_alloc();
  233. MU_RUN_TEST_1(stream_composite_subtest, stream);
  234. stream_free(stream);
  235. // test file stream
  236. Storage* storage = furi_record_open(RECORD_STORAGE);
  237. stream = file_stream_alloc(storage);
  238. mu_check(
  239. file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  240. MU_RUN_TEST_1(stream_composite_subtest, stream);
  241. stream_free(stream);
  242. // test buffered file stream
  243. stream = buffered_file_stream_alloc(storage);
  244. mu_check(buffered_file_stream_open(
  245. stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  246. MU_RUN_TEST_1(stream_composite_subtest, stream);
  247. stream_free(stream);
  248. furi_record_close(RECORD_STORAGE);
  249. }
  250. MU_TEST_1(stream_write_subtest, Stream* stream) {
  251. mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
  252. }
  253. MU_TEST_1(stream_read_subtest, Stream* stream) {
  254. uint8_t data[256] = {0};
  255. mu_check(stream_rewind(stream));
  256. mu_assert_int_eq(strlen(stream_test_data), stream_read(stream, data, 256));
  257. mu_assert_string_eq(stream_test_data, (const char*)data);
  258. }
  259. MU_TEST(stream_write_read_save_load_test) {
  260. Stream* stream_orig = string_stream_alloc();
  261. Stream* stream_copy = string_stream_alloc();
  262. Storage* storage = furi_record_open(RECORD_STORAGE);
  263. // write, read
  264. MU_RUN_TEST_1(stream_write_subtest, stream_orig);
  265. MU_RUN_TEST_1(stream_read_subtest, stream_orig);
  266. // copy, read
  267. mu_assert_int_eq(strlen(stream_test_data), stream_copy_full(stream_orig, stream_copy));
  268. MU_RUN_TEST_1(stream_read_subtest, stream_orig);
  269. // save to file
  270. mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart));
  271. mu_assert_int_eq(
  272. strlen(stream_test_data),
  273. stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS));
  274. stream_free(stream_copy);
  275. stream_free(stream_orig);
  276. // load from file, read
  277. Stream* stream_new = string_stream_alloc();
  278. mu_assert_int_eq(
  279. strlen(stream_test_data),
  280. stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str")));
  281. MU_RUN_TEST_1(stream_read_subtest, stream_new);
  282. stream_free(stream_new);
  283. furi_record_close(RECORD_STORAGE);
  284. }
  285. MU_TEST_1(stream_split_subtest, Stream* stream) {
  286. stream_clean(stream);
  287. stream_write_cstring(stream, stream_test_left_data);
  288. stream_write_cstring(stream, stream_test_right_data);
  289. Stream* stream_left = string_stream_alloc();
  290. Stream* stream_right = string_stream_alloc();
  291. mu_check(stream_seek(stream, strlen(stream_test_left_data), StreamOffsetFromStart));
  292. mu_check(stream_split(stream, stream_left, stream_right));
  293. uint8_t data[256] = {0};
  294. mu_check(stream_rewind(stream_left));
  295. mu_assert_int_eq(strlen(stream_test_left_data), stream_read(stream_left, data, 256));
  296. mu_assert_string_eq(stream_test_left_data, (const char*)data);
  297. mu_check(stream_rewind(stream_right));
  298. mu_assert_int_eq(strlen(stream_test_right_data), stream_read(stream_right, data, 256));
  299. mu_assert_string_eq(stream_test_right_data, (const char*)data);
  300. stream_free(stream_right);
  301. stream_free(stream_left);
  302. }
  303. MU_TEST(stream_split_test) {
  304. // test string stream
  305. Stream* stream;
  306. stream = string_stream_alloc();
  307. MU_RUN_TEST_1(stream_split_subtest, stream);
  308. stream_free(stream);
  309. // test file stream
  310. Storage* storage = furi_record_open(RECORD_STORAGE);
  311. stream = file_stream_alloc(storage);
  312. mu_check(
  313. file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  314. MU_RUN_TEST_1(stream_split_subtest, stream);
  315. stream_free(stream);
  316. // test buffered stream
  317. stream = buffered_file_stream_alloc(storage);
  318. mu_check(buffered_file_stream_open(
  319. stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  320. MU_RUN_TEST_1(stream_split_subtest, stream);
  321. stream_free(stream);
  322. furi_record_close(RECORD_STORAGE);
  323. }
  324. MU_TEST(stream_buffered_write_after_read_test) {
  325. const char* prefix = "I write ";
  326. const char* substr = "Hello there";
  327. const size_t substr_len = strlen(substr);
  328. const size_t prefix_len = strlen(prefix);
  329. const size_t buf_size = substr_len + 1;
  330. char buf[buf_size];
  331. memset(buf, 0, buf_size);
  332. Storage* storage = furi_record_open(RECORD_STORAGE);
  333. Stream* stream = buffered_file_stream_alloc(storage);
  334. mu_check(buffered_file_stream_open(
  335. stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  336. mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
  337. mu_check(stream_rewind(stream));
  338. mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len));
  339. mu_assert_string_eq(prefix, buf);
  340. mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len));
  341. mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart));
  342. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  343. mu_assert_string_eq(substr, buf);
  344. stream_free(stream);
  345. furi_record_close(RECORD_STORAGE);
  346. }
  347. MU_TEST(stream_buffered_large_file_test) {
  348. FuriString* input_data;
  349. FuriString* output_data;
  350. input_data = furi_string_alloc();
  351. output_data = furi_string_alloc();
  352. Storage* storage = furi_record_open(RECORD_STORAGE);
  353. // generate test data consisting of several identical lines
  354. const size_t data_size = 4096;
  355. const size_t line_size = strlen(stream_test_data);
  356. const size_t rep_count = data_size / line_size + 1;
  357. for(size_t i = 0; i < rep_count; ++i) {
  358. furi_string_cat_printf(input_data, "%s\n", stream_test_data);
  359. }
  360. // write test data to file
  361. Stream* stream = buffered_file_stream_alloc(storage);
  362. mu_check(buffered_file_stream_open(
  363. stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  364. mu_assert_int_eq(0, stream_size(stream));
  365. mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data));
  366. mu_assert_int_eq(furi_string_size(input_data), stream_size(stream));
  367. const size_t substr_start = 8;
  368. const size_t substr_len = 11;
  369. mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
  370. mu_assert_int_eq(substr_start, stream_tell(stream));
  371. // copy one substring from test data
  372. char test_substr[substr_len + 1];
  373. memset(test_substr, 0, substr_len + 1);
  374. memcpy(test_substr, stream_test_data + substr_start, substr_len);
  375. char buf[substr_len + 1];
  376. memset(buf, 0, substr_len + 1);
  377. // read substring
  378. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  379. mu_assert_string_eq(test_substr, buf);
  380. memset(buf, 0, substr_len + 1);
  381. // forward seek to cause a cache miss
  382. mu_check(stream_seek(
  383. stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
  384. // read same substring from a different line
  385. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  386. mu_assert_string_eq(test_substr, buf);
  387. memset(buf, 0, substr_len + 1);
  388. // backward seek to cause a cache miss
  389. mu_check(stream_seek(
  390. stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
  391. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  392. mu_assert_string_eq(test_substr, buf);
  393. // read the whole file
  394. mu_check(stream_rewind(stream));
  395. FuriString* tmp;
  396. tmp = furi_string_alloc();
  397. while(stream_read_line(stream, tmp)) {
  398. furi_string_cat(output_data, tmp);
  399. }
  400. furi_string_free(tmp);
  401. // check against generated data
  402. mu_assert_int_eq(furi_string_size(input_data), furi_string_size(output_data));
  403. mu_check(furi_string_equal(input_data, output_data));
  404. mu_check(stream_eof(stream));
  405. stream_free(stream);
  406. furi_record_close(RECORD_STORAGE);
  407. furi_string_free(input_data);
  408. furi_string_free(output_data);
  409. }
  410. MU_TEST_SUITE(stream_suite) {
  411. MU_RUN_TEST(stream_write_read_save_load_test);
  412. MU_RUN_TEST(stream_composite_test);
  413. MU_RUN_TEST(stream_split_test);
  414. MU_RUN_TEST(stream_buffered_write_after_read_test);
  415. MU_RUN_TEST(stream_buffered_large_file_test);
  416. }
  417. int run_minunit_test_stream() {
  418. MU_RUN_SUITE(stream_suite);
  419. return MU_EXIT_CODE;
  420. }