stream_test.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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("storage");
  237. stream = file_stream_alloc(storage);
  238. mu_check(file_stream_open(stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  239. MU_RUN_TEST_1(stream_composite_subtest, stream);
  240. stream_free(stream);
  241. // test buffered file stream
  242. stream = buffered_file_stream_alloc(storage);
  243. mu_check(buffered_file_stream_open(
  244. stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  245. MU_RUN_TEST_1(stream_composite_subtest, stream);
  246. stream_free(stream);
  247. furi_record_close("storage");
  248. }
  249. MU_TEST_1(stream_write_subtest, Stream* stream) {
  250. mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
  251. }
  252. MU_TEST_1(stream_read_subtest, Stream* stream) {
  253. uint8_t data[256] = {0};
  254. mu_check(stream_rewind(stream));
  255. mu_assert_int_eq(strlen(stream_test_data), stream_read(stream, data, 256));
  256. mu_assert_string_eq(stream_test_data, (const char*)data);
  257. }
  258. MU_TEST(stream_write_read_save_load_test) {
  259. Stream* stream_orig = string_stream_alloc();
  260. Stream* stream_copy = string_stream_alloc();
  261. Storage* storage = furi_record_open("storage");
  262. // write, read
  263. MU_RUN_TEST_1(stream_write_subtest, stream_orig);
  264. MU_RUN_TEST_1(stream_read_subtest, stream_orig);
  265. // copy, read
  266. mu_assert_int_eq(strlen(stream_test_data), stream_copy_full(stream_orig, stream_copy));
  267. MU_RUN_TEST_1(stream_read_subtest, stream_orig);
  268. // save to file
  269. mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart));
  270. mu_assert_int_eq(
  271. strlen(stream_test_data),
  272. stream_save_to_file(stream_orig, storage, "/ext/filestream.str", FSOM_CREATE_ALWAYS));
  273. stream_free(stream_copy);
  274. stream_free(stream_orig);
  275. // load from file, read
  276. Stream* stream_new = string_stream_alloc();
  277. mu_assert_int_eq(
  278. strlen(stream_test_data),
  279. stream_load_from_file(stream_new, storage, "/ext/filestream.str"));
  280. MU_RUN_TEST_1(stream_read_subtest, stream_new);
  281. stream_free(stream_new);
  282. furi_record_close("storage");
  283. }
  284. MU_TEST_1(stream_split_subtest, Stream* stream) {
  285. stream_clean(stream);
  286. stream_write_cstring(stream, stream_test_left_data);
  287. stream_write_cstring(stream, stream_test_right_data);
  288. Stream* stream_left = string_stream_alloc();
  289. Stream* stream_right = string_stream_alloc();
  290. mu_check(stream_seek(stream, strlen(stream_test_left_data), StreamOffsetFromStart));
  291. mu_check(stream_split(stream, stream_left, stream_right));
  292. uint8_t data[256] = {0};
  293. mu_check(stream_rewind(stream_left));
  294. mu_assert_int_eq(strlen(stream_test_left_data), stream_read(stream_left, data, 256));
  295. mu_assert_string_eq(stream_test_left_data, (const char*)data);
  296. mu_check(stream_rewind(stream_right));
  297. mu_assert_int_eq(strlen(stream_test_right_data), stream_read(stream_right, data, 256));
  298. mu_assert_string_eq(stream_test_right_data, (const char*)data);
  299. stream_free(stream_right);
  300. stream_free(stream_left);
  301. }
  302. MU_TEST(stream_split_test) {
  303. // test string stream
  304. Stream* stream;
  305. stream = string_stream_alloc();
  306. MU_RUN_TEST_1(stream_split_subtest, stream);
  307. stream_free(stream);
  308. // test file stream
  309. Storage* storage = furi_record_open("storage");
  310. stream = file_stream_alloc(storage);
  311. mu_check(file_stream_open(stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  312. MU_RUN_TEST_1(stream_split_subtest, stream);
  313. stream_free(stream);
  314. // test buffered stream
  315. stream = buffered_file_stream_alloc(storage);
  316. mu_check(buffered_file_stream_open(
  317. stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  318. MU_RUN_TEST_1(stream_split_subtest, stream);
  319. stream_free(stream);
  320. furi_record_close("storage");
  321. }
  322. MU_TEST(stream_buffered_large_file_test) {
  323. string_t input_data;
  324. string_t output_data;
  325. string_init(input_data);
  326. string_init(output_data);
  327. Storage* storage = furi_record_open("storage");
  328. // generate test data consisting of several identical lines
  329. const size_t data_size = 4096;
  330. const size_t line_size = strlen(stream_test_data);
  331. const size_t rep_count = data_size / line_size + 1;
  332. for(size_t i = 0; i < rep_count; ++i) {
  333. string_cat_printf(input_data, "%s\n", stream_test_data);
  334. }
  335. // write test data to file
  336. Stream* stream = buffered_file_stream_alloc(storage);
  337. mu_check(buffered_file_stream_open(
  338. stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
  339. mu_assert_int_eq(0, stream_size(stream));
  340. mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data));
  341. mu_assert_int_eq(string_size(input_data), stream_size(stream));
  342. const size_t substr_start = 8;
  343. const size_t substr_len = 11;
  344. mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
  345. mu_assert_int_eq(substr_start, stream_tell(stream));
  346. // copy one substring from test data
  347. char test_substr[substr_len + 1];
  348. memset(test_substr, 0, substr_len + 1);
  349. memcpy(test_substr, stream_test_data + substr_start, substr_len);
  350. char buf[substr_len + 1];
  351. memset(buf, 0, substr_len + 1);
  352. // read substring
  353. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  354. mu_assert_string_eq(test_substr, buf);
  355. memset(buf, 0, substr_len + 1);
  356. // forward seek to cause a cache miss
  357. mu_check(stream_seek(
  358. stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
  359. // read same substring from a different line
  360. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  361. mu_assert_string_eq(test_substr, buf);
  362. memset(buf, 0, substr_len + 1);
  363. // backward seek to cause a cache miss
  364. mu_check(stream_seek(
  365. stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
  366. mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
  367. mu_assert_string_eq(test_substr, buf);
  368. // read the whole file
  369. mu_check(stream_rewind(stream));
  370. string_t tmp;
  371. string_init(tmp);
  372. while(stream_read_line(stream, tmp)) {
  373. string_cat(output_data, tmp);
  374. }
  375. string_clear(tmp);
  376. // check against generated data
  377. mu_assert_int_eq(string_size(input_data), string_size(output_data));
  378. mu_check(string_equal_p(input_data, output_data));
  379. mu_check(stream_eof(stream));
  380. stream_free(stream);
  381. furi_record_close("storage");
  382. string_clear(input_data);
  383. string_clear(output_data);
  384. }
  385. MU_TEST_SUITE(stream_suite) {
  386. MU_RUN_TEST(stream_write_read_save_load_test);
  387. MU_RUN_TEST(stream_composite_test);
  388. MU_RUN_TEST(stream_split_test);
  389. MU_RUN_TEST(stream_buffered_large_file_test);
  390. }
  391. int run_minunit_test_stream() {
  392. MU_RUN_SUITE(stream_suite);
  393. return MU_EXIT_CODE;
  394. }