From 0ddd851f6f163f8b5c0eeddac43dddca67ddcb96 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 20 Jul 2023 01:41:36 -0500 Subject: [PATCH] Added more dir remove tests These mirror the lfsr_mkdir tests, but backwards. It's interesting to note the rm powerloss testing is much slower than mkdir powerloss testing. This is because the rm tests can make significant backwards progress if power is lost (these tests both make and remove dirs), but mkdir tests always make forward progress (by only making dirs). --- tests/t5_dirs.toml | 860 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 860 insertions(+) diff --git a/tests/t5_dirs.toml b/tests/t5_dirs.toml index af76cd3a..3f1092c9 100644 --- a/tests/t5_dirs.toml +++ b/tests/t5_dirs.toml @@ -2175,6 +2175,866 @@ code = ''' 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', + # this test sort of fights against itself when powerloss testing, + # limit it to a _very_ small number of entries for this reason + '!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', + # this test sort of fights against itself when powerloss testing, + # limit it to a _very_ small number of entries for this reason + '!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', + # this test sort of fights against itself when powerloss testing, + # limit it to a _very_ small number of entries for this reason + '!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; +''' + +# TODO this eventually +# +## Recursive here just refers to deleting entries in a directory while +## iterating over the directory +## +## This is a useful feature, but it's unintuitive that this should have +## well-defined behavior, so make sure to test for it +#[cases.t5_dirs_rm_many_recursive] +#defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] +#defines.PARENT = [false, true] +## this test sort of fights against itself when powerloss testing, +## limit it to a _very_ small number of entries for this reason +#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; +# } +# 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, "/") => 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_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) % 1000; + // insert into our sim + for (lfs_size_t j = 0;; j++) { + if (j >= sim_size || sim[j] >= x) { + // already seen? skip + if (j < sim_size && sim[j] == x) { + goto next; + } + + // 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); + err = lfsr_mkdir(&lfs, name); + assert(!err || (TEST_PL && 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; + } + next:; + } + + // 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; +''' + + + + +