stream_test.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. string_t string_lee;
  19. string_init_set(string_lee, "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 formated 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 formated 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 formated 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. string_clear(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_large_file_test) {
  325. string_t input_data;
  326. string_t output_data;
  327. string_init(input_data);
  328. string_init(output_data);
  329. Storage* storage = furi_record_open(RECORD_STORAGE);
  330. // generate test data consisting of several identical lines
  331. const size_t data_size = 4096;
  332. const size_t line_size = strlen(stream_test_data);
  333. const size_t rep_count = data_size / line_size + 1;
  334. for(size_t i = 0; i < rep_count; ++i) {
  335. string_cat_printf(input_data, "%s\n", stream_test_data);
  336. }
  337. // write test data to file
  338. Stream* stream = buffered_file_stream_alloc(storage);
  339. mu_check(buffered_file_stream_open(
  340. stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  341. mu_assert_int_eq(0, stream_size(stream));
  342. mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data));
  343. mu_assert_int_eq(string_size(input_data), stream_size(stream));
  344. const size_t substr_start = 8;
  345. const size_t substr_len = 11;
  346. mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
  347. mu_assert_int_eq(substr_start, stream_tell(stream));
  348. // copy one substring from test data
  349. char test_substr[substr_len + 1];
  350. memset(test_substr, 0, substr_len + 1);
  351. memcpy(test_substr, stream_test_data + substr_start, substr_len);
  352. char buf[substr_len + 1];
  353. memset(buf, 0, substr_len + 1);
  354. // read substring
  355. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  356. mu_assert_string_eq(test_substr, buf);
  357. memset(buf, 0, substr_len + 1);
  358. // forward seek to cause a cache miss
  359. mu_check(stream_seek(
  360. stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
  361. // read same substring from a different line
  362. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  363. mu_assert_string_eq(test_substr, buf);
  364. memset(buf, 0, substr_len + 1);
  365. // backward seek to cause a cache miss
  366. mu_check(stream_seek(
  367. stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
  368. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  369. mu_assert_string_eq(test_substr, buf);
  370. // read the whole file
  371. mu_check(stream_rewind(stream));
  372. string_t tmp;
  373. string_init(tmp);
  374. while(stream_read_line(stream, tmp)) {
  375. string_cat(output_data, tmp);
  376. }
  377. string_clear(tmp);
  378. // check against generated data
  379. mu_assert_int_eq(string_size(input_data), string_size(output_data));
  380. mu_check(string_equal_p(input_data, output_data));
  381. mu_check(stream_eof(stream));
  382. stream_free(stream);
  383. furi_record_close(RECORD_STORAGE);
  384. string_clear(input_data);
  385. string_clear(output_data);
  386. }
  387. MU_TEST_SUITE(stream_suite) {
  388. MU_RUN_TEST(stream_write_read_save_load_test);
  389. MU_RUN_TEST(stream_composite_test);
  390. MU_RUN_TEST(stream_split_test);
  391. MU_RUN_TEST(stream_buffered_large_file_test);
  392. }
  393. int run_minunit_test_stream() {
  394. MU_RUN_SUITE(stream_suite);
  395. return MU_EXIT_CODE;
  396. }