From b1ce27f7336ebbe2c3d07ff39a77d2aa0d7a626f Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 30 Nov 2023 03:23:47 -0600 Subject: [PATCH] Reorganized test suites a bit - Renamed test_dtree -> test_dirs - Renamed test_dseek -> test_dread - Split test_files -> test_files, test_fwrite --- tests/test_alloc.toml | 2 +- tests/{test_dtree.toml => test_dirs.toml} | 96 +- tests/{test_dseek.toml => test_dread.toml} | 36 +- tests/test_files.toml | 2964 +------------------- tests/test_fwrite.toml | 2445 ++++++++++++++++ 5 files changed, 2513 insertions(+), 3030 deletions(-) rename tests/{test_dtree.toml => test_dirs.toml} (99%) rename tests/{test_dseek.toml => test_dread.toml} (98%) create mode 100644 tests/test_fwrite.toml diff --git a/tests/test_alloc.toml b/tests/test_alloc.toml index 31f77c4e..05630fe2 100644 --- a/tests/test_alloc.toml +++ b/tests/test_alloc.toml @@ -7,7 +7,7 @@ # since you can usually ignore allocator issues temporarily by making the test # device really big (-DDISK_SIZE=16777216, etc) # -after = ['test_mtree', 'test_dtree', 'test_files'] +after = ['test_mtree', 'test_dirs', 'test_files'] # TODO test all of these with weird block sizes? would be nice to make this diff --git a/tests/test_dtree.toml b/tests/test_dirs.toml similarity index 99% rename from tests/test_dtree.toml rename to tests/test_dirs.toml index 9b1197fd..f1e8153b 100644 --- a/tests/test_dtree.toml +++ b/tests/test_dirs.toml @@ -3,7 +3,7 @@ after = 'test_mtree' ## mkdir tests -[cases.test_dtree_mkdir] +[cases.test_dirs_mkdir] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -52,7 +52,7 @@ code = ''' ''' # test that noent errors work -[cases.test_dtree_noent] +[cases.test_dirs_noent] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; @@ -99,7 +99,7 @@ code = ''' ''' # test that stat on root works -[cases.test_dtree_stat_root] +[cases.test_dirs_stat_root] defines.REMOUNT = [false, true] code = ''' lfs_t lfs; @@ -146,7 +146,7 @@ code = ''' ''' # test that creating the same directory twice errors -[cases.test_dtree_mkdir_exists] +[cases.test_dirs_mkdir_exists] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -197,7 +197,7 @@ code = ''' ''' # test that dir existance is the same before/after mkconsistent -[cases.test_dtree_mkdir_consistent] +[cases.test_dirs_mkdir_consistent] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -268,7 +268,7 @@ code = ''' ''' # test what happens if we try to make root -[cases.test_dtree_mkdir_root] +[cases.test_dirs_mkdir_root] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -324,7 +324,7 @@ code = ''' ''' # test that creating a directory with an invalid path errors -[cases.test_dtree_mkdir_noent] +[cases.test_dirs_mkdir_noent] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -377,7 +377,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_siblings] +[cases.test_dirs_mkdir_siblings] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -479,7 +479,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_children] +[cases.test_dirs_mkdir_children] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -629,7 +629,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_many] +[cases.test_dirs_mkdir_many] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMOUNT = [false, true] # limit powerloss testing due to time @@ -692,7 +692,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_many_backwards] +[cases.test_dirs_mkdir_many_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMOUNT = [false, true] # limit powerloss testing due to time @@ -755,7 +755,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_many_2layers] +[cases.test_dirs_mkdir_many_2layers] defines.N = [1, 2, 4, 8, 16] defines.REMOUNT = [false, true] # limit powerloss testing due to time @@ -856,7 +856,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_many_3layers] +[cases.test_dirs_mkdir_many_3layers] defines.N = [1, 2, 4] defines.REMOUNT = [false, true] # limit powerloss testing due to time @@ -995,7 +995,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_many_linkedlist] +[cases.test_dirs_mkdir_many_linkedlist] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.REMOUNT = [false, true] # limit powerloss testing due to time @@ -1065,7 +1065,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mkdir_fuzz] +[cases.test_dirs_mkdir_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] defines.REMOUNT = [false, true] @@ -1166,7 +1166,7 @@ code = ''' # test that did collisions don't cause issues -[cases.test_dtree_did_collisions] +[cases.test_dirs_did_collisions] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -1327,7 +1327,7 @@ code = ''' ''' # these will also collide with the root -[cases.test_dtree_did_zero] +[cases.test_dirs_did_zero] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -1490,7 +1490,7 @@ code = ''' # these will need to rollover from 0xffffffff -> 0x00000000 correctly # # note this is true even if you truncate -[cases.test_dtree_did_ones] +[cases.test_dirs_did_ones] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -1653,7 +1653,7 @@ code = ''' # these test some boundary conditions on the underlying leb128 encoding, # if the leb128 disk-size is not calculated correctly these can cause # issues -[cases.test_dtree_did_leb128_boundaries] +[cases.test_dirs_did_leb128_boundaries] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -1816,7 +1816,7 @@ code = ''' ## dir remove tests -[cases.test_dtree_rm] +[cases.test_dirs_rm] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -1890,7 +1890,7 @@ code = ''' ''' # test that noent errors work -[cases.test_dtree_rm_noent] +[cases.test_dirs_rm_noent] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -1947,7 +1947,7 @@ code = ''' ''' # test that we catch removing of a non-empty directory -[cases.test_dtree_rm_notempty] +[cases.test_dirs_rm_notempty] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -2018,7 +2018,7 @@ code = ''' ''' # test that dir existance is the same before/after mkconsistent -[cases.test_dtree_rm_consistent] +[cases.test_dirs_rm_consistent] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -2114,7 +2114,7 @@ code = ''' ''' # test what happens if we try to remove root -[cases.test_dtree_rm_root] +[cases.test_dirs_rm_root] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -2170,7 +2170,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_siblings] +[cases.test_dirs_rm_siblings] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -2365,7 +2365,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_children] +[cases.test_dirs_rm_children] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -2638,7 +2638,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_many] +[cases.test_dirs_rm_many] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMAINING = [64, 2, 1, 0] defines.REMOUNT = [false, true] @@ -2753,7 +2753,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_many_backwards] +[cases.test_dirs_rm_many_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMAINING = [64, 2, 1, 0] defines.REMOUNT = [false, true] @@ -2868,7 +2868,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_many_2layers] +[cases.test_dirs_rm_many_2layers] defines.N = [1, 2, 4, 8, 16] defines.REMAINING = [2, 1, 0] defines.REMOUNT = [false, true] @@ -3076,7 +3076,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_many_3layers] +[cases.test_dirs_rm_many_3layers] defines.N = [1, 2, 4] defines.REMAINING = [2, 1, 0] defines.REMOUNT = [false, true] @@ -3374,7 +3374,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_many_linkedlist] +[cases.test_dirs_rm_many_linkedlist] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.REMAINING = [16, 2, 1, 0] defines.REMOUNT = [false, true] @@ -3497,7 +3497,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_rm_fuzz] +[cases.test_dirs_rm_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] defines.REMOUNT = [false, true] @@ -3617,7 +3617,7 @@ code = ''' ## dir rename tests -[cases.test_dtree_mv] +[cases.test_dirs_mv] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -3703,7 +3703,7 @@ code = ''' ''' # test that we can rename, and replace, other directories -[cases.test_dtree_mv_replace] +[cases.test_dirs_mv_replace] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -3788,7 +3788,7 @@ code = ''' ''' # test that we can rename to ourselves -[cases.test_dtree_mv_noop] +[cases.test_dirs_mv_noop] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -3867,7 +3867,7 @@ code = ''' ''' # test that we catch replacing an invalid path -[cases.test_dtree_mv_noent] +[cases.test_dirs_mv_noent] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -3927,7 +3927,7 @@ code = ''' ''' # test that we catch replacing a non-empty directory -[cases.test_dtree_mv_notempty] +[cases.test_dirs_mv_notempty] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -4005,7 +4005,7 @@ code = ''' ''' # test that dir existance is the same before/after mkconsistent -[cases.test_dtree_mv_consistent] +[cases.test_dirs_mv_consistent] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -4122,7 +4122,7 @@ code = ''' ''' # test what happens if we try to rename root -[cases.test_dtree_mv_root] +[cases.test_dirs_mv_root] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -4175,7 +4175,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_siblings] +[cases.test_dirs_mv_siblings] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -4437,7 +4437,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_children] +[cases.test_dirs_mv_children] defines.REMOUNT = [false, true] reentrant = true code = ''' @@ -4863,7 +4863,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_many] +[cases.test_dirs_mv_many] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.BEFORE = [false, true] defines.REMOUNT = [false, true] @@ -4978,7 +4978,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_many_backwards] +[cases.test_dirs_mv_many_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.BEFORE = [false, true] defines.REMOUNT = [false, true] @@ -5093,7 +5093,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_many_2layers] +[cases.test_dirs_mv_many_2layers] defines.N = [1, 2, 4, 8, 16] defines.BEFORE = [false, true] defines.REMOUNT = [false, true] @@ -5298,7 +5298,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_many_3layers] +[cases.test_dirs_mv_many_3layers] defines.N = [1, 2, 4] defines.BEFORE = [false, true] defines.REMOUNT = [false, true] @@ -5600,7 +5600,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_many_linkedlist] +[cases.test_dirs_mv_many_linkedlist] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.BEFORE = [false, true] defines.REMOUNT = [false, true] @@ -5736,7 +5736,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dtree_mv_fuzz] +[cases.test_dirs_mv_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] defines.REMOUNT = [false, true] @@ -5877,7 +5877,7 @@ code = ''' ''' # test all of the operations together -[cases.test_dtree_general_fuzz] +[cases.test_dirs_general_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] defines.REMOUNT = [false, true] diff --git a/tests/test_dseek.toml b/tests/test_dread.toml similarity index 98% rename from tests/test_dseek.toml rename to tests/test_dread.toml index c0e707c6..2d98ddba 100644 --- a/tests/test_dseek.toml +++ b/tests/test_dread.toml @@ -1,13 +1,13 @@ # Test the annoying subtle corner cases of directory seeking+reading # -# Note there may be some overlap with test_dtree, since some dir operations +# Note there may be some overlap with test_dirs, since some dir operations # are needed to validate the directory tree works -after = 'test_dtree' +after = 'test_dirs' # test some dir functions -[cases.test_dseek_tell] +[cases.test_dread_tell] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] code = ''' @@ -58,7 +58,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_rewind] +[cases.test_dread_rewind] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] code = ''' @@ -129,7 +129,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_seek] +[cases.test_dread_seek] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] code = ''' @@ -202,7 +202,7 @@ code = ''' # test we don't ever get extra entries back after we reach # the end of a directory -[cases.test_dseek_read_idempotent] +[cases.test_dread_read_idempotent] defines.PARENT = [false, true] # bit 0x2 = left neighbor # bit 0x1 = right neighbor @@ -289,7 +289,7 @@ code = ''' ''' # test neighbor changes don't mess with an unrelated dir read -[cases.test_dseek_read_neighbor_mkdirs] +[cases.test_dread_read_neighbor_mkdirs] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' @@ -410,7 +410,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_read_neighbor_rms] +[cases.test_dread_read_neighbor_rms] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' @@ -541,7 +541,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_read_neighbor_mvs] +[cases.test_dread_read_neighbor_mvs] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' @@ -677,7 +677,7 @@ code = ''' ''' # test dir read has somewhat reasonable behaviour when the dir is modified -[cases.test_dseek_read_with_mkdirs] +[cases.test_dread_read_with_mkdirs] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' @@ -772,7 +772,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_read_with_rms] +[cases.test_dread_read_with_rms] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' @@ -871,7 +871,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_read_with_mvs] +[cases.test_dread_read_with_mvs] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' @@ -989,7 +989,7 @@ code = ''' # dir reads with 2x ops have better chances of catching bugs that depend on # invalid dir states -[cases.test_dseek_read_with_2_mkdirs] +[cases.test_dread_read_with_2_mkdirs] defines.N = 5 # where in the dir read do we mkdir? defines.I = 'range(6)' @@ -1094,7 +1094,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_read_with_2_rms] +[cases.test_dread_read_with_2_rms] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' @@ -1201,7 +1201,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_dseek_read_with_2_mvs] +[cases.test_dread_read_with_2_mvs] defines.N = 5 # where in the dir read do we rm? defines.I = 'range(5)' @@ -1331,7 +1331,7 @@ code = ''' ''' # test removing the directory we are iterating over -[cases.test_dseek_read_rm] +[cases.test_dread_read_rm] defines.N = 5 # where in the dir read do we remove? defines.I = 'range(6)' @@ -1424,7 +1424,7 @@ code = ''' # # This is a useful feature, but it's unintuitive if this should have # well-defined behavior, so make sure to test for it -[cases.test_dseek_recursive_rm] +[cases.test_dread_recursive_rm] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.PARENT = [false, true] # 0 => don't seek @@ -1546,7 +1546,7 @@ code = ''' # # This is a useful feature, but it's unintuitive if this should have # well-defined behavior, so make sure to test for it -[cases.test_dseek_recursive_mv] +[cases.test_dread_recursive_mv] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.BEFORE = [false, true] # 0 => no diff --git a/tests/test_files.toml b/tests/test_files.toml index 011d25d5..777a9327 100644 --- a/tests/test_files.toml +++ b/tests/test_files.toml @@ -1,12 +1,5 @@ # Test basic file operations -after = ['test_dtree', 'test_btree'] - -# TODO should fragment_size accept 0? -# test with different fragment sizes -defines.FRAGMENT_SIZE = [1, 16, 64] - -# test with different crystal sizes -defines.CRYSTAL_SIZE = [512] +after = ['test_dirs', 'test_btree'] # test creation/deletion @@ -442,10 +435,6 @@ defines.SIZE = [ ] defines.REMOUNT = [false, true] defines.CACHE_SIZE = 64 -if = [ - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; @@ -505,2441 +494,8 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -# more complex writing patterns to inlined files - -# write files incrementally -[cases.test_files_incr] -defines.SIZE = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - uint8_t wbuf[SIZE]; - uint32_t prng = 42; - for (lfs_size_t i = 0; i < SIZE; i++) { - wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { - lfsr_file_write(&lfs, &file, &wbuf[i], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - // note the switch to append here - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_APPEND) => 0; - } - } - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => SIZE; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# overwrite files -# TODO this is too slow right now, but should speed up with better -# write strategies -[cases.test_files_overwrite] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -# bit 0 => first chunk -# bit 1 => middle chunk -# bit 2 => last chunk -defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] -# 0 => in-order -# 1 => reversed -defines.ORDER = [0, 1] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - uint32_t prng = 42; - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // write first chunk? - if (MASK & 0x1) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - - // write second chunk? - if (MASK & 0x2) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) - => SIZE/2 - CHUNK/2; - lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - - // write third chunk? - if (MASK & 0x4) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } - } - - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => SIZE; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; - // does our file match our simulation? - assert(memcmp(rbuf, sim, SIZE) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# similar to overwrite files, but without underlying data -[cases.test_files_holes] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -# bit 0 => first chunk -# bit 1 => middle chunk -# bit 2 => last chunk -defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] -# 0 => in-order -# 1 => reversed -defines.ORDER = [0, 1] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - uint32_t prng = 42; - memset(sim, 0, SIZE); - // we may not write the entire file - lfs_off_t size - = (MASK & ((ORDER == 0) ? 0x4 : 0x1)) ? SIZE - : (MASK & ((ORDER == 0) ? 0x2 : 0x2)) ? SIZE/2 + (CHUNK+2-1)/2 - : (MASK & ((ORDER == 0) ? 0x1 : 0x4)) ? CHUNK - : 0; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // write first chunk? - if (MASK & 0x1) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - - // write second chunk? - if (MASK & 0x2) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) - => SIZE/2 - CHUNK/2; - lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - - // write third chunk? - if (MASK & 0x4) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } - } - - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# simple truncate test -[cases.test_files_truncate] -defines.FROM = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.TO = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - # these just save testing time - 'FROM / FRAGMENT_SIZE <= 4096', - 'TO / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[lfs_max32(FROM,TO)]; - memset(sim, 0, lfs_max32(FROM,TO)); - uint32_t prng = 42; - for (lfs_size_t i = 0; i < FROM; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, FROM) => FROM; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // truncate to new size - lfsr_file_truncate(&lfs, &file, TO) => 0; - if (TO < FROM) { - memset(sim+TO, 0, FROM-TO); - } - - // close - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => TO; - // try reading - uint8_t rbuf[2*TO]; - memset(rbuf, 0xaa, 2*TO); - lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; - assert(memcmp(rbuf, sim, TO) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# one purpose of this test is to check that data is not hidden -# and then revealed by truncate, that would be bad -[cases.test_files_truncate_2] -defines.FROM = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.AND = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.TO = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - # these just save testing time - 'FROM / FRAGMENT_SIZE <= 4096', - 'AND / FRAGMENT_SIZE <= 4096', - 'TO / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[lfs_max32(FROM,lfs_max32(AND,TO))]; - memset(sim, 0, lfs_max32(FROM,lfs_max32(AND,TO))); - uint32_t prng = 42; - for (lfs_size_t i = 0; i < FROM; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, FROM) => FROM; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // truncate to intermediate size - lfsr_file_truncate(&lfs, &file, AND) => 0; - if (AND < FROM) { - memset(sim+AND, 0, FROM-AND); - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // truncate to new size - lfsr_file_truncate(&lfs, &file, TO) => 0; - if (TO < AND) { - memset(sim+TO, 0, AND-TO); - } - - // close - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => TO; - // try reading - uint8_t rbuf[2*TO]; - memset(rbuf, 0xaa, 2*TO); - lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; - assert(memcmp(rbuf, sim, TO) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# simple fruncate test -[cases.test_files_fruncate] -defines.FROM = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.TO = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - # these just save testing time - 'FROM / FRAGMENT_SIZE <= 4096', - 'TO / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[lfs_max32(FROM,TO)]; - memset(sim, 0, lfs_max32(FROM,TO)); - uint32_t prng = 42; - for (lfs_size_t i = 0; i < FROM; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, FROM) => FROM; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // fruncate to new size - lfsr_file_fruncate(&lfs, &file, TO) => 0; - if (TO > FROM) { - memmove(sim+TO-FROM, sim, FROM); - memset(sim, 0, TO-FROM); - } else if (TO < FROM) { - memmove(sim, sim+FROM-TO, TO); - memset(sim+TO, 0, FROM-TO); - } - - // close - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => TO; - // try reading - uint8_t rbuf[2*TO]; - memset(rbuf, 0xaa, 2*TO); - lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; - assert(memcmp(rbuf, sim, TO) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# one purpose of this test is to check that data is not hidden -# and then revealed by fruncate, that would be bad -[cases.test_files_fruncate_2] -defines.FROM = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.AND = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.TO = [ - '0', - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - # these just save testing time - 'FROM / FRAGMENT_SIZE <= 4096', - 'AND / FRAGMENT_SIZE <= 4096', - 'TO / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[lfs_max32(FROM,lfs_max32(AND,TO))]; - memset(sim, 0, lfs_max32(FROM,lfs_max32(AND,TO))); - uint32_t prng = 42; - for (lfs_size_t i = 0; i < FROM; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, FROM) => FROM; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // fruncate to intermediate size - lfsr_file_fruncate(&lfs, &file, AND) => 0; - if (AND > FROM) { - memmove(sim+AND-FROM, sim, FROM); - memset(sim, 0, AND-FROM); - } else if (AND < FROM) { - memmove(sim, sim+FROM-AND, AND); - memset(sim+AND, 0, FROM-AND); - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // fruncate to new size - lfsr_file_fruncate(&lfs, &file, TO) => 0; - if (TO > AND) { - memmove(sim+TO-AND, sim, AND); - memset(sim, 0, TO-AND); - } else if (TO < AND) { - memmove(sim, sim+AND-TO, TO); - memset(sim+TO, 0, AND-TO); - } - - // close - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == TO); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => TO; - // try reading - uint8_t rbuf[2*TO]; - memset(rbuf, 0xaa, 2*TO); - lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; - assert(memcmp(rbuf, sim, TO) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# writing any data structure backwards always reveals issues -[cases.test_files_reversed] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - uint32_t prng = 42; - if (INIT == 0) { - memset(sim, 0, SIZE); - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // write to file incrementally and backwards - for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { - for (lfs_size_t j = 0; j < CHUNK; j++) { - sim[SIZE-i-CHUNK+j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_seek(&lfs, &file, SIZE-i-CHUNK, LFS_SEEK_SET) => SIZE-i-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-i-CHUNK], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => SIZE; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; - // does our file match our simulation? - assert(memcmp(rbuf, sim, SIZE) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# these are like the overwrite/hole tests, but with enough rewrites to -# trigger compaction -[cases.test_files_overwrite_compaction] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -# bit 0 => first chunk -# bit 1 => middle chunk -# bit 2 => last chunk -defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] -# 0 => in-order -# 1 => reversed -defines.ORDER = [0, 1] -# writing this many times guarantees a compaction -defines.WRITES = '2*(BLOCK_SIZE/PROG_SIZE)' -# TODO is setting PROG_SIZE here reasonable? -defines.PROG_SIZE = 64 -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - uint32_t prng = 42; - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // write first chunk? - if (MASK & 0x1) { - for (lfs_size_t w = 0; w < WRITES; w++) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - } - - // write second chunk? - if (MASK & 0x2) { - for (lfs_size_t w = 0; w < WRITES; w++) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) - => SIZE/2 - CHUNK/2; - lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - } - - // write third chunk? - if (MASK & 0x4) { - for (lfs_size_t w = 0; w < WRITES; w++) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } - } - } - - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => SIZE; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; - // does our file match our simulation? - assert(memcmp(rbuf, sim, SIZE) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -[cases.test_files_hole_compaction] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -# bit 0 => first chunk -# bit 1 => middle chunk -# bit 2 => last chunk -defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] -# 0 => in-order -# 1 => reversed -defines.ORDER = [0, 1] -# writing this many times guarantees a compaction -defines.WRITES = '2*(BLOCK_SIZE/PROG_SIZE)' -# TODO is setting PROG_SIZE here reasonable? -defines.PROG_SIZE = 64 -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file, truncating in case of powerloss - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - uint32_t prng = 42; - memset(sim, 0, SIZE); - // we may not write the entire file - lfs_off_t size - = (MASK & ((ORDER == 0) ? 0x4 : 0x1)) ? SIZE - : (MASK & ((ORDER == 0) ? 0x2 : 0x2)) ? SIZE/2 + (CHUNK+2-1)/2 - : (MASK & ((ORDER == 0) ? 0x1 : 0x4)) ? CHUNK - : 0; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - // write first chunk? - if (MASK & 0x1) { - for (lfs_size_t w = 0; w < WRITES; w++) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - } - - // write second chunk? - if (MASK & 0x2) { - for (lfs_size_t w = 0; w < WRITES; w++) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) - => SIZE/2 - CHUNK/2; - lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - } - - // write third chunk? - if (MASK & 0x4) { - for (lfs_size_t w = 0; w < WRITES; w++) { - if (ORDER == 0) { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; - lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } else { - for (lfs_size_t i = 0; i < CHUNK; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - - lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; - lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } - } - } - - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# fuzz testing -[cases.test_files_fuzz_aligned] -defines.N = 20 -defines.SEED = 'range(10)' -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -defines.CHUNK = [32, 8, 1] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - lfs_off_t size; - uint32_t prng = SEED; - if (INIT == 0) { - memset(sim, 0, SIZE); - size = 0; - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - size = SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - size = SIZE; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - for (lfs_size_t i = 0; i < N; i++) { - // choose a random chunk-aligned location - lfs_off_t off = (TEST_PRNG(&prng) % (SIZE/CHUNK)) * CHUNK; - - // update sim - for (lfs_size_t j = 0; j < CHUNK; j++) { - sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); - } - size = lfs_max32(size, off+CHUNK); - - // update file - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - lfsr_file_write(&lfs, &file, &sim[off], CHUNK) => CHUNK; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# fuzz testing -[cases.test_files_fuzz_unaligned] -defines.N = 20 -defines.SEED = 'range(10)' -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -# chunk is more an upper limit here -defines.CHUNK = [32, 8] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - lfs_off_t size; - uint32_t prng = SEED; - if (INIT == 0) { - memset(sim, 0, SIZE); - size = 0; - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - size = SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - size = SIZE; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - - for (lfs_size_t i = 0; i < N; i++) { - // choose a random location - lfs_off_t off = TEST_PRNG(&prng) % SIZE; - // and a random size, up to the chunk size - lfs_size_t chunk = lfs_min32( - TEST_PRNG(&prng) % CHUNK, - SIZE - off); - - // update sim - for (lfs_size_t j = 0; j < chunk; j++) { - sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); - } - if (chunk != 0) { - size = lfs_max32(size, off+chunk); - } - - // update file - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - } - } - lfsr_file_close(&lfs, &file) => 0; - - // remount? - if (REMOUNT) { - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - } - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - - -# more seek testing -[cases.test_files_r_seek] -defines.N = 20 -defines.SEED = 'range(10)' -defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -# chunk is more an upper limit here -defines.CHUNK = [32, 8] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - uint32_t prng = SEED; - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - lfsr_file_close(&lfs, &file) => 0; - - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - lfs_soff_t off_ = 0; - for (lfs_size_t i = 0; i < N; i++) { - // choose a random location - lfs_soff_t off = TEST_PRNG(&prng) % SIZE; - // and a random size, up to the chunk size - lfs_size_t chunk = lfs_min32( - TEST_PRNG(&prng) % CHUNK, - SIZE - off); - - // test different seek methods - if (WHENCE == LFS_SEEK_SET) { - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - } else if (WHENCE == LFS_SEEK_CUR) { - lfsr_file_seek(&lfs, &file, off-off_, LFS_SEEK_CUR) => off; - } else if (WHENCE == LFS_SEEK_END) { - lfsr_file_seek(&lfs, &file, off-SIZE, LFS_SEEK_END) => off; - } - - // tell should always report the correct position - lfsr_file_tell(&lfs, &file) => off; - - // read the file and assert we got the correct data - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, chunk) => chunk; - assert(memcmp(rbuf, &sim[off], chunk) == 0); - - // tell should report the new position - lfsr_file_tell(&lfs, &file) => off + chunk; - - // keep track of previous off for LFS_SEEK_CUR - off_ = off + chunk; - } - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# this is pretty much the same as earlier fuzz testing, except we test -# different seek methods -[cases.test_files_w_seek] -defines.N = 10 -defines.SEED = 'range(10)' -defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -# chunk is more an upper limit here -defines.CHUNK = [32, 8] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.SYNC = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - lfs_off_t size; - uint32_t prng = SEED; - if (INIT == 0) { - memset(sim, 0, SIZE); - size = 0; - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - size = SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - size = SIZE; - } - lfsr_file_close(&lfs, &file) => 0; - - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; - lfs_soff_t off_ = 0; - for (lfs_size_t i = 0; i < N; i++) { - // choose a random location - lfs_off_t off = TEST_PRNG(&prng) % SIZE; - // and a random size, up to the chunk size - lfs_size_t chunk = lfs_min32( - TEST_PRNG(&prng) % CHUNK, - SIZE - off); - - // test different seek methods - if (WHENCE == LFS_SEEK_SET) { - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - } else if (WHENCE == LFS_SEEK_CUR) { - lfsr_file_seek(&lfs, &file, off-off_, LFS_SEEK_CUR) => off; - } else if (WHENCE == LFS_SEEK_END) { - lfsr_file_seek(&lfs, &file, off-size, LFS_SEEK_END) => off; - } - - // tell should always report the correct position - lfsr_file_tell(&lfs, &file) => off; - - // update the sim - for (lfs_size_t j = 0; j < chunk; j++) { - sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); - } - if (chunk != 0) { - size = lfs_max32(size, off+chunk); - } - - // update the file - lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // tell should report the new position - lfsr_file_tell(&lfs, &file) => off + chunk; - - // keep track of previous off for LFS_SEEK_CUR - off_ = off + chunk; - } - lfsr_file_close(&lfs, &file) => 0; - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# the above was just warmup, here's the real seek test -[cases.test_files_rw_seek] -defines.N = 10 -defines.SEED = 'range(10)' -defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -# chunk is more an upper limit here -defines.CHUNK = [32, 8] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.SYNC = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - lfs_off_t size; - uint32_t prng = SEED; - if (INIT == 0) { - memset(sim, 0, SIZE); - size = 0; - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - size = SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - size = SIZE; - } - lfsr_file_close(&lfs, &file) => 0; - - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0; - lfs_soff_t off_ = 0; - for (lfs_size_t i = 0; i < N; i++) { - // choose a random location - lfs_off_t off = TEST_PRNG(&prng) % SIZE; - // and a random size, up to the chunk size - lfs_size_t chunk = lfs_min32( - TEST_PRNG(&prng) % CHUNK, - SIZE - off); - // and if we are reading or writing - uint8_t op = TEST_PRNG(&prng) % 2; - - // test different seek methods - if (WHENCE == LFS_SEEK_SET) { - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - } else if (WHENCE == LFS_SEEK_CUR) { - lfsr_file_seek(&lfs, &file, off-off_, LFS_SEEK_CUR) => off; - } else if (WHENCE == LFS_SEEK_END) { - lfsr_file_seek(&lfs, &file, off-size, LFS_SEEK_END) => off; - } - - // tell should always report the correct position - lfsr_file_tell(&lfs, &file) => off; - - // writing? - if (op == 0) { - // update the sim - for (lfs_size_t j = 0; j < chunk; j++) { - sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); - } - if (chunk != 0) { - size = lfs_max32(size, off+chunk); - } - - // update the file - lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // tell should report the new position - lfsr_file_tell(&lfs, &file) => off + chunk; - - // keep track of previous off for LFS_SEEK_CUR - off_ = off + chunk; - - // reading? - } else if (op == 1) { - // we may read less than chunk if we're past eof - lfs_off_t expected = lfs_min32( - chunk, - size - lfs_min32(off, size)); - - // read the file and assert we got the correct data - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, chunk) => expected; - assert(memcmp(rbuf, &sim[off], expected) == 0); - - // tell should report the new position - lfsr_file_tell(&lfs, &file) => off + expected; - - // keep track of previous off for LFS_SEEK_CUR - off_ = off + expected; - } - } - lfsr_file_close(&lfs, &file) => 0; - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# test other corner conditions -[cases.test_files_seek_negative] -defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.MODE = ['LFS_O_RDONLY', 'LFS_O_WRONLY', 'LFS_O_RDWR'] -if = [ - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - lfs_off_t size; - uint32_t prng = 42; - if (INIT == 0) { - memset(sim, 0, SIZE); - size = 0; - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - size = SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - size = SIZE; - } - lfsr_file_close(&lfs, &file) => 0; - - // try to seek before the beginning of the file, this should fail - lfsr_file_open(&lfs, &file, "hello", MODE) => 0; - if (WHENCE == LFS_SEEK_SET) { - lfsr_file_seek(&lfs, &file, -1, LFS_SEEK_SET) => LFS_ERR_INVAL; - } else if (WHENCE == LFS_SEEK_CUR) { - lfsr_file_seek(&lfs, &file, -1, LFS_SEEK_CUR) => LFS_ERR_INVAL; - } else if (WHENCE == LFS_SEEK_END) { - lfsr_file_seek(&lfs, &file, -(size+1), LFS_SEEK_END) => LFS_ERR_INVAL; - } - lfsr_file_close(&lfs, &file) => 0; - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - -# heavy fuzz test with rw seeks, truncate, and fruncate -[cases.test_files_rwtf_fuzz] -defines.N = 20 -defines.SEED = 'range(10)' -defines.SIZE = [ - 'CACHE_SIZE/2', - '2*CACHE_SIZE', - 'BLOCK_SIZE/2', - 'BLOCK_SIZE', - '2*BLOCK_SIZE', - '4*BLOCK_SIZE', -] -# chunk is more an upper limit here -defines.CHUNK = [32, 8] -# 0 => no init -# 1 => fill with data -# 2 => truncate to size -defines.INIT = [0, 1, 2] -defines.SYNC = [false, true] -defines.REMOUNT = [false, true] -if = [ - 'CHUNK <= SIZE', - # this just save testing time - 'SIZE / FRAGMENT_SIZE <= 4096', -] -code = ''' - lfs_t lfs; - lfsr_format(&lfs, CFG) => 0; - lfsr_mount(&lfs, CFG) => 0; - - // create a file - lfsr_file_t file; - lfsr_file_open(&lfs, &file, "hello", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - // simulate our file in ram - uint8_t sim[SIZE]; - lfs_off_t size; - uint32_t prng = SEED; - if (INIT == 0) { - memset(sim, 0, SIZE); - size = 0; - } else if (INIT == 1) { - for (lfs_size_t i = 0; i < SIZE; i++) { - sim[i] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; - size = SIZE; - } else if (INIT == 2) { - memset(sim, 0, SIZE); - lfsr_file_truncate(&lfs, &file, SIZE) => 0; - size = SIZE; - } - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0; - } - - for (lfs_size_t i = 0; i < N; i++) { - // and if we are reading, writing, truncating, or fruncating - uint8_t op = TEST_PRNG(&prng) % 4; - - // writing? - if (op == 0) { - // choose a random location - lfs_off_t off = TEST_PRNG(&prng) % SIZE; - // and a random size, up to the chunk size - lfs_size_t chunk = lfs_min32( - TEST_PRNG(&prng) % CHUNK, - SIZE - off); - - // seek - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - - // update the sim - for (lfs_size_t j = 0; j < chunk; j++) { - sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); - } - if (chunk != 0) { - size = lfs_max32(size, off+chunk); - } - - // update the file - lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; - - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } - - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0; - } - - // reading? - } else if (op == 1) { - // choose a random location - lfs_off_t off = TEST_PRNG(&prng) % SIZE; - // and a random size, up to the chunk size - lfs_size_t chunk = lfs_min32( - TEST_PRNG(&prng) % CHUNK, - SIZE - off); - - // seek - lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; - - // we may read less than chunk if we're past eof - lfs_off_t expected = lfs_min32( - chunk, - size - lfs_min32(off, size)); - - // read the file and assert we got the correct data - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, chunk) => expected; - assert(memcmp(rbuf, &sim[off], expected) == 0); - - // truncating? - } else if (op == 2) { - // choose a random new file size - lfs_off_t size_ = TEST_PRNG(&prng) % SIZE; - - // update the sim - if (size_ < size) { - memset(sim+size_, 0, size-size_); - } - size = size_; - - // truncate the file - lfsr_file_truncate(&lfs, &file, size_) => 0; - - } else if (op == 3) { - // choose a random new file size - lfs_off_t size_ = TEST_PRNG(&prng) % SIZE; - - // update the sim - if (size_ > size) { - memmove(sim+size_-size, sim, size); - memset(sim, 0, size_-size); - } else if (size_ < size) { - memmove(sim, sim+size-size_, size_); - memset(sim+size_, 0, size-size_); - } - size = size_; - - // truncate the file - lfsr_file_fruncate(&lfs, &file, size_) => 0; - } - } - lfsr_file_close(&lfs, &file) => 0; - - // check our file with stat - struct lfs_info info; - lfsr_stat(&lfs, "hello", &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - - // and with dir read - lfsr_dir_t dir; - lfsr_dir_open(&lfs, &dir, "/") => 0; - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, ".") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "..") == 0); - assert(info.type == LFS_TYPE_DIR); - lfsr_dir_read(&lfs, &dir, &info) => 0; - assert(strcmp(info.name, "hello") == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == size); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - - // try reading our file - lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; - // is size correct? - lfsr_file_size(&lfs, &file) => size; - // try reading - uint8_t rbuf[2*SIZE]; - memset(rbuf, 0xaa, 2*SIZE); - lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; - // does our file match our simulation? - assert(memcmp(rbuf, sim, size) == 0); - lfsr_file_close(&lfs, &file) => 0; - - lfsr_unmount(&lfs) => 0; -''' - # TODO -# [cases.test_files_push] ? -# [cases.test_files_pop] ? -# [cases.test_files_rwtfpp_fuzz] ? - # [cases.test_files_rm] # [cases.test_files_mv] # [cases.test_files_mvrm] @@ -2959,521 +515,3 @@ code = ''' # [cases.test_files_dtree_fuzz_fuzz] - - -# -#[cases.test_files_simple] -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_t file; -# lfs_file_open(&lfs, &file, "hello", -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# lfs_size_t size = strlen("Hello World!")+1; -# uint8_t buffer[1024]; -# strcpy((char*)buffer, "Hello World!"); -# lfs_file_write(&lfs, &file, buffer, size) => size; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; -# lfs_file_read(&lfs, &file, buffer, size) => size; -# assert(strcmp((char*)buffer, "Hello World!") == 0); -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_large] -#defines.SIZE = [32, 8192, 262144, 0, 7, 8193] -#defines.CHUNKSIZE = [31, 16, 33, 1, 1023] -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# -# // write -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_t file; -# lfs_file_open(&lfs, &file, "avacado", -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# uint32_t prng = 1; -# uint8_t buffer[1024]; -# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_rewrite] -#defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -#defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -#defines.CHUNKSIZE = [31, 16, 1] -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# -# // write -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_t file; -# uint8_t buffer[1024]; -# lfs_file_open(&lfs, &file, "avacado", -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# uint32_t prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE1; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // rewrite -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0; -# prng = 2; -# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2); -# prng = 2; -# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# if (SIZE1 > SIZE2) { -# prng = 1; -# for (lfs_size_t b = 0; b < SIZE2; b++) { -# TEST_PRNG(&prng); -# } -# for (lfs_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_append] -#defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -#defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -#defines.CHUNKSIZE = [31, 16, 1] -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# -# // write -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_t file; -# uint8_t buffer[1024]; -# lfs_file_open(&lfs, &file, "avacado", -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# uint32_t prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE1; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // append -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0; -# prng = 2; -# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE1 + SIZE2; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# prng = 2; -# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_truncate] -#defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193] -#defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193] -#defines.CHUNKSIZE = [31, 16, 1] -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# -# // write -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_t file; -# uint8_t buffer[1024]; -# lfs_file_open(&lfs, &file, "avacado", -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# uint32_t prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE1; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // truncate -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0; -# prng = 2; -# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# // read -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE2; -# prng = 2; -# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_reentrant_write] -#defines.SIZE = [32, 0, 7, 2049] -#defines.CHUNKSIZE = [31, 16, 65] -#reentrant = true -#code = ''' -# lfs_t lfs; -# int err = lfs_mount(&lfs, cfg); -# if (err) { -# lfs_format(&lfs, cfg) => 0; -# lfs_mount(&lfs, cfg) => 0; -# } -# -# lfs_file_t file; -# uint8_t buffer[1024]; -# err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY); -# assert(err == LFS_ERR_NOENT || err == 0); -# if (err == 0) { -# // can only be 0 (new file) or full size -# lfs_size_t size = lfs_file_size(&lfs, &file); -# assert(size == 0 || size == SIZE); -# lfs_file_close(&lfs, &file) => 0; -# } -# -# // write -# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT) => 0; -# uint32_t prng = 1; -# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# } -# lfs_file_close(&lfs, &file) => 0; -# -# // read -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_reentrant_write_sync] -#defines = [ -# # append (O(n)) -# {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]}, -# # truncate (O(n^2)) -# {MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, -# # rewrite (O(n^2)) -# {MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]}, -#] -#reentrant = true -#code = ''' -# lfs_t lfs; -# int err = lfs_mount(&lfs, cfg); -# if (err) { -# lfs_format(&lfs, cfg) => 0; -# lfs_mount(&lfs, cfg) => 0; -# } -# -# lfs_file_t file; -# uint8_t buffer[1024]; -# err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY); -# assert(err == LFS_ERR_NOENT || err == 0); -# if (err == 0) { -# // with syncs we could be any size, but it at least must be valid data -# lfs_size_t size = lfs_file_size(&lfs, &file); -# assert(size <= SIZE); -# uint32_t prng = 1; -# for (lfs_size_t i = 0; i < size; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, size-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_close(&lfs, &file) => 0; -# } -# -# // write -# lfs_file_open(&lfs, &file, "avacado", -# LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0; -# lfs_size_t size = lfs_file_size(&lfs, &file); -# assert(size <= SIZE); -# uint32_t prng = 1; -# lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0; -# for (lfs_size_t b = 0; b < skip; b++) { -# TEST_PRNG(&prng); -# } -# for (lfs_size_t i = skip; i < SIZE; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); -# for (lfs_size_t b = 0; b < chunk; b++) { -# buffer[b] = TEST_PRNG(&prng) & 0xff; -# } -# lfs_file_write(&lfs, &file, buffer, chunk) => chunk; -# lfs_file_sync(&lfs, &file) => 0; -# } -# lfs_file_close(&lfs, &file) => 0; -# -# // read -# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0; -# lfs_file_size(&lfs, &file) => SIZE; -# prng = 1; -# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) { -# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i); -# lfs_file_read(&lfs, &file, buffer, chunk) => chunk; -# for (lfs_size_t b = 0; b < chunk; b++) { -# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff)); -# } -# } -# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_many] -#defines.N = 300 -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# // create N files of 7 bytes -# lfs_mount(&lfs, cfg) => 0; -# for (int i = 0; i < N; i++) { -# lfs_file_t file; -# char path[1024]; -# sprintf(path, "file_%03d", i); -# lfs_file_open(&lfs, &file, path, -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# char wbuffer[1024]; -# lfs_size_t size = 7; -# sprintf(wbuffer, "Hi %03d", i); -# lfs_file_write(&lfs, &file, wbuffer, size) => size; -# lfs_file_close(&lfs, &file) => 0; -# -# char rbuffer[1024]; -# lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; -# lfs_file_read(&lfs, &file, rbuffer, size) => size; -# assert(strcmp(rbuffer, wbuffer) == 0); -# lfs_file_close(&lfs, &file) => 0; -# } -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_many_power_cycle] -#defines.N = 300 -#code = ''' -# lfs_t lfs; -# lfs_format(&lfs, cfg) => 0; -# // create N files of 7 bytes -# lfs_mount(&lfs, cfg) => 0; -# for (int i = 0; i < N; i++) { -# lfs_file_t file; -# char path[1024]; -# sprintf(path, "file_%03d", i); -# lfs_file_open(&lfs, &file, path, -# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; -# char wbuffer[1024]; -# lfs_size_t size = 7; -# sprintf(wbuffer, "Hi %03d", i); -# lfs_file_write(&lfs, &file, wbuffer, size) => size; -# lfs_file_close(&lfs, &file) => 0; -# lfs_unmount(&lfs) => 0; -# -# char rbuffer[1024]; -# lfs_mount(&lfs, cfg) => 0; -# lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; -# lfs_file_read(&lfs, &file, rbuffer, size) => size; -# assert(strcmp(rbuffer, wbuffer) == 0); -# lfs_file_close(&lfs, &file) => 0; -# } -# lfs_unmount(&lfs) => 0; -#''' -# -#[cases.test_files_many_power_loss] -#defines.N = 300 -#reentrant = true -#code = ''' -# lfs_t lfs; -# int err = lfs_mount(&lfs, cfg); -# if (err) { -# lfs_format(&lfs, cfg) => 0; -# lfs_mount(&lfs, cfg) => 0; -# } -# // create N files of 7 bytes -# for (int i = 0; i < N; i++) { -# lfs_file_t file; -# char path[1024]; -# sprintf(path, "file_%03d", i); -# err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT); -# char wbuffer[1024]; -# lfs_size_t size = 7; -# sprintf(wbuffer, "Hi %03d", i); -# if ((lfs_size_t)lfs_file_size(&lfs, &file) != size) { -# lfs_file_write(&lfs, &file, wbuffer, size) => size; -# } -# lfs_file_close(&lfs, &file) => 0; -# -# char rbuffer[1024]; -# lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; -# lfs_file_read(&lfs, &file, rbuffer, size) => size; -# assert(strcmp(rbuffer, wbuffer) == 0); -# lfs_file_close(&lfs, &file) => 0; -# } -# lfs_unmount(&lfs) => 0; -#''' diff --git a/tests/test_fwrite.toml b/tests/test_fwrite.toml new file mode 100644 index 00000000..9cd39f4e --- /dev/null +++ b/tests/test_fwrite.toml @@ -0,0 +1,2445 @@ +# More extensive file writing tests +after = 'test_files' + +# TODO should fragment_size accept 0? +# test with different fragment sizes +defines.FRAGMENT_SIZE = [1, 16, 64] + +# test with different crystal sizes +defines.CRYSTAL_SIZE = [512] + +# more complex writing patterns to inlined files + +# write files incrementally +[cases.test_fwrite_incr] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + uint8_t wbuf[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { + lfsr_file_write(&lfs, &file, &wbuf[i], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + // note the switch to append here + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_APPEND) => 0; + } + } + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# overwrite files +# TODO this is too slow right now, but should speed up with better +# write strategies +[cases.test_fwrite_overwrite] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +# bit 0 => first chunk +# bit 1 => middle chunk +# bit 2 => last chunk +defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] +# 0 => in-order +# 1 => reversed +defines.ORDER = [0, 1] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write first chunk? + if (MASK & 0x1) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + + // write second chunk? + if (MASK & 0x2) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) + => SIZE/2 - CHUNK/2; + lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + + // write third chunk? + if (MASK & 0x4) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } + } + + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + // does our file match our simulation? + assert(memcmp(rbuf, sim, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# similar to overwrite files, but without underlying data +[cases.test_fwrite_holes] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +# bit 0 => first chunk +# bit 1 => middle chunk +# bit 2 => last chunk +defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] +# 0 => in-order +# 1 => reversed +defines.ORDER = [0, 1] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + memset(sim, 0, SIZE); + // we may not write the entire file + lfs_off_t size + = (MASK & ((ORDER == 0) ? 0x4 : 0x1)) ? SIZE + : (MASK & ((ORDER == 0) ? 0x2 : 0x2)) ? SIZE/2 + (CHUNK+2-1)/2 + : (MASK & ((ORDER == 0) ? 0x1 : 0x4)) ? CHUNK + : 0; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write first chunk? + if (MASK & 0x1) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + + // write second chunk? + if (MASK & 0x2) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) + => SIZE/2 - CHUNK/2; + lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + + // write third chunk? + if (MASK & 0x4) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } + } + + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# simple truncate test +[cases.test_fwrite_truncate] +defines.FROM = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.TO = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + # these just save testing time + 'FROM / FRAGMENT_SIZE <= 4096', + 'TO / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[lfs_max32(FROM,TO)]; + memset(sim, 0, lfs_max32(FROM,TO)); + uint32_t prng = 42; + for (lfs_size_t i = 0; i < FROM; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, FROM) => FROM; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // truncate to new size + lfsr_file_truncate(&lfs, &file, TO) => 0; + if (TO < FROM) { + memset(sim+TO, 0, FROM-TO); + } + + // close + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => TO; + // try reading + uint8_t rbuf[2*TO]; + memset(rbuf, 0xaa, 2*TO); + lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; + assert(memcmp(rbuf, sim, TO) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# one purpose of this test is to check that data is not hidden +# and then revealed by truncate, that would be bad +[cases.test_fwrite_truncate_2] +defines.FROM = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.AND = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.TO = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + # these just save testing time + 'FROM / FRAGMENT_SIZE <= 4096', + 'AND / FRAGMENT_SIZE <= 4096', + 'TO / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[lfs_max32(FROM,lfs_max32(AND,TO))]; + memset(sim, 0, lfs_max32(FROM,lfs_max32(AND,TO))); + uint32_t prng = 42; + for (lfs_size_t i = 0; i < FROM; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, FROM) => FROM; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // truncate to intermediate size + lfsr_file_truncate(&lfs, &file, AND) => 0; + if (AND < FROM) { + memset(sim+AND, 0, FROM-AND); + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // truncate to new size + lfsr_file_truncate(&lfs, &file, TO) => 0; + if (TO < AND) { + memset(sim+TO, 0, AND-TO); + } + + // close + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => TO; + // try reading + uint8_t rbuf[2*TO]; + memset(rbuf, 0xaa, 2*TO); + lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; + assert(memcmp(rbuf, sim, TO) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# simple fruncate test +[cases.test_fwrite_fruncate] +defines.FROM = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.TO = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + # these just save testing time + 'FROM / FRAGMENT_SIZE <= 4096', + 'TO / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[lfs_max32(FROM,TO)]; + memset(sim, 0, lfs_max32(FROM,TO)); + uint32_t prng = 42; + for (lfs_size_t i = 0; i < FROM; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, FROM) => FROM; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // fruncate to new size + lfsr_file_fruncate(&lfs, &file, TO) => 0; + if (TO > FROM) { + memmove(sim+TO-FROM, sim, FROM); + memset(sim, 0, TO-FROM); + } else if (TO < FROM) { + memmove(sim, sim+FROM-TO, TO); + memset(sim+TO, 0, FROM-TO); + } + + // close + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => TO; + // try reading + uint8_t rbuf[2*TO]; + memset(rbuf, 0xaa, 2*TO); + lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; + assert(memcmp(rbuf, sim, TO) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# one purpose of this test is to check that data is not hidden +# and then revealed by fruncate, that would be bad +[cases.test_fwrite_fruncate_2] +defines.FROM = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.AND = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.TO = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + # these just save testing time + 'FROM / FRAGMENT_SIZE <= 4096', + 'AND / FRAGMENT_SIZE <= 4096', + 'TO / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[lfs_max32(FROM,lfs_max32(AND,TO))]; + memset(sim, 0, lfs_max32(FROM,lfs_max32(AND,TO))); + uint32_t prng = 42; + for (lfs_size_t i = 0; i < FROM; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, FROM) => FROM; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // fruncate to intermediate size + lfsr_file_fruncate(&lfs, &file, AND) => 0; + if (AND > FROM) { + memmove(sim+AND-FROM, sim, FROM); + memset(sim, 0, AND-FROM); + } else if (AND < FROM) { + memmove(sim, sim+FROM-AND, AND); + memset(sim+AND, 0, FROM-AND); + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // fruncate to new size + lfsr_file_fruncate(&lfs, &file, TO) => 0; + if (TO > AND) { + memmove(sim+TO-AND, sim, AND); + memset(sim, 0, TO-AND); + } else if (TO < AND) { + memmove(sim, sim+AND-TO, TO); + memset(sim+TO, 0, AND-TO); + } + + // close + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == TO); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => TO; + // try reading + uint8_t rbuf[2*TO]; + memset(rbuf, 0xaa, 2*TO); + lfsr_file_read(&lfs, &file, rbuf, 2*TO) => TO; + assert(memcmp(rbuf, sim, TO) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# writing any data structure backwards always reveals issues +[cases.test_fwrite_reversed] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + if (INIT == 0) { + memset(sim, 0, SIZE); + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write to file incrementally and backwards + for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { + for (lfs_size_t j = 0; j < CHUNK; j++) { + sim[SIZE-i-CHUNK+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_seek(&lfs, &file, SIZE-i-CHUNK, LFS_SEEK_SET) => SIZE-i-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-i-CHUNK], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + // does our file match our simulation? + assert(memcmp(rbuf, sim, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# these are like the overwrite/hole tests, but with enough rewrites to +# trigger compaction +[cases.test_fwrite_overwrite_compaction] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +# bit 0 => first chunk +# bit 1 => middle chunk +# bit 2 => last chunk +defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] +# 0 => in-order +# 1 => reversed +defines.ORDER = [0, 1] +# writing this many times guarantees a compaction +defines.WRITES = '2*(BLOCK_SIZE/PROG_SIZE)' +# TODO is setting PROG_SIZE here reasonable? +defines.PROG_SIZE = 64 +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write first chunk? + if (MASK & 0x1) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write second chunk? + if (MASK & 0x2) { + for (lfs_size_t w = 0; w < WRITES; w++) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) + => SIZE/2 - CHUNK/2; + lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write third chunk? + if (MASK & 0x4) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } + } + } + + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + // does our file match our simulation? + assert(memcmp(rbuf, sim, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fwrite_hole_compaction] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +# bit 0 => first chunk +# bit 1 => middle chunk +# bit 2 => last chunk +defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] +# 0 => in-order +# 1 => reversed +defines.ORDER = [0, 1] +# writing this many times guarantees a compaction +defines.WRITES = '2*(BLOCK_SIZE/PROG_SIZE)' +# TODO is setting PROG_SIZE here reasonable? +defines.PROG_SIZE = 64 +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + memset(sim, 0, SIZE); + // we may not write the entire file + lfs_off_t size + = (MASK & ((ORDER == 0) ? 0x4 : 0x1)) ? SIZE + : (MASK & ((ORDER == 0) ? 0x2 : 0x2)) ? SIZE/2 + (CHUNK+2-1)/2 + : (MASK & ((ORDER == 0) ? 0x1 : 0x4)) ? CHUNK + : 0; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write first chunk? + if (MASK & 0x1) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write second chunk? + if (MASK & 0x2) { + for (lfs_size_t w = 0; w < WRITES; w++) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) + => SIZE/2 - CHUNK/2; + lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write third chunk? + if (MASK & 0x4) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } + } + } + + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# fuzz testing +[cases.test_fwrite_fuzz_aligned] +defines.N = 20 +defines.SEED = 'range(10)' +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = SEED; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + for (lfs_size_t i = 0; i < N; i++) { + // choose a random chunk-aligned location + lfs_off_t off = (TEST_PRNG(&prng) % (SIZE/CHUNK)) * CHUNK; + + // update sim + for (lfs_size_t j = 0; j < CHUNK; j++) { + sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + size = lfs_max32(size, off+CHUNK); + + // update file + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + lfsr_file_write(&lfs, &file, &sim[off], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# fuzz testing +[cases.test_fwrite_fuzz_unaligned] +defines.N = 20 +defines.SEED = 'range(10)' +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# chunk is more an upper limit here +defines.CHUNK = [32, 8] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = SEED; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + for (lfs_size_t i = 0; i < N; i++) { + // choose a random location + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min32( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + + // update sim + for (lfs_size_t j = 0; j < chunk; j++) { + sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + if (chunk != 0) { + size = lfs_max32(size, off+chunk); + } + + // update file + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + + +# more seek testing +[cases.test_fwrite_r_seek] +defines.N = 20 +defines.SEED = 'range(10)' +defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# chunk is more an upper limit here +defines.CHUNK = [32, 8] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = SEED; + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + lfs_soff_t off_ = 0; + for (lfs_size_t i = 0; i < N; i++) { + // choose a random location + lfs_soff_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min32( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + + // test different seek methods + if (WHENCE == LFS_SEEK_SET) { + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + } else if (WHENCE == LFS_SEEK_CUR) { + lfsr_file_seek(&lfs, &file, off-off_, LFS_SEEK_CUR) => off; + } else if (WHENCE == LFS_SEEK_END) { + lfsr_file_seek(&lfs, &file, off-SIZE, LFS_SEEK_END) => off; + } + + // tell should always report the correct position + lfsr_file_tell(&lfs, &file) => off; + + // read the file and assert we got the correct data + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, chunk) => chunk; + assert(memcmp(rbuf, &sim[off], chunk) == 0); + + // tell should report the new position + lfsr_file_tell(&lfs, &file) => off + chunk; + + // keep track of previous off for LFS_SEEK_CUR + off_ = off + chunk; + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# this is pretty much the same as earlier fuzz testing, except we test +# different seek methods +[cases.test_fwrite_w_seek] +defines.N = 10 +defines.SEED = 'range(10)' +defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# chunk is more an upper limit here +defines.CHUNK = [32, 8] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = SEED; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + lfs_soff_t off_ = 0; + for (lfs_size_t i = 0; i < N; i++) { + // choose a random location + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min32( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + + // test different seek methods + if (WHENCE == LFS_SEEK_SET) { + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + } else if (WHENCE == LFS_SEEK_CUR) { + lfsr_file_seek(&lfs, &file, off-off_, LFS_SEEK_CUR) => off; + } else if (WHENCE == LFS_SEEK_END) { + lfsr_file_seek(&lfs, &file, off-size, LFS_SEEK_END) => off; + } + + // tell should always report the correct position + lfsr_file_tell(&lfs, &file) => off; + + // update the sim + for (lfs_size_t j = 0; j < chunk; j++) { + sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + if (chunk != 0) { + size = lfs_max32(size, off+chunk); + } + + // update the file + lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // tell should report the new position + lfsr_file_tell(&lfs, &file) => off + chunk; + + // keep track of previous off for LFS_SEEK_CUR + off_ = off + chunk; + } + lfsr_file_close(&lfs, &file) => 0; + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# the above was just warmup, here's the real seek test +[cases.test_fwrite_rw_seek] +defines.N = 10 +defines.SEED = 'range(10)' +defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# chunk is more an upper limit here +defines.CHUNK = [32, 8] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = SEED; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0; + lfs_soff_t off_ = 0; + for (lfs_size_t i = 0; i < N; i++) { + // choose a random location + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min32( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + // and if we are reading or writing + uint8_t op = TEST_PRNG(&prng) % 2; + + // test different seek methods + if (WHENCE == LFS_SEEK_SET) { + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + } else if (WHENCE == LFS_SEEK_CUR) { + lfsr_file_seek(&lfs, &file, off-off_, LFS_SEEK_CUR) => off; + } else if (WHENCE == LFS_SEEK_END) { + lfsr_file_seek(&lfs, &file, off-size, LFS_SEEK_END) => off; + } + + // tell should always report the correct position + lfsr_file_tell(&lfs, &file) => off; + + // writing? + if (op == 0) { + // update the sim + for (lfs_size_t j = 0; j < chunk; j++) { + sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + if (chunk != 0) { + size = lfs_max32(size, off+chunk); + } + + // update the file + lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // tell should report the new position + lfsr_file_tell(&lfs, &file) => off + chunk; + + // keep track of previous off for LFS_SEEK_CUR + off_ = off + chunk; + + // reading? + } else if (op == 1) { + // we may read less than chunk if we're past eof + lfs_off_t expected = lfs_min32( + chunk, + size - lfs_min32(off, size)); + + // read the file and assert we got the correct data + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, chunk) => expected; + assert(memcmp(rbuf, &sim[off], expected) == 0); + + // tell should report the new position + lfsr_file_tell(&lfs, &file) => off + expected; + + // keep track of previous off for LFS_SEEK_CUR + off_ = off + expected; + } + } + lfsr_file_close(&lfs, &file) => 0; + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# test other corner conditions +[cases.test_fwrite_seek_negative] +defines.WHENCE = ['LFS_SEEK_SET', 'LFS_SEEK_CUR', 'LFS_SEEK_END'] +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.MODE = ['LFS_O_RDONLY', 'LFS_O_WRONLY', 'LFS_O_RDWR'] +if = [ + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = 42; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + lfsr_file_close(&lfs, &file) => 0; + + // try to seek before the beginning of the file, this should fail + lfsr_file_open(&lfs, &file, "hello", MODE) => 0; + if (WHENCE == LFS_SEEK_SET) { + lfsr_file_seek(&lfs, &file, -1, LFS_SEEK_SET) => LFS_ERR_INVAL; + } else if (WHENCE == LFS_SEEK_CUR) { + lfsr_file_seek(&lfs, &file, -1, LFS_SEEK_CUR) => LFS_ERR_INVAL; + } else if (WHENCE == LFS_SEEK_END) { + lfsr_file_seek(&lfs, &file, -(size+1), LFS_SEEK_END) => LFS_ERR_INVAL; + } + lfsr_file_close(&lfs, &file) => 0; + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# heavy fuzz test with rw seeks, truncate, and fruncate +[cases.test_fwrite_rwtf_fuzz] +defines.N = 20 +defines.SEED = 'range(10)' +defines.SIZE = [ + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +# chunk is more an upper limit here +defines.CHUNK = [32, 8] +# 0 => no init +# 1 => fill with data +# 2 => truncate to size +defines.INIT = [0, 1, 2] +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +if = [ + 'CHUNK <= SIZE', + # this just save testing time + 'SIZE / FRAGMENT_SIZE <= 4096', +] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + lfs_off_t size; + uint32_t prng = SEED; + if (INIT == 0) { + memset(sim, 0, SIZE); + size = 0; + } else if (INIT == 1) { + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + size = SIZE; + } else if (INIT == 2) { + memset(sim, 0, SIZE); + lfsr_file_truncate(&lfs, &file, SIZE) => 0; + size = SIZE; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0; + } + + for (lfs_size_t i = 0; i < N; i++) { + // and if we are reading, writing, truncating, or fruncating + uint8_t op = TEST_PRNG(&prng) % 4; + + // writing? + if (op == 0) { + // choose a random location + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min32( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + + // seek + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + + // update the sim + for (lfs_size_t j = 0; j < chunk; j++) { + sim[off+j] = 'a' + (TEST_PRNG(&prng) % 26); + } + if (chunk != 0) { + size = lfs_max32(size, off+chunk); + } + + // update the file + lfsr_file_write(&lfs, &file, &sim[off], chunk) => chunk; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDWR) => 0; + } + + // reading? + } else if (op == 1) { + // choose a random location + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + // and a random size, up to the chunk size + lfs_size_t chunk = lfs_min32( + TEST_PRNG(&prng) % CHUNK, + SIZE - off); + + // seek + lfsr_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off; + + // we may read less than chunk if we're past eof + lfs_off_t expected = lfs_min32( + chunk, + size - lfs_min32(off, size)); + + // read the file and assert we got the correct data + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, chunk) => expected; + assert(memcmp(rbuf, &sim[off], expected) == 0); + + // truncating? + } else if (op == 2) { + // choose a random new file size + lfs_off_t size_ = TEST_PRNG(&prng) % SIZE; + + // update the sim + if (size_ < size) { + memset(sim+size_, 0, size-size_); + } + size = size_; + + // truncate the file + lfsr_file_truncate(&lfs, &file, size_) => 0; + + } else if (op == 3) { + // choose a random new file size + lfs_off_t size_ = TEST_PRNG(&prng) % SIZE; + + // update the sim + if (size_ > size) { + memmove(sim+size_-size, sim, size); + memset(sim, 0, size_-size); + } else if (size_ < size) { + memmove(sim, sim+size-size_, size_); + memset(sim+size_, 0, size-size_); + } + size = size_; + + // truncate the file + lfsr_file_fruncate(&lfs, &file, size_) => 0; + } + } + lfsr_file_close(&lfs, &file) => 0; + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + + +# TODO +# [cases.test_fwrite_push] ? +# [cases.test_fwrite_pop] ? +# [cases.test_fwrite_rwtfpp_fuzz] ? +