Files
littlefs/tests/test_fsync.toml
T
Christopher Haster f6742eefb3 Fixed test failures caused by changes to open semantics
- Fixed fsync tests, which needed more lfsr_file_sync calls so multiple
  file handles can be opened correctly.

  Though this points out there's no way to open a rdonly file on an
  uncreated file until sync is called... But I guess you wouldn't be
  able to recieve broadcasts until sync anyways? at which point the file
  would be created?

- Update mtree tests based on the new remove behavior for regular files.

  Before this changed the mid to -1, now it points to the next mid with
  the zombie flag set. Upper layers use this to migrate mdirs to a
  scratch file if necessary.

- Removed the orphaned mdir test. We don't create orphaned mdirs
  anymore.

  Technically, orphaned mdirs are currently possible if we lose power in
  the middle of the mtree update, but this is a bug and should be fixed
  (previous revisions did not have this issue).
2024-02-03 18:15:37 -06:00

3276 lines
107 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(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_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
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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
# 0 => no flush
# 1 => flush via lfsr_file_flush
# 2 => flush via LFS_O_FLUSH
defines.FLUSH = [0, 1, 2]
defines.SEED = 'range(10)'
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;
'''