6261bafed2
Fortunately these operations are heavily tested in test_dirs. The only difference with files is the possibility for shrubs to need to be copied. Bugs fixed: - It's counterintuitive, but lfsr_rbyd_appendcompactattr _can_ error with LFS_ERR_RANGE when we are copying a shrub. This can happen if the underlying mdir needs compaction itself. - It's possible to null-trunk bshrubs to appear in our filesystem traversal. Null-trunk bshrubs don't usually appear in any stable state, but they are created by lfsr_bshrub_alloc and lfsr_btree_commit to represent new, yet-uncommitted shrubs. This gets a bit tricky because we also use null-trunks to indicate if lfsr_btree_traversal has traversed the root. We can't rely on bid >= weight for this because zero-weight btrees are allowed. The solution here, though maybe temporary (famous last words), is to treat null-trunk btrees as not having a root. Which isn't really true, but null-trunk btree roots only exist between allocator checkpoints, so they are allowed to be unreachable. We really need more asserts that this is the case though... At least added an assert that we never commit/read null trunks on disk.
2412 lines
72 KiB
TOML
2412 lines
72 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);
|
|
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 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);
|
|
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];
|
|
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);
|
|
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];
|
|
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);
|
|
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];
|
|
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);
|
|
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 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);
|
|
|
|
// 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];
|
|
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_ISDIR;
|
|
|
|
// try writing our root as a file
|
|
lfsr_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_ISDIR;
|
|
lfsr_file_open(&lfs, &file, "/",
|
|
LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_ISDIR;
|
|
lfsr_file_open(&lfs, &file, "/",
|
|
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
|
|
lfsr_file_open(&lfs, &file, "/",
|
|
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", "/") => LFS_ERR_ISDIR;
|
|
|
|
// 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);
|
|
|
|
// 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, "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);
|
|
|
|
// 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, "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);
|
|
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[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%04d", 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%04d", 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(10)'
|
|
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%04d", 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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%04d", 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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
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%04d", 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%04d", 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%04d", 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%04d", 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(10)'
|
|
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%04d", 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%04d", 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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%04d", 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%04d", 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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char path[256];
|
|
sprintf(path, "basalt%04d", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, path) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
}
|
|
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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
char path[256];
|
|
sprintf(path, "basalt%04d", i);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, path) == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
}
|
|
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);
|
|
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, "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);
|
|
|
|
// 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, "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);
|
|
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);
|
|
|
|
// 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, "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);
|
|
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_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);
|
|
|
|
// 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, "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);
|
|
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, "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%04d", 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%04d", 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%04d", i);
|
|
char new_name[256];
|
|
sprintf(new_name, "basalt%04d", 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%04d", i);
|
|
struct lfs_info info;
|
|
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
|
|
sprintf(name, "basalt%04d", 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(10)'
|
|
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%04d", 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%04d", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%04d", 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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%04d", 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%04d", 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(10)'
|
|
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%04d", 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%04d", 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%04d", x);
|
|
char new_name[256];
|
|
sprintf(new_name, "amethyst%04d", 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%04d", 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);
|
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS_TYPE_DIR);
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%04d", 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%04d", 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]
|
|
|
|
|