Files
littlefs/tests/t5_dirs.toml
T
Christopher Haster edd12e1f93 Changed how dir offsets in tell/seek are encoded
This is an attempt to fix issues with dir seeking in a filesystem
undergoing changes. The problem with the previous dstart-relative
position encoding is that if we deleted/created new entries outside of
our current directory, we didn't if they were inside or outside of the
current directory, so we couldn't always update our position correctly.

Instead of using a dstart-relative position, this solution crams both
the mid and rid into a single 31-bit integer. Things get a bit tight
here, so we use the current block_size as a heuristic for how many
possible rids we can ever have in a single mdir. The idea is the larger
the rid encoding needs to be, the smaller the mid encoding needs to be,
and we should, _roughly_, approach the same encoding limitation we would
have to dstart-relative position anyways.

Making some assumptions about the maximum possible number of rids in a
block gives us at most ~block_size/8 rids per mdir.

So for 4096 byte blocks (note the exact encoding is dynamic):

  sbbbbbbb bbbbbbbb bbbbbbbr rrrrrrrr
  ^'-----------+----------''----+---'
  '------------|----------------|----- sign bit (used for errors)
               '----------------|----- 22-bit metadata bid
                                '----- 9-bit metadata rid

Note this introduced as new, significant limitation on the number of
total mdirs in the system. Normally I would be against this solution for
that reason, however if we adopt this encoding elsewhere in the system it
may improve some RAM cost and in general simplify things by being able to
store any mid in a single integer. More work needs to be done here...

This approach needs some fleshing out and has its own issues (the
offset returned by tell quickly becomes out of date if the filesystem
is modified, but is that really a problem?), but it improves over the
previous implementation by making tell always correct at that moment.
2023-07-27 16:59:34 -05:00

7206 lines
232 KiB
TOML

