# Tests over block relocations and wear-leveling after = [ 'test_mtree', 'test_dirs', 'test_files', 'test_forphans', ] # Note that most of the delicate relocation operations are already tested # in test_mtree. This mostly just covers high-level operations with # relatively aggressive wear-leveling. # dirs + relocations may create problems for gstate [cases.test_relocations_dir_fuzz] defines.BLOCK_RECYCLES = [-1, 5, 1, 0] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256] defines.OPS = 1024 defines.REMOUNT = [false, true] defines.SEED = 'range(10)' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // 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 < OPS; i++) { // choose a pseudo-random op, either mkdir, remove, or rename uint8_t op = TEST_PRNG(&prng) % 3; if (op == 0 || sim_size == 0) { // choose a pseudo-random number, truncate to 3 hexadecimals lfs_size_t x = TEST_PRNG(&prng) % N; // insert into our sim for (lfs_size_t j = 0;; j++) { if (j >= sim_size || sim[j] >= x) { // already seen? if (j < sim_size && sim[j] == x) { // do nothing } else { // insert memmove(&sim[j+1], &sim[j], (sim_size-j)*sizeof(lfs_size_t)); sim_size += 1; sim[j] = x; } break; } } // create a directory here char name[256]; sprintf(name, "dir%03x", x); int err = lfsr_mkdir(&lfs, name); assert(!err || err == LFS_ERR_EXIST); } else if (op == 1) { // choose a pseudo-random entry to delete lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); sim_size -= 1; // remove this directory char name[256]; sprintf(name, "dir%03x", x); lfsr_remove(&lfs, name) => 0; } else { // choose a pseudo-random entry to rename, and a pseudo-random // number to rename to lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // already seen and not a noop? if (k < sim_size && sim[k] == y && x != y) { // just delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); sim_size -= 1; } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); sim[k] = y; } break; } } // rename this directory char old_name[256]; sprintf(old_name, "dir%03x", x); char new_name[256]; sprintf(new_name, "dir%03x", y); lfsr_rename(&lfs, old_name, new_name) => 0; } } // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; // grm should be zero here assert(lfs.grm_p[0] == 0); } // test that our directories match our simulation for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "dir%03x", sim[j]); struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; char name2[256]; sprintf(name2, "dir%03x", sim[j]); 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, "/") => 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 j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "dir%03x", sim[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; // clean up sim/lfs free(sim); lfsr_unmount(&lfs) => 0; ''' # files + relocations may create problems for shrubs [cases.test_relocations_file_fuzz] defines.BLOCK_RECYCLES = [-1, 5, 1, 0] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = 1024 defines.SIZE = [ '0', 'FBUFFER_SIZE/2', '2*FBUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.REMOUNT = [false, true] defines.SEED = 'range(10)' if = '(SIZE*N)/BLOCK_SIZE <= 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); lfs_size_t sim_size = 0; uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 3; // creating a new file? if (op == 0 || sim_size == 0) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; // associate each file with a prng that generates its contents uint32_t wprng = TEST_PRNG(&prng); // insert into our sim for (lfs_size_t j = 0;; j++) { if (j >= sim_size || sim[j] >= x) { // already seen? if (j < sim_size && sim[j] == x) { // new prng sim_prngs[j] = wprng; } else { // insert memmove(&sim[j+1], &sim[j], (sim_size-j)*sizeof(lfs_size_t)); memmove(&sim_prngs[j+1], &sim_prngs[j], (sim_size-j)*sizeof(uint32_t)); sim_size += 1; sim[j] = x; sim_prngs[j] = wprng; } break; } } // create a file here char name[256]; sprintf(name, "amethyst%03x", x); uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 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) % 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)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); sim_size -= 1; // delete this file char name[256]; sprintf(name, "amethyst%03x", x); 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) % sim_size; lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng sim_prngs[k] = wprng; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); sim[k] = y; sim_prngs[k] = wprng; } break; } } // rename this file char old_name[256]; sprintf(old_name, "amethyst%03x", x); char new_name[256]; sprintf(new_name, "amethyst%03x", y); lfsr_rename(&lfs, old_name, new_name) => 0; } } // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, CFG) => 0; } // check that our files match our simulation for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "amethyst%03x", sim[j]); 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); } 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 j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "amethyst%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // check the file contents for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "amethyst%03x", sim[j]); lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; } // clean up sim/lfs free(sim); free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' # open files + relocations may create problems for orphans/zombies [cases.test_relocations_orphanzombie_fuzz] defines.BLOCK_RECYCLES = [-1, 5, 1, 0] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = 1024 defines.SIZE = [ '0', 'FBUFFER_SIZE/2', '2*FBUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 'range(10)' if = '(SIZE*N)/BLOCK_SIZE <= 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; bool orphan; bool zombie; uint32_t prng; lfsr_file_t file; } sim_file_t; sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); lfs_size_t sim_file_count = 0; uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { nonsense:; // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 5; // open a new file? if (op == 0) { if (sim_file_count >= N) { goto nonsense; } // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? bool orphan = true; uint32_t wprng = 0; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { orphan = false; wprng = sim_prngs[j]; break; } } // choose a random seed if we don't exist if (orphan) { wprng = TEST_PRNG(&prng); } // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); sim_files[j]->x = x; sim_files[j]->orphan = orphan; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; // open the actual file char name[256]; sprintf(name, "batman%03x", x); lfsr_file_open(&lfs, &sim_files[j]->file, name, LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist if (orphan) { uint8_t wbuf[SIZE]; for (lfs_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs_size_t x = sim_files[j]->x; // choose a random seed uint32_t wprng = TEST_PRNG(&prng); // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { // insert into our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { // new prng sim_prngs[k] = wprng; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); sim_size += 1; sim[k] = x; sim_prngs[k] = wprng; } break; } } // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { sim_files[k]->orphan = false; sim_files[k]->prng = wprng; } } } // write to the file lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; for (lfs_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; lfsr_file_sync(&lfs, &sim_files[j]->file) => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; // close a file? } else if (op == 2) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; // remove a file? } else if (op == 3) { if (sim_size == 0) { goto nonsense; } // choose a random file 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)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); sim_size -= 1; // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // delete this file char name[256]; sprintf(name, "batman%03x", x); lfsr_remove(&lfs, name) => 0; // rename a file? } else if (op == 4) { if (sim_size == 0) { goto nonsense; } // choose a random file to rename, and a random number to // rename to lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng sim_prngs[k] = wprng; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); sim[k] = y; sim_prngs[k] = wprng; } break; } } // update any related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { // move source files if (sim_files[k]->x == x) { sim_files[k]->x = y; // mark target files as zombied } else if (sim_files[k]->x == y) { sim_files[k]->zombie = true; } } // rename this file char old_name[256]; sprintf(old_name, "batman%03x", x); char new_name[256]; sprintf(new_name, "batman%03x", y); lfsr_rename(&lfs, old_name, new_name) => 0; } } // check that disk matches our simulation for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); 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); } 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 j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; } // check that our file handles match our simulation for (lfs_size_t j = 0; j < sim_file_count; j++) { uint32_t wprng = sim_files[j]->prng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // clean up sim/lfs free(sim); free(sim_prngs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfsr_unmount(&lfs) => 0; ''' # open files + dirs + relocations can cause so many problems it's not worth # listing them [cases.test_relocations_orphanzombiedir_fuzz] defines.BLOCK_RECYCLES = [-1, 5, 1, 0] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = 1024 defines.SIZE = [ '0', 'FBUFFER_SIZE/2', '2*FBUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 'range(10)' if = '(SIZE*N)/BLOCK_SIZE <= 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; bool orphan; bool zombie; uint32_t prng; lfsr_file_t file; } sim_file_t; sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); lfs_size_t sim_file_count = 0; uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { nonsense:; // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 8; // open a new file? if (op == 0) { if (sim_file_count >= N) { goto nonsense; } // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? bool orphan = true; uint32_t wprng = 0; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } orphan = false; wprng = sim_prngs[j]; break; } } // choose a random seed if we don't exist if (orphan) { wprng = TEST_PRNG(&prng); } // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); sim_files[j]->x = x; sim_files[j]->orphan = orphan; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; // open the actual file char name[256]; sprintf(name, "batman%03x", x); lfsr_file_open(&lfs, &sim_files[j]->file, name, LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist if (orphan) { uint8_t wbuf[SIZE]; for (lfs_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs_size_t x = sim_files[j]->x; // choose a random seed uint32_t wprng = TEST_PRNG(&prng); // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { // insert into our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { // new prng sim_prngs[k] = wprng; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = wprng; sim_isdirs[k] = false; } break; } } // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { sim_files[k]->orphan = false; sim_files[k]->prng = wprng; } } } // write to the file lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; for (lfs_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; lfsr_file_sync(&lfs, &sim_files[j]->file) => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; // close a file? } else if (op == 2) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; // remove a file? } else if (op == 3) { if (sim_size == 0) { goto nonsense; } // choose a random file 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)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // delete this file char name[256]; sprintf(name, "batman%03x", x); lfsr_remove(&lfs, name) => 0; // rename a file? } else if (op == 4) { if (sim_size == 0) { goto nonsense; } // choose a random file to rename, and a random number to // rename to lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; bool isdir = sim_isdirs[j]; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // type mismatch? if (sim_isdirs[k] != isdir) { goto nonsense; } // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng sim_prngs[k] = wprng; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; sim_isdirs[k] = isdir; } break; } } // update any related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { // move source files if (sim_files[k]->x == x) { sim_files[k]->x = y; // mark target files as zombied } else if (sim_files[k]->x == y) { sim_files[k]->zombie = true; } } // rename this file char old_name[256]; sprintf(old_name, "batman%03x", x); char new_name[256]; sprintf(new_name, "batman%03x", y); lfsr_rename(&lfs, old_name, new_name) => 0; // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = 0; sim_isdirs[k] = true; } break; } } // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // make the directory char name[256]; sprintf(name, "batman%03x", x); lfsr_mkdir(&lfs, name) => 0; } } // check that disk matches our simulation for (lfs_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); } } 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 j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim_isdirs[j]) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; } else { char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; } } // check that our file handles match our simulation for (lfs_size_t j = 0; j < sim_file_count; j++) { uint32_t wprng = sim_files[j]->prng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // clean up sim/lfs free(sim); free(sim_prngs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfsr_unmount(&lfs) => 0; ''' # and don't forget potential powerloss problems # # 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_relocations_pl_fuzz] defines.BLOCK_RECYCLES = [-1, 5, 1, 0] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.OPS = 256 defines.SIZE = [ '0', 'FBUFFER_SIZE/2', '2*FBUFFER_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.REMOUNT = [false, true] defines.SEED = 'range(10)' if = '(SIZE*N)/BLOCK_SIZE <= 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; } // 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; // remount? if (REMOUNT) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, 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; // 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; ''' ## specific corner cases worth explicitly testing for #[cases.test_relocations_dangling_split_dir] #defines.ITERATIONS = 20 #defines.COUNT = 10 #defines.BLOCK_CYCLES = [8, 1] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # // fill up filesystem so only ~16 blocks are left # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0; # uint8_t buffer[512]; # memset(buffer, 0, 512); # while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { # lfs_file_write(&lfs, &file, buffer, 512) => 512; # } # lfs_file_close(&lfs, &file) => 0; # // make a child dir to use in bounded space # lfs_mkdir(&lfs, "child") => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (unsigned j = 0; j < ITERATIONS; j++) { # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; # lfs_file_close(&lfs, &file) => 0; # } # # lfs_dir_t dir; # struct lfs_info info; # lfs_dir_open(&lfs, &dir, "child") => 0; # lfs_dir_read(&lfs, &dir, &info) => 1; # lfs_dir_read(&lfs, &dir, &info) => 1; # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "test%03d_loooooooooooooooooong_name", i); # lfs_dir_read(&lfs, &dir, &info) => 1; # strcmp(info.name, path) => 0; # } # lfs_dir_read(&lfs, &dir, &info) => 0; # lfs_dir_close(&lfs, &dir) => 0; # # if (j == (unsigned)ITERATIONS-1) { # break; # } # # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_remove(&lfs, path) => 0; # } # } # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_dir_t dir; # struct lfs_info info; # lfs_dir_open(&lfs, &dir, "child") => 0; # lfs_dir_read(&lfs, &dir, &info) => 1; # lfs_dir_read(&lfs, &dir, &info) => 1; # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "test%03d_loooooooooooooooooong_name", i); # lfs_dir_read(&lfs, &dir, &info) => 1; # strcmp(info.name, path) => 0; # } # lfs_dir_read(&lfs, &dir, &info) => 0; # lfs_dir_close(&lfs, &dir) => 0; # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_remove(&lfs, path) => 0; # } # lfs_unmount(&lfs) => 0; #''' # #[cases.test_relocations_outdated_head] #defines.ITERATIONS = 20 #defines.COUNT = 10 #defines.BLOCK_CYCLES = [8, 1] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # // fill up filesystem so only ~16 blocks are left # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0; # uint8_t buffer[512]; # memset(buffer, 0, 512); # while (BLOCK_COUNT - lfs_fs_size(&lfs) > 16) { # lfs_file_write(&lfs, &file, buffer, 512) => 512; # } # lfs_file_close(&lfs, &file) => 0; # // make a child dir to use in bounded space # lfs_mkdir(&lfs, "child") => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (unsigned j = 0; j < ITERATIONS; j++) { # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0; # lfs_file_close(&lfs, &file) => 0; # } # # lfs_dir_t dir; # struct lfs_info info; # lfs_dir_open(&lfs, &dir, "child") => 0; # lfs_dir_read(&lfs, &dir, &info) => 1; # lfs_dir_read(&lfs, &dir, &info) => 1; # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "test%03d_loooooooooooooooooong_name", i); # lfs_dir_read(&lfs, &dir, &info) => 1; # strcmp(info.name, path) => 0; # info.size => 0; # # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0; # lfs_file_write(&lfs, &file, "hi", 2) => 2; # lfs_file_close(&lfs, &file) => 0; # } # lfs_dir_read(&lfs, &dir, &info) => 0; # # lfs_dir_rewind(&lfs, &dir) => 0; # lfs_dir_read(&lfs, &dir, &info) => 1; # lfs_dir_read(&lfs, &dir, &info) => 1; # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "test%03d_loooooooooooooooooong_name", i); # lfs_dir_read(&lfs, &dir, &info) => 1; # strcmp(info.name, path) => 0; # info.size => 2; # # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0; # lfs_file_write(&lfs, &file, "hi", 2) => 2; # lfs_file_close(&lfs, &file) => 0; # } # lfs_dir_read(&lfs, &dir, &info) => 0; # # lfs_dir_rewind(&lfs, &dir) => 0; # lfs_dir_read(&lfs, &dir, &info) => 1; # lfs_dir_read(&lfs, &dir, &info) => 1; # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "test%03d_loooooooooooooooooong_name", i); # lfs_dir_read(&lfs, &dir, &info) => 1; # strcmp(info.name, path) => 0; # info.size => 2; # } # lfs_dir_read(&lfs, &dir, &info) => 0; # lfs_dir_close(&lfs, &dir) => 0; # # for (unsigned i = 0; i < COUNT; i++) { # char path[1024]; # sprintf(path, "child/test%03d_loooooooooooooooooong_name", i); # lfs_remove(&lfs, path) => 0; # } # } # lfs_unmount(&lfs) => 0; #''' # ## reentrant testing for relocations, this is the same as the ## orphan testing, except here we also set block_cycles so that ## almost every tree operation needs a relocation #[cases.test_relocations_reentrant] #reentrant = true ## TODO fix this case, caused by non-DAG trees ## NOTE the second condition is required #if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT' #defines = [ # {FILES=6, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, # {FILES=26, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, # {FILES=3, DEPTH=3, CYCLES=20, BLOCK_CYCLES=1}, #] #code = ''' # lfs_t lfs; # int err = lfs_mount(&lfs, cfg); # if (err) { # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # } # # uint32_t prng = 1; # const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; # for (unsigned i = 0; i < CYCLES; i++) { # // create random path # char full_path[256]; # for (unsigned d = 0; d < DEPTH; d++) { # sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]); # } # # // if it does not exist, we create it, else we destroy # struct lfs_info info; # int res = lfs_stat(&lfs, full_path, &info); # if (res == LFS_ERR_NOENT) { # // create each directory in turn, ignore if dir already exists # for (unsigned d = 0; d < DEPTH; d++) { # char path[1024]; # strcpy(path, full_path); # path[2*d+2] = '\0'; # err = lfs_mkdir(&lfs, path); # assert(!err || err == LFS_ERR_EXIST); # } # # for (unsigned d = 0; d < DEPTH; d++) { # char path[1024]; # strcpy(path, full_path); # path[2*d+2] = '\0'; # lfs_stat(&lfs, path, &info) => 0; # assert(strcmp(info.name, &path[2*d+1]) == 0); # assert(info.type == LFS_TYPE_DIR); # } # } else { # // is valid dir? # assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); # assert(info.type == LFS_TYPE_DIR); # # // try to delete path in reverse order, ignore if dir is not empty # for (unsigned d = DEPTH-1; d+1 > 0; d--) { # char path[1024]; # strcpy(path, full_path); # path[2*d+2] = '\0'; # err = lfs_remove(&lfs, path); # assert(!err || err == LFS_ERR_NOTEMPTY); # } # # lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; # } # } # lfs_unmount(&lfs) => 0; #''' # ## reentrant testing for relocations, but now with random renames! #[cases.test_relocations_reentrant_renames] #reentrant = true ## TODO fix this case, caused by non-DAG trees ## NOTE the second condition is required #if = '!(DEPTH == 3 && CACHE_SIZE != 64) && 2*FILES < BLOCK_COUNT' #defines = [ # {FILES=6, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, # {FILES=26, DEPTH=1, CYCLES=20, BLOCK_CYCLES=1}, # {FILES=3, DEPTH=3, CYCLES=20, BLOCK_CYCLES=1}, #] #code = ''' # lfs_t lfs; # int err = lfs_mount(&lfs, cfg); # if (err) { # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # } # # uint32_t prng = 1; # const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; # for (unsigned i = 0; i < CYCLES; i++) { # // create random path # char full_path[256]; # for (unsigned d = 0; d < DEPTH; d++) { # sprintf(&full_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]); # } # # // if it does not exist, we create it, else we destroy # struct lfs_info info; # int res = lfs_stat(&lfs, full_path, &info); # assert(!res || res == LFS_ERR_NOENT); # if (res == LFS_ERR_NOENT) { # // create each directory in turn, ignore if dir already exists # for (unsigned d = 0; d < DEPTH; d++) { # char path[1024]; # strcpy(path, full_path); # path[2*d+2] = '\0'; # err = lfs_mkdir(&lfs, path); # assert(!err || err == LFS_ERR_EXIST); # } # # for (unsigned d = 0; d < DEPTH; d++) { # char path[1024]; # strcpy(path, full_path); # path[2*d+2] = '\0'; # lfs_stat(&lfs, path, &info) => 0; # assert(strcmp(info.name, &path[2*d+1]) == 0); # assert(info.type == LFS_TYPE_DIR); # } # } else { # assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); # assert(info.type == LFS_TYPE_DIR); # # // create new random path # char new_path[256]; # for (unsigned d = 0; d < DEPTH; d++) { # sprintf(&new_path[2*d], "/%c", alpha[TEST_PRNG(&prng) % FILES]); # } # # // if new path does not exist, rename, otherwise destroy # res = lfs_stat(&lfs, new_path, &info); # assert(!res || res == LFS_ERR_NOENT); # if (res == LFS_ERR_NOENT) { # // stop once some dir is renamed # for (unsigned d = 0; d < DEPTH; d++) { # char path[1024]; # strcpy(&path[2*d], &full_path[2*d]); # path[2*d+2] = '\0'; # strcpy(&path[128+2*d], &new_path[2*d]); # path[128+2*d+2] = '\0'; # err = lfs_rename(&lfs, path, path+128); # assert(!err || err == LFS_ERR_NOTEMPTY); # if (!err) { # strcpy(path, path+128); # } # } # # for (unsigned d = 0; d < DEPTH; d++) { # char path[1024]; # strcpy(path, new_path); # path[2*d+2] = '\0'; # lfs_stat(&lfs, path, &info) => 0; # assert(strcmp(info.name, &path[2*d+1]) == 0); # assert(info.type == LFS_TYPE_DIR); # } # # lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; # } else { # // try to delete path in reverse order, # // ignore if dir is not empty # for (unsigned d = DEPTH-1; d+1 > 0; d--) { # char path[1024]; # strcpy(path, full_path); # path[2*d+2] = '\0'; # err = lfs_remove(&lfs, path); # assert(!err || err == LFS_ERR_NOTEMPTY); # } # # lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; # } # } # } # lfs_unmount(&lfs) => 0; #'''