# Many tests already test the internal machinery under powerloss, these # tests are more interested in running the filesystem under # difficult/weird powerloss environments # # Try to keep these below O(n^2), which can be tricky after = [ 'test_mtree', 'test_dirs', 'test_files', 'test_forphans', 'test_traversal', 'test_gc', 'test_mount', 'test_ck', 'test_compat', ] # Create many dirs under powerloss # # We always make progress so this should be O(n) progs, (but maybe # O(n^2) reads) # [cases.test_powerloss_spam_dir_many] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_ATOMIC', 'LFS_EMUBD_POWERLOSS_SOMEBITS', 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] defines.MKCONSISTENT = [false, true] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); if (err) { lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfsr_fs_mkconsistent(&lfs) => 0; } // make this many directories for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); err = lfsr_mkdir(&lfs, name); assert(!err || (TEST_PLS && err == LFS_ERR_EXIST)); } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // grm should be zero here assert(lfs.grm_p[0] == 0); // check that our mkdir worked for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", i); struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_DIR); assert(info.size == 0); } 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); 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; for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "dir%03x", 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); 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; ''' # Create many files under powerloss # # We always make progress so this should be O(n) progs, (but maybe # O(n^2) reads) # [cases.test_powerloss_spam_file_many] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_ATOMIC', 'LFS_EMUBD_POWERLOSS_SOMEBITS', 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] defines.MKCONSISTENT = [false, true] # inlining has a tendency to hide sync issues, so try without defines.SHRUB_SIZE = ['BLOCK_SIZE/4', '0'] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FILE_BUFFER_SIZE/2', '2*FILE_BUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] if = '(SIZE*N)/BLOCK_SIZE <= 32' reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); if (err) { lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfsr_fs_mkconsistent(&lfs) => 0; } // create this many files uint32_t prng = 42; for (lfs_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "amethyst%03x", i); uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_t file; err = lfsr_file_open(&lfs, &file, name, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL); assert(!err || (TEST_PLS && err == LFS_ERR_EXIST)); if (!err) { lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; } } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // check that our writes worked prng = 42; for (lfs_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "amethyst%03x", i); struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // try reading the file, note we reset prng above uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_t file; uint8_t rbuf[SIZE]; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; } } lfsr_unmount(&lfs) => 0; ''' # A general purpose powerloss fuzz test # # Under powerloss, we can't really keep track of a sim reliably/ # efficiently, instead just do random operations, store a counter in a # special file so we know how much progress has been made, and hope for # the best. Most likely an internal assert will trigger if anything goes # wrong. # [cases.test_powerloss_spam_f_pl_fuzz] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_ATOMIC', 'LFS_EMUBD_POWERLOSS_SOMEBITS', 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] defines.MKCONSISTENT = [false, true] # inlining has a tendency to hide sync issues, so try without defines.SHRUB_SIZE = ['BLOCK_SIZE/4', '0'] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = 256 defines.SIZE = [ '0', 'FILE_BUFFER_SIZE/2', '2*FILE_BUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 'range(10)' fuzz = 'SEED' if = '(SIZE*N)/BLOCK_SIZE <= 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, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfsr_fs_mkconsistent(&lfs) => 0; } // keep some test state on disk to survive powerloss typedef struct fuzz_state { lfs_size_t i; uint32_t prng; } fuzz_state_t; fuzz_state_t state = {.i = 0, .prng = SEED}; lfsr_file_t state_file; lfsr_file_open(&lfs, &state_file, "state", LFS_O_RDWR | LFS_O_CREAT) => 0; lfs_ssize_t d = lfsr_file_read(&lfs, &state_file, &state, sizeof(state)); assert(d == 0 || d == sizeof(state)); // keep test files in a separate directory err = lfsr_mkdir(&lfs, "test"); assert(!err || err == LFS_ERR_EXIST); uint32_t prng = state.prng; for (lfs_size_t i = state.i; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 3; // how many files do we have? lfs_size_t count = 0; lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "test") => 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); while (true) { int err = lfsr_dir_read(&lfs, &dir, &info); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { break; } assert(strlen(info.name) == strlen("amethyst...")); assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); count++; } lfsr_dir_close(&lfs, &dir) => 0; // creating a new file? if (op == 0 || count == 0) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; uint32_t wprng = TEST_PRNG(&prng); // create a file here char name[256]; sprintf(name, "test/amethyst%03x", x); uint8_t wbuf[SIZE]; uint8_t ck = 0; for (lfs_size_t j = 0; j < SIZE-1; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); ck = (ck + (wbuf[j] - 'a')) % 26; } // make the sum equal to 'a' mod 26 if (SIZE > 0) { wbuf[SIZE-1] = 'a' + ((26 - ck) % 26); } lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; // deleting a file? } else if (op == 1) { // choose a random file to delete lfs_size_t j = TEST_PRNG(&prng) % count; // find the file lfsr_dir_open(&lfs, &dir, "test") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= j; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // delete this file char name[256]; assert(strlen(info.name) == strlen("amethyst...")); sprintf(name, "test/%s", info.name); lfsr_remove(&lfs, name) => 0; // renaming a file? } else { // choose a random file to rename, and a random number to // rename to lfs_size_t j = TEST_PRNG(&prng) % count; lfs_size_t y = TEST_PRNG(&prng) % N; // find the file lfsr_dir_open(&lfs, &dir, "test") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= j; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // rename this file char old_name[256]; assert(strlen(info.name) == strlen("amethyst...")); sprintf(old_name, "test/%s", info.name); char new_name[256]; sprintf(new_name, "test/amethyst%03x", y); lfsr_rename(&lfs, old_name, new_name) => 0; } // update our state file state.i = i; state.prng = prng; lfsr_file_rewind(&lfs, &state_file) => 0; lfsr_file_write(&lfs, &state_file, &state, sizeof(state)) => sizeof(state); lfsr_file_sync(&lfs, &state_file) => 0; } // go ahead and close our state file in case we remount lfsr_file_close(&lfs, &state_file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // check that things look more-or-less ok lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "test") => 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); while (true) { int err = lfsr_dir_read(&lfs, &dir, &info); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { break; } assert(strlen(info.name) == strlen("amethyst...")); assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); // at least try to read the files char name[256]; sprintf(name, "test/%s", info.name); lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; // all data should be lowercase ascii for (lfs_size_t j = 0; j < SIZE; j++) { assert(rbuf[j] >= 'a' && rbuf[j] <= 'z'); } // sum should be equal to 'a' mod 26 uint8_t ck = 0; for (lfs_size_t j = 0; j < SIZE; j++) { ck = (ck + (rbuf[j] - 'a')) % 26; } assert(ck == 0); lfsr_file_close(&lfs, &file) => 0; } lfsr_dir_close(&lfs, &dir) => 0; } lfsr_unmount(&lfs) => 0; ''' # A general purpose powerloss fuzz test, with directories! # # Under powerloss, we can't really keep track of a sim reliably/ # efficiently, instead just do random operations, store a counter in a # special file so we know how much progress has been made, and hope for # the best. Most likely an internal assert will trigger if anything goes # wrong. # [cases.test_powerloss_spam_fd_pl_fuzz] defines.POWERLOSS_BEHAVIOR = [ 'LFS_EMUBD_POWERLOSS_ATOMIC', 'LFS_EMUBD_POWERLOSS_SOMEBITS', 'LFS_EMUBD_POWERLOSS_MOSTBITS', 'LFS_EMUBD_POWERLOSS_OOO', ] defines.MKCONSISTENT = [false, true] # inlining has a tendency to hide sync issues, so try without defines.SHRUB_SIZE = ['BLOCK_SIZE/4', '0'] # note dirs x files grows O(n^2) defines.N = [1, 2, 4, 8] defines.M = 'N' defines.OPS = 256 defines.SIZE = [ '0', 'FILE_BUFFER_SIZE/2', '2*FILE_BUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 'range(10)' fuzz = 'SEED' if = '(SIZE*N)/BLOCK_SIZE <= 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, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfsr_fs_mkconsistent(&lfs) => 0; } // keep some test state on disk to survive powerloss typedef struct fuzz_state { lfs_size_t i; uint32_t prng; } fuzz_state_t; fuzz_state_t state = {.i = 0, .prng = SEED}; lfsr_file_t state_file; lfsr_file_open(&lfs, &state_file, "state", LFS_O_RDWR | LFS_O_CREAT) => 0; lfs_ssize_t d = lfsr_file_read(&lfs, &state_file, &state, sizeof(state)); assert(d == 0 || d == sizeof(state)); // keep test files in a separate directory err = lfsr_mkdir(&lfs, "test"); assert(!err || err == LFS_ERR_EXIST); uint32_t prng = state.prng; for (lfs_size_t i = state.i; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 6; // how many dirs do we have? lfs_size_t dir_count = 0; lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "test") => 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); while (true) { int err = lfsr_dir_read(&lfs, &dir, &info); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { break; } assert(strlen(info.name) == strlen("quartz...")); assert(memcmp(info.name, "quartz", strlen("quartz")) == 0); assert(info.type == LFS_TYPE_DIR); assert(info.size == 0); dir_count++; } lfsr_dir_close(&lfs, &dir) => 0; // dir op? if (op < 3 || dir_count == 0) { // creating a new dir? if (op == 0 || dir_count == 0) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; // create a dir here char name[256]; sprintf(name, "test/quartz%03x", x); int err = lfsr_mkdir(&lfs, name); assert(!err || err == LFS_ERR_EXIST); // deleting a dir? } else if (op == 1) { // choose a random dir to delete lfs_size_t j = TEST_PRNG(&prng) % dir_count; // find the dir lfsr_dir_open(&lfs, &dir, "test") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= j; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // try to delete this dir, ignore non-empty dirs! char name[256]; assert(strlen(info.name) == strlen("quartz...")); sprintf(name, "test/%s", info.name); int err = lfsr_remove(&lfs, name); assert(!err || err == LFS_ERR_NOTEMPTY); // renaming a dir? } else { // choose a random dir to rename, and a random number to // rename to lfs_size_t j = TEST_PRNG(&prng) % dir_count; lfs_size_t y = TEST_PRNG(&prng) % N; // find the dir lfsr_dir_open(&lfs, &dir, "test") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= j; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // rename this dir, ignore conflicts! char old_name[256]; assert(strlen(info.name) == strlen("quartz...")); sprintf(old_name, "test/%s", info.name); char new_name[256]; sprintf(new_name, "test/quartz%03x", y); int err = lfsr_rename(&lfs, old_name, new_name); assert(!err || err == LFS_ERR_NOTEMPTY); } // file op? } else { // choose a pseudo-random dir lfs_size_t dir_i = TEST_PRNG(&prng) % dir_count; // find the dir lfsr_dir_open(&lfs, &dir, "test") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= dir_i; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; char dir_path[256]; sprintf(dir_path, "test/%s", info.name); // how many files do we have? lfs_size_t count = 0; lfsr_dir_open(&lfs, &dir, dir_path) => 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); while (true) { int err = lfsr_dir_read(&lfs, &dir, &info); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { break; } assert(strlen(info.name) == strlen("amethyst...")); assert(memcmp( info.name, "amethyst", strlen("amethyst")) == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); count++; } lfsr_dir_close(&lfs, &dir) => 0; // creating a new file? if (op == 3 || count == 0) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % M; uint32_t wprng = TEST_PRNG(&prng); // create a file here char name[256]; sprintf(name, "%s/amethyst%03x", dir_path, x); uint8_t wbuf[SIZE]; uint8_t ck = 0; for (lfs_size_t j = 0; j < SIZE-1; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); ck = (ck + (wbuf[j] - 'a')) % 26; } // make the sum equal to 'a' mod 26 if (SIZE > 0) { wbuf[SIZE-1] = 'a' + ((26 - ck) % 26); } lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; // deleting a file? } else if (op == 4) { // choose a random file to delete lfs_size_t j = TEST_PRNG(&prng) % count; // find the file lfsr_dir_open(&lfs, &dir, dir_path) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= j; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // delete this file char name[256]; assert(strlen(info.name) == strlen("amethyst...")); sprintf(name, "%s/%s", dir_path, info.name); lfsr_remove(&lfs, name) => 0; // renaming a file? } else { // choose a random file to rename lfs_size_t j = TEST_PRNG(&prng) % count; // find the file lfsr_dir_open(&lfs, &dir, dir_path) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; for (lfs_size_t k = 0; k <= j; k++) { lfsr_dir_read(&lfs, &dir, &info) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // choose a random dir to rename to lfs_size_t dir_j = TEST_PRNG(&prng) % dir_count; // find the dir struct lfs_info info_; lfsr_dir_open(&lfs, &dir, "test") => 0; lfsr_dir_read(&lfs, &dir, &info_) => 0; lfsr_dir_read(&lfs, &dir, &info_) => 0; for (lfs_size_t k = 0; k <= dir_j; k++) { lfsr_dir_read(&lfs, &dir, &info_) => 0; } lfsr_dir_close(&lfs, &dir) => 0; // choose a random file to rename to lfs_size_t y = TEST_PRNG(&prng) % M; // rename this file char old_name[256]; assert(strlen(info.name) == strlen("amethyst...")); sprintf(old_name, "%s/%s", dir_path, info.name); char new_name[256]; sprintf(new_name, "test/%s/amethyst%03x", info_.name, y); lfsr_rename(&lfs, old_name, new_name) => 0; } } // update our state file state.i = i; state.prng = prng; lfsr_file_rewind(&lfs, &state_file) => 0; lfsr_file_write(&lfs, &state_file, &state, sizeof(state)) => sizeof(state); lfsr_file_sync(&lfs, &state_file) => 0; } // go ahead and close our state file in case we remount lfsr_file_close(&lfs, &state_file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // check that things look more-or-less ok lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "test") => 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); while (true) { int err = lfsr_dir_read(&lfs, &dir, &info); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { break; } assert(strlen(info.name) == strlen("quartz...")); assert(memcmp(info.name, "quartz", strlen("quartz")) == 0); assert(info.type == LFS_TYPE_DIR); assert(info.size == 0); // check that our dirs look more-or-less ok char name[256]; sprintf(name, "test/%s", info.name); 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); 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); while (true) { err = lfsr_dir_read(&lfs, &dir_, &info_); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { break; } assert(strlen(info_.name) == strlen("amethyst...")); assert(memcmp( info_.name, "amethyst", strlen("amethyst")) == 0); assert(info_.type == LFS_TYPE_REG); assert(info_.size == SIZE); // at least try to read the files sprintf(name, "test/%s/%s", info.name, info_.name); lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; // all data should be lowercase ascii for (lfs_size_t j = 0; j < SIZE; j++) { assert(rbuf[j] >= 'a' && rbuf[j] <= 'z'); } // sum should be equal to 'a' mod 26 uint8_t ck = 0; for (lfs_size_t j = 0; j < SIZE; j++) { ck = (ck + (rbuf[j] - 'a')) % 26; } assert(ck == 0); lfsr_file_close(&lfs, &file) => 0; } lfsr_dir_close(&lfs, &dir_) => 0; } lfsr_dir_close(&lfs, &dir) => 0; } lfsr_unmount(&lfs) => 0; ''' ## There are already a number of tests that test general operations under ## power-loss (see the reentrant attribute). These tests are for explicitly ## testing specific corner cases. # ## only a revision count #[cases.test_powerloss_only_rev] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "notebook") => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "notebook/paper", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; # char buffer[256]; # strcpy(buffer, "hello"); # lfs_size_t size = strlen("hello"); # for (int i = 0; i < 5; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_sync(&lfs, &file) => 0; # } # lfs_file_close(&lfs, &file) => 0; # # char rbuffer[256]; # lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0; # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // get pair/rev count # lfs_mount(&lfs, cfg) => 0; # lfs_dir_t dir; # lfs_dir_open(&lfs, &dir, "notebook") => 0; # lfs_block_t pair[2] = {dir.m.pair[0], dir.m.pair[1]}; # uint32_t rev = dir.m.rev; # lfs_dir_close(&lfs, &dir) => 0; # lfs_unmount(&lfs) => 0; # # // write just the revision count # uint8_t bbuffer[BLOCK_SIZE]; # cfg->read(cfg, pair[1], 0, bbuffer, BLOCK_SIZE) => 0; # # memcpy(bbuffer, &(uint32_t){lfs_tole32(rev+1)}, sizeof(uint32_t)); # # cfg->erase(cfg, pair[1]) => 0; # cfg->prog(cfg, pair[1], 0, bbuffer, BLOCK_SIZE) => 0; # # lfs_mount(&lfs, cfg) => 0; # # // can read? # lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0; # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # # // can write? # lfs_file_open(&lfs, &file, "notebook/paper", # LFS_O_WRONLY | LFS_O_APPEND) => 0; # strcpy(buffer, "goodbye"); # size = strlen("goodbye"); # for (int i = 0; i < 5; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_sync(&lfs, &file) => 0; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0; # strcpy(buffer, "hello"); # size = strlen("hello"); # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # strcpy(buffer, "goodbye"); # size = strlen("goodbye"); # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; #''' # ## partial prog, may not be byte in order! #[cases.test_powerloss_partial_prog] #if = "PROG_SIZE < BLOCK_SIZE" #defines.BYTE_OFF = ["0", "PROG_SIZE-1", "PROG_SIZE/2"] #defines.BYTE_VALUE = [0x33, 0xcc] #in = "lfs.c" #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "notebook") => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "notebook/paper", # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; # char buffer[256]; # strcpy(buffer, "hello"); # lfs_size_t size = strlen("hello"); # for (int i = 0; i < 5; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_sync(&lfs, &file) => 0; # } # lfs_file_close(&lfs, &file) => 0; # # char rbuffer[256]; # lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0; # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # // imitate a partial prog, value should not matter, if littlefs # // doesn't notice the partial prog testbd will assert # # // get offset to next prog # lfs_mount(&lfs, cfg) => 0; # lfs_dir_t dir; # lfs_dir_open(&lfs, &dir, "notebook") => 0; # lfs_block_t block = dir.m.pair[0]; # lfs_off_t off = dir.m.off; # lfs_dir_close(&lfs, &dir) => 0; # lfs_unmount(&lfs) => 0; # # // tweak byte # uint8_t bbuffer[BLOCK_SIZE]; # cfg->read(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0; # # bbuffer[off + BYTE_OFF] = BYTE_VALUE; # # cfg->erase(cfg, block) => 0; # cfg->prog(cfg, block, 0, bbuffer, BLOCK_SIZE) => 0; # # lfs_mount(&lfs, cfg) => 0; # # // can read? # lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0; # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # # // can write? # lfs_file_open(&lfs, &file, "notebook/paper", # LFS_O_WRONLY | LFS_O_APPEND) => 0; # strcpy(buffer, "goodbye"); # size = strlen("goodbye"); # for (int i = 0; i < 5; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_sync(&lfs, &file) => 0; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_file_open(&lfs, &file, "notebook/paper", LFS_O_RDONLY) => 0; # strcpy(buffer, "hello"); # size = strlen("hello"); # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # strcpy(buffer, "goodbye"); # size = strlen("goodbye"); # for (int i = 0; i < 5; i++) { # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; #'''