2586fe68a2
- test_traversal -> test_trvs - lfs3_traversal_t -> lfs3_trv_t - lfs3_btraversal_t -> lfs3_btrv_t - t -> trv - bt -> btrv - lfs3_traversal_* -> lfs3_trv_* - lfs3_btraversal_* -> lfs3_btrv_* The traversal type is becoming one of the more fundamental types in littlefs, and if DIR and REG both get shortened names, it makes sense for TRV to have one as well. This also removes the temptation to use t for traversals, which is probably an even worse name. --- Note that lfs3_btree_traverse, lfs3_mtree_traverse, etc, remain unaffected. This may change in the future, but it's interesting to note that verbs seem to need much less typing than nouns.
821 lines
28 KiB
TOML
821 lines
28 KiB
TOML
# 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_stickynotes',
|
|
'test_trvs',
|
|
'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 = [
|
|
'LFS3_EMUBD_POWERLOSS_ATOMIC',
|
|
'LFS3_EMUBD_POWERLOSS_SOMEBITS',
|
|
'LFS3_EMUBD_POWERLOSS_MOSTBITS',
|
|
'LFS3_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
|
|
lfs3_t lfs3;
|
|
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
|
|
if (err) {
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfs3_fs_mkconsistent(&lfs3) => 0;
|
|
}
|
|
|
|
// make this many directories
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || (TEST_PLS && err == LFS3_ERR_EXIST));
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// grm should be zero here
|
|
assert(lfs3.grm_p[0] == 0);
|
|
|
|
// check that our mkdir worked
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "/") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
}
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "dir%03x", i);
|
|
lfs3_dir_open(&lfs3, &dir, name) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT;
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 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 = [
|
|
'LFS3_EMUBD_POWERLOSS_ATOMIC',
|
|
'LFS3_EMUBD_POWERLOSS_SOMEBITS',
|
|
'LFS3_EMUBD_POWERLOSS_MOSTBITS',
|
|
'LFS3_EMUBD_POWERLOSS_OOO',
|
|
]
|
|
defines.MKCONSISTENT = [false, true]
|
|
# inlining has a tendency to hide sync issues, so try without
|
|
defines.INLINE_SIZE = ['BLOCK_SIZE/4', '0']
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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
|
|
lfs3_t lfs3;
|
|
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
|
|
if (err) {
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfs3_fs_mkconsistent(&lfs3) => 0;
|
|
}
|
|
|
|
// create this many files
|
|
uint32_t prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
err = lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL);
|
|
assert(!err || (TEST_PLS && err == LFS3_ERR_EXIST));
|
|
if (!err) {
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that our writes worked
|
|
prng = 42;
|
|
for (lfs3_size_t i = 0; i < N; i++) {
|
|
// check with stat
|
|
char name[256];
|
|
sprintf(name, "amethyst%03x", i);
|
|
struct lfs3_info info;
|
|
lfs3_stat(&lfs3, name, &info) => 0;
|
|
assert(strcmp(info.name, name) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// try reading the file, note we reset prng above
|
|
uint8_t wbuf[SIZE];
|
|
for (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 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 = [
|
|
'LFS3_EMUBD_POWERLOSS_ATOMIC',
|
|
'LFS3_EMUBD_POWERLOSS_SOMEBITS',
|
|
'LFS3_EMUBD_POWERLOSS_MOSTBITS',
|
|
'LFS3_EMUBD_POWERLOSS_OOO',
|
|
]
|
|
defines.MKCONSISTENT = [false, true]
|
|
# inlining has a tendency to hide sync issues, so try without
|
|
defines.INLINE_SIZE = ['BLOCK_SIZE/4', '0']
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
|
defines.OPS = 256
|
|
defines.SIZE = [
|
|
'0',
|
|
'FILE_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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
|
|
lfs3_t lfs3;
|
|
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
|
|
if (err) {
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfs3_fs_mkconsistent(&lfs3) => 0;
|
|
}
|
|
|
|
// keep some test state on disk to survive powerloss
|
|
typedef struct fuzz_state {
|
|
lfs3_size_t i;
|
|
uint32_t prng;
|
|
} fuzz_state_t;
|
|
fuzz_state_t state = {.i = 0, .prng = SEED};
|
|
|
|
lfs3_file_t state_file;
|
|
lfs3_file_open(&lfs3, &state_file,
|
|
"state", LFS3_O_RDWR | LFS3_O_CREAT) => 0;
|
|
lfs3_ssize_t d = lfs3_file_read(&lfs3, &state_file, &state, sizeof(state));
|
|
assert(d == 0 || d == sizeof(state));
|
|
|
|
// keep test files in a separate directory
|
|
err = lfs3_mkdir(&lfs3, "test");
|
|
assert(!err || err == LFS3_ERR_EXIST);
|
|
|
|
uint32_t prng = state.prng;
|
|
for (lfs3_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?
|
|
lfs3_size_t count = 0;
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
count++;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// creating a new file?
|
|
if (op == 0 || count == 0) {
|
|
// choose a pseudo-random number
|
|
lfs3_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 (lfs3_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);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 1) {
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % count;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
sprintf(name, "test/%s", info.name);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % count;
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
}
|
|
|
|
// update our state file
|
|
state.i = i;
|
|
state.prng = prng;
|
|
lfs3_file_rewind(&lfs3, &state_file) => 0;
|
|
lfs3_file_write(&lfs3, &state_file, &state, sizeof(state))
|
|
=> sizeof(state);
|
|
lfs3_file_sync(&lfs3, &state_file) => 0;
|
|
}
|
|
|
|
// go ahead and close our state file in case we remount
|
|
lfs3_file_close(&lfs3, &state_file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that things look more-or-less ok
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
assert(memcmp(info.name, "amethyst", strlen("amethyst")) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
|
|
// at least try to read the files
|
|
char name[256];
|
|
sprintf(name, "test/%s", info.name);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
// all data should be lowercase ascii
|
|
for (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
ck = (ck + (rbuf[j] - 'a')) % 26;
|
|
}
|
|
assert(ck == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 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 = [
|
|
'LFS3_EMUBD_POWERLOSS_ATOMIC',
|
|
'LFS3_EMUBD_POWERLOSS_SOMEBITS',
|
|
'LFS3_EMUBD_POWERLOSS_MOSTBITS',
|
|
'LFS3_EMUBD_POWERLOSS_OOO',
|
|
]
|
|
defines.MKCONSISTENT = [false, true]
|
|
# inlining has a tendency to hide sync issues, so try without
|
|
defines.INLINE_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_CACHE_SIZE/2',
|
|
'2*FILE_CACHE_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
|
|
lfs3_t lfs3;
|
|
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
|
|
if (err) {
|
|
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
if (MKCONSISTENT) {
|
|
lfs3_fs_mkconsistent(&lfs3) => 0;
|
|
}
|
|
|
|
// keep some test state on disk to survive powerloss
|
|
typedef struct fuzz_state {
|
|
lfs3_size_t i;
|
|
uint32_t prng;
|
|
} fuzz_state_t;
|
|
fuzz_state_t state = {.i = 0, .prng = SEED};
|
|
|
|
lfs3_file_t state_file;
|
|
lfs3_file_open(&lfs3, &state_file,
|
|
"state", LFS3_O_RDWR | LFS3_O_CREAT) => 0;
|
|
lfs3_ssize_t d = lfs3_file_read(&lfs3, &state_file, &state, sizeof(state));
|
|
assert(d == 0 || d == sizeof(state));
|
|
|
|
// keep test files in a separate directory
|
|
err = lfs3_mkdir(&lfs3, "test");
|
|
assert(!err || err == LFS3_ERR_EXIST);
|
|
|
|
uint32_t prng = state.prng;
|
|
for (lfs3_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?
|
|
lfs3_size_t dir_count = 0;
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("quartz..."));
|
|
assert(memcmp(info.name, "quartz", strlen("quartz")) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
dir_count++;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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
|
|
lfs3_size_t x = TEST_PRNG(&prng) % N;
|
|
|
|
// create a dir here
|
|
char name[256];
|
|
sprintf(name, "test/quartz%03x", x);
|
|
int err = lfs3_mkdir(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_EXIST);
|
|
|
|
// deleting a dir?
|
|
} else if (op == 1) {
|
|
// choose a random dir to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % dir_count;
|
|
// find the dir
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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 = lfs3_remove(&lfs3, name);
|
|
assert(!err || err == LFS3_ERR_NOTEMPTY);
|
|
|
|
// renaming a dir?
|
|
} else {
|
|
// choose a random dir to rename, and a random number to
|
|
// rename to
|
|
lfs3_size_t j = TEST_PRNG(&prng) % dir_count;
|
|
lfs3_size_t y = TEST_PRNG(&prng) % N;
|
|
// find the dir
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &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 = lfs3_rename(&lfs3, old_name, new_name);
|
|
assert(!err || err == LFS3_ERR_NOTEMPTY);
|
|
}
|
|
|
|
// file op?
|
|
} else {
|
|
// choose a pseudo-random dir
|
|
lfs3_size_t dir_i = TEST_PRNG(&prng) % dir_count;
|
|
// find the dir
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= dir_i; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
char dir_path[256];
|
|
sprintf(dir_path, "test/%s", info.name);
|
|
|
|
// how many files do we have?
|
|
lfs3_size_t count = 0;
|
|
lfs3_dir_open(&lfs3, &dir, dir_path) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
assert(memcmp(
|
|
info.name,
|
|
"amethyst", strlen("amethyst")) == 0);
|
|
assert(info.type == LFS3_TYPE_REG);
|
|
assert(info.size == SIZE);
|
|
count++;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// creating a new file?
|
|
if (op == 3 || count == 0) {
|
|
// choose a pseudo-random number
|
|
lfs3_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 (lfs3_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);
|
|
}
|
|
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name,
|
|
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
|
|
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
|
|
// deleting a file?
|
|
} else if (op == 4) {
|
|
// choose a random file to delete
|
|
lfs3_size_t j = TEST_PRNG(&prng) % count;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, dir_path) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// delete this file
|
|
char name[256];
|
|
assert(strlen(info.name) == strlen("amethyst..."));
|
|
sprintf(name, "%s/%s", dir_path, info.name);
|
|
lfs3_remove(&lfs3, name) => 0;
|
|
|
|
// renaming a file?
|
|
} else {
|
|
// choose a random file to rename
|
|
lfs3_size_t j = TEST_PRNG(&prng) % count;
|
|
// find the file
|
|
lfs3_dir_open(&lfs3, &dir, dir_path) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
for (lfs3_size_t k = 0; k <= j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// choose a random dir to rename to
|
|
lfs3_size_t dir_j = TEST_PRNG(&prng) % dir_count;
|
|
// find the dir
|
|
struct lfs3_info info_;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info_) => 0;
|
|
lfs3_dir_read(&lfs3, &dir, &info_) => 0;
|
|
for (lfs3_size_t k = 0; k <= dir_j; k++) {
|
|
lfs3_dir_read(&lfs3, &dir, &info_) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
|
|
// choose a random file to rename to
|
|
lfs3_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);
|
|
lfs3_rename(&lfs3, old_name, new_name) => 0;
|
|
}
|
|
}
|
|
|
|
// update our state file
|
|
state.i = i;
|
|
state.prng = prng;
|
|
lfs3_file_rewind(&lfs3, &state_file) => 0;
|
|
lfs3_file_write(&lfs3, &state_file, &state, sizeof(state))
|
|
=> sizeof(state);
|
|
lfs3_file_sync(&lfs3, &state_file) => 0;
|
|
}
|
|
|
|
// go ahead and close our state file in case we remount
|
|
lfs3_file_close(&lfs3, &state_file) => 0;
|
|
|
|
for (int remount = 0; remount < 2; remount++) {
|
|
// remount?
|
|
if (remount) {
|
|
lfs3_unmount(&lfs3) => 0;
|
|
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
|
|
}
|
|
|
|
// check that things look more-or-less ok
|
|
lfs3_dir_t dir;
|
|
lfs3_dir_open(&lfs3, &dir, "test") => 0;
|
|
struct lfs3_info info;
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, ".") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir, &info) => 0;
|
|
assert(strcmp(info.name, "..") == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
while (true) {
|
|
int err = lfs3_dir_read(&lfs3, &dir, &info);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info.name) == strlen("quartz..."));
|
|
assert(memcmp(info.name, "quartz", strlen("quartz")) == 0);
|
|
assert(info.type == LFS3_TYPE_DIR);
|
|
assert(info.size == 0);
|
|
|
|
// check that our dirs look more-or-less ok
|
|
char name[256];
|
|
sprintf(name, "test/%s", info.name);
|
|
lfs3_dir_t dir_;
|
|
lfs3_dir_open(&lfs3, &dir_, name) => 0;
|
|
struct lfs3_info info_;
|
|
lfs3_dir_read(&lfs3, &dir_, &info_) => 0;
|
|
assert(strcmp(info_.name, ".") == 0);
|
|
assert(info_.type == LFS3_TYPE_DIR);
|
|
assert(info_.size == 0);
|
|
lfs3_dir_read(&lfs3, &dir_, &info_) => 0;
|
|
assert(strcmp(info_.name, "..") == 0);
|
|
assert(info_.type == LFS3_TYPE_DIR);
|
|
assert(info_.size == 0);
|
|
|
|
while (true) {
|
|
err = lfs3_dir_read(&lfs3, &dir_, &info_);
|
|
assert(!err || err == LFS3_ERR_NOENT);
|
|
if (err == LFS3_ERR_NOENT) {
|
|
break;
|
|
}
|
|
assert(strlen(info_.name) == strlen("amethyst..."));
|
|
assert(memcmp(
|
|
info_.name,
|
|
"amethyst", strlen("amethyst")) == 0);
|
|
assert(info_.type == LFS3_TYPE_REG);
|
|
assert(info_.size == SIZE);
|
|
|
|
// at least try to read the files
|
|
sprintf(name, "test/%s/%s", info.name, info_.name);
|
|
lfs3_file_t file;
|
|
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
|
|
|
|
uint8_t rbuf[SIZE];
|
|
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
|
|
// all data should be lowercase ascii
|
|
for (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
|
|
ck = (ck + (rbuf[j] - 'a')) % 26;
|
|
}
|
|
assert(ck == 0);
|
|
lfs3_file_close(&lfs3, &file) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir_) => 0;
|
|
}
|
|
lfs3_dir_close(&lfs3, &dir) => 0;
|
|
}
|
|
|
|
lfs3_unmount(&lfs3) => 0;
|
|
'''
|
|
|