| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- #include <furi.h>
- #include <toolbox/stream/stream.h>
- #include <toolbox/stream/string_stream.h>
- #include <toolbox/stream/file_stream.h>
- #include <toolbox/stream/buffered_file_stream.h>
- #include <storage/storage.h>
- #include "../minunit.h"
- static const char* stream_test_data = "I write differently from what I speak, "
- "I speak differently from what I think, "
- "I think differently from the way I ought to think, "
- "and so it all proceeds into deepest darkness.";
- static const char* stream_test_left_data = "There are two cardinal human sins ";
- static const char* stream_test_right_data =
- "from which all others derive: impatience and indolence.";
- MU_TEST_1(stream_composite_subtest, Stream* stream) {
- const size_t data_size = 128;
- uint8_t data[data_size];
- FuriString* string_lee;
- string_lee = furi_string_alloc_set("lee");
- // test that stream is empty
- // "" -> ""
- mu_check(stream_size(stream) == 0);
- mu_check(stream_eof(stream));
- mu_check(stream_tell(stream) == 0);
- mu_check(stream_read(stream, data, data_size) == 0);
- mu_check(stream_eof(stream));
- mu_check(stream_tell(stream) == 0);
- // write char
- // "" -> "2"
- mu_check(stream_write_char(stream, '2') == 1);
- mu_check(stream_size(stream) == 1);
- mu_check(stream_tell(stream) == 1);
- mu_check(stream_eof(stream));
- // test rewind and eof
- stream_rewind(stream);
- mu_check(stream_size(stream) == 1);
- mu_check(stream_tell(stream) == 0);
- mu_check(!stream_eof(stream));
- // add another char with replacement
- // "2" -> "1"
- mu_check(stream_write_char(stream, '1') == 1);
- mu_check(stream_size(stream) == 1);
- mu_check(stream_tell(stream) == 1);
- mu_check(stream_eof(stream));
- // write string
- // "1" -> "1337_69"
- mu_check(stream_write_cstring(stream, "337_69") == 6);
- mu_check(stream_size(stream) == 7);
- mu_check(stream_tell(stream) == 7);
- mu_check(stream_eof(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_check(stream_read(stream, data, data_size) == 7);
- mu_check(strcmp((char*)data, "1337_69") == 0);
- // test misc seeks
- mu_check(stream_seek(stream, 2, StreamOffsetFromStart));
- mu_check(stream_tell(stream) == 2);
- mu_check(!stream_seek(stream, 9000, StreamOffsetFromStart));
- mu_check(stream_tell(stream) == 7);
- mu_check(stream_eof(stream));
- mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));
- mu_check(stream_tell(stream) == 4);
- // test seeks to char. content: '1337_69'
- stream_rewind(stream);
- mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward));
- mu_check(stream_tell(stream) == 1);
- mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward));
- mu_check(stream_tell(stream) == 2);
- mu_check(stream_seek_to_char(stream, '_', StreamDirectionForward));
- mu_check(stream_tell(stream) == 4);
- mu_check(stream_seek_to_char(stream, '9', StreamDirectionForward));
- mu_check(stream_tell(stream) == 6);
- mu_check(!stream_seek_to_char(stream, '9', StreamDirectionForward));
- mu_check(stream_tell(stream) == 6);
- mu_check(stream_seek_to_char(stream, '_', StreamDirectionBackward));
- mu_check(stream_tell(stream) == 4);
- mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward));
- mu_check(stream_tell(stream) == 2);
- mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward));
- mu_check(stream_tell(stream) == 1);
- mu_check(!stream_seek_to_char(stream, '3', StreamDirectionBackward));
- mu_check(stream_tell(stream) == 1);
- mu_check(stream_seek_to_char(stream, '1', StreamDirectionBackward));
- mu_check(stream_tell(stream) == 0);
- // write string with replacement
- // "1337_69" -> "1337lee"
- mu_check(stream_seek(stream, 4, StreamOffsetFromStart));
- mu_check(stream_write_string(stream, string_lee) == 3);
- mu_check(stream_size(stream) == 7);
- mu_check(stream_tell(stream) == 7);
- mu_check(stream_eof(stream));
- // append char
- // "1337lee" -> "1337leet"
- mu_check(stream_write(stream, (uint8_t*)"t", 1) == 1);
- mu_check(stream_size(stream) == 8);
- mu_check(stream_tell(stream) == 8);
- mu_check(stream_eof(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_check(stream_read(stream, data, data_size) == 8);
- mu_check(strcmp((char*)data, "1337leet") == 0);
- mu_check(stream_tell(stream) == 8);
- mu_check(stream_eof(stream));
- // negative seek from current position -> clamp to 0
- mu_check(!stream_seek(stream, -9000, StreamOffsetFromCurrent));
- mu_check(stream_tell(stream) == 0);
- // negative seek from start position -> clamp to 0
- stream_rewind(stream);
- mu_check(!stream_seek(stream, -3, StreamOffsetFromStart));
- mu_check(stream_tell(stream) == 0);
- // zero seek from current position -> clamp to stream size
- mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
- mu_check(stream_tell(stream) == 8);
- // negative seek from end position -> clamp to 0
- mu_check(!stream_seek(stream, -9000, StreamOffsetFromEnd));
- mu_check(stream_tell(stream) == 0);
- // clean stream
- stream_clean(stream);
- mu_check(stream_size(stream) == 0);
- mu_check(stream_eof(stream));
- mu_check(stream_tell(stream) == 0);
- // write format
- // "" -> "dio666"
- mu_check(stream_write_format(stream, "%s%d", "dio", 666) == 6);
- mu_check(stream_size(stream) == 6);
- mu_check(stream_eof(stream));
- mu_check(stream_tell(stream) == 6);
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_check(stream_read(stream, data, data_size) == 6);
- mu_check(strcmp((char*)data, "dio666") == 0);
- // clean and write cstring
- // "dio666" -> "" -> "1234567890"
- stream_clean(stream);
- mu_check(stream_write_cstring(stream, "1234567890") == 10);
- // delete 4 bytes from 1 pos
- // "1xxxx67890" -> "167890"
- mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
- mu_check(stream_delete(stream, 4));
- mu_assert_int_eq(6, stream_size(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_assert_int_eq(6, stream_read(stream, data, data_size));
- mu_check(strcmp((char*)data, "167890") == 0);
- // write cstring
- // "167890" -> "167890It Was Me, Dio!"
- mu_check(stream_write_cstring(stream, "It Was Me, Dio!") == 15);
- // delete 1337 bytes from 1 pos
- // and check that we can delete only 20 bytes
- // "1xxxxxxxxxxxxxxxxxxxx" -> "1"
- mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
- mu_check(stream_delete(stream, 1337));
- mu_assert_int_eq(1, stream_size(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_check(stream_read(stream, data, data_size) == 1);
- mu_check(strcmp((char*)data, "1") == 0);
- // write cstring from 0 pos, replacing 1 byte
- // "1" -> "Oh? You're roaching me?"
- mu_check(stream_rewind(stream));
- mu_assert_int_eq(23, stream_write_cstring(stream, "Oh? You're roaching me?"));
- // insert 11 bytes to 0 pos
- // "Oh? You're roaching me?" -> "Za Warudo! Oh? You're roaching me?"
- mu_check(stream_rewind(stream));
- mu_check(stream_insert(stream, (uint8_t*)"Za Warudo! ", 11));
- mu_assert_int_eq(34, stream_size(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_assert_int_eq(34, stream_read(stream, data, data_size));
- mu_assert_string_eq("Za Warudo! Oh? You're roaching me?", (char*)data);
- // insert cstring to 22 pos
- // "Za Warudo! Oh? You're roaching me?" -> "Za Warudo! Oh? You're approaching me?"
- mu_check(stream_seek(stream, 22, StreamOffsetFromStart));
- mu_check(stream_insert_cstring(stream, "app"));
- mu_assert_int_eq(37, stream_size(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_assert_int_eq(37, stream_read(stream, data, data_size));
- mu_assert_string_eq("Za Warudo! Oh? You're approaching me?", (char*)data);
- // insert cstring to the end of the stream
- // "Za Warudo! Oh? You're approaching me?" -> "Za Warudo! Oh? You're approaching me? It was me, Dio!"
- mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
- mu_check(stream_insert_cstring(stream, " It was me, Dio!"));
- mu_assert_int_eq(53, stream_size(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_assert_int_eq(53, stream_read(stream, data, data_size));
- mu_assert_string_eq("Za Warudo! Oh? You're approaching me? It was me, Dio!", (char*)data);
- // delete 168430090 bytes from stream
- // and test that we can delete only 53
- mu_check(stream_rewind(stream));
- mu_check(stream_delete(stream, 0x0A0A0A0A));
- mu_assert_int_eq(0, stream_size(stream));
- mu_check(stream_eof(stream));
- mu_assert_int_eq(0, stream_tell(stream));
- // clean stream
- stream_clean(stream);
- mu_assert_int_eq(0, stream_size(stream));
- mu_check(stream_eof(stream));
- mu_assert_int_eq(0, stream_tell(stream));
- // insert formatted string at the end of stream
- // "" -> "dio666"
- mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
- mu_assert_int_eq(6, stream_size(stream));
- mu_check(stream_eof(stream));
- mu_assert_int_eq(6, stream_tell(stream));
- // insert formatted string at the end of stream
- // "dio666" -> "dio666zlo555"
- mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
- mu_assert_int_eq(12, stream_size(stream));
- mu_check(stream_eof(stream));
- mu_assert_int_eq(12, stream_tell(stream));
- // insert formatted string at the 6 pos
- // "dio666" -> "dio666baba13zlo555"
- mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
- mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
- mu_assert_int_eq(18, stream_size(stream));
- mu_assert_int_eq(12, stream_tell(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_assert_int_eq(18, stream_read(stream, data, data_size));
- mu_assert_string_eq("dio666baba13zlo555", (char*)data);
- // delete 6 chars from pos 6 and insert 1 chars
- // "dio666baba13zlo555" -> "dio666xzlo555"
- mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
- mu_check(stream_delete_and_insert_char(stream, 6, 'x'));
- mu_assert_int_eq(13, stream_size(stream));
- mu_assert_int_eq(7, stream_tell(stream));
- // read data
- memset(data, 0, data_size);
- stream_rewind(stream);
- mu_check(stream_read(stream, data, data_size) == 13);
- mu_assert_string_eq("dio666xzlo555", (char*)data);
- // delete 9000 chars from pos 6 and insert 3 chars from string
- // "dio666xzlo555" -> "dio666777"
- mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
- mu_check(stream_delete_and_insert_cstring(stream, 9000, "777"));
- mu_assert_int_eq(9, stream_size(stream));
- mu_assert_int_eq(9, stream_tell(stream));
- mu_check(stream_eof(stream));
- furi_string_free(string_lee);
- }
- MU_TEST(stream_composite_test) {
- // test string stream
- Stream* stream;
- stream = string_stream_alloc();
- MU_RUN_TEST_1(stream_composite_subtest, stream);
- stream_free(stream);
- // test file stream
- Storage* storage = furi_record_open(RECORD_STORAGE);
- stream = file_stream_alloc(storage);
- mu_check(
- file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
- MU_RUN_TEST_1(stream_composite_subtest, stream);
- stream_free(stream);
- // test buffered file stream
- stream = buffered_file_stream_alloc(storage);
- mu_check(buffered_file_stream_open(
- stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
- MU_RUN_TEST_1(stream_composite_subtest, stream);
- stream_free(stream);
- furi_record_close(RECORD_STORAGE);
- }
- MU_TEST_1(stream_write_subtest, Stream* stream) {
- mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
- }
- MU_TEST_1(stream_read_subtest, Stream* stream) {
- uint8_t data[256] = {0};
- mu_check(stream_rewind(stream));
- mu_assert_int_eq(strlen(stream_test_data), stream_read(stream, data, 256));
- mu_assert_string_eq(stream_test_data, (const char*)data);
- }
- MU_TEST(stream_write_read_save_load_test) {
- Stream* stream_orig = string_stream_alloc();
- Stream* stream_copy = string_stream_alloc();
- Storage* storage = furi_record_open(RECORD_STORAGE);
- // write, read
- MU_RUN_TEST_1(stream_write_subtest, stream_orig);
- MU_RUN_TEST_1(stream_read_subtest, stream_orig);
- // copy, read
- mu_assert_int_eq(strlen(stream_test_data), stream_copy_full(stream_orig, stream_copy));
- MU_RUN_TEST_1(stream_read_subtest, stream_orig);
- // save to file
- mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart));
- mu_assert_int_eq(
- strlen(stream_test_data),
- stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS));
- stream_free(stream_copy);
- stream_free(stream_orig);
- // load from file, read
- Stream* stream_new = string_stream_alloc();
- mu_assert_int_eq(
- strlen(stream_test_data),
- stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str")));
- MU_RUN_TEST_1(stream_read_subtest, stream_new);
- stream_free(stream_new);
- furi_record_close(RECORD_STORAGE);
- }
- MU_TEST_1(stream_split_subtest, Stream* stream) {
- stream_clean(stream);
- stream_write_cstring(stream, stream_test_left_data);
- stream_write_cstring(stream, stream_test_right_data);
- Stream* stream_left = string_stream_alloc();
- Stream* stream_right = string_stream_alloc();
- mu_check(stream_seek(stream, strlen(stream_test_left_data), StreamOffsetFromStart));
- mu_check(stream_split(stream, stream_left, stream_right));
- uint8_t data[256] = {0};
- mu_check(stream_rewind(stream_left));
- mu_assert_int_eq(strlen(stream_test_left_data), stream_read(stream_left, data, 256));
- mu_assert_string_eq(stream_test_left_data, (const char*)data);
- mu_check(stream_rewind(stream_right));
- mu_assert_int_eq(strlen(stream_test_right_data), stream_read(stream_right, data, 256));
- mu_assert_string_eq(stream_test_right_data, (const char*)data);
- stream_free(stream_right);
- stream_free(stream_left);
- }
- MU_TEST(stream_split_test) {
- // test string stream
- Stream* stream;
- stream = string_stream_alloc();
- MU_RUN_TEST_1(stream_split_subtest, stream);
- stream_free(stream);
- // test file stream
- Storage* storage = furi_record_open(RECORD_STORAGE);
- stream = file_stream_alloc(storage);
- mu_check(
- file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
- MU_RUN_TEST_1(stream_split_subtest, stream);
- stream_free(stream);
- // test buffered stream
- stream = buffered_file_stream_alloc(storage);
- mu_check(buffered_file_stream_open(
- stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
- MU_RUN_TEST_1(stream_split_subtest, stream);
- stream_free(stream);
- furi_record_close(RECORD_STORAGE);
- }
- MU_TEST(stream_buffered_write_after_read_test) {
- const char* prefix = "I write ";
- const char* substr = "Hello there";
- const size_t substr_len = strlen(substr);
- const size_t prefix_len = strlen(prefix);
- const size_t buf_size = substr_len + 1;
- char buf[buf_size];
- memset(buf, 0, buf_size);
- Storage* storage = furi_record_open(RECORD_STORAGE);
- Stream* stream = buffered_file_stream_alloc(storage);
- mu_check(buffered_file_stream_open(
- stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
- mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
- mu_check(stream_rewind(stream));
- mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len));
- mu_assert_string_eq(prefix, buf);
- mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len));
- mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart));
- mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
- mu_assert_string_eq(substr, buf);
- stream_free(stream);
- furi_record_close(RECORD_STORAGE);
- }
- MU_TEST(stream_buffered_large_file_test) {
- FuriString* input_data;
- FuriString* output_data;
- input_data = furi_string_alloc();
- output_data = furi_string_alloc();
- Storage* storage = furi_record_open(RECORD_STORAGE);
- // generate test data consisting of several identical lines
- const size_t data_size = 4096;
- const size_t line_size = strlen(stream_test_data);
- const size_t rep_count = data_size / line_size + 1;
- for(size_t i = 0; i < rep_count; ++i) {
- furi_string_cat_printf(input_data, "%s\n", stream_test_data);
- }
- // write test data to file
- Stream* stream = buffered_file_stream_alloc(storage);
- mu_check(buffered_file_stream_open(
- stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
- mu_assert_int_eq(0, stream_size(stream));
- mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data));
- mu_assert_int_eq(furi_string_size(input_data), stream_size(stream));
- const size_t substr_start = 8;
- const size_t substr_len = 11;
- mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
- mu_assert_int_eq(substr_start, stream_tell(stream));
- // copy one substring from test data
- char test_substr[substr_len + 1];
- memset(test_substr, 0, substr_len + 1);
- memcpy(test_substr, stream_test_data + substr_start, substr_len);
- char buf[substr_len + 1];
- memset(buf, 0, substr_len + 1);
- // read substring
- mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
- mu_assert_string_eq(test_substr, buf);
- memset(buf, 0, substr_len + 1);
- // forward seek to cause a cache miss
- mu_check(stream_seek(
- stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
- // read same substring from a different line
- mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
- mu_assert_string_eq(test_substr, buf);
- memset(buf, 0, substr_len + 1);
- // backward seek to cause a cache miss
- mu_check(stream_seek(
- stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
- mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
- mu_assert_string_eq(test_substr, buf);
- // read the whole file
- mu_check(stream_rewind(stream));
- FuriString* tmp;
- tmp = furi_string_alloc();
- while(stream_read_line(stream, tmp)) {
- furi_string_cat(output_data, tmp);
- }
- furi_string_free(tmp);
- // check against generated data
- mu_assert_int_eq(furi_string_size(input_data), furi_string_size(output_data));
- mu_check(furi_string_equal(input_data, output_data));
- mu_check(stream_eof(stream));
- stream_free(stream);
- furi_record_close(RECORD_STORAGE);
- furi_string_free(input_data);
- furi_string_free(output_data);
- }
- MU_TEST_SUITE(stream_suite) {
- MU_RUN_TEST(stream_write_read_save_load_test);
- MU_RUN_TEST(stream_composite_test);
- MU_RUN_TEST(stream_split_test);
- MU_RUN_TEST(stream_buffered_write_after_read_test);
- MU_RUN_TEST(stream_buffered_large_file_test);
- }
- int run_minunit_test_stream() {
- MU_RUN_SUITE(stream_suite);
- return MU_EXIT_CODE;
- }
|