Files
littlefs/tests/test_dread.toml
T
Christopher Haster acfae9e072 Extended lfsr_mount to accept mount flags
This has been a long-time coming, mount flags are just too useful for
configuring a filesystem at runtime.

Currently this is limited to LFS_M_RDONLY and LFS_M_CKPROGS, but there
are a few more planned in the future:

  LFS_M_RDWR     = 0x0000, // Mount the filesystem as read and write
  LFS_M_RDONLY   = 0x0001, // Mount the filesystem as readonly
  LFS_M_STRICT*  = 0x0002, // Error if on-disk config does not match
  LFS_M_FORCE*   = 0x0004, // Ignore compat flags, mount readonly
  LFS_M_FORCEWITHRECKLESSABANDON*
                 = 0x0008, // Ignore compat flags, mount read write

  LFS_M_CKPROGS  = 0x0010, // Check progs by reading back progged data
  LFS_M_CKREADS* = 0x0020, // Check reads via checksums

  * Hypothetical

As a convenience, we also return mount flags in the struct lfs_fsinfo's
flags field as their relevant LFS_I_* variants. Though only to match
statvfs, and only because it's cheap, littlefs's API is low-level and we
should expect users to know what flags they passed to lfsr_mount.

As for the new mount flags:

- LFS_M_RDONLY - For consistency with existing APIs, this just asserts
  on write operations, which makes it a bit useless... But the info flag
  LFS_I_RDONLY may be useful for falling back to a readonly mode if
  we encounter on-disk compat issues.

  At least if implement the theoretical LFS_UNTRUSTED_USER mode
  LFS_M_RDONLY could become a runtime error.

- LFS_M_RDWR - This really just exists to compliment LFS_M_RDONLY and to
  match LFS_O_RDONLY/LFS_O_RDWR. It's just an alias for 0, and I don't
  think there will ever be a reason to make it non-0 (but I can always
  be wrong!).

- LFS_M_CKPROGS - This replaces the check_progs config option and avoids
  using a full byte to store a bool.

  We should probably also have a compile-time option to compile this out
  (LFS_NO_CKPROGS?), but that's a future thing to do.

This ended up adding a surprising bit of code, considering we're just
moving flags around, and noise in lfs_alloc added a bit of stack again:

           code          stack
  before: 35880           2672
  after:  35932 (+0.1%)   2680 (+0.3%)
2024-07-17 20:39:31 -05:00

1914 lines
58 KiB
TOML

