Changed all dir tests to be reentrant

To help with this, added TEST_PL, which is set to true when powerloss
testing. This way tests can check for stronger conditions (no EEXIST)
when not powerloss testing.

With TEST_PL, there's really no reason every test in t5_dirs shouldn't
be reentrant, and this gives us a huge improvement of test coverage very
cheaply.

---

The increased test coverage caught a bug, which is that gstate wasn't
being consumed properly when mtree uninlining. Humorously, this went
unnoticed because the most common form of mtree uninlining, mdir splitting,
ended up incorrectly consuming the gstate twice, which canceled itself
out since the consume operation is basically just xor.

Also added support for printing dstarts to dbglfs.py, to help debugging.
This commit is contained in:
Christopher Haster
2023-07-18 16:32:49 -05:00
parent 97f867b28d
commit c928ed131f
7 changed files with 270 additions and 175 deletions
+19 -10
View File
@@ -5386,18 +5386,9 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// - mid = wl => only alloc if mdir is tired (wear-leveling)
// - otherwise => always alloc, use this mid (new mdir)
// consume gstate on original rbyd, we need this even if we drop
// our mdir to avoid losing info
//
// if succesful, this should get immediately appended to our new commit
int err = lfsr_fs_consumegdelta(lfs, msource);
if (err) {
return err;
}
// first thing we need to do is read our current revision count
uint32_t rev;
err = lfsr_bd_read(lfs, msource->rbyd.block, 0, sizeof(uint32_t),
int err = lfsr_bd_read(lfs, msource->rbyd.block, 0, sizeof(uint32_t),
&rev, sizeof(uint32_t));
if (err && err != LFS_ERR_CORRUPT) {
return err;
@@ -5430,6 +5421,19 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
rev = (err != LFS_ERR_CORRUPT ? lfs_fromle32_(&rev) : 0);
}
// only consume gstate here during normal compacts
// TODO avoid duplicate conditions somehow?
if (mid == LFSR_MID_WL) {
// consume gstate on original rbyd, we need this even if we drop
// our mdir to avoid losing info
//
// if succesful, this should get immediately appended to our new commit
int err = lfsr_fs_consumegdelta(lfs, msource);
if (err) {
return err;
}
}
// swap our rbyds
lfs_swap32(&mdir->rbyd.block, &mdir->redund_block);
// update our revision count
@@ -7509,6 +7513,11 @@ static int lfsr_fs_preparemutation(lfs_t *lfs) {
LFS_DEBUG("Fixing grm (%"PRId32".%"PRId32")",
lfs->grm_.mid,
lfs->grm_.rid);
// checkpoint the allocator in case fixing the grm causes mtree
// manipulation
lfs_alloc_ack(lfs);
int err = lfsr_fs_fixgrm(lfs);
if (err) {
return err;
+7 -5
View File
@@ -478,6 +478,7 @@ extern size_t test_geometry_count;
extern const test_powerloss_t *test_powerlosses;
extern size_t test_powerloss_count;
bool test_pl = false;
const test_id_t *test_ids = (const test_id_t[]) {
{NULL, NULL, 0, NULL, 0},
@@ -606,7 +607,6 @@ void test_trace(const char *fmt, ...) {
}
}
// test prng
uint32_t test_prng(uint32_t *state) {
// A simple xorshift32 generator, easily reproducible. Keep in mind
@@ -771,6 +771,7 @@ static void case_forperm(
// explicit powerloss cycles?
if (cycles) {
test_pl = true;
cb(data, suite, case_, &(test_powerloss_t){
.run=run_powerloss_cycles,
.cycles=cycles,
@@ -778,8 +779,8 @@ static void case_forperm(
} else {
for (size_t p = 0; p < test_powerloss_count; p++) {
// skip non-reentrant tests when powerloss testing
if (test_powerlosses[p].run != run_powerloss_none
&& !(case_->flags & TEST_REENTRANT)) {
test_pl = test_powerlosses[p].run != run_powerloss_none;
if (test_pl && !(case_->flags & TEST_REENTRANT)) {
continue;
}
@@ -820,6 +821,7 @@ static void case_forperm(
}
if (cycles) {
test_pl = true;
cb(data, suite, case_, &(test_powerloss_t){
.run=run_powerloss_cycles,
.cycles=cycles,
@@ -827,8 +829,8 @@ static void case_forperm(
} else {
for (size_t p = 0; p < test_powerloss_count; p++) {
// skip non-reentrant tests when powerloss testing
if (test_powerlosses[p].run != run_powerloss_none
&& !(case_->flags & TEST_REENTRANT)) {
test_pl = test_powerlosses[p].run != run_powerloss_none;
if (test_pl && !(case_->flags & TEST_REENTRANT)) {
continue;
}
+5
View File
@@ -69,6 +69,11 @@ struct test_suite {
};
// this is defined as true when powerloss-testing
extern bool test_pl;
#define TEST_PL test_pl
// deterministic prng for pseudo-randomness in testes
uint32_t test_prng(uint32_t *state);
+2 -1
View File
@@ -511,7 +511,8 @@ def main(disk, roots=None, *,
btree.addr(), btree.rev, btree.weight))
# look up an id, while keeping track of the search path
def btree_lookup(bid, depth=None):
def btree_lookup(bid, *,
depth=None):
rbyd = btree
rid = bid
depth_ = 1
+49 -25
View File
@@ -396,7 +396,8 @@ class Rbyd:
yield id, tag, w, j, d, data
# btree lookup with this rbyd as the root
def btree_lookup(self, f, block_size, bid, depth=None):
def btree_lookup(self, f, block_size, bid, *,
depth=None):
rbyd = self
rid = bid
depth_ = 1
@@ -558,13 +559,13 @@ class Rbyd:
mtree = Rbyd.fetch(f, block_size, block, trunk)
# corrupted?
if not mtree:
return False, 0, b'', -1, None, -1, 0, 0
return False, -1, None, -1, 0, 0
# lookup our name in the mtree
mid, tag_, w, data = mtree.btree_namelookup(
f, block_size, did, name)
if tag_ != TAG_MDIR:
return False, 0, b'', -1, None, -1, 0, 0
return False, -1, None, -1, 0, 0
# fetch the mdir
blocks = frommdir(data)
@@ -588,12 +589,32 @@ class Rbyd:
return found, mid, mdir, rid, tag, w
# iterate through a directory assuming this is the mtree root
def mtree_dir(self, f, block_size, did):
def mtree_dir(self, f, block_size, did, *,
all=False):
all_, all = all, __builtins__.all
# lookup the dstart
found, mid, mdir, rid, tag, w = self.mtree_namelookup(
f, block_size, did, b'')
# iterate through all files until the next dstart
while found:
# lookup each rid
done, rid, tag, w, j, d, data, _ = mdir.lookup(rid, TAG_NAME)
if done:
break
# parse out each name
did_, d_ = fromleb128(data)
name_ = data[d_:]
# end if we see another did
if did_ != did:
break
# skip dstarts unless all entries are requested
if tag != TAG_DSTART or all_:
yield name_, mid, mdir, rid, tag, w
rid += w
if rid >= mdir.weight:
rid -= mdir.weight
@@ -603,17 +624,6 @@ class Rbyd:
if not mdir:
break
# lookup each rid
done, rid, tag, w, j, d, data, _ = mdir.lookup(rid, TAG_NAME)
if done or tag == TAG_DSTART:
break
# parse out each name
did_, d_ = fromleb128(data)
name_ = data[d_:]
yield did_, name_, mid, mdir, rid, tag, w
# read the superconfig
def superconfig(mroot):
@@ -679,7 +689,15 @@ def grepr(tag, data):
return 'gstate 0x%02x %d' % (tag, len(data))
def frepr(mdir, rid, tag):
if tag == TAG_DIR:
if tag == TAG_DSTART:
# read the did
did = '?'
done, rid_, tag_, w_, j, d, data, _ = mdir.lookup(rid, TAG_DSTART)
if not done and rid_ == rid and tag_ == TAG_DSTART:
did, _ = fromleb128(data)
did = '0x%x' % did
return 'dstart %s' % did
elif tag == TAG_DIR:
# read the did
did = '?'
done, rid_, tag_, w_, j, d, data, _ = mdir.lookup(rid, TAG_DID)
@@ -815,8 +833,9 @@ def main(disk, mroots=None, *,
def rec_f_width(did, depth):
depth_ = 0
width_ = 0
for did, name, mid, mdir, rid, tag, w in mroot.mtree_dir(
f, block_size, did):
for name, mid, mdir, rid, tag, w in mroot.mtree_dir(
f, block_size, did,
all=args.get('all')):
width_ = max(width_, len(name))
# recurse?
if tag == TAG_DIR and depth > 1:
@@ -928,14 +947,16 @@ def main(disk, mroots=None, *,
# print gdeltas?
if args.get('gdelta'):
for mid, mdir, j, d, data in gstate.gdelta[tag]:
print('{%s}: %*s %-22s %s' % (
print('%s{%s}: %*s %-22s %s%s' % (
'\x1b[90m' if color else '',
','.join('%04x' % block
for block in it.chain([mdir.block],
mdir.redund_blocks)),
w_width, mid,
tagrepr(tag, 0, len(data)),
next(xxd(data, 8), '')
if not args.get('no_truncate') else ''))
if not args.get('no_truncate') else '',
'\x1b[m' if color else ''))
# show in-device representation
if args.get('device'):
@@ -976,9 +997,11 @@ def main(disk, mroots=None, *,
def rec_dir(did, depth, prefixes=('', '', '', '')):
nonlocal pmid
# collect all entries first so we know when the dir ends
dir = list(mroot.mtree_dir(f, block_size, did))
for i, (did, name, mid, mdir, rid, tag, w) in enumerate(dir):
print('%12s %*s %-*s %s' % (
dir = list(mroot.mtree_dir(f, block_size, did,
all=args.get('all')))
for i, (name, mid, mdir, rid, tag, w) in enumerate(dir):
print('%s%12s %*s %-*s %s%s' % (
'\x1b[90m' if color and tag == TAG_DSTART else '',
'{%s}:' % ','.join('%04x' % block
for block in it.chain([mdir.block],
mdir.redund_blocks))
@@ -989,7 +1012,8 @@ def main(disk, mroots=None, *,
f_width, '%s%s' % (
prefixes[0+(i==len(dir)-1)],
name.decode('utf8')),
frepr(mdir, rid, tag)))
frepr(mdir, rid, tag),
'\x1b[m' if color and tag == TAG_DSTART else ''))
pmid = mid
# print attrs associated with this file?
@@ -1107,7 +1131,7 @@ if __name__ == "__main__":
parser.add_argument(
'-a', '--all',
action='store_true',
help="Show all files including grmed files.")
help="Show all files including dstarts and grmed files.")
parser.add_argument(
'-r', '--raw',
action='store_true',
+6 -3
View File
@@ -488,7 +488,8 @@ class Rbyd:
return tree, t_depth
# btree lookup with this rbyd as the root
def btree_lookup(self, f, block_size, bid, depth=None):
def btree_lookup(self, f, block_size, bid, *,
depth=None):
rbyd = self
rid = bid
depth_ = 1
@@ -543,7 +544,8 @@ class Rbyd:
return not tags, bid + (rid_-rid), w, rbyd, rid_, tags, path
# btree rbyd-tree generation for debugging
def btree_tree(self, f, block_size, depth=None, *,
def btree_tree(self, f, block_size, *,
depth=None,
inner=False):
# find the max depth of each layer to nicely align trees
bdepths = {}
@@ -671,7 +673,8 @@ class Rbyd:
return tree, max((branch.d+1 for branch in tree), default=0)
# btree B-tree generation for debugging
def btree_btree(self, f, block_size, depth=None, *,
def btree_btree(self, f, block_size, *,
depth=None,
inner=False):
# find all branches
tree = set()
+182 -131
View File
@@ -2,13 +2,19 @@
[cases.t5_dirs_mkdir]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
lfsr_mkdir(&lfs, "ardvark") => 0;
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -44,13 +50,19 @@ code = '''
# test that noent errors work
[cases.t5_dirs_noent]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
lfsr_mkdir(&lfs, "ardvark") => 0;
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// try to read a nonsense path
struct lfs_info info;
@@ -90,13 +102,19 @@ code = '''
# test that creating the same directory twice errors
[cases.t5_dirs_mkdir_exists]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
lfsr_mkdir(&lfs, "ardvark") => 0;
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// make the same directory, should error
lfsr_mkdir(&lfs, "ardvark") => LFS_ERR_EXIST;
@@ -134,13 +152,19 @@ code = '''
# test that creating a directory with an invalid path errors
[cases.t5_dirs_mkdir_noent]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make a directory
lfsr_mkdir(&lfs, "ardvark") => 0;
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// make a nonsense directory, should error
lfsr_mkdir(&lfs, "no/hmm") => LFS_ERR_NOENT;
@@ -180,15 +204,23 @@ code = '''
[cases.t5_dirs_mkdir_siblings]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
lfsr_mkdir(&lfs, "ardvark") => 0;
lfsr_mkdir(&lfs, "batman") => 0;
lfsr_mkdir(&lfs, "cantaloupe") => 0;
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "batman");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "cantaloupe");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -234,15 +266,23 @@ code = '''
[cases.t5_dirs_mkdir_children]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make some directories
lfsr_mkdir(&lfs, "ardvark") => 0;
lfsr_mkdir(&lfs, "ardvark/batman") => 0;
lfsr_mkdir(&lfs, "ardvark/batman/cantaloupe") => 0;
err = lfsr_mkdir(&lfs, "ardvark");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/batman");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "ardvark/batman/cantaloupe");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -307,16 +347,22 @@ code = '''
[cases.t5_dirs_mkdir_many]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -359,23 +405,30 @@ code = '''
'''
[cases.t5_dirs_mkdir_many_2layers]
defines.N = [1, 2, 4, 8, 16, 32]
defines.N = [1, 2, 4, 8, 16]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -449,28 +502,36 @@ code = '''
'''
[cases.t5_dirs_mkdir_many_3layers]
defines.N = [1, 2, 4, 8]
defines.N = [1, 2, 4]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t j = 0; j < N; j++) {
sprintf(name, "dir%04d/child%04d", i, j);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// containing this many directories
for (lfs_size_t k = 0; k < N; k++) {
sprintf(name, "dir%04d/child%04d/grandchild%04d", i, j, k);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -577,17 +638,23 @@ code = '''
[cases.t5_dirs_mkdir_many_linkedlist]
defines.N = [1, 2, 4, 8, 16, 32, 64]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// create this many directory in a sort of linked-list by nesting
char name[4096];
memset(name, 0, sizeof(name));
for (lfs_size_t i = 0; i < N; i++) {
sprintf(&name[strlen(name)], "/dir%04d", i);
lfsr_mkdir(&lfs, name) => 0;
err = lfsr_mkdir(&lfs, name);
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -738,10 +805,15 @@ code = '''
# test that did collisions don't cause issues
[cases.t5_dirs_did_collisions]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_SNmwMTHH", 10) == 0x12345678);
assert(lfs_crc32c(0, "b_skvjpWJH", 10) == 0x12345678);
@@ -751,12 +823,18 @@ code = '''
assert(lfs_crc32c(0, "f_lUoVuhJH", 10) == 0x12345678);
// make directories
lfsr_mkdir(&lfs, "a_SNmwMTHH") => 0;
lfsr_mkdir(&lfs, "b_skvjpWJH") => 0;
lfsr_mkdir(&lfs, "c_OnOQhVPH") => 0;
lfsr_mkdir(&lfs, "d_puMpPjRH") => 0;
lfsr_mkdir(&lfs, "e_LptKHkHH") => 0;
lfsr_mkdir(&lfs, "f_lUoVuhJH") => 0;
err = lfsr_mkdir(&lfs, "a_SNmwMTHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_skvjpWJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_OnOQhVPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_puMpPjRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_LptKHkHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_lUoVuhJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -821,10 +899,15 @@ code = '''
# these will also collide with the root
[cases.t5_dirs_did_zero]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_IplRNrPH", 10) == 0x00000000);
assert(lfs_crc32c(0, "b_iUwOsqRH", 10) == 0x00000000);
@@ -833,21 +916,19 @@ code = '''
assert(lfs_crc32c(0, "e_VNunKMPH", 10) == 0x00000000);
assert(lfs_crc32c(0, "f_vknsvNRH", 10) == 0x00000000);
// remount?
if (REMOUNT) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, cfg) => 0;
// grm should be zero here
assert(lfs.grm[0] == 0);
}
// make directories
lfsr_mkdir(&lfs, "a_IplRNrPH") => 0;
lfsr_mkdir(&lfs, "b_iUwOsqRH") => 0;
lfsr_mkdir(&lfs, "c_UPNtkpHH") => 0;
lfsr_mkdir(&lfs, "d_jKLUSLJH") => 0;
lfsr_mkdir(&lfs, "e_VNunKMPH") => 0;
lfsr_mkdir(&lfs, "f_vknsvNRH") => 0;
err = lfsr_mkdir(&lfs, "a_IplRNrPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_iUwOsqRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_UPNtkpHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_jKLUSLJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_VNunKMPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_vknsvNRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -914,10 +995,15 @@ code = '''
# note this is true even if you truncate
[cases.t5_dirs_did_ones]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_iomlVKPH", 10) == 0xffffffff);
assert(lfs_crc32c(0, "b_IJvqkHRH", 10) == 0xffffffff);
@@ -927,12 +1013,18 @@ code = '''
assert(lfs_crc32c(0, "f_VtoMnwRH", 10) == 0xffffffff);
// make directories
lfsr_mkdir(&lfs, "a_iomlVKPH") => 0;
lfsr_mkdir(&lfs, "b_IJvqkHRH") => 0;
lfsr_mkdir(&lfs, "c_uOOJsIHH") => 0;
lfsr_mkdir(&lfs, "d_JTMkKuJH") => 0;
lfsr_mkdir(&lfs, "e_vQtPStPH") => 0;
lfsr_mkdir(&lfs, "f_VtoMnwRH") => 0;
err = lfsr_mkdir(&lfs, "a_iomlVKPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_IJvqkHRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_uOOJsIHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_JTMkKuJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_vQtPStPH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_VtoMnwRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -999,10 +1091,15 @@ code = '''
# issues
[cases.t5_dirs_did_leb128_boundaries]
defines.REMOUNT = [false, true]
reentrant = true
code = '''
// format once per test
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
int err = lfsr_mount(&lfs, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
assert(lfs_crc32c(0, "a_IOtUptRH", 10) == 0x0000007f);
assert(lfs_crc32c(0, "b_nquQsKHH", 10) == 0x00000080);
@@ -1012,12 +1109,18 @@ code = '''
assert(lfs_crc32c(0, "f_pNtQTPJH", 10) == 0x00004001);
// make directories
lfsr_mkdir(&lfs, "a_IOtUptRH") => 0;
lfsr_mkdir(&lfs, "b_nquQsKHH") => 0;
lfsr_mkdir(&lfs, "c_vwQtKjHH") => 0;
lfsr_mkdir(&lfs, "d_sVrvrWHH") => 0;
lfsr_mkdir(&lfs, "e_thrRIsRH") => 0;
lfsr_mkdir(&lfs, "f_pNtQTPJH") => 0;
err = lfsr_mkdir(&lfs, "a_IOtUptRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "b_nquQsKHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "c_vwQtKjHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "d_sVrvrWHH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "e_thrRIsRH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
err = lfsr_mkdir(&lfs, "f_pNtQTPJH");
assert(!err || (TEST_PL && err == LFS_ERR_EXIST));
// remount?
if (REMOUNT) {
@@ -1079,58 +1182,6 @@ code = '''
lfsr_unmount(&lfs) => 0;
'''
[cases.t5_dirs_mkdir_many_pl]
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, cfg);
if (err) {
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
}
// make this many directories
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
int err = lfsr_mkdir(&lfs, name);
assert(!err || err == LFS_ERR_EXIST);
}
// check that our mkdir worked
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
struct lfs_info info;
lfsr_stat(&lfs, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_t dir;
lfsr_dir_open(&lfs, &dir, "/") => 0;
struct lfs_info info;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
for (lfs_size_t i = 0; i < N; i++) {
char name[256];
sprintf(name, "dir%04d", i);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS_TYPE_DIR);
}
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
#[cases.test_dirs_root]