Files
littlefs/tests/test_ftree.toml
T
Christopher Haster 2f38822820 Still missing quite a bit, but rudimentary inlined-trees are now working
And by working, I mean you can create inlined trees, just don't
compact/split/move/etc anything. But this does outline the path files
take when writing buffers into inlined trees.

"Inlined trees" in littlefs are entire small rbyd trees embedded as
secondary trees in an mdir's main rbyd tree. When fetching, we can
indicate if a given trunk belongs to the main tree or secondary tree by
setting one of the unused mode bits in the trunk's tag, now called the
"deferred" bit. This bit doesn't need to be included in the alt's "key"
field, so there's no issue with it conflicting with the alt's mode bits.

This requires a bit of tweaking lfsr_rbyd_fetch, since it needs to fall
back to the previous trunk if it discovers the most recent trunk belongs
to an inlined tree. But as a benefit we can leverage the full power of
rbyds in inlined files, including holes, partial updates, etc.

One downside is it looks like these inlined trees may involve more work
in maintining their state correctly, since they need to be sort of
"brought along" when mdirs are compacted, even if they don't actually
have a reference in the mdir yet. But the sheer amount of flexibility
this gives inlined files may make this overhead worth it.
2023-10-13 23:11:35 -05:00

1035 lines
32 KiB
TOML

