Files
littlefs/tests/test_fsync.toml
T
Christopher Haster 1344d416d2 Relaxed asserts, allow syncing rdonly files, error on unsync
This is a compromise on consistency and not breaking expected
invariants.

The problem: rdonly files can become unsynced:

1. file is opened rdonly + desync
2. the same file is opened and written to
3. we try to sync our original file handle

What we want:

1. sync should ensure disk + files are in-sync
2. rdonly implies sync should not write to disk

Without desync, and in other systems, this is not a problem, because
rdonly files can never become unsynced.

But with desync, a state (albiet a roundabout one) can be reached where
we can't satisfy both of these invariants.

I wanted to just assert on syncing a rdonly file, but this is supported
on POSIX and other systems, and it makes sense that you would want to
unconditionally call sync in certain circumstances (ensuring close can't
write to disk for example).

So adopts the approach of allowing flush and sync on rdonly files when
possible, and when not possible, sync simply returns LFS_ERR_INVAL and
makes it the user's problem.

For the above example, this has the side effect of making the rdonly file
desync again, so close can complete without touching disk.

As a plus, a desynced rdonly file can now be used to test if a file has
been written to. Though I'm not sure when this would be useful... Or
if it's a good idea to suggest this use of the API...
2024-02-03 18:15:22 -06:00

3266 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_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_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_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_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_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_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_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_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_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_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;
'''