Files
littlefs/tests/test_powerloss.toml
T
Christopher Haster 3ab7ecb2b0 Renamed file_cache -> fcache and gbmap_re -> regbmap
This walks back some of the attempt at strict object namespacing in
struct lfs3_cfg:

- cfg.file_cache_size  -> cfg.fcache_size
- filecfg.cache_size   -> filecfg.fcache_size
- filecfg.cache_buffer -> filecfg.fcache_buffer
- cfg.gbmap_re_thresh  -> cfg.regbmap_thresh

Motivation:

- cfg.regbmap_thresh now matches cfg.gc_regbmap_thresh, instead of using
  awkwardly different namespacing patterns.

- Giving fcache a more unique name is useful for discussion. Having
  pcache, rcache, and then file_cache was a bit awkward.

  Hopefully it's also more clear that cfg.fcache_size and
  filecfg.fcache_size are related.

- Config in struct lfs3_cfg is named a bit more consistently, well, if
  you ignore gc_*_* options.

- Less typing.

Though this gets into pretty subjective naming territory. May revert
this if the new terms are uncomfortable after use.
2025-10-24 00:18:54 -05:00

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.SHRUB_SIZE = ['BLOCK_SIZE/4', '0']
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_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.SHRUB_SIZE = ['BLOCK_SIZE/4', '0']
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.OPS = 256
defines.SIZE = [
'0',
'FCACHE_SIZE/2',
'2*FCACHE_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.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',
'FCACHE_SIZE/2',
'2*FCACHE_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;
'''