# Test basic file operations
after = ['test_dtree']
# test creation/deletion
[cases.test_ftree_create]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_close(&lfs, &file) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// check our file with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
// and with dir read
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// try reading our file
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => 0;
// try reading
uint8_t buf[8192];
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => 0;
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test we can write some data, should be inlined
[cases.test_ftree_hello]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
uint8_t wbuf[8192];
strcpy((char*)wbuf, "Hello World!");
lfs_size_t wsize = strlen((const char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// check our file with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
// and with dir read
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// try reading our file
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => wsize;
// try reading
uint8_t rbuf[8192];
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test we can rewrite a file
[cases.test_ftree_trunc]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
uint8_t wbuf[8192];
strcpy((char*)wbuf, "Oh no!");
lfs_size_t wsize = strlen((const char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// rewrite the file
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
strcpy((char*)wbuf, "Hello World!");
wsize = strlen((const char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
// check our file with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
// and with dir read
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// try reading our file
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => wsize;
// try reading
uint8_t rbuf[8192];
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# check for LFS_F_EXCL errors
[cases.test_ftree_excl]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[8192];
strcpy((char*)wbuf, "Hello World!");
lfs_size_t wsize = strlen((const char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// try to recreate file, this should error
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// check our file with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
// and with dir read
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// try reading our file
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => wsize;
// try reading
uint8_t buf[8192];
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# a file is not a directory
[cases.test_ftree_file_not_dir]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[8192];
strcpy((char*)wbuf, "Hello World!");
lfs_size_t wsize = strlen((const char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// try to open our file as a directory
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "hello") => LFS_ERR_NOTDIR;
// try to create a directory on top of our file
lfsr_mkdir(&lfs, "hello") => LFS_ERR_EXIST;
// try to rename a directory onto our file
lfsr_mkdir(&lfs, "not_hello") => 0;
lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// check our file with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
// and with dir read
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "not_hello") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// try reading our file
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => wsize;
// try reading
uint8_t buf[8192];
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# a directory is not a file
[cases.test_ftree_dir_not_file]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
// create a directory
lfsr_mkdir(&lfs, "hello") => 0;
// try reading our directory as a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => LFS_ERR_ISDIR;
// try writing our directory as a file
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => LFS_ERR_ISDIR;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_ISDIR;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
lfsr_file_open(&lfs, &file, "hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_ISDIR;
// try rename a file on top of our directory
lfsr_file_open(&lfs, &file, "not_hello",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
uint8_t wbuf[8192];
strcpy((char*)wbuf, "Hello World!");
lfs_size_t wsize = strlen((const char*)wbuf);
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
lfsr_file_close(&lfs, &file) => 0;
lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// check our dir with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_DIR);
// and with dir read
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "not_hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == wsize);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// did we corrupt our renaming file?
// try reading our file
lfsr_file_open(&lfs, &file, "not_hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => wsize;
// try reading
uint8_t rbuf[8192];
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize;
assert(memcmp(rbuf, wbuf, wsize) == 0);
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
# try writing larger files?
#
# at 2*CACHE_SIZE we need an inlined tree
# ? single block?
# at 2*BLOCK_SIZE we need a b-tree
#
[cases.test_ftree_sprout]
defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// create a file
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
uint8_t wbuf[8192];
uint32_t prng = 42;
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
lfsr_file_close(&lfs, &file) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
}
// check our file with stat
struct lfs_info info;
lfsr_stat(&lfs, "hello", &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
// and with dir read
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "hello") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == SIZE);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// try reading our file
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
// is size correct?
lfsr_file_size(&lfs, &file) => SIZE;
// try reading
uint8_t rbuf[8192];
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
'''
## more complex writing patterns to inlined files
#
## write files incrementally
#[cases.test_ftree_incr]
#defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE']
#defines.SYNC = [false, true]
#defines.REMOUNT = [false, true]
#reentrant = true
#code = '''
#'''
#
#[cases.test_files_simple]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_t file;
# lfs_file_open(&lfs, &file, "hello",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# lfs_size_t size = strlen("Hello World!")+1;
# uint8_t buffer[1024];
# strcpy((char*)buffer, "Hello World!");
# lfs_file_write(&lfs, &file, buffer, size) => size;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
# lfs_file_read(&lfs, &file, buffer, size) => size;
# assert(strcmp((char*)buffer, "Hello World!") == 0);
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_large]
#defines.SIZE = [32, 8192, 262144, 0, 7, 8193]
#defines.CHUNKSIZE = [31, 16, 33, 1, 1023]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# // write
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_t file;
# lfs_file_open(&lfs, &file, "avacado",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# uint32_t prng = 1;
# uint8_t buffer[1024];
# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_rewrite]
#defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
#defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
#defines.CHUNKSIZE = [31, 16, 1]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# // write
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_t file;
# uint8_t buffer[1024];
# lfs_file_open(&lfs, &file, "avacado",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# uint32_t prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE1;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // rewrite
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY) => 0;
# prng = 2;
# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => lfs_max(SIZE1, SIZE2);
# prng = 2;
# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# if (SIZE1 > SIZE2) {
# prng = 1;
# for (lfs_size_t b = 0; b < SIZE2; b++) {
# TEST_PRNG(&prng);
# }
# for (lfs_size_t i = SIZE2; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_append]
#defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
#defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
#defines.CHUNKSIZE = [31, 16, 1]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# // write
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_t file;
# uint8_t buffer[1024];
# lfs_file_open(&lfs, &file, "avacado",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# uint32_t prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE1;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // append
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_APPEND) => 0;
# prng = 2;
# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE1 + SIZE2;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# prng = 2;
# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_truncate]
#defines.SIZE1 = [32, 8192, 131072, 0, 7, 8193]
#defines.SIZE2 = [32, 8192, 131072, 0, 7, 8193]
#defines.CHUNKSIZE = [31, 16, 1]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# // write
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_t file;
# uint8_t buffer[1024];
# lfs_file_open(&lfs, &file, "avacado",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# uint32_t prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE1;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE1; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE1-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // truncate
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
# prng = 2;
# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# // read
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE2;
# prng = 2;
# for (lfs_size_t i = 0; i < SIZE2; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE2-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_reentrant_write]
#defines.SIZE = [32, 0, 7, 2049]
#defines.CHUNKSIZE = [31, 16, 65]
#reentrant = true
#code = '''
# lfs_t lfs;
# int err = lfs_mount(&lfs, cfg);
# if (err) {
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# }
#
# lfs_file_t file;
# uint8_t buffer[1024];
# err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY);
# assert(err == LFS_ERR_NOENT || err == 0);
# if (err == 0) {
# // can only be 0 (new file) or full size
# lfs_size_t size = lfs_file_size(&lfs, &file);
# assert(size == 0 || size == SIZE);
# lfs_file_close(&lfs, &file) => 0;
# }
#
# // write
# lfs_file_open(&lfs, &file, "avacado", LFS_O_WRONLY | LFS_O_CREAT) => 0;
# uint32_t prng = 1;
# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# }
# lfs_file_close(&lfs, &file) => 0;
#
# // read
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_reentrant_write_sync]
#defines = [
# # append (O(n))
# {MODE='LFS_O_APPEND', SIZE=[32, 0, 7, 2049], CHUNKSIZE=[31, 16, 65]},
# # truncate (O(n^2))
# {MODE='LFS_O_TRUNC', SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
# # rewrite (O(n^2))
# {MODE=0, SIZE=[32, 0, 7, 200], CHUNKSIZE=[31, 16, 65]},
#]
#reentrant = true
#code = '''
# lfs_t lfs;
# int err = lfs_mount(&lfs, cfg);
# if (err) {
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# }
#
# lfs_file_t file;
# uint8_t buffer[1024];
# err = lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY);
# assert(err == LFS_ERR_NOENT || err == 0);
# if (err == 0) {
# // with syncs we could be any size, but it at least must be valid data
# lfs_size_t size = lfs_file_size(&lfs, &file);
# assert(size <= SIZE);
# uint32_t prng = 1;
# for (lfs_size_t i = 0; i < size; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, size-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_close(&lfs, &file) => 0;
# }
#
# // write
# lfs_file_open(&lfs, &file, "avacado",
# LFS_O_WRONLY | LFS_O_CREAT | MODE) => 0;
# lfs_size_t size = lfs_file_size(&lfs, &file);
# assert(size <= SIZE);
# uint32_t prng = 1;
# lfs_size_t skip = (MODE == LFS_O_APPEND) ? size : 0;
# for (lfs_size_t b = 0; b < skip; b++) {
# TEST_PRNG(&prng);
# }
# for (lfs_size_t i = skip; i < SIZE; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
# for (lfs_size_t b = 0; b < chunk; b++) {
# buffer[b] = TEST_PRNG(&prng) & 0xff;
# }
# lfs_file_write(&lfs, &file, buffer, chunk) => chunk;
# lfs_file_sync(&lfs, &file) => 0;
# }
# lfs_file_close(&lfs, &file) => 0;
#
# // read
# lfs_file_open(&lfs, &file, "avacado", LFS_O_RDONLY) => 0;
# lfs_file_size(&lfs, &file) => SIZE;
# prng = 1;
# for (lfs_size_t i = 0; i < SIZE; i += CHUNKSIZE) {
# lfs_size_t chunk = lfs_min(CHUNKSIZE, SIZE-i);
# lfs_file_read(&lfs, &file, buffer, chunk) => chunk;
# for (lfs_size_t b = 0; b < chunk; b++) {
# assert(buffer[b] == (TEST_PRNG(&prng) & 0xff));
# }
# }
# lfs_file_read(&lfs, &file, buffer, CHUNKSIZE) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_many]
#defines.N = 300
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# // create N files of 7 bytes
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# lfs_file_t file;
# char path[1024];
# sprintf(path, "file_%03d", i);
# lfs_file_open(&lfs, &file, path,
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# char wbuffer[1024];
# lfs_size_t size = 7;
# sprintf(wbuffer, "Hi %03d", i);
# lfs_file_write(&lfs, &file, wbuffer, size) => size;
# lfs_file_close(&lfs, &file) => 0;
#
# char rbuffer[1024];
# lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
# lfs_file_read(&lfs, &file, rbuffer, size) => size;
# assert(strcmp(rbuffer, wbuffer) == 0);
# lfs_file_close(&lfs, &file) => 0;
# }
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_many_power_cycle]
#defines.N = 300
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# // create N files of 7 bytes
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# lfs_file_t file;
# char path[1024];
# sprintf(path, "file_%03d", i);
# lfs_file_open(&lfs, &file, path,
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# char wbuffer[1024];
# lfs_size_t size = 7;
# sprintf(wbuffer, "Hi %03d", i);
# lfs_file_write(&lfs, &file, wbuffer, size) => size;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# char rbuffer[1024];
# lfs_mount(&lfs, cfg) => 0;
# lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
# lfs_file_read(&lfs, &file, rbuffer, size) => size;
# assert(strcmp(rbuffer, wbuffer) == 0);
# lfs_file_close(&lfs, &file) => 0;
# }
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_files_many_power_loss]
#defines.N = 300
#reentrant = true
#code = '''
# lfs_t lfs;
# int err = lfs_mount(&lfs, cfg);
# if (err) {
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# }
# // create N files of 7 bytes
# for (int i = 0; i < N; i++) {
# lfs_file_t file;
# char path[1024];
# sprintf(path, "file_%03d", i);
# err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT);
# char wbuffer[1024];
# lfs_size_t size = 7;
# sprintf(wbuffer, "Hi %03d", i);
# if ((lfs_size_t)lfs_file_size(&lfs, &file) != size) {
# lfs_file_write(&lfs, &file, wbuffer, size) => size;
# }
# lfs_file_close(&lfs, &file) => 0;
#
# char rbuffer[1024];
# lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
# lfs_file_read(&lfs, &file, rbuffer, size) => size;
# assert(strcmp(rbuffer, wbuffer) == 0);
# lfs_file_close(&lfs, &file) => 0;
# }
# lfs_unmount(&lfs) => 0;
#'''