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