bd4a5e5ab3
The main idea here is that diverse tests are better than many similar tests. Sure, if we throw fuzz tests at the system all day we'll eventually find more bugs, but if a developer is in the loop that time is going to be better spent writing specific tests targeting the fragile parts of the system. And don't worry, we can still throw fuzz tests at the system all day by specifying explicit seeds with -DSEED=blah. Changes: - Limited dir-related powerloss fuzz testing to N <= 16. These tests were the biggest culprit of excessive test runtime, requiring O(n^2) redundant operations to recover from powerlosses (they just replay the full sequence on powerloss). - As a tradeoff, bumped most fuzz tests to a minimum of 20 seeds. The big exception being the test_fwrite tests, which are heavily parameterized and already take the most time to run. Each parameter combination also multiplies the effective number of seeds, so increasing the number of base seeds will probably have diminishing returns. - Limited test_fwrite_reversed to SIZE <= 4*1024*CHUNK. Writing a file backwards is just about the worst way you could write a file, since all buffering/coalescing expect writes to eventually make forward progress. On the flip side, because it's uncommon, writing a file backwards is also a great way to find bugs. But at some point a compromise needs to be made. Impacted test runtimes: case otime ntime dtime test_btree_push_fuzz 0.3 0.5 +0.2 (+60.2%) test_btree_push_sparse_fuzz 0.4 3.3 +2.9 (+720.4%) test_btree_update_fuzz 0.4 0.9 +0.6 (+141.6%) test_btree_update_sparse_fuzz 0.5 4.5 +4.1 (+857.4%) test_btree_pop_fuzz 0.6 2.3 +1.7 (+314.7%) test_btree_pop_sparse_fuzz 1.2 5.7 +4.4 (+356.2%) test_btree_split_fuzz 0.5 1.4 +0.8 (+150.2%) test_btree_split_sparse_fuzz 0.4 5.6 +5.1 (+1163.2%) test_btree_find_fuzz 0.5 0.7 +0.2 (+50.7%) test_btree_find_sparse_fuzz 1.0 3.0 +2.0 (+189.8%) test_btree_traversal_fuzz 0.6 2.3 +1.6 (+260.4%) test_dirs_mkdir_many 3.3 2.1 -1.3 (-37.8%) test_dirs_mkdir_many_backwards 3.5 2.1 -1.4 (-39.9%) test_dirs_mkdir_fuzz 115.3 106.4 -8.9 (-7.7%) test_dirs_rm_many 283.9 76.8 -207.0 (-72.9%) test_dirs_rm_many_backwards 216.1 80.6 -135.5 (-62.7%) test_dirs_rm_fuzz 647.0 68.5 -578.5 (-89.4%) test_dirs_mv_many 14.2 15.4 +1.1 (+7.9%) test_dirs_mv_many_backwards 16.5 14.5 -2.1 (-12.5%) test_dirs_mv_fuzz 1932.5 156.7 -1775.8 (-91.9%) test_dirs_general_fuzz 561.9 74.5 -487.4 (-86.7%) test_dread_recursive_rm 336.6 46.2 -290.4 (-86.3%) test_dread_recursive_mv 55.5 44.6 -11.0 (-19.8%) test_fsync_rrrr_fuzz 0.4 0.3 -0.1 (-18.4%) test_fsync_wrrr_fuzz 8.0 12.4 +4.5 (+56.0%) test_fsync_wwww_fuzz 13.2 33.4 +20.2 (+152.6%) test_fsync_wwrr_fuzz 5.4 50.9 +45.5 (+841.6%) test_fsync_rwrw_fuzz 2.4 8.4 +6.0 (+253.9%) test_fsync_rwrw_sparse_fuzz 3.2 7.5 +4.2 (+129.9%) test_fsync_rwtfrwtf_sparse_fuzz 6.1 8.5 +2.4 (+39.3%) test_fsync_drrr_fuzz 11.8 9.2 -2.6 (-21.8%) test_fsync_wddd_fuzz 9.3 11.9 +2.6 (+28.0%) test_fsync_rwdrwd_fuzz 1.6 33.1 +31.5 (+1963.4%) test_fsync_rwdrwd_sparse_fuzz 0.3 1.8 +1.4 (+418.8%) test_fsync_rwtfdrwtfd_sparse_fuzz 0.3 1.1 +0.8 (+260.2%) test_fwrite_reversed 728.5 345.2 -383.3 (-52.6%) TOTAL 7587.5 3792.3 -3795.2 (-50.0%)
2465 lines
73 KiB
TOML
2465 lines
73 KiB
TOML
# Test basic file operations
|
|
after = ['test_dirs', 'test_btree']
|
|
|
|
|
|
# test creation/deletion
|
|
[cases.test_files_create]
|
|
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) => 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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 rbuf[8192];
|
|
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => 0;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test we can write some data, should be inlined
|
|
[cases.test_files_hello]
|
|
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) => 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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_files_trunc]
|
|
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_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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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_files_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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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;
|
|
'''
|
|
|
|
# a file is not a directory
|
|
[cases.test_files_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_NOTDIR;
|
|
|
|
// 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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);
|
|
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) => wsize;
|
|
// try reading
|
|
uint8_t rbuf[8192];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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;
|
|
'''
|
|
|
|
# a directory is not a file
|
|
[cases.test_files_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);
|
|
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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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;
|
|
'''
|
|
|
|
# root is also not a file
|
|
[cases.test_files_root_not_file]
|
|
defines.REMOUNT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try reading our root as a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "/", LFS_O_RDONLY) => LFS_ERR_INVAL;
|
|
|
|
// try writing our root as a file
|
|
lfsr_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_INVAL;
|
|
lfsr_file_open(&lfs, &file, "/",
|
|
LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_INVAL;
|
|
lfsr_file_open(&lfs, &file, "/",
|
|
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_INVAL;
|
|
lfsr_file_open(&lfs, &file, "/",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_INVAL;
|
|
|
|
// 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", "/") => LFS_ERR_INVAL;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check our root with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "/", &info) => 0;
|
|
assert(strcmp(info.name, "/") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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;
|
|
'''
|
|
|
|
# an invalid path is also not a file (kind of?)
|
|
[cases.test_files_noent_not_file]
|
|
defines.REMOUNT = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// try reading our invalid path as a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "no/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
|
|
// try writing our root as a file
|
|
lfsr_file_open(&lfs, &file, "no/hello", LFS_O_WRONLY) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file, "no/hello",
|
|
LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file, "no/hello",
|
|
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NOENT;
|
|
lfsr_file_open(&lfs, &file, "no/hello",
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_NOENT;
|
|
|
|
// try rename a file on top of our invalid path
|
|
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", "no/hello") => LFS_ERR_NOENT;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check our root with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "/", &info) => 0;
|
|
assert(strcmp(info.name, "/") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
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];
|
|
memset(rbuf, 0xaa, sizeof(rbuf));
|
|
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
|
|
#
|
|
# note:
|
|
# - at 2*CACHE_SIZE we need a shrub
|
|
# - at BLOCK_SIZE/2 we need a block pointer
|
|
# - at 2*BLOCK_SIZE we need a btree
|
|
#
|
|
[cases.test_files_more]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "hello") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
// try reading our file
|
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test creating multiple files
|
|
[cases.test_files_many]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 32',
|
|
# limit powerloss testing due to time
|
|
# TODO can this be increased after optimizing file writes?
|
|
'!TEST_PLS || (SIZE*N) <= 1*BLOCK_SIZE',
|
|
]
|
|
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 this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// TODO remove this eventually?
|
|
//
|
|
// file may exist from a previous run, but we need to check
|
|
// for pesky zero-sized files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
lfs_ssize_t size = lfsr_file_size(&lfs, &file);
|
|
assert(size == 0 || size == SIZE);
|
|
if (size == 0) {
|
|
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 that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz test file creation
|
|
[cases.test_files_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
# do more ops than files to encourage file rewrites
|
|
defines.DENSITY = 2
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.SEED = 'range(20)'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
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 that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 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;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# test removing files of various sizes
|
|
#
|
|
# to be honest, this doesn't really test much and is just included
|
|
# for completeness
|
|
#
|
|
[cases.test_files_rm]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
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;
|
|
}
|
|
|
|
// remove our file
|
|
lfsr_remove(&lfs, "amethyst") => 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, "amethyst", &info) => LFS_ERR_NOENT;
|
|
|
|
// 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
|
lfsr_dir_close(&lfs, &dir) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test creating and deleting multiple files
|
|
[cases.test_files_rm_many]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.REMAINING = [4, 1, 0]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
if = [
|
|
'N > REMAINING',
|
|
'(SIZE*N)/BLOCK_SIZE <= 32',
|
|
# limit powerloss testing due to time
|
|
# TODO can this be increased after optimizing file writes?
|
|
'!TEST_PLS || ((SIZE*N) <= BLOCK_SIZE/4 && N <= 16)',
|
|
]
|
|
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 this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// TODO remove this eventually?
|
|
//
|
|
// file may exist from a previous run, but we need to check
|
|
// for pesky zero-sized files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
lfs_ssize_t size = lfsr_file_size(&lfs, &file);
|
|
assert(size == 0 || size == SIZE);
|
|
if (size == 0) {
|
|
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 that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// now remove some number of files
|
|
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our removes worked
|
|
//
|
|
// note we need to keep the prng in sync
|
|
//
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
// keep prng in sync
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
if (i < N-REMAINING) {
|
|
// check with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
|
|
|
|
// and try to open
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_NOENT;
|
|
} else {
|
|
// check with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz test file creation and deletion
|
|
[cases.test_files_rm_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
# do more ops than files to encourage file rewrites
|
|
defines.DENSITY = 2
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.SEED = 'range(20)'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else {
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
}
|
|
}
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 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;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming files of various sizes
|
|
[cases.test_files_mv]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.N = [0, 128]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a number of directories to distance our files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char path[256];
|
|
sprintf(path, "basalt%03x", i);
|
|
lfsr_mkdir(&lfs, path) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
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;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs, "amethyst", "calcite") => 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, "calcite", &info) => 0;
|
|
assert(strcmp(info.name, "calcite") == 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char path[256];
|
|
sprintf(path, "basalt%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, path) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "calcite") == 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, "calcite", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming files of various sizes over existing files
|
|
[cases.test_files_mv_replace]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.N = [0, 128]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// create a number of directories to distance our files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char path[256];
|
|
sprintf(path, "basalt%03x", i);
|
|
lfsr_mkdir(&lfs, path) => 0;
|
|
}
|
|
|
|
// create a file
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create another file
|
|
lfsr_file_open(&lfs, &file, "calcite", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf_[SIZE];
|
|
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;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs, "amethyst", "calcite") => 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, "amethyst", &info) => LFS_ERR_NOENT;
|
|
lfsr_stat(&lfs, "calcite", &info) => 0;
|
|
assert(strcmp(info.name, "calcite") == 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char path[256];
|
|
sprintf(path, "basalt%03x", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, path) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "calcite") == 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, "calcite", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming a file to itself
|
|
[cases.test_files_mv_noop]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
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;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs, "amethyst", "amethyst") => 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, "amethyst", &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 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, "amethyst", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming a file onto a dir
|
|
[cases.test_files_mv_not_file]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create a dir
|
|
lfsr_mkdir(&lfs, "basalt") => 0;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs, "amethyst", "basalt") => LFS_ERR_ISDIR;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that nothing changed in our file/dir with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "amethyst", &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, "basalt", &info) => 0;
|
|
assert(strcmp(info.name, "basalt") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "basalt") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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, "amethyst", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming a dir onto a file
|
|
[cases.test_files_mv_not_dir]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// create a dir
|
|
lfsr_mkdir(&lfs, "basalt") => 0;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// rename the dir
|
|
lfsr_rename(&lfs, "basalt", "amethyst") => LFS_ERR_NOTDIR;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that nothing changed in our file/dir with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "amethyst", &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_stat(&lfs, "basalt", &info) => 0;
|
|
assert(strcmp(info.name, "basalt") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "basalt") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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, "amethyst", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming a file onto root
|
|
[cases.test_files_mv_not_root]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
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;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs, "amethyst", "/") => LFS_ERR_INVAL;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that nothing changed in our file/dir with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "amethyst", &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 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, "amethyst", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming a file onto an invalid path
|
|
[cases.test_files_mv_not_noent]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.CACHE_SIZE = 64
|
|
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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
uint8_t wbuf[SIZE];
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < SIZE; i++) {
|
|
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
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;
|
|
}
|
|
|
|
// rename the file
|
|
lfsr_rename(&lfs, "amethyst", "no/amethyst") => LFS_ERR_NOENT;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that nothing changed in our file/dir with stat
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, "amethyst", &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 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);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "amethyst") == 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, "amethyst", LFS_O_RDONLY) => 0;
|
|
// is size correct?
|
|
lfsr_file_size(&lfs, &file) => SIZE;
|
|
// try reading
|
|
uint8_t rbuf[2*SIZE];
|
|
memset(rbuf, 0xaa, 2*SIZE);
|
|
lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test renaming multiple files
|
|
[cases.test_files_mv_many]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
if = [
|
|
'(SIZE*N)/BLOCK_SIZE <= 32',
|
|
# limit powerloss testing due to time
|
|
# TODO can this be increased after optimizing file writes?
|
|
'!TEST_PLS || ((SIZE*N) <= BLOCK_SIZE/4 && N <= 16)',
|
|
]
|
|
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 this many files
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
// TODO remove this eventually?
|
|
//
|
|
// file may exist from a previous run, but we need to check
|
|
// for pesky zero-sized files
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
|
lfs_ssize_t size = lfsr_file_size(&lfs, &file);
|
|
assert(size == 0 || size == SIZE);
|
|
if (size == 0) {
|
|
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 that our writes worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// now rename our files
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", i);
|
|
char new_name[256];
|
|
sprintf(new_name, "basalt%03x", i);
|
|
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
}
|
|
|
|
// check that our renames worked
|
|
prng = 42;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
|
|
sprintf(name, "basalt%03x", i);
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz test file creation and rename
|
|
[cases.test_files_mv_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
# do more ops than files to encourage file rewrites
|
|
defines.DENSITY = 2
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.SEED = 'range(20)'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 2;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 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;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# fuzz test file creation/deletion/rename
|
|
[cases.test_files_mvrm_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
# do more ops than files to encourage file rewrites
|
|
defines.DENSITY = 2
|
|
defines.SIZE = [
|
|
'0',
|
|
'CACHE_SIZE/2',
|
|
'2*CACHE_SIZE',
|
|
'BLOCK_SIZE/2',
|
|
'BLOCK_SIZE',
|
|
'2*BLOCK_SIZE',
|
|
'4*BLOCK_SIZE',
|
|
]
|
|
defines.REMOUNT = [false, true]
|
|
defines.SEED = 'range(20)'
|
|
if = '(SIZE*N)/BLOCK_SIZE <= 32'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, CFG) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
|
|
// set up a simulation to compare against
|
|
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
|
|
uint32_t *sim_prngs = malloc(N*sizeof(uint32_t));
|
|
lfs_size_t sim_size = 0;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose which operation to do
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || sim_size == 0) {
|
|
// choose a pseudo-random number
|
|
lfs_size_t x = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
|
// associate each file with a prng that generates its contents
|
|
uint32_t wprng = TEST_PRNG(&prng);
|
|
|
|
// insert into our sim
|
|
for (lfs_size_t j = 0;; j++) {
|
|
if (j >= sim_size || sim[j] >= x) {
|
|
// already seen?
|
|
if (j < sim_size && sim[j] == x) {
|
|
// new prng
|
|
sim_prngs[j] = wprng;
|
|
} else {
|
|
// insert
|
|
memmove(&sim[j+1], &sim[j],
|
|
(sim_size-j)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j+1], &sim_prngs[j],
|
|
(sim_size-j)*sizeof(uint32_t));
|
|
sim_size += 1;
|
|
sim[j] = x;
|
|
sim_prngs[j] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create a file here
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name,
|
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
|
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
// delete from our sim
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", x);
|
|
lfsr_remove(&lfs, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs_size_t j = TEST_PRNG(&prng) % sim_size;
|
|
lfs_size_t x = sim[j];
|
|
lfs_size_t y = TEST_PRNG(&prng) % ((N+DENSITY-1) / DENSITY);
|
|
uint32_t wprng = sim_prngs[j];
|
|
|
|
// update our sim
|
|
for (lfs_size_t k = 0;; k++) {
|
|
if (k >= sim_size || sim[k] >= y) {
|
|
// renaming and replacing
|
|
if (k < sim_size && sim[k] == y && x != y) {
|
|
// delete the original entry
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
sim_size -= 1;
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// update the prng
|
|
sim_prngs[k] = wprng;
|
|
// just renaming
|
|
} else {
|
|
// first delete
|
|
memmove(&sim[j], &sim[j+1],
|
|
(sim_size-(j+1))*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[j], &sim_prngs[j+1],
|
|
(sim_size-(j+1))*sizeof(uint32_t));
|
|
if (k > j) {
|
|
k -= 1;
|
|
}
|
|
// then insert
|
|
memmove(&sim[k+1], &sim[k],
|
|
(sim_size-k)*sizeof(lfs_size_t));
|
|
memmove(&sim_prngs[k+1], &sim_prngs[k],
|
|
(sim_size-k)*sizeof(uint32_t));
|
|
sim[k] = y;
|
|
sim_prngs[k] = wprng;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rename this file
|
|
char old_name[256];
|
|
sprintf(old_name, "amethyst%03x", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%03x", y);
|
|
lfsr_rename(&lfs, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// remount?
|
|
if (REMOUNT) {
|
|
lfsr_unmount(&lfs) => 0;
|
|
lfsr_mount(&lfs, CFG) => 0;
|
|
}
|
|
|
|
// check that our files match our simulation
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
}
|
|
|
|
lfsr_dir_t dir;
|
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
|
struct lfs_info info;
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 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;
|
|
|
|
// check the file contents
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", sim[j]);
|
|
lfsr_file_t file;
|
|
lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0;
|
|
|
|
uint32_t wprng = sim_prngs[j];
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
|
|
}
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfsr_file_close(&lfs, &file) => 0;
|
|
}
|
|
|
|
// clean up sim/lfs
|
|
free(sim);
|
|
free(sim_prngs);
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# TODO
|
|
# [cases.test_files_multi_readers]
|
|
# [cases.test_files_multi_readers_one_writer]
|
|
# [cases.test_files_multi_writers]
|
|
# [cases.test_files_multi_readers_multi_writers]
|
|
|
|
# [cases.test_files_interleaved]
|
|
# [cases.test_files_interleaved_fuzz]
|
|
# [cases.test_files_interleaved_fuzz_fuzz]
|
|
# [cases.test_files_dtree_fuzz]
|
|
# [cases.test_files_dtree_fuzz_fuzz]
|
|
|
|
|