# Test the annoying subtle corner cases of directory seeking+reading
#
# Note there may be some overlap with test_dirs, since some dir operations
# are needed to validate the directory tree works
after = 'test_dirs'
# test some dir functions
[cases.test_dread_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, LFS_M_RDWR, 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%03x", ((PARENT) ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
// read our directory
//
// Note tell's value is not guaranteed! We can test the exact value only
// because these tests are tightly bound to the current littlefs version.
//
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, ((PARENT) ? "pricklypear" : "/")) => 0;
lfsr_dir_tell(&lfs, &dir) => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_tell(&lfs, &dir) => 1;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
lfsr_dir_tell(&lfs, &dir) => 2 + i;
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_tell(&lfs, &dir) => 2 + N;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_tell(&lfs, &dir) => 2 + N;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_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, LFS_M_RDWR, 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%03x", ((PARENT) ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, ((PARENT) ? "pricklypear" : "/")) => 0;
// read our directory once
lfsr_dir_tell(&lfs, &dir) => 0;;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_tell(&lfs, &dir) => 1;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
lfsr_dir_tell(&lfs, &dir) => 2 + i;
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_tell(&lfs, &dir) => 2 + N;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_tell(&lfs, &dir) => 2 + N;
// now read it again
lfsr_dir_rewind(&lfs, &dir) => 0;
lfsr_dir_tell(&lfs, &dir) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_tell(&lfs, &dir) => 1;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
lfsr_dir_tell(&lfs, &dir) => 2 + i;
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_tell(&lfs, &dir) => 2 + N;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_tell(&lfs, &dir) => 2 + N;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_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, LFS_M_RDWR, 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%03x", ((PARENT) ? "pricklypear" : ""), i);
lfsr_mkdir(&lfs, name) => 0;
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, ((PARENT) ? "pricklypear" : "/")) => 0;
// read our directory once
lfsr_dir_tell(&lfs, &dir) => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_tell(&lfs, &dir) => 1;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
lfsr_dir_tell(&lfs, &dir) => 2 + i;
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_tell(&lfs, &dir) => 2 + N;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_tell(&lfs, &dir) => 2 + N;
// now try to seek to each entry explicitly
lfsr_dir_seek(&lfs, &dir, 0) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_seek(&lfs, &dir, 1) => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
lfsr_dir_seek(&lfs, &dir, 2 + i) => 0;
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_seek(&lfs, &dir, 2 + N) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_seek(&lfs, &dir, 2 + N) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test we don't ever get extra entries back after we reach
# the end of a directory
[cases.test_dread_read_idempotent]
defines.PARENT = [false, true]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&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, LFS_M_RDWR, 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;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_tell(&lfs, &dir) => 3;
// 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, 3) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_seek(&lfs, &dir, 4) => 0;
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_seek(&lfs, &dir, 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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "ardvark") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test neighbor changes don't mess with an unrelated dir read
[cases.test_dread_read_neighbor_mkdirs]
defines.N = 5
# where in the dir read do we mkdir?
defines.I = 'range(6)'
defines.PARENT = true
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x1, 0x2, 0x3]
# more neighbors ensures mdir splits which can be its own source
# of problems
defines.NEIGHBOR_N = [1, 10, 100]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
# SEEK=3 => seek with offset before mutation
# SEEK=4 => rewind then seek with offset before mutation
defines.SEEK = [0, 1, 2, 3, 4]
# 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, LFS_M_RDWR, 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%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// since modification is unrelated, the dir position should go unchanged
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
assert(off == I+2);
// make unrelated dirs
if (NEIGHBORS & 0x2) {
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "a_IplRNrPH/a_%03x", i);
lfsr_mkdir(&lfs, name) => 0;
}
}
if (NEIGHBORS & 0x1) {
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "f_VtoMnwRH/f_%03x", i);
lfsr_mkdir(&lfs, name) => 0;
}
}
// seek after mkdir?
if (SEEK) {
// note dir pos after reaching end of dir can be anything
lfs_soff_t off_ = lfsr_dir_tell(&lfs, &dir);
assert(off_ >= 0);
if (I < N) {
assert(off_ == off);
}
if (SEEK == 2 || SEEK == 4) {
lfsr_dir_rewind(&lfs, &dir) => 0;
}
if (SEEK == 3 || SEEK == 4) {
lfsr_dir_seek(&lfs, &dir, off) => 0;
} else {
lfsr_dir_seek(&lfs, &dir, off_) => 0;
}
}
// we should be able to keep reading where we left off
for (lfs_size_t i = I; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_read_neighbor_rms]
defines.N = 5
# where in the dir read do we mkdir?
defines.I = 'range(6)'
defines.PARENT = true
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x1, 0x2, 0x3]
# more neighbors ensures mdir splits which can be its own source
# of problems
defines.NEIGHBOR_N = [1, 10, 100]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
# SEEK=3 => seek with offset before mutation
# SEEK=4 => rewind then seek with offset before mutation
defines.SEEK = [0, 1, 2, 3, 4]
# 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, LFS_M_RDWR, 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;
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "a_IplRNrPH/a_%03x", i);
lfsr_mkdir(&lfs, name) => 0;
}
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "f_VtoMnwRH/f_%03x", i);
lfsr_mkdir(&lfs, name) => 0;
}
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// since modification is unrelated, the dir position should go unchanged
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
assert(off == I+2);
// remove unrelated dirs
if (NEIGHBORS & 0x2) {
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "a_IplRNrPH/a_%03x", i);
lfsr_remove(&lfs, name) => 0;
}
}
if (NEIGHBORS & 0x1) {
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "f_VtoMnwRH/f_%03x", i);
lfsr_remove(&lfs, name) => 0;
}
}
// seek after mkdir?
if (SEEK) {
// note dir pos after reaching end of dir can be anything
lfs_soff_t off_ = lfsr_dir_tell(&lfs, &dir);
assert(off_ >= 0);
if (I < N) {
assert(off_ == off);
}
if (SEEK == 2 || SEEK == 4) {
lfsr_dir_rewind(&lfs, &dir) => 0;
}
if (SEEK == 3 || SEEK == 4) {
lfsr_dir_seek(&lfs, &dir, off) => 0;
} else {
lfsr_dir_seek(&lfs, &dir, off_) => 0;
}
}
// we should be able to keep reading where we left off
for (lfs_size_t i = I; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_read_neighbor_mvs]
defines.N = 5
# where in the dir read do we mkdir?
defines.I = 'range(6)'
defines.PARENT = true
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x1, 0x2, 0x3]
# more neighbors ensures mdir splits which can be its own source
# of problems
defines.NEIGHBOR_N = [1, 10, 100]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
# SEEK=3 => seek with offset before mutation
# SEEK=4 => rewind then seek with offset before mutation
defines.SEEK = [0, 1, 2, 3, 4]
# 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, LFS_M_RDWR, 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;
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "a_IplRNrPH/a_%03x", i);
lfsr_mkdir(&lfs, name) => 0;
}
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "f_VtoMnwRH/f_%03x", i);
lfsr_mkdir(&lfs, name) => 0;
}
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// since modification is unrelated, the dir position should go unchanged
lfs_soff_t off = lfsr_dir_tell(&lfs, &dir);
assert(off == I+2);
// rename unrelated dirs
if (NEIGHBORS & 0x2) {
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "a_IplRNrPH/a_%03x", i);
char name_[256];
sprintf(name_, "a_IplRNrPH/b_%03x", i);
lfsr_rename(&lfs, name, name_) => 0;
}
}
if (NEIGHBORS & 0x1) {
for (lfs_size_t i = 0; i < NEIGHBOR_N; i++) {
char name[256];
sprintf(name, "f_VtoMnwRH/f_%03x", i);
char name_[256];
sprintf(name_, "f_VtoMnwRH/b_%03x", i);
lfsr_rename(&lfs, name, name_) => 0;
}
}
// seek after mkdir?
if (SEEK) {
// note dir pos after reaching end of dir can be anything
lfs_soff_t off_ = lfsr_dir_tell(&lfs, &dir);
assert(off_ >= 0);
if (I < N) {
assert(off_ == off);
}
if (SEEK == 2 || SEEK == 4) {
lfsr_dir_rewind(&lfs, &dir) => 0;
}
if (SEEK == 3 || SEEK == 4) {
lfsr_dir_seek(&lfs, &dir, off) => 0;
} else {
lfsr_dir_seek(&lfs, &dir, off_) => 0;
}
}
// we should be able to keep reading where we left off
for (lfs_size_t i = I; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test dir read has somewhat reasonable behaviour when the dir is modified
[cases.test_dread_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]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
# 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, LFS_M_RDWR, 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;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// make a dir at J
char name[256];
sprintf(name, "%s/dir%03x_", ((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);
if (SEEK >= 2) {
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%03x%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);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_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]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
# 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, LFS_M_RDWR, 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;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// remove the dir at J
char name[256];
sprintf(name, "%s/dir%03x", ((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);
if (SEEK >= 2) {
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%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_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]
# PARENT=0 => no
# PARENT=1 => yes
# PARENT=2 => yes, and rename to new parent
defines.PARENT = [0, 1, 2]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
# 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, LFS_M_RDWR, 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;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// rename the dir at J
char old_name[256];
sprintf(old_name, "%s/dir%03x", ((PARENT) ? "pricklypear" : ""), (int)J);
char new_name[256];
sprintf(new_name, "%s/%smved%03x",
((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);
if (SEEK >= 2) {
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%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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.test_dread_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]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
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, LFS_M_RDWR, 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%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// make a dir at J
char name[256];
sprintf(name, "%s/dir%03x_", ((PARENT) ? "pricklypear" : ""), (int)J);
lfsr_mkdir(&lfs, name) => 0;
// make a dir at K
sprintf(name, "%s/dir%03x_", ((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);
if (SEEK >= 2) {
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%03x%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);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_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]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
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, LFS_M_RDWR, 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;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// remove the dir at J
char name[256];
sprintf(name, "%s/dir%03x", ((PARENT) ? "pricklypear" : ""), (int)J);
lfsr_remove(&lfs, name) => 0;
// remove the dir at K
sprintf(name, "%s/dir%03x", ((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);
if (SEEK >= 2) {
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%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_dread_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]
# PARENT=0 => no
# PARENT=1 => yes
# PARENT=2 => yes, and rename to new parent
defines.PARENT = [0, 1, 2]
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
# 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, LFS_M_RDWR, 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;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// rename the dir at J
char old_name[256];
sprintf(old_name, "%s/dir%03x", ((PARENT) ? "pricklypear" : ""), (int)J);
char new_name[256];
sprintf(new_name, "%s/%smved%03x",
((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%03x", ((PARENT) ? "pricklypear" : ""), (int)K);
sprintf(new_name, "%s/%smved%03x",
((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);
if (SEEK >= 2) {
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%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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;
'''
# test removing the directory we are iterating over
[cases.test_dread_read_rm]
defines.N = 5
# where in the dir read do we remove?
defines.I = 'range(6)'
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "pricklypear/dir%03x", i+1);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "pricklypear") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// remove the directory
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "pricklypear/dir%03x", i+1);
lfsr_remove(&lfs, name) => 0;
}
lfsr_remove(&lfs, "pricklypear") => 0;
// seek after mkdir?
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
if (SEEK >= 2) {
lfsr_dir_rewind(&lfs, &dir) => 0;
}
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// try to read, but this should return an error
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_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# test removing and recreating the directory we are iterating over
[cases.test_dread_read_rm_remkdir]
defines.N = 5
# where in the dir read do we remove?
defines.I = 'range(6)'
# NEIGHBORS&0x2 = left neighbor
# NEIGHBORS&0x1 = right neighbor
defines.NEIGHBORS = [0x0, 0x1, 0x2, 0x3]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_mkdir(&lfs, "pricklypear") => 0;
if (NEIGHBORS & 0x2) {
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
lfsr_mkdir(&lfs, "a_IplRNrPH/a_child") => 0;
}
if (NEIGHBORS & 0x1) {
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH/f_child") => 0;
}
// create our directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "pricklypear/dir%03x", i+1);
lfsr_mkdir(&lfs, name) => 0;
}
// start reading
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "pricklypear") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// read until I
for (lfs_size_t i = 0; i < I; i++) {
char name[256];
sprintf(name, "dir%03x", i+1);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
// remove the directory
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "pricklypear/dir%03x", i+1);
lfsr_remove(&lfs, name) => 0;
}
lfsr_remove(&lfs, "pricklypear") => 0;
// recreate the directory, note this is technically a different
// directory
lfsr_mkdir(&lfs, "pricklypear") => 0;
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "pricklypear/dir%03x", i+1);
lfsr_mkdir(&lfs, name) => 0;
}
// we should have ended up with the same did, which is what makes
// this tricky
lfsr_dir_t dir_;
lfsr_dir_open(&lfs, &dir_, "pricklypear") => 0;
assert(dir.did == dir_.did);
lfsr_dir_close(&lfs, &dir_) => 0;
// seek after mkdir?
if (SEEK) {
lfs_ssize_t off = lfsr_dir_tell(&lfs, &dir);
assert(off >= 0);
if (SEEK >= 2) {
lfsr_dir_rewind(&lfs, &dir) => 0;
}
lfsr_dir_seek(&lfs, &dir, off) => 0;
}
// try to read, but this should return an error
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_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.test_dread_recursive_rm]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.PARENT = [false, true]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
# limit powerloss testing due to time
if = '!TEST_PLS || N <= 16'
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PLS && err == LFS_ERR_EXIST));
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%s/dir%03x", ((PARENT) ? "pricklypear" : ""), i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PLS && 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%03x", ((PARENT) ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%03x", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// 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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
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);
if (SEEK >= 2) {
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%03x", ((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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
# 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.test_dread_recursive_mv]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.BEFORE = [false, true]
# PARENT=0 => no
# PARENT=1 => yes
# PARENT=2 => yes, and rename to new parent
defines.PARENT = [0, 1, 2]
# SEEK=0 => don't seek
# SEEK=1 => seek
# SEEK=2 => rewind then seek
defines.SEEK = [0, 1, 2]
# limit powerloss testing due to time
if = '!TEST_PLS || N <= 16'
reentrant = true
code = '''
// format once per test
lfs_t lfs;
int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG);
if (err) {
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
if (PARENT) {
err = lfsr_mkdir(&lfs, "pricklypear");
assert(!err || (TEST_PLS && err == LFS_ERR_EXIST));
if (PARENT >= 2) {
err = lfsr_mkdir(&lfs, "quiabentia");
assert(!err || (TEST_PLS && 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/0mved000"
: "pricklypear/mved000")
: (PARENT >= 2) ? ((BEFORE)
? "quiabentia/0mved000"
: "quiabentia/mved000")
: ((BEFORE)
? "/0mved000"
: "/mved000")), &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%03x", ((PARENT) ? "pricklypear" : ""), i);
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PLS && 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%03x", ((PARENT) ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "dir%03x", i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%03x", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
}
// 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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0;; i++) {
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);
assert(info.size == 0);
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_PLS && 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);
if (SEEK >= 2) {
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%03x", ((PARENT) ? "pricklypear" : ""), i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT;
sprintf(name, "%s/%smved%03x",
((PARENT == 1) ? "pricklypear"
: (PARENT >= 2) ? "quiabentia"
: ""),
((BEFORE) ? "0" : ""),
i);
lfsr_stat(&lfs, name, &info) => 0;
char name2[256];
sprintf(name2, "%smved%03x", ((BEFORE) ? "0" : ""), i);
assert(strcmp(info.name, name2) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "%smved%03x", ((BEFORE) ? "0" : ""), i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''