Added a bit of fuzz testing over mtree splits
This isn't the greatest coverage as we don't have a verifiable simulation. Simulating the splitting-bucket-tree that is the mtree is tricky. So right now this mostly just checks there's no internal assert failures and if we have the expected number of entries afterwards.
This commit is contained in:
@@ -4523,12 +4523,7 @@ static inline int lfsr_mtree_isinlined(lfs_t *lfs) {
|
||||
}
|
||||
|
||||
static inline lfs_ssize_t lfsr_mtree_weight(lfs_t *lfs) {
|
||||
// inlined mdir?
|
||||
if (lfsr_mtree_isinlined(lfs)) {
|
||||
return lfs->mroot.rbyd.weight;
|
||||
} else {
|
||||
return lfsr_btree_weight(&lfs->mtree);
|
||||
}
|
||||
return lfsr_btree_weight(&lfs->mtree);
|
||||
}
|
||||
|
||||
static int lfsr_mtree_lookup(lfs_t *lfs, lfs_ssize_t mid, lfsr_mdir_t *mdir_) {
|
||||
|
||||
+85
-2
@@ -63,7 +63,7 @@ code = '''
|
||||
# TODO test many mroots
|
||||
|
||||
# try creating a range of entries that may or may not split our mtree
|
||||
[cases.test_mtree_splitting]
|
||||
[cases.test_mtree_split]
|
||||
defines.N = [5, 10, 20, 40, 80, 160, 320]
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
@@ -71,6 +71,7 @@ code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, cfg) => 0;
|
||||
|
||||
// create entries
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0;
|
||||
@@ -88,6 +89,7 @@ code = '''
|
||||
}
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
|
||||
// try looking up each entry
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
lfs_ssize_t mid = -1;
|
||||
rid = 0;
|
||||
@@ -112,7 +114,7 @@ code = '''
|
||||
# create a range of entries, and commit to the mroot several times,
|
||||
# this makes it more likely to force uninlining without necessarily
|
||||
# splitting
|
||||
[cases.test_mtree_uninlining]
|
||||
[cases.test_mtree_uninline]
|
||||
defines.N = [5, 10, 20, 40, 80, 160, 320]
|
||||
defines.M = [5, 5000]
|
||||
in = 'lfs.c'
|
||||
@@ -121,6 +123,7 @@ code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, cfg) => 0;
|
||||
|
||||
// create entries
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0;
|
||||
@@ -137,6 +140,8 @@ code = '''
|
||||
rid += 1;
|
||||
}
|
||||
|
||||
// also append a bunch of fs-level attributes, if the mdir should be
|
||||
// uninlined this will force it to happen
|
||||
for (lfs_size_t i = 0; i < M; i++) {
|
||||
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
||||
LFSR_ATTR(-1, UATTR(1), 0, &alphas[i % 26], 1))) => 0;
|
||||
@@ -148,6 +153,7 @@ code = '''
|
||||
}
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
|
||||
// try looking up each entry
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
lfs_ssize_t mid = -1;
|
||||
rid = 0;
|
||||
@@ -174,3 +180,80 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# create random entries
|
||||
[cases.test_mtree_split_fuzz]
|
||||
defines.N = [5, 10, 20, 40, 80, 160, 320]
|
||||
defines.SAMPLES = 10
|
||||
# -1 => all pseudo-random seeds
|
||||
# n => reproduce a specific seed
|
||||
defines.SEED = -1
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
// iterate through severals seeds that we can reproduce easily
|
||||
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
|
||||
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
|
||||
seed++) {
|
||||
printf("--- seed: %d ---\n", seed);
|
||||
// create lfs here since we need to reset each iteration, we're
|
||||
// space constrained and we can't expect gc to work at this point
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, cfg) => 0;
|
||||
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
// at least keep track of the number of entries we expect
|
||||
lfs_size_t count = 0;
|
||||
|
||||
uint32_t prng = seed;
|
||||
for (lfs_size_t i = 0; i < N; i++) {
|
||||
// choose a pseudo-random mid
|
||||
lfs_ssize_t mid = lfsr_mtree_weight(&lfs) == 0
|
||||
? -1
|
||||
: (lfs_ssize_t)(TEST_PRNG(&prng) % lfsr_mtree_weight(&lfs));
|
||||
// fetch mdir
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtree_lookup(&lfs, mid, &mdir) => 0;
|
||||
// choose a pseudo-random rid
|
||||
lfs_ssize_t rid = TEST_PRNG(&prng) % (mdir.rbyd.weight+1);
|
||||
|
||||
// add to rbyd, potentially splitting the mdir
|
||||
lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS(
|
||||
LFSR_ATTR(rid, MKINLINED, +1, &alphas[i % 26], 1))) => 0;
|
||||
|
||||
// make sure we can look up the new entry
|
||||
uint8_t buffer[4];
|
||||
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED, buffer, 4) => 1;
|
||||
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
||||
|
||||
count += 1;
|
||||
}
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
|
||||
lfsr_mount(&lfs, cfg) => 0;
|
||||
// try looking up each entry
|
||||
lfs_size_t count_ = 0;
|
||||
|
||||
for (lfs_ssize_t mid = (lfsr_mtree_isinlined(&lfs) ? -1 : 0);
|
||||
mid < lfsr_mtree_weight(&lfs);
|
||||
mid++) {
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtree_lookup(&lfs, mid, &mdir) => 0;
|
||||
for (lfs_ssize_t rid = 0;
|
||||
rid < (lfs_ssize_t)mdir.rbyd.weight;
|
||||
rid++) {
|
||||
uint8_t buffer[4];
|
||||
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
||||
buffer, 4) => 1;
|
||||
|
||||
count_ += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// the mtree is a bit difficult to simulate, but we can at least test
|
||||
// we ended up with the right number of entries
|
||||
assert(count_ == count);
|
||||
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
}
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user