bd4a5e5ab3
The main idea here is that diverse tests are better than many similar tests. Sure, if we throw fuzz tests at the system all day we'll eventually find more bugs, but if a developer is in the loop that time is going to be better spent writing specific tests targeting the fragile parts of the system. And don't worry, we can still throw fuzz tests at the system all day by specifying explicit seeds with -DSEED=blah. Changes: - Limited dir-related powerloss fuzz testing to N <= 16. These tests were the biggest culprit of excessive test runtime, requiring O(n^2) redundant operations to recover from powerlosses (they just replay the full sequence on powerloss). - As a tradeoff, bumped most fuzz tests to a minimum of 20 seeds. The big exception being the test_fwrite tests, which are heavily parameterized and already take the most time to run. Each parameter combination also multiplies the effective number of seeds, so increasing the number of base seeds will probably have diminishing returns. - Limited test_fwrite_reversed to SIZE <= 4*1024*CHUNK. Writing a file backwards is just about the worst way you could write a file, since all buffering/coalescing expect writes to eventually make forward progress. On the flip side, because it's uncommon, writing a file backwards is also a great way to find bugs. But at some point a compromise needs to be made. Impacted test runtimes: case otime ntime dtime test_btree_push_fuzz 0.3 0.5 +0.2 (+60.2%) test_btree_push_sparse_fuzz 0.4 3.3 +2.9 (+720.4%) test_btree_update_fuzz 0.4 0.9 +0.6 (+141.6%) test_btree_update_sparse_fuzz 0.5 4.5 +4.1 (+857.4%) test_btree_pop_fuzz 0.6 2.3 +1.7 (+314.7%) test_btree_pop_sparse_fuzz 1.2 5.7 +4.4 (+356.2%) test_btree_split_fuzz 0.5 1.4 +0.8 (+150.2%) test_btree_split_sparse_fuzz 0.4 5.6 +5.1 (+1163.2%) test_btree_find_fuzz 0.5 0.7 +0.2 (+50.7%) test_btree_find_sparse_fuzz 1.0 3.0 +2.0 (+189.8%) test_btree_traversal_fuzz 0.6 2.3 +1.6 (+260.4%) test_dirs_mkdir_many 3.3 2.1 -1.3 (-37.8%) test_dirs_mkdir_many_backwards 3.5 2.1 -1.4 (-39.9%) test_dirs_mkdir_fuzz 115.3 106.4 -8.9 (-7.7%) test_dirs_rm_many 283.9 76.8 -207.0 (-72.9%) test_dirs_rm_many_backwards 216.1 80.6 -135.5 (-62.7%) test_dirs_rm_fuzz 647.0 68.5 -578.5 (-89.4%) test_dirs_mv_many 14.2 15.4 +1.1 (+7.9%) test_dirs_mv_many_backwards 16.5 14.5 -2.1 (-12.5%) test_dirs_mv_fuzz 1932.5 156.7 -1775.8 (-91.9%) test_dirs_general_fuzz 561.9 74.5 -487.4 (-86.7%) test_dread_recursive_rm 336.6 46.2 -290.4 (-86.3%) test_dread_recursive_mv 55.5 44.6 -11.0 (-19.8%) test_fsync_rrrr_fuzz 0.4 0.3 -0.1 (-18.4%) test_fsync_wrrr_fuzz 8.0 12.4 +4.5 (+56.0%) test_fsync_wwww_fuzz 13.2 33.4 +20.2 (+152.6%) test_fsync_wwrr_fuzz 5.4 50.9 +45.5 (+841.6%) test_fsync_rwrw_fuzz 2.4 8.4 +6.0 (+253.9%) test_fsync_rwrw_sparse_fuzz 3.2 7.5 +4.2 (+129.9%) test_fsync_rwtfrwtf_sparse_fuzz 6.1 8.5 +2.4 (+39.3%) test_fsync_drrr_fuzz 11.8 9.2 -2.6 (-21.8%) test_fsync_wddd_fuzz 9.3 11.9 +2.6 (+28.0%) test_fsync_rwdrwd_fuzz 1.6 33.1 +31.5 (+1963.4%) test_fsync_rwdrwd_sparse_fuzz 0.3 1.8 +1.4 (+418.8%) test_fsync_rwtfdrwtfd_sparse_fuzz 0.3 1.1 +0.8 (+260.2%) test_fwrite_reversed 728.5 345.2 -383.3 (-52.6%) TOTAL 7587.5 3792.3 -3795.2 (-50.0%)
3276 lines
108 KiB
TOML
3276 lines
108 KiB
TOML
# Test multiple open file handles in different r/w configurations
|
|
after = 'test_fwrite'
|
|
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_sync_wrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
|
|
// should not show up in b yet
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// close a
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a, without doing anything
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b, this gets tricky
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b one last time, this time with sync
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_sync_wwrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a/b
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and b
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
|
|
// should not show up in c yet
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// or on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync a
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b's contents were clobbered, so we should see a
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// lets rewrite a one last time
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// close a and b
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// now our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!ohayo!zdrasti!",
|
|
strlen("hello!ohayo!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test multiple readers, this shouldn't really have any issues
|
|
[cases.test_fsync_rrrr]
|
|
defines.R = 4
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// read R handles in parallel
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rrrr_fuzz]
|
|
defines.R = 4
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = SEED;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// read R handles in parallel
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple readers
|
|
[cases.test_fsync_wrrr]
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wrrr_fuzz]
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple writers
|
|
[cases.test_fsync_wwww]
|
|
defines.W = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W handles in parallel
|
|
lfsr_file_t writers[W];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wwww_fuzz]
|
|
defines.W = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W files in parallel
|
|
lfsr_file_t writers[W];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writers[w], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, size) => size;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple writers and multiple readers
|
|
[cases.test_fsync_wwrr]
|
|
defines.W = 4
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W handles, R handles in parallel
|
|
lfsr_file_t writers[W];
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wwrr_fuzz]
|
|
defines.W = 4
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write W files in parallel
|
|
lfsr_file_t writers[W];
|
|
lfsr_file_t readers[R];
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_open(&lfs, &writers[w], "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writers[w], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writers[w], wbuf, size) => size;
|
|
if (SYNC == 0) {
|
|
if (w == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writers[w]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t w = 0; w < W; w++) {
|
|
lfsr_file_close(&lfs, &writers[w]) => 0;
|
|
}
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rd/wrers
|
|
[cases.test_fsync_rwrw]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK;
|
|
memcpy(&between[rw][i], wbuf, CHUNK);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rwrw_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rw files without fixed size
|
|
[cases.test_fsync_rwrw_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (SYNC) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rw files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfrwtf_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
if (FLUSH == 1) { // (flush does nothing)
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
if (FLUSH == 1) { // (flush does nothing)
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (SYNC) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Desynced files make things interesting
|
|
|
|
# Some specific tests
|
|
[cases.test_fsync_desync_wdrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// now mark as desync, rewrite, and close
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// b should still have previous contents
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// reopen a, mark as desync, rewrite a, and sync
|
|
lfsr_file_open(&lfs, &a, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// this sync should clear the desync
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// now our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
// rewrite a, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wrrd]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - desynced reader
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// but not in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// rewrite and sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
|
|
// rewrite and close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!");
|
|
assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0);
|
|
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wdwdrr]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, rewrite, and close
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, rewrite
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!");
|
|
assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a, close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!"))
|
|
=> strlen("czesc!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
|
|
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!");
|
|
assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wdwdrr_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, this should freeze its contents
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
// rewrite a, sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b, this may be tricky since we haven't touched b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite b, close, desync flag should have been cleared
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!");
|
|
assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// rewrite a, close
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!");
|
|
assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wrrd_noop]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - reader kept open, recvs updates from a
|
|
// c - desynced reader
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// but not in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0;
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// rewrite a and sync
|
|
lfsr_file_rewind(&lfs, &a) => 0;
|
|
lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// sync b, this should be a noop
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
|
|
// as if things couldn't get weirder
|
|
//
|
|
// what do you think should happend if we sync c?
|
|
//
|
|
// in theory, this should update a + b + disk, but that would require
|
|
// writing to disk... instead we just error
|
|
lfsr_file_sync(&lfs, &c) => LFS_ERR_INVAL;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// but not in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// reopen c, should now be up to date
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
|
|
// we _are_ allowed to sync c if it results in a noop
|
|
lfsr_file_sync(&lfs, &c) => 0;
|
|
|
|
// our write should show up in b
|
|
lfsr_file_rewind(&lfs, &b) => 0;
|
|
lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
// and in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!");
|
|
assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0);
|
|
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_desync_wdwdrr_append]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// a - writer
|
|
// b - writer
|
|
// c - reader kept open, recvs updates from a
|
|
// d - reader kept closed, checks disk state
|
|
lfsr_file_t a;
|
|
lfsr_file_t b;
|
|
lfsr_file_t c;
|
|
lfsr_file_t d;
|
|
uint8_t rbuf[256];
|
|
lfsr_file_open(&lfs, &a, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0;
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0;
|
|
|
|
// write to a and sync
|
|
lfsr_file_write(&lfs, &a, "hello!", strlen("hello!"))
|
|
=> strlen("hello!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// mark b as desync, write, and close
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!"))
|
|
=> strlen("bonjour!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// c should still have previous contents
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!");
|
|
assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// reopen b, mark as desync, write
|
|
lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0;
|
|
lfsr_file_desync(&lfs, &b) => 0;
|
|
lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!"))
|
|
=> strlen("ohayo!");
|
|
// write a, sync
|
|
lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!"))
|
|
=> strlen("zdrasti!");
|
|
lfsr_file_sync(&lfs, &a) => 0;
|
|
|
|
// a should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!");
|
|
assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// sync b
|
|
lfsr_file_sync(&lfs, &b) => 0;
|
|
|
|
// b should show up in c, without a's changes
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!");
|
|
assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write b, close, desync flag should have been cleared
|
|
lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!"))
|
|
=> strlen("annyeong!");
|
|
lfsr_file_close(&lfs, &b) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
|
|
strlen("hello!ohayo!annyeong!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!",
|
|
strlen("hello!ohayo!annyeong!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
// write a, close
|
|
lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!"))
|
|
=> strlen("czesc!");
|
|
lfsr_file_close(&lfs, &a) => 0;
|
|
|
|
// our write should show up in c
|
|
lfsr_file_rewind(&lfs, &c) => 0;
|
|
lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!czesc!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
|
|
strlen("hello!ohayo!annyeong!czesc!")) == 0);
|
|
// and on disk
|
|
lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf))
|
|
=> strlen("hello!ohayo!annyeong!czesc!");
|
|
assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!",
|
|
strlen("hello!ohayo!annyeong!czesc!")) == 0);
|
|
lfsr_file_close(&lfs, &d) => 0;
|
|
|
|
lfsr_file_close(&lfs, &c) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# Test one desynced writer, multiple readers
|
|
[cases.test_fsync_drrr]
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_drrr_fuzz]
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test one writer, multiple desynced readers
|
|
[cases.test_fsync_wddd]
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK;
|
|
assert(memcmp(rbuf, &before[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_wddd_fuzz]
|
|
defines.R = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write 1 handle, read R handles in parallel
|
|
lfsr_file_t writer;
|
|
lfsr_file_t readers[R];
|
|
lfsr_file_open(&lfs, &writer, "jello",
|
|
LFS_O_WRONLY
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_open(&lfs, &readers[r], "jello",
|
|
LFS_O_RDONLY | LFS_O_DESYNC) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
|
|
memcpy(&after[off], wbuf, size);
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &writer) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &writer) => 0;
|
|
}
|
|
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &before[off], size) == 0);
|
|
}
|
|
}
|
|
lfsr_file_close(&lfs, &writer) => 0;
|
|
for (lfs_size_t r = 0; r < R; r++) {
|
|
lfsr_file_close(&lfs, &readers[r]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple desynced rd/wrers
|
|
[cases.test_fsync_rwdrwd]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < CHUNK; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK;
|
|
memcpy(&between[rw][i], wbuf, CHUNK);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (i == 0) {
|
|
memcpy(after, between[rw], SIZE);
|
|
} else {
|
|
memcpy(&after[i], wbuf, CHUNK);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[i], CHUNK) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_fsync_rwdrwd_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no sync, readers not updated
|
|
# FLUSH=1 => sync via lfsr_file_sync
|
|
# FLUSH=2 => sync via LFS_O_SYNC
|
|
defines.SYNC = [0, 1, 2]
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 20
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a file
|
|
uint32_t prng = 42;
|
|
uint8_t before[SIZE];
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
before[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
uint8_t between[RW][SIZE];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
memcpy(between[rw], before, SIZE);
|
|
}
|
|
uint8_t after[SIZE];
|
|
memcpy(after, before, SIZE);
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
|
lfsr_file_write(&lfs, &file, before, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_DESYNC
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)
|
|
| ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// write
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
if (SYNC == 0) {
|
|
if (rw == 0) {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
} else {
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (SYNC == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
if (i == 0) {
|
|
memcpy(after, between[rw], SIZE);
|
|
} else {
|
|
memcpy(&after[off], wbuf, size);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random offset
|
|
lfs_off_t off = TEST_PRNG(&prng) % SIZE;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
|
|
// read
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, &after[off], size) == 0);
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
if (SYNC == 0) {
|
|
assert(memcmp(rbuf, before, SIZE) == 0);
|
|
} else {
|
|
assert(memcmp(rbuf, after, SIZE) == 0);
|
|
}
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files without fixed size
|
|
[cases.test_fsync_rwdrwd_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 3;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// otherwise no change
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (sync == 1) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# Test multiple rwd files while also truncating/fruncating
|
|
[cases.test_fsync_rwtfdrwtfd_sparse_fuzz]
|
|
defines.RW = 4
|
|
# FLUSH=0 => no flush
|
|
# FLUSH=1 => flush via lfsr_file_flush
|
|
# FLUSH=2 => flush via LFS_O_FLUSH
|
|
defines.FLUSH = [0, 1, 2]
|
|
defines.SEED = 'range(20)'
|
|
defines.N = 40
|
|
defines.SIZE = [
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.CHUNK = '(SIZE+16-1) / 16'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
uint32_t prng = 42;
|
|
uint8_t between[RW][SIZE];
|
|
lfs_size_t between_size[RW];
|
|
bool between_desync[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
between_size[rw] = 0;
|
|
between_desync[rw] = false;
|
|
}
|
|
uint8_t after[SIZE];
|
|
lfs_size_t after_size = 0;
|
|
|
|
// write RW rdwrs in parallel
|
|
lfsr_file_t rdwrs[RW];
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// open files
|
|
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
|
|
LFS_O_RDWR
|
|
| LFS_O_CREAT
|
|
| ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0;
|
|
}
|
|
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// choose a random operation
|
|
uint8_t op = TEST_PRNG(&prng) % 4;
|
|
// choose a random sync state
|
|
uint8_t sync = TEST_PRNG(&prng) % 3;
|
|
|
|
// writing?
|
|
if (op == 0) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, SIZE - off);
|
|
uint8_t wbuf[CHUNK];
|
|
for (lfs_size_t j = 0; j < size; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// write
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size;
|
|
if (FLUSH == 1) {
|
|
lfsr_file_flush(&lfs, &rdwrs[rw]) => 0;
|
|
}
|
|
|
|
// update sim
|
|
if (off > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
off - between_size[rw]);
|
|
}
|
|
memcpy(&between[rw][off], wbuf, size);
|
|
between_size[rw] = lfs_max32(off + size, between_size[rw]);
|
|
|
|
// reading?
|
|
} else if (op == 1) {
|
|
// choose a random offset
|
|
lfs_off_t off = (between_size[rw] > 0)
|
|
? TEST_PRNG(&prng) % between_size[rw]
|
|
: 0;
|
|
lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off);
|
|
|
|
// read
|
|
lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off;
|
|
uint8_t rbuf[CHUNK];
|
|
lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size;
|
|
assert(memcmp(rbuf, &between[rw][off], size) == 0);
|
|
|
|
// truncating?
|
|
} else if (op == 2) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// truncate
|
|
lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memset(&between[rw][between_size[rw]],
|
|
0,
|
|
size - between_size[rw]);
|
|
}
|
|
between_size[rw] = size;
|
|
|
|
// fruncating?
|
|
} else if (op == 3) {
|
|
// choose a random new file size
|
|
lfs_off_t size = TEST_PRNG(&prng) % SIZE;
|
|
|
|
// fruncate
|
|
lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0;
|
|
|
|
// update the sim
|
|
if (size > between_size[rw]) {
|
|
memmove(&between[rw][size - between_size[rw]],
|
|
between[rw],
|
|
between_size[rw]);
|
|
memset(between[rw],
|
|
0,
|
|
size - between_size[rw]);
|
|
} else {
|
|
memmove(between[rw],
|
|
&between[rw][between_size[rw] - size],
|
|
size);
|
|
}
|
|
between_size[rw] = size;
|
|
}
|
|
|
|
// desync?
|
|
if (sync == 0) {
|
|
lfsr_file_desync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = true;
|
|
|
|
// sync?
|
|
} else if (sync == 1) {
|
|
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
|
|
between_desync[rw] = false;
|
|
|
|
// otherwise no change
|
|
}
|
|
|
|
// broadcast sim?
|
|
if (sync == 1) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (lfs_size_t rw = 0; rw < RW; rw++) {
|
|
// close files
|
|
lfsr_file_close(&lfs, &rdwrs[rw]) => 0;
|
|
|
|
// broadcast sim one last time?
|
|
if (!between_desync[rw]) {
|
|
memcpy(after, between[rw], SIZE);
|
|
after_size = between_size[rw];
|
|
|
|
for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) {
|
|
if (!between_desync[rw_]) {
|
|
memcpy(between[rw_], between[rw], SIZE);
|
|
between_size[rw_] = between_size[rw];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that file was written as expected
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size;
|
|
assert(memcmp(rbuf, after, after_size) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|