# Directory tests
## mkdir tests
[cases.t5_dirs_mkdir]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked with stat
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 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, "ardvark") == 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 that noent errors work
[cases.t5_dirs_noent]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
// make a directory
lfsr_mkdir(&lfs, "ardvark") => 0;
// try to read a nonsense path
struct lfs_info info;
lfsr_stat(&lfs, "no", &info) => LFS_ERR_NOENT;
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "no") => LFS_ERR_NOENT;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_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, "ardvark") == 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 that stat on root works
[cases.t5_dirs_stat_root]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
// make a directory
lfsr_mkdir(&lfs, "ardvark") => 0;
// stat the root
struct lfs_info info;
lfsr_stat(&lfs, "/", &info) => 0;
assert(strcmp(info.name, "/") == 0);
assert(info.type == LFS_TYPE_DIR);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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 that creating the same directory twice errors
[cases.t5_dirs_mkdir_exists]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// make the same directory, should error
lfsr_mkdir(&lfs, "ardvark") => LFS_ERR_EXIST;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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 what happens if we try to make root
[cases.t5_dirs_mkdir_root]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// try to make root, which doesn't make sense
err = lfsr_mkdir(&lfs, "/");
assert(err == LFS_ERR_EXIST || err == LFS_ERR_INVAL);
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to make root, which doesn't make sense
err = lfsr_mkdir(&lfs, "/");
assert(err == LFS_ERR_EXIST || err == LFS_ERR_INVAL);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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 that creating a directory with an invalid path errors
[cases.t5_dirs_mkdir_noent]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// make a nonsense directory, should error
lfsr_mkdir(&lfs, "no/hmm") => LFS_ERR_NOENT;
// make a nonsense child directory, should error
lfsr_mkdir(&lfs, "ardvark/no/hmm") => LFS_ERR_NOENT;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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;
'''
[cases.t5_dirs_mkdir_siblings]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "cat") == 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;
'''
[cases.t5_dirs_mkdir_children]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/banana/cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana/cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark") => 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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark/banana") => 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, "cat") == 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;
'''
[cases.t5_dirs_mkdir_many]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 32'
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mkdir_many_backwards]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 32'
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-1-i));
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mkdir_many_2layers]
defines.N = [1, 2, 4, 8, 16]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 4'
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
}
// check that our mkdirs worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "child%04d", j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mkdir_many_3layers]
defines.N = [1, 2, 4]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 2'
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t k = 0; k < N; k++) {
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
}
}
// check that our mkdirs worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t k = 0; k < N; k++) {
char name[256];
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "grandchild%04d", k);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "child%04d", j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_dir_open(&lfs, &dir, name) => 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 k = 0; k < N; k++) {
char name[256];
sprintf(name, "grandchild%04d", k);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mkdir_many_linkedlist]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || 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 directory in a sort of linked-list by nesting
char name[4096];
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our mkdir worked
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, name) => 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);
if (i < N-1) {
char name2[256];
sprintf(name2, "dir%04d", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name2) == 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;
'''
[cases.t5_dirs_mkdir_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
defines.REMOUNT = [false, true]
defines.SEED = 'range(10)'
# limit powerloss testing due to time
if = '!TEST_PL || N <= 64'
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;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_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, truncate to 4 decimals
lfs_size_t x = TEST_PRNG(&prng) % 10000;
// 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) {
// do nothing
} else {
// insert
memmove(&sim[j+1], &sim[j],
(sim_size-j)*sizeof(lfs_size_t));
sim_size += 1;
sim[j] = x;
}
break;
}
}
// create a directory here
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// test that our directories match our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", sim[j]);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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, "dir%04d", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// clean up sim/lfs
free(sim);
lfsr_unmount(&lfs) => 0;
'''
# test that did collisions don't cause issues
[cases.t5_dirs_did_collisions]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_SNmwMTHH", 10) == 0x12345678);
assert(lfs_crc32c(0, "b_skvjpWJH", 10) == 0x12345678);
assert(lfs_crc32c(0, "c_OnOQhVPH", 10) == 0x12345678);
assert(lfs_crc32c(0, "d_puMpPjRH", 10) == 0x12345678);
assert(lfs_crc32c(0, "e_LptKHkHH", 10) == 0x12345678);
assert(lfs_crc32c(0, "f_lUoVuhJH", 10) == 0x12345678);
// make directories
err = lfsr_mkdir(&lfs, "a_SNmwMTHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_skvjpWJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_OnOQhVPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_puMpPjRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_LptKHkHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_lUoVuhJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "a_SNmwMTHH", &info) => 0;
assert(strcmp(info.name, "a_SNmwMTHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "b_skvjpWJH", &info) => 0;
assert(strcmp(info.name, "b_skvjpWJH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "c_OnOQhVPH", &info) => 0;
assert(strcmp(info.name, "c_OnOQhVPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "d_puMpPjRH", &info) => 0;
assert(strcmp(info.name, "d_puMpPjRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "e_LptKHkHH", &info) => 0;
assert(strcmp(info.name, "e_LptKHkHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "f_lUoVuhJH", &info) => 0;
assert(strcmp(info.name, "f_lUoVuhJH") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "a_SNmwMTHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b_skvjpWJH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c_OnOQhVPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "d_puMpPjRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "e_LptKHkHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "f_lUoVuhJH") == 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;
'''
# these will also collide with the root
[cases.t5_dirs_did_zero]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
assert(lfs_crc32c(0, "b_iUwOsqRH", 10) == 0x00000000);
assert(lfs_crc32c(0, "c_UPNtkpHH", 10) == 0x00000000);
assert(lfs_crc32c(0, "d_jKLUSLJH", 10) == 0x00000000);
assert(lfs_crc32c(0, "e_VNunKMPH", 10) == 0x00000000);
assert(lfs_crc32c(0, "f_vknsvNRH", 10) == 0x00000000);
// make directories
err = lfsr_mkdir(&lfs, "a_IplRNrPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_iUwOsqRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_UPNtkpHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_jKLUSLJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_VNunKMPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_vknsvNRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "a_IplRNrPH", &info) => 0;
assert(strcmp(info.name, "a_IplRNrPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "b_iUwOsqRH", &info) => 0;
assert(strcmp(info.name, "b_iUwOsqRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "c_UPNtkpHH", &info) => 0;
assert(strcmp(info.name, "c_UPNtkpHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "d_jKLUSLJH", &info) => 0;
assert(strcmp(info.name, "d_jKLUSLJH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "e_VNunKMPH", &info) => 0;
assert(strcmp(info.name, "e_VNunKMPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "f_vknsvNRH", &info) => 0;
assert(strcmp(info.name, "f_vknsvNRH") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "a_IplRNrPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b_iUwOsqRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c_UPNtkpHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "d_jKLUSLJH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "e_VNunKMPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "f_vknsvNRH") == 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;
'''
# these will need to rollover from 0xffffffff -> 0x00000000 correctly
#
# note this is true even if you truncate
[cases.t5_dirs_did_ones]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_iomlVKPH", 10) == 0xffffffff);
assert(lfs_crc32c(0, "b_IJvqkHRH", 10) == 0xffffffff);
assert(lfs_crc32c(0, "c_uOOJsIHH", 10) == 0xffffffff);
assert(lfs_crc32c(0, "d_JTMkKuJH", 10) == 0xffffffff);
assert(lfs_crc32c(0, "e_vQtPStPH", 10) == 0xffffffff);
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
// make directories
err = lfsr_mkdir(&lfs, "a_iomlVKPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_IJvqkHRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_uOOJsIHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_JTMkKuJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_vQtPStPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_VtoMnwRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "a_iomlVKPH", &info) => 0;
assert(strcmp(info.name, "a_iomlVKPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "b_IJvqkHRH", &info) => 0;
assert(strcmp(info.name, "b_IJvqkHRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "c_uOOJsIHH", &info) => 0;
assert(strcmp(info.name, "c_uOOJsIHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "d_JTMkKuJH", &info) => 0;
assert(strcmp(info.name, "d_JTMkKuJH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "e_vQtPStPH", &info) => 0;
assert(strcmp(info.name, "e_vQtPStPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "f_VtoMnwRH", &info) => 0;
assert(strcmp(info.name, "f_VtoMnwRH") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "a_iomlVKPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b_IJvqkHRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c_uOOJsIHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "d_JTMkKuJH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "e_vQtPStPH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "f_VtoMnwRH") == 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;
'''
# these test some boundary conditions on the underlying leb128 encoding,
# if the leb128 disk-size is not calculated correctly these can cause
# issues
[cases.t5_dirs_did_leb128_boundaries]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_IOtUptRH", 10) == 0x0000007f);
assert(lfs_crc32c(0, "b_nquQsKHH", 10) == 0x00000080);
assert(lfs_crc32c(0, "c_vwQtKjHH", 10) == 0x00000081);
assert(lfs_crc32c(0, "d_sVrvrWHH", 10) == 0x00003fff);
assert(lfs_crc32c(0, "e_thrRIsRH", 10) == 0x00004000);
assert(lfs_crc32c(0, "f_pNtQTPJH", 10) == 0x00004001);
// make directories
err = lfsr_mkdir(&lfs, "a_IOtUptRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_nquQsKHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_vwQtKjHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_sVrvrWHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_thrRIsRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_pNtQTPJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "a_IOtUptRH", &info) => 0;
assert(strcmp(info.name, "a_IOtUptRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "b_nquQsKHH", &info) => 0;
assert(strcmp(info.name, "b_nquQsKHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "c_vwQtKjHH", &info) => 0;
assert(strcmp(info.name, "c_vwQtKjHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "d_sVrvrWHH", &info) => 0;
assert(strcmp(info.name, "d_sVrvrWHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "e_thrRIsRH", &info) => 0;
assert(strcmp(info.name, "e_thrRIsRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "f_pNtQTPJH", &info) => 0;
assert(strcmp(info.name, "f_pNtQTPJH") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "a_IOtUptRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b_nquQsKHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c_vwQtKjHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "d_sVrvrWHH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "e_thrRIsRH") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "f_pNtQTPJH") == 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;
'''
## dir remove tests
[cases.t5_dirs_rm]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked with stat
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// and remove the directory
lfsr_remove(&lfs, "ardvark") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that remove worked with stat
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
// 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) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test that noent errors work
[cases.t5_dirs_rm_noent]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to remove a nonsense directory
lfsr_remove(&lfs, "no") => LFS_ERR_NOENT;
// try to remove a directory in a nonsense directory
lfsr_remove(&lfs, "no/ardvark") => LFS_ERR_NOENT;
// try to remove a nonsense child directory
lfsr_remove(&lfs, "ardvark/no") => LFS_ERR_NOENT;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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 that we catch removing of a non-empty directory
[cases.t5_dirs_rm_notempty]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// fill it with stuff
err = lfsr_mkdir(&lfs, "ardvark/banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to remove the parent directory
lfsr_remove(&lfs, "ardvark") => LFS_ERR_NOTEMPTY;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "ardvark") => 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, "banana") == 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 what happens if we try to remove root
[cases.t5_dirs_rm_root]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// try to remove root, which doesn't really make sense
lfsr_remove(&lfs, "/") => LFS_ERR_INVAL;
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to remove root, which doesn't really make sense
//
// it doesn't really matter which error returns first, so accept both
err = lfsr_remove(&lfs, "/");
assert(err == LFS_ERR_NOTEMPTY || err == LFS_ERR_INVAL);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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;
'''
[cases.t5_dirs_rm_siblings]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now remove each directory
lfsr_remove(&lfs, "ardvark") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our remove worked
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// remove another
lfsr_remove(&lfs, "banana") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our remove worked
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "banana", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_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, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// remove another
lfsr_remove(&lfs, "cat") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our remove worked
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "banana", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "cat", &info) => LFS_ERR_NOENT;
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;
'''
[cases.t5_dirs_rm_children]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/banana/cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana/cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark") => 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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark/banana") => 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, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now remove each directory
lfsr_remove(&lfs, "ardvark/banana/cat") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our remove worked
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana/cat", &info) => LFS_ERR_NOENT;
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark") => 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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark/banana") => 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;
// remove another
lfsr_remove(&lfs, "ardvark/banana") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our remove worked
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "ardvark/banana/cat", &info) => LFS_ERR_NOENT;
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark") => 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;
// remove another
lfsr_remove(&lfs, "ardvark") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our remove worked
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "ardvark/banana", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "ardvark/banana/cat", &info) => LFS_ERR_NOENT;
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;
'''
[cases.t5_dirs_rm_many]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.REMAINING = [64, 2, 1, 0]
defines.REMOUNT = [false, true]
if = [
'N > REMAINING',
# limit powerloss testing due to time
'!TEST_PL || N <= 32',
]
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now remove some number of directories
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_remove(&lfs, name) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our removes worked
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
for (lfs_size_t i = 0; i < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-REMAINING + i));
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_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 < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-REMAINING + i));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_rm_many_backwards]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.REMAINING = [64, 2, 1, 0]
defines.REMOUNT = [false, true]
if = [
'N > REMAINING',
# limit powerloss testing due to time
'!TEST_PL || N <= 32',
]
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-1-i));
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now remove some number of directories
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-1-i));
lfsr_remove(&lfs, name) => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our removes worked
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-1-i));
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
for (lfs_size_t i = 0; i < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_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 < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_rm_many_2layers]
defines.N = [1, 2, 4, 8, 16]
defines.REMAINING = [2, 1, 0]
defines.REMOUNT = [false, true]
if = [
'N > REMAINING',
# limit powerloss testing due to time
'!TEST_PL || N <= 4',
]
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "child%04d", j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
// now remove some number of directories
for (lfs_size_t i = 0; i < N; i++) {
for (lfs_size_t j = 0; j < N; j++) {
if (i < N-REMAINING || j < N-REMAINING) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_remove(&lfs, name) => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
if (i < N-REMAINING) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_remove(&lfs, name) => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our removes worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
if (i < N-REMAINING) {
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
} else {
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
if (j < N-REMAINING) {
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
} else {
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_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 < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-REMAINING + i));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-REMAINING + i));
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < REMAINING; j++) {
char name[256];
sprintf(name, "child%04d", (int)(N-REMAINING + j));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_rm_many_3layers]
defines.N = [1, 2, 4]
defines.REMAINING = [2, 1, 0]
defines.REMOUNT = [false, true]
if = [
'N > REMAINING',
# limit powerloss testing due to time
'!TEST_PL || N <= 2',
]
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;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t k = 0; k < N; k++) {
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t k = 0; k < N; k++) {
char name[256];
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "grandchild%04d", k);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "child%04d", j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_dir_open(&lfs, &dir, name) => 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 k = 0; k < N; k++) {
char name[256];
sprintf(name, "grandchild%04d", k);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
}
// now remove some number of directories
for (lfs_size_t i = 0; i < N; i++) {
for (lfs_size_t j = 0; j < N; j++) {
for (lfs_size_t k = 0; k < N; k++) {
if (i < N-REMAINING || j < N-REMAINING || k < N-REMAINING) {
char name[256];
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
lfsr_remove(&lfs, name) => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
if (i < N-REMAINING || j < N-REMAINING) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_remove(&lfs, name) => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
if (i < N-REMAINING) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_remove(&lfs, name) => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our removes worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
if (i < N-REMAINING) {
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
} else {
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
if (j < N-REMAINING) {
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
} else {
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t k = 0; k < N; k++) {
char name[256];
sprintf(name, "dir%04d/child%04d/grandchild%04d",
i, j, k);
struct lfs_info info;
if (k < N-REMAINING) {
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
} else {
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "grandchild%04d", k);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_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 < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-REMAINING + i));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < REMAINING; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-REMAINING + i));
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < REMAINING; j++) {
char name[256];
sprintf(name, "child%04d", (int)(N-REMAINING + j));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
for (lfs_size_t i = 0; i < REMAINING; i++) {
for (lfs_size_t j = 0; j < REMAINING; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d",
(int)(N-REMAINING + i),
(int)(N-REMAINING + j));
lfsr_dir_open(&lfs, &dir, name) => 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 k = 0; k < REMAINING; k++) {
char name[256];
sprintf(name, "grandchild%04d", (int)(N-REMAINING + k));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_rm_many_linkedlist]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.REMAINING = [16, 2, 1, 0]
defines.REMOUNT = [false, true]
if = [
'N > REMAINING',
# limit powerloss testing due to time
'!TEST_PL || 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 directory in a sort of linked-list by nesting
char name[4096];
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, name) => 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);
if (i < N-1) {
char name2[256];
sprintf(name2, "dir%04d", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
// now remove some number of directories
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
lfsr_remove(&lfs, name) => 0;
name[strlen(name) - strlen("/dir....")] = '\0';
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our remove worked
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < REMAINING; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < REMAINING; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, name) => 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);
if (i < REMAINING-1) {
char name2[256];
sprintf(name2, "dir%04d", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name2) == 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;
'''
[cases.t5_dirs_rm_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
defines.REMOUNT = [false, true]
defines.SEED = 'range(10)'
# limit powerloss testing due to time
if = '!TEST_PL || N <= 64'
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;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
lfs_size_t sim_size = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < N; i++) {
// choose a pseudo-random op, either mkdir or rmdir
uint8_t op = TEST_PRNG(&prng) % 2;
if (op == 0 || sim_size == 0) {
// choose a pseudo-random number, truncate to 4 decimals
lfs_size_t x = TEST_PRNG(&prng) % 10000;
// 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) {
// do nothing
} else {
// insert
memmove(&sim[j+1], &sim[j],
(sim_size-j)*sizeof(lfs_size_t));
sim_size += 1;
sim[j] = x;
}
break;
}
}
// create a directory here
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
} else {
// choose a pseudo-random entry 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));
sim_size -= 1;
// remove this directory
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
lfsr_remove(&lfs, name) => 0;
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// test that our directories match our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", sim[j]);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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, "dir%04d", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// clean up sim/lfs
free(sim);
lfsr_unmount(&lfs) => 0;
'''
## dir rename tests
[cases.t5_dirs_mv]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// clean up interrupted renames
if (TEST_PL) {
err = lfsr_remove(&lfs, "banana");
assert(!err || err == LFS_ERR_NOENT);
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked with stat
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now rename the directory
lfsr_rename(&lfs, "ardvark", "banana") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that rename worked with stat
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
// 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, "banana") == 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 that we can rename, and replace, other directories
[cases.t5_dirs_mv_replace]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked with stat
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now rename the directory
lfsr_rename(&lfs, "ardvark", "banana") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that rename worked with stat
lfsr_stat(&lfs, "ardvark", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
// 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, "banana") == 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 that we can rename to ourselves
[cases.t5_dirs_mv_noop]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked with stat
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now rename the directory to itself
lfsr_rename(&lfs, "ardvark", "ardvark") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that rename worked with stat
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
// 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, "ardvark") == 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 that we catch replacing an invalid path
[cases.t5_dirs_mv_noent]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to rename a nonsense directory
lfsr_rename(&lfs, "no", "ardvark") => LFS_ERR_NOENT;
// try to rename a directory in a nonsense directory
lfsr_rename(&lfs, "no/ardvark", "ardvark") => LFS_ERR_NOENT;
// try to rename a nonsense child directory
lfsr_rename(&lfs, "ardvark/no", "banana") => LFS_ERR_NOENT;
// try to rename to a nonense directory
lfsr_rename(&lfs, "ardvark", "no/ardvark") => LFS_ERR_NOENT;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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 that we catch replacing a non-empty directory
[cases.t5_dirs_mv_notempty]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// fill dest with stuff
err = lfsr_mkdir(&lfs, "banana/cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to rename
lfsr_rename(&lfs, "ardvark", "banana") => LFS_ERR_NOTEMPTY;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "banana/cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "banana") => 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, "cat") == 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 what happens if we try to rename root
[cases.t5_dirs_mv_root]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// try to rename root, which doesn't really make sense
lfsr_rename(&lfs, "/", "notroot") => LFS_ERR_INVAL;
// make a directory
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to rename root, which doesn't really make sense
lfsr_rename(&lfs, "/", "notroot") => LFS_ERR_INVAL;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// and check that this didn't interfere with our original directory
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 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;
'''
[cases.t5_dirs_mv_siblings]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// clean up interrupted renames
if (TEST_PL) {
err = lfsr_remove(&lfs, "disco");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "eggplant");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "fish");
assert(!err || err == LFS_ERR_NOENT);
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now rename each directory
lfsr_rename(&lfs, "ardvark", "disco") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our rename worked
lfsr_stat(&lfs, "banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco", &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rename another
lfsr_rename(&lfs, "banana", "eggplant") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our rename worked
lfsr_stat(&lfs, "cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco", &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "eggplant", &info) => 0;
assert(strcmp(info.name, "eggplant") == 0);
assert(info.type == LFS_TYPE_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, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rename another
lfsr_rename(&lfs, "cat", "fish") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our rename worked
lfsr_stat(&lfs, "disco", &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "eggplant", &info) => 0;
assert(strcmp(info.name, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "fish", &info) => 0;
assert(strcmp(info.name, "fish") == 0);
assert(info.type == LFS_TYPE_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, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "fish") == 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;
'''
[cases.t5_dirs_mv_children]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/banana");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/banana/cat");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// clean up interrupted renames
if (TEST_PL) {
err = lfsr_remove(&lfs, "disco/eggplant/fish");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "disco/eggplant/cat");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "disco/eggplant");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "disco/banana/cat");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "disco/banana");
assert(!err || err == LFS_ERR_NOENT);
err = lfsr_remove(&lfs, "disco");
assert(!err || err == LFS_ERR_NOENT);
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "ardvark", &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "ardvark/banana/cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
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, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark") => 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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/ardvark/banana") => 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, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now rename each directory
lfsr_rename(&lfs, "ardvark", "disco") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our rename worked
lfsr_stat(&lfs, "disco", &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco/banana", &info) => 0;
assert(strcmp(info.name, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco/banana/cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_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, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/disco") => 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, "banana") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/disco/banana") => 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, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rename another
lfsr_rename(&lfs, "disco/banana", "disco/eggplant") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our rename worked
lfsr_stat(&lfs, "disco", &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco/eggplant", &info) => 0;
assert(strcmp(info.name, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco/eggplant/cat", &info) => 0;
assert(strcmp(info.name, "cat") == 0);
assert(info.type == LFS_TYPE_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, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/disco") => 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, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/disco/eggplant") => 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, "cat") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// rename another
lfsr_rename(&lfs, "disco/eggplant/cat", "disco/eggplant/fish") => 0;
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our rename worked
lfsr_stat(&lfs, "disco", &info) => 0;
assert(strcmp(info.name, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco/eggplant", &info) => 0;
assert(strcmp(info.name, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_stat(&lfs, "disco/eggplant/fish", &info) => 0;
assert(strcmp(info.name, "fish") == 0);
assert(info.type == LFS_TYPE_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, "disco") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/disco") => 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, "eggplant") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_dir_open(&lfs, &dir, "/disco/eggplant") => 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, "fish") == 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;
'''
[cases.t5_dirs_mv_many]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.BEFORE = [false, true]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 32'
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;
}
// check if we have already started renaming, in case of powerloss
struct lfs_info info;
err = lfsr_stat(&lfs, (BEFORE ? "/0mved0000" : "/mved0000"), &info);
if (err == LFS_ERR_NOENT) {
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
// now rename the directories
for (lfs_size_t i = 0; i < N; i++) {
char old_name[256];
sprintf(old_name, "dir%04d", i);
char new_name[256];
sprintf(new_name, "%smved%04d", (BEFORE ? "0" : ""), i);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our renames worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mv_many_backwards]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.BEFORE = [false, true]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 32'
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;
}
// check if we have already started renaming, in case of powerloss
struct lfs_info info;
err = lfsr_stat(&lfs, (BEFORE ? "/0mved0000" : "/mved0000"), &info);
if (err == LFS_ERR_NOENT) {
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", (int)(N-1-i));
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
// now rename the directories
for (lfs_size_t i = 0; i < N; i++) {
char old_name[256];
sprintf(old_name, "dir%04d", i);
char new_name[256];
sprintf(new_name, "%smved%04d", (BEFORE ? "0" : ""), i);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
// check that our renames worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
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 name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mv_many_2layers]
defines.N = [1, 2, 4, 8, 16]
defines.BEFORE = [false, true]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 4'
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;
}
// check if we have already started renaming, in case of powerloss
struct lfs_info info;
err = lfsr_stat(&lfs, (BEFORE ? "/0mved0000" : "/mved0000"), &info);
if (err == LFS_ERR_NOENT) {
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "child%04d", j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
}
// now rename our directories
for (lfs_size_t i = 0; i < N; i++) {
char old_name[256];
sprintf(old_name, "dir%04d", i);
char new_name[256];
sprintf(new_name, "%smved%04d", (BEFORE ? "0" : ""), i);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
for (lfs_size_t j = 0; j < N; j++) {
char old_name[256];
sprintf(old_name, "%smved%04d/child%04d",
(BEFORE ? "0" : ""), i, j);
char new_name[256];
sprintf(new_name, "%smved%04d/%schmved%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
}
// check that our renames worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "%smved%04d/%schmved%04d",
(BEFORE ? "0" : ""), i, (BEFORE ? "0" : ""), j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "%schmved%04d", (BEFORE ? "0" : ""), j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
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 name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "%schmved%04d", (BEFORE ? "0" : ""), j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mv_many_3layers]
defines.N = [1, 2, 4]
defines.BEFORE = [false, true]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 2'
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;
}
// check if we have already started renaming, in case of powerloss
struct lfs_info info;
err = lfsr_stat(&lfs, (BEFORE ? "/0mved0000" : "/mved0000"), &info);
if (err == LFS_ERR_NOENT) {
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t k = 0; k < N; k++) {
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdirs worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "child%04d", j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t k = 0; k < N; k++) {
char name[256];
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "grandchild%04d", k);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
}
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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "child%04d", j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_dir_open(&lfs, &dir, name) => 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 k = 0; k < N; k++) {
char name[256];
sprintf(name, "grandchild%04d", k);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
}
}
// now rename our directories
for (lfs_size_t i = 0; i < N; i++) {
char old_name[256];
sprintf(old_name, "dir%04d", i);
char new_name[256];
sprintf(new_name, "%smved%04d", (BEFORE ? "0" : ""), i);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
for (lfs_size_t j = 0; j < N; j++) {
char old_name[256];
sprintf(old_name, "%smved%04d/child%04d",
(BEFORE ? "0" : ""), i, j);
char new_name[256];
sprintf(new_name, "%smved%04d/%schmved%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
for (lfs_size_t k = 0; k < N; k++) {
char old_name[256];
sprintf(old_name, "%smved%04d/%schmved%04d/grandchild%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j, k);
char new_name[256];
sprintf(new_name, "%smved%04d/%schmved%04d/%sgrmved%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j,
(BEFORE ? "0" : ""), k);
int err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
}
}
}
// check that our removes worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "%smved%04d/%schmved%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "%schmved%04d", (BEFORE ? "0" : ""), j);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t k = 0; k < N; k++) {
char name[256];
sprintf(name, "%smved%04d/%schmved%04d/%sgrmved%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j,
(BEFORE ? "0" : ""), k);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
sprintf(name, "%sgrmved%04d", (BEFORE ? "0" : ""), k);
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
}
}
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 name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_open(&lfs, &dir, name) => 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 j = 0; j < N; j++) {
char name[256];
sprintf(name, "%schmved%04d", (BEFORE ? "0" : ""), j);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
for (lfs_size_t j = 0; j < N; j++) {
char name[256];
sprintf(name, "%smved%04d/%schmved%04d",
(BEFORE ? "0" : ""), i,
(BEFORE ? "0" : ""), j);
lfsr_dir_open(&lfs, &dir, name) => 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 k = 0; k < N; k++) {
char name[256];
sprintf(name, "%sgrmved%04d", (BEFORE ? "0" : ""), k);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_mv_many_linkedlist]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.BEFORE = [false, true]
defines.REMOUNT = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || 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;
}
// check if we have already started renaming, in case of powerloss
struct lfs_info info;
err = lfsr_stat(&lfs, (BEFORE ? "/0mved0000" : "/mved0000"), &info);
if (err == LFS_ERR_NOENT) {
// create this many directory in a sort of linked-list by nesting
char name[4096];
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// check that our mkdir worked
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, name) => 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);
if (i < N-1) {
char name2[256];
sprintf(name2, "dir%04d", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
}
// now rename our directories
char old_name[4096];
memset(old_name, 0, sizeof(old_name));
char new_name[4096];
memset(new_name, 0, sizeof(new_name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&old_name[strlen(old_name)], "/dir%04d", i);
sprintf(&new_name[strlen(new_name)], "/%smved%04d",
(BEFORE ? "0" : ""), i);
err = lfsr_rename(&lfs, old_name, new_name);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// update old_name's path
memcpy(old_name, new_name, sizeof(old_name));
}
// check that our renames worked
char name[4096];
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/%smved%04d", (BEFORE ? "0" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "%smved%04d", (BEFORE ? "0" : ""), i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, name) => 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);
if (i < N-1) {
char name2[256];
sprintf(name2, "%smved%04d", (BEFORE ? "0" : ""), i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name2) == 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;
'''
[cases.t5_dirs_mv_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
defines.REMOUNT = [false, true]
defines.SEED = 'range(10)'
# limit powerloss testing due to time
if = '!TEST_PL || N <= 64'
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;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
lfs_size_t sim_size = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < N; i++) {
// choose a pseudo-random op, either mkdir or rename
uint8_t op = TEST_PRNG(&prng) % 2;
if (op == 0 || sim_size == 0) {
// choose a pseudo-random number, truncate to 4 decimals
lfs_size_t x = TEST_PRNG(&prng) % 10000;
// 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) {
// do nothing
} else {
// insert
memmove(&sim[j+1], &sim[j],
(sim_size-j)*sizeof(lfs_size_t));
sim_size += 1;
sim[j] = x;
}
break;
}
}
// create a directory here
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
} else {
// choose a pseudo-random entry to rename, and a pseudo-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) % 10000;
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// already seen and not a noop?
if (k < sim_size && sim[k] == y && x != y) {
// just delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
sim_size -= 1;
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
sim[k] = y;
}
break;
}
}
// rename this directory
char old_name[256];
sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
char new_name[256];
sprintf(new_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), y);
lfsr_rename(&lfs, old_name, new_name) => 0;
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// test that our directories match our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", sim[j]);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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, "dir%04d", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// clean up sim/lfs
free(sim);
lfsr_unmount(&lfs) => 0;
'''
# test all of the operations together
[cases.t5_dirs_general_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
defines.REMOUNT = [false, true]
defines.SEED = 'range(10)'
# limit powerloss testing due to time
if = '!TEST_PL || N <= 64'
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;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// set up a simulation to compare against
lfs_size_t *sim = malloc(N*sizeof(lfs_size_t));
lfs_size_t sim_size = 0;
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < N; i++) {
// choose a pseudo-random op, either mkdir, remove, or rename
uint8_t op = TEST_PRNG(&prng) % 3;
if (op == 0 || sim_size == 0) {
// choose a pseudo-random number, truncate to 4 decimals
lfs_size_t x = TEST_PRNG(&prng) % 10000;
// 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) {
// do nothing
} else {
// insert
memmove(&sim[j+1], &sim[j],
(sim_size-j)*sizeof(lfs_size_t));
sim_size += 1;
sim[j] = x;
}
break;
}
}
// create a directory here
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
} else if (op == 1) {
// choose a pseudo-random entry 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));
sim_size -= 1;
// remove this directory
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
lfsr_remove(&lfs, name) => 0;
} else {
// choose a pseudo-random entry to rename, and a pseudo-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) % 10000;
for (lfs_size_t k = 0;; k++) {
if (k >= sim_size || sim[k] >= y) {
// already seen and not a noop?
if (k < sim_size && sim[k] == y && x != y) {
// just delete the original entry
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
sim_size -= 1;
} else {
// first delete
memmove(&sim[j], &sim[j+1],
(sim_size-(j+1))*sizeof(lfs_size_t));
if (k > j) {
k -= 1;
}
// then insert
memmove(&sim[k+1], &sim[k],
(sim_size-k)*sizeof(lfs_size_t));
sim[k] = y;
}
break;
}
}
// rename this directory
char old_name[256];
sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), x);
char new_name[256];
sprintf(new_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), y);
lfsr_rename(&lfs, old_name, new_name) => 0;
}
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// test that our directories match our simulation
for (lfs_size_t j = 0; j < sim_size; j++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), sim[j]);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", sim[j]);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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, "dir%04d", sim[j]);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// clean up sim/lfs
free(sim);
lfsr_unmount(&lfs) => 0;
'''
## Test seeking and stuff
[cases.t5_dirs_tell]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
// read our directory
//
// we can't really make many assumptions about tell's value, so just
// check for no errors
//
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0;
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
struct lfs_info info;
off = lfsr_dir_read(&lfs, &dir, &info);
assert(off >= 0);
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
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++) {
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.t5_dirs_rewind]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0;
// read our directory once
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
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++) {
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
// now read it again
lfsr_dir_rewind(&lfs, &dir) => 0;
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
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++) {
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.t5_dirs_seek]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 0;
// read our directory once
lfs_soff_t offs[2+N+2];
offs[0] = lfsr_dir_tell(&lfs, &dir);
assert(offs[0] >= 0);
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
offs[1] = lfsr_dir_tell(&lfs, &dir);
assert(offs[1] >= 0);
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++) {
offs[2+i] = lfsr_dir_tell(&lfs, &dir);
assert(offs[2+i] >= 0);
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
offs[2+N+0] = lfsr_dir_tell(&lfs, &dir);
assert(offs[2+N+0] >= 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
offs[2+N+1] = lfsr_dir_tell(&lfs, &dir);
assert(offs[2+N+1] >= 0);
// now try to seek to each entry explicitly
lfsr_dir_seek(&lfs, &dir, offs[0]) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_seek(&lfs, &dir, offs[1]) => 0;
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++) {
lfsr_dir_seek(&lfs, &dir, offs[2+i]) => 0;
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_seek(&lfs, &dir, offs[2+N+0]) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_seek(&lfs, &dir, offs[2+N+1]) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.t5_dirs_read_idempotent]
defines.PARENT = [false, true]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# neighbors only make sense if we have a parent
if = 'PARENT || NEIGHBORS == 0'
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
char name[256];
sprintf(name, "%s/ardvark", (PARENT ? "pricklypear" : ""));
lfsr_mkdir(&lfs, name) => 0;
// read to the end
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
// reading again should still return noent
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
// seeking past the end of the directory should still return noent
lfsr_dir_seek(&lfs, &dir, off) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_seek(&lfs, &dir, off + 1000) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
// but we should be able to read again
lfsr_dir_rewind(&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, "ardvark") == 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;
'''
[cases.t5_dirs_read_with_mkdirs]
defines.N = 5
# where in the dir read do we mkdir?
defines.I = 'range(6)'
# where do we mkdir?
defines.J = 'range(6)'
defines.PARENT = [false, true]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
defines.SEEK = [false, true]
# neighbors only make sense if we have a parent
if = 'PARENT || NEIGHBORS == 0'
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i+1);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%04d", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
// make a dir at J
char name[256];
sprintf(name, "%s/dir%04d_", (PARENT ? "pricklypear" : ""), (int)J);
lfsr_mkdir(&lfs, name) => 0;
// seek after mkdir? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// we should be able to keep reading, though we may pick up J
for (lfs_size_t i = I + (I >= J ? 1 : 0); i < N+1; i++) {
char name[256];
sprintf(name, "dir%04d%s", i+1 - (i >= J ? 1 : 0), (i == J ? "_" : ""));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_read_with_rms]
defines.N = 5
# where in the dir read do we rm?
defines.I = 'range(5)'
# where do we rm?
defines.J = 'range(5)'
defines.PARENT = [false, true]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
defines.SEEK = [false, true]
# neighbors only make sense if we have a parent
if = 'PARENT || NEIGHBORS == 0'
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
// remove the dir at J
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J);
lfsr_remove(&lfs, name) => 0;
// seek after remove? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// we should be able to keep reading
for (lfs_size_t i = I; i < N; i++) {
if (i == J) {
continue;
}
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_read_with_mvs]
defines.N = 5
# where in the dir read do we rm?
defines.I = 'range(5)'
# where do we rm?
defines.J = 'range(5)'
defines.K = 'range(5)'
defines.BEFORE = [false, true]
# 0 => no
# 1 => yes
# 2 => yes, and rename to new parent
defines.PARENT = [0, 1, 2]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
defines.SEEK = [false, true]
# neighbors only make sense if we have a parent
if = 'PARENT || NEIGHBORS == 0'
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (PARENT >= 2) {
lfsr_mkdir(&lfs, "quiabentia") => 0;
}
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
// rename the dir at J
char old_name[256];
sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J);
char new_name[256];
sprintf(new_name, "%s/%smved%04d",
(PARENT == 1 ? "pricklypear"
: PARENT >= 2 ? "quiabentia"
: ""),
(BEFORE ? "0" : ""),
(int)J);
lfsr_rename(&lfs, old_name, new_name) => 0;
// seek after remove? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// we should be able to keep reading
for (lfs_size_t i = I; i < N; i++) {
if (i == J) {
continue;
}
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
int err = lfsr_dir_read(&lfs, &dir, &info);
assert(err == LFS_ERR_NOENT || (!BEFORE && PARENT < 2));
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# dir reads with 2x ops have better chances of catching bugs that depend on
# invalid dir states
[cases.t5_dirs_read_with_2_mkdirs]
defines.N = 5
# where in the dir read do we mkdir?
defines.I = 'range(6)'
# where do we mkdir?
defines.J = 'range(6)'
defines.K = 'range(6)'
defines.PARENT = [false, true]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
defines.SEEK = [false, true]
if = [
'J != K',
# neighbors only make sense if we have a parent
'PARENT || NEIGHBORS == 0',
]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i+1);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%04d", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
// make a dir at J
char name[256];
sprintf(name, "%s/dir%04d_", (PARENT ? "pricklypear" : ""), (int)J);
lfsr_mkdir(&lfs, name) => 0;
// make a dir at K
sprintf(name, "%s/dir%04d_", (PARENT ? "pricklypear" : ""), (int)K);
lfsr_mkdir(&lfs, name) => 0;
// seek after mkdir? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// we should be able to keep reading, though we may pick up J
for (lfs_size_t i = I + (I >= J ? 1 : 0) + (I >= K ? 1 : 0); i < N+2; i++) {
char name[256];
sprintf(name, "dir%04d%s",
i+1 - (i >= J + (J >= K ? 1 : 0) ? 1 : 0)
- (i >= K + (K >= J ? 1 : 0) ? 1 : 0),
(i == J + (J > K ? 1 : 0) || i == K + (K > J ? 1 : 0)
? "_" : ""));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_read_with_2_rms]
defines.N = 5
# where in the dir read do we rm?
defines.I = 'range(5)'
# where do we rm?
defines.J = 'range(5)'
defines.K = 'range(5)'
defines.PARENT = [false, true]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
defines.SEEK = [false, true]
if = [
'J != K',
# neighbors only make sense if we have a parent
'PARENT || NEIGHBORS == 0',
]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
// remove the dir at J
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J);
lfsr_remove(&lfs, name) => 0;
// remove the dir at K
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)K);
lfsr_remove(&lfs, name) => 0;
// seek after remove? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// we should be able to keep reading
for (lfs_size_t i = I; i < N; i++) {
if (i == J || i == K) {
continue;
}
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
[cases.t5_dirs_read_with_2_mvs]
defines.N = 5
# where in the dir read do we rm?
defines.I = 'range(5)'
# where do we rm?
defines.J = 'range(5)'
defines.K = 'range(5)'
defines.BEFORE = [false, true]
# 0 => no
# 1 => yes
# 2 => yes, and rename to new parent
defines.PARENT = [0, 1, 2]
# bit 0x2 = left neighbor
# bit 0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
defines.SEEK = [false, true]
# neighbors only make sense if we have a parent
if = [
'J != K',
# neighbors only make sense if we have a parent
'PARENT || NEIGHBORS == 0',
]
code = '''
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
if (PARENT) {
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (PARENT >= 2) {
lfsr_mkdir(&lfs, "quiabentia") => 0;
}
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
// rename the dir at J
char old_name[256];
sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)J);
char new_name[256];
sprintf(new_name, "%s/%smved%04d",
(PARENT == 1 ? "pricklypear"
: PARENT >= 2 ? "quiabentia"
: ""),
(BEFORE ? "0" : ""),
(int)J);
lfsr_rename(&lfs, old_name, new_name) => 0;
// rename the dir at K
sprintf(old_name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), (int)K);
sprintf(new_name, "%s/%smved%04d",
(PARENT == 1 ? "pricklypear"
: PARENT >= 2 ? "quiabentia"
: ""),
(BEFORE ? "0" : ""),
(int)K);
lfsr_rename(&lfs, old_name, new_name) => 0;
// seek after remove? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// we should be able to keep reading
for (lfs_size_t i = I; i < N; i++) {
if (i == J || i == K) {
continue;
}
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
int err = lfsr_dir_read(&lfs, &dir, &info);
assert(err == LFS_ERR_NOENT || (!BEFORE && PARENT < 2));
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
## Recursive tests
# Recursive here just refers to removing entries in a directory while
# iterating over the directory
#
# This is a useful feature, but it's unintuitive if this should have
# well-defined behavior, so make sure to test for it
[cases.t5_dirs_rm_recursive]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
defines.SEEK = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 32'
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;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// now remove directories recursively
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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 name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
char path[1024];
sprintf(path, "%s/%s", (PARENT ? "pricklypear" : ""), info.name);
lfsr_remove(&lfs, path) => 0;
// seek between removes? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// check that our removes worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
}
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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;
'''
# Recursive here just refers to renaming entries in a directory while
# iterating over the directory
#
# This is a useful feature, but it's unintuitive if this should have
# well-defined behavior, so make sure to test for it
[cases.t5_dirs_mv_recursive]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.BEFORE = [false, true]
# 0 => no
# 1 => yes
# 2 => yes, and rename to new parent
defines.PARENT = [0, 1, 2]
defines.SEEK = [false, true]
# limit powerloss testing due to time
if = '!TEST_PL || N <= 32'
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;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
if (PARENT >= 2) {
err = lfsr_mkdir(&lfs, "quiabentia");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
}
// check if we have already started renaming, in case of powerloss
struct lfs_info info;
err = lfsr_stat(&lfs,
(PARENT == 1 ? (BEFORE
? "pricklypear/0mved0000"
: "pricklypear/mved0000")
: PARENT >= 2 ? (BEFORE
? "quiabentia/0mved0000"
: "quiabentia/mved0000")
: (BEFORE
? "/0mved0000"
: "/mved0000")), &info);
if (err == LFS_ERR_NOENT) {
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%04d", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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 i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
// now rename directories recursively
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, (PARENT ? "pricklypear" : "/")) => 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++) {
int err = lfsr_dir_read(&lfs, &dir, &info);
assert(!err || err == LFS_ERR_NOENT);
// reached the end?
if (err == LFS_ERR_NOENT) {
break;
}
// skip already moved?
if (memcmp(info.name, "dir", strlen("dir")) != 0) {
continue;
}
assert(i < 2*N);
assert(info.type == LFS_TYPE_DIR);
char old_path[1024];
sprintf(old_path, "%s/%s", (PARENT ? "pricklypear" : ""), info.name);
char new_path[1024];
sprintf(new_path, "%s/%smved%s",
(PARENT == 1 ? "pricklypear"
: PARENT >= 2 ? "quiabentia"
: ""),
(BEFORE ? "0" : ""),
&info.name[strlen("dir")]);
err = lfsr_rename(&lfs, old_path, new_path);
assert(!err || (TEST_PL && err == LFS_ERR_NOENT));
// seek between renames? this tests that the internal position is
// updated correctly
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
}
lfsr_dir_close(&lfs, &dir) => 0;
// check that our renames worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%04d", (PARENT ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
sprintf(name, "%s/%smved%04d",
(PARENT == 1 ? "pricklypear"
: PARENT >= 2 ? "quiabentia"
: ""),
(BEFORE ? "0" : ""),
i);
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "%smved%04d", (BEFORE ? "0" : ""), i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_open(&lfs, &dir,
(PARENT == 1 ? "pricklypear"
: PARENT >= 2 ? "quiabentia"
: "/")) => 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 name[256];
sprintf(name, "%smved%04d", (BEFORE ? "0" : ""), i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, 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;
'''
#[cases.test_dirs_root]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_many_creation]
#defines.N = 'range(3, 100, 3)'
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "dir%03d", i);
# lfs_mkdir(&lfs, path) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "dir%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_many_removal]
#defines.N = 'range(3, 100, 11)'
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "removeme%03d", i);
# lfs_mkdir(&lfs, path) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "removeme%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "removeme%03d", i);
# lfs_remove(&lfs, path) => 0;
# }
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_many_rename]
#defines.N = 'range(3, 100, 11)'
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "test%03d", i);
# lfs_mkdir(&lfs, path) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "test%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char oldpath[128];
# char newpath[128];
# sprintf(oldpath, "test%03d", i);
# sprintf(newpath, "tedd%03d", i);
# lfs_rename(&lfs, oldpath, newpath) => 0;
# }
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "tedd%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#'''
#
#[cases.test_dirs_many_reentrant]
#defines.N = [5, 11]
#if = 'BLOCK_COUNT >= 4*N'
#reentrant = true
#code = '''
# lfs_t lfs;
# int err = lfs_mount(&lfs, cfg);
# if (err) {
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# }
#
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hi%03d", i);
# err = lfs_mkdir(&lfs, path);
# assert(err == 0 || err == LFS_ERR_EXIST);
# }
#
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hello%03d", i);
# err = lfs_remove(&lfs, path);
# assert(err == 0 || err == LFS_ERR_NOENT);
# }
#
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hi%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
#
# for (int i = 0; i < N; i++) {
# char oldpath[128];
# char newpath[128];
# sprintf(oldpath, "hi%03d", i);
# sprintf(newpath, "hello%03d", i);
# // YES this can overwrite an existing newpath
# lfs_rename(&lfs, oldpath, newpath) => 0;
# }
#
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hello%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
#
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hello%03d", i);
# lfs_remove(&lfs, path) => 0;
# }
#
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_file_creation]
#defines.N = 'range(3, 100, 11)'
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "file%03d", i);
# lfs_file_t file;
# lfs_file_open(&lfs, &file, path,
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# lfs_file_close(&lfs, &file) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "file%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#'''
#
#[cases.test_dirs_file_removal]
#defines.N = 'range(3, 100, 11)'
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "removeme%03d", i);
# lfs_file_t file;
# lfs_file_open(&lfs, &file, path,
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# lfs_file_close(&lfs, &file) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "removeme%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "removeme%03d", i);
# lfs_remove(&lfs, path) => 0;
# }
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_file_rename]
#defines.N = 'range(3, 100, 11)'
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "test%03d", i);
# lfs_file_t file;
# lfs_file_open(&lfs, &file, path,
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# lfs_file_close(&lfs, &file) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "test%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < N; i++) {
# char oldpath[128];
# char newpath[128];
# sprintf(oldpath, "test%03d", i);
# sprintf(newpath, "tedd%03d", i);
# lfs_rename(&lfs, oldpath, newpath) => 0;
# }
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "tedd%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#'''
#
#[cases.test_dirs_file_reentrant]
#defines.N = [5, 25]
#if = 'N < BLOCK_COUNT/2'
#reentrant = true
#code = '''
# lfs_t lfs;
# int err = lfs_mount(&lfs, cfg);
# if (err) {
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# }
#
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hi%03d", i);
# lfs_file_t file;
# lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
# lfs_file_close(&lfs, &file) => 0;
# }
#
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hello%03d", i);
# err = lfs_remove(&lfs, path);
# assert(err == 0 || err == LFS_ERR_NOENT);
# }
#
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hi%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
#
# for (int i = 0; i < N; i++) {
# char oldpath[128];
# char newpath[128];
# sprintf(oldpath, "hi%03d", i);
# sprintf(newpath, "hello%03d", i);
# // YES this can overwrite an existing newpath
# lfs_rename(&lfs, oldpath, newpath) => 0;
# }
#
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hello%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
#
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "hello%03d", i);
# lfs_remove(&lfs, path) => 0;
# }
#
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_nested]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# lfs_mkdir(&lfs, "potato") => 0;
# lfs_file_t file;
# lfs_file_open(&lfs, &file, "burito",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_mkdir(&lfs, "potato/baked") => 0;
# lfs_mkdir(&lfs, "potato/sweet") => 0;
# lfs_mkdir(&lfs, "potato/fried") => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "potato") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "baked") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "fried") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "sweet") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#
# // try removing?
# lfs_mount(&lfs, cfg) => 0;
# lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY;
# lfs_unmount(&lfs) => 0;
#
# // try renaming?
# lfs_mount(&lfs, cfg) => 0;
# lfs_rename(&lfs, "potato", "coldpotato") => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_rename(&lfs, "coldpotato", "warmpotato") => 0;
# lfs_rename(&lfs, "warmpotato", "hotpotato") => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_remove(&lfs, "potato") => LFS_ERR_NOENT;
# lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT;
# lfs_remove(&lfs, "warmpotato") => LFS_ERR_NOENT;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_unmount(&lfs) => 0;
#
# // try cross-directory renaming
# lfs_mount(&lfs, cfg) => 0;
# lfs_mkdir(&lfs, "coldpotato") => 0;
# lfs_rename(&lfs, "hotpotato/baked", "coldpotato/baked") => 0;
# lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_rename(&lfs, "hotpotato/fried", "coldpotato/fried") => 0;
# lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_rename(&lfs, "hotpotato/sweet", "coldpotato/sweet") => 0;
# lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
# lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "hotpotato") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "baked") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "fried") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "sweet") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#
# // final remove
# lfs_mount(&lfs, cfg) => 0;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "hotpotato/baked") => 0;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "hotpotato/fried") => 0;
# lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
# lfs_remove(&lfs, "hotpotato/sweet") => 0;
# lfs_remove(&lfs, "hotpotato") => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# info.type => LFS_TYPE_DIR;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "burito") == 0);
# info.type => LFS_TYPE_REG;
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_recursive_remove]
#defines.N = [10, 100]
#if = 'N < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# lfs_mkdir(&lfs, "prickly-pear") => 0;
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "prickly-pear/cactus%03d", i);
# lfs_mkdir(&lfs, path) => 0;
# }
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "cactus%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs);
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
#
# lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# for (int i = 0; i < N; i++) {
# char path[1024];
# sprintf(path, "cactus%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, path) == 0);
# sprintf(path, "prickly-pear/%s", info.name);
# lfs_remove(&lfs, path) => 0;
# }
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
#
# lfs_remove(&lfs, "prickly-pear") => 0;
# lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
# lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_other_errors]
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# lfs_mkdir(&lfs, "potato") => 0;
# lfs_file_t file;
# lfs_file_open(&lfs, &file, "burito",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
# lfs_file_close(&lfs, &file) => 0;
# lfs_unmount(&lfs) => 0;
#
# lfs_mount(&lfs, cfg) => 0;
#
# lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST;
# lfs_mkdir(&lfs, "burito") => LFS_ERR_EXIST;
# lfs_file_open(&lfs, &file, "burito",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
# lfs_file_open(&lfs, &file, "potato",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "tomato") => LFS_ERR_NOENT;
# lfs_dir_open(&lfs, &dir, "burito") => LFS_ERR_NOTDIR;
# lfs_file_open(&lfs, &file, "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT;
# lfs_file_open(&lfs, &file, "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR;
# lfs_file_open(&lfs, &file, "tomato", LFS_O_WRONLY) => LFS_ERR_NOENT;
# lfs_file_open(&lfs, &file, "potato", LFS_O_WRONLY) => LFS_ERR_ISDIR;
# lfs_file_open(&lfs, &file, "potato",
# LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
#
# lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST;
# lfs_file_open(&lfs, &file, "/",
# LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
# lfs_file_open(&lfs, &file, "/", LFS_O_RDONLY) => LFS_ERR_ISDIR;
# lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_ISDIR;
# lfs_file_open(&lfs, &file, "/",
# LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
#
# // check that errors did not corrupt directory
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, "burito") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "potato") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
#
# lfs_unmount(&lfs) => 0;
#
# // or on disk
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, ".") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "..") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_REG);
# assert(strcmp(info.name, "burito") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(info.type == LFS_TYPE_DIR);
# assert(strcmp(info.name, "potato") == 0);
# lfs_dir_read(&lfs, &dir, &info) => 0;
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
#'''
#
#[cases.test_dirs_seek]
#defines.COUNT = [4, 128, 132]
#if = 'COUNT < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# lfs_mkdir(&lfs, "hello") => 0;
# for (int i = 0; i < COUNT; i++) {
# char path[1024];
# sprintf(path, "hello/kitty%03d", i);
# lfs_mkdir(&lfs, path) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# for (int j = 2; j < COUNT; j++) {
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "hello") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# assert(info.type == LFS_TYPE_DIR);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_soff_t pos;
# for (int i = 0; i < j; i++) {
# char path[1024];
# sprintf(path, "kitty%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
# pos = lfs_dir_tell(&lfs, &dir);
# assert(pos >= 0);
# }
#
# lfs_dir_seek(&lfs, &dir, pos) => 0;
# char path[1024];
# sprintf(path, "kitty%03d", j);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_dir_rewind(&lfs, &dir) => 0;
# sprintf(path, "kitty%03u", 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# assert(info.type == LFS_TYPE_DIR);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# assert(info.type == LFS_TYPE_DIR);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_dir_seek(&lfs, &dir, pos) => 0;
# sprintf(path, "kitty%03d", j);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
# }
#'''
#
#[cases.test_dirs_toot_seek]
#defines.COUNT = [4, 128, 132]
#if = 'COUNT < BLOCK_COUNT/2'
#code = '''
# lfs_t lfs;
# lfs_format(&lfs, cfg) => 0;
# lfs_mount(&lfs, cfg) => 0;
# for (int i = 0; i < COUNT; i++) {
# char path[1024];
# sprintf(path, "hi%03d", i);
# lfs_mkdir(&lfs, path) => 0;
# }
# lfs_unmount(&lfs) => 0;
#
# for (int j = 2; j < COUNT; j++) {
# lfs_mount(&lfs, cfg) => 0;
# lfs_dir_t dir;
# lfs_dir_open(&lfs, &dir, "/") => 0;
# struct lfs_info info;
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# assert(info.type == LFS_TYPE_DIR);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_soff_t pos;
# for (int i = 0; i < j; i++) {
# char path[1024];
# sprintf(path, "hi%03d", i);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
# pos = lfs_dir_tell(&lfs, &dir);
# assert(pos >= 0);
# }
#
# lfs_dir_seek(&lfs, &dir, pos) => 0;
# char path[1024];
# sprintf(path, "hi%03d", j);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_dir_rewind(&lfs, &dir) => 0;
# sprintf(path, "hi%03u", 0);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, ".") == 0);
# assert(info.type == LFS_TYPE_DIR);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, "..") == 0);
# assert(info.type == LFS_TYPE_DIR);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_dir_seek(&lfs, &dir, pos) => 0;
# sprintf(path, "hi%03d", j);
# lfs_dir_read(&lfs, &dir, &info) => 1;
# assert(strcmp(info.name, path) == 0);
# assert(info.type == LFS_TYPE_DIR);
#
# lfs_dir_close(&lfs, &dir) => 0;
# lfs_unmount(&lfs) => 0;
# }
#'''
#