Added explicit test over directory ordering

It turned out the previous version had a subtle ordering bug when names
where the same length that went unnoticed for years. And at this point
is probably baked into the on-disk format permanently.

This redesign, with a named-ordered btree, relies quite a bit more on
name ordering, so it's unlikely the same mistake would make it through
without breaking something. And sure enough this bug was unintentionally
fixed at some point.

But still, better safe than sorry. Added tests over character ordering
and length ordering. Open to more ordering tests in the future.

Found by andriyndev
This commit is contained in:
Christopher Haster
2024-01-28 00:32:22 -06:00
parent 40b926b947
commit 15cd1d29e0
+224
View File
@@ -1396,6 +1396,230 @@ code = '''
'''
# test some common ordering mistakes
[cases.test_dirs_ordering]
# ORDER=0 => inorder
# ORDER=1 => reversed
# ORDER=2 => random
defines.ORDER = [0, 1, 2]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
if (ORDER == 0) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "c") => 0;
lfsr_mkdir(&lfs, "d") => 0;
lfsr_mkdir(&lfs, "e") => 0;
lfsr_mkdir(&lfs, "f") => 0;
} else if (ORDER == 1) {
lfsr_mkdir(&lfs, "f") => 0;
lfsr_mkdir(&lfs, "e") => 0;
lfsr_mkdir(&lfs, "d") => 0;
lfsr_mkdir(&lfs, "c") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "a") => 0;
} else if (ORDER == 2) {
// "random", trust me
lfsr_mkdir(&lfs, "e") => 0;
lfsr_mkdir(&lfs, "c") => 0;
lfsr_mkdir(&lfs, "b") => 0;
lfsr_mkdir(&lfs, "d") => 0;
lfsr_mkdir(&lfs, "f") => 0;
lfsr_mkdir(&lfs, "a") => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
// grm should be zero here
assert(lfs.grm_g[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "a", &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "c", &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "d", &info) => 0;
assert(strcmp(info.name, "d") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "e", &info) => 0;
assert(strcmp(info.name, "e") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "f", &info) => 0;
assert(strcmp(info.name, "f") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// check the directory order
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "d") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "e") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "f") == 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_dirs_ordering_length]
# ORDER=0 => inorder
# ORDER=1 => reversed
# ORDER=2 => random
defines.ORDER = [0, 1, 2]
defines.REMOUNT = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, CFG) => 0;
lfsr_mount(&lfs, CFG) => 0;
if (ORDER == 0) {
lfsr_mkdir(&lfs, "a") => 0;
lfsr_mkdir(&lfs, "aa") => 0;
lfsr_mkdir(&lfs, "aaa") => 0;
lfsr_mkdir(&lfs, "aaaa") => 0;
lfsr_mkdir(&lfs, "aaaaa") => 0;
lfsr_mkdir(&lfs, "aaaaaa") => 0;
} else if (ORDER == 1) {
lfsr_mkdir(&lfs, "aaaaaa") => 0;
lfsr_mkdir(&lfs, "aaaaa") => 0;
lfsr_mkdir(&lfs, "aaaa") => 0;
lfsr_mkdir(&lfs, "aaa") => 0;
lfsr_mkdir(&lfs, "aa") => 0;
lfsr_mkdir(&lfs, "a") => 0;
} else if (ORDER == 2) {
// "random", trust me
lfsr_mkdir(&lfs, "aaaaa") => 0;
lfsr_mkdir(&lfs, "aaa") => 0;
lfsr_mkdir(&lfs, "aa") => 0;
lfsr_mkdir(&lfs, "aaaa") => 0;
lfsr_mkdir(&lfs, "aaaaaa") => 0;
lfsr_mkdir(&lfs, "a") => 0;
}
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, CFG) => 0;
// grm should be zero here
assert(lfs.grm_g[0] == 0);
}
// check that our mkdirs worked
struct lfs_info info;
lfsr_stat(&lfs, "a", &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "aa", &info) => 0;
assert(strcmp(info.name, "aa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "aaa", &info) => 0;
assert(strcmp(info.name, "aaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "aaaa", &info) => 0;
assert(strcmp(info.name, "aaaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "aaaaa", &info) => 0;
assert(strcmp(info.name, "aaaaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_stat(&lfs, "aaaaaa", &info) => 0;
assert(strcmp(info.name, "aaaaaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
// check the directory order
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "aa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "aaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "aaaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "aaaaa") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "aaaaaa") == 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 that did collisions don't cause issues
[cases.test_dirs_did_collisions]
defines.REMOUNT = [false, true]