ef4fb9d3d3
Dropped the high-level "large entry" tests in exchange for these low-level tests. The high-level tests accomplished the same thing, but worse and less reliably. Added some rough fixes (this whole code path needs to be rewritten). Also made lfsr_rbyd_bisect a bit better behaved when dealing with a small number of large entries. This was necessary for the split/drop corner case tests since these rely on precise control of when mdirs split.
1080 lines
35 KiB
TOML
1080 lines
35 KiB
TOML
# test a single mroot
|
|
[cases.test_mtree_one_mroot]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test a single mroot with a custom attribute
|
|
[cases.test_mtree_one_mroot_attr]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, "ardvark", 7))) => 0;
|
|
|
|
uint8_t buffer[7];
|
|
lfsr_mdir_get(&lfs, &lfs.mroot,
|
|
-1, LFSR_TAG_UATTR(1), buffer, 7) => 7;
|
|
assert(memcmp(buffer, "ardvark", 7) == 0);
|
|
|
|
lfsr_mdir_get(&lfs, &lfs.mroot,
|
|
-1, LFSR_TAG_UATTR(1), buffer, 7) => 7;
|
|
assert(memcmp(buffer, "ardvark", 7) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# test a single mroot with many commits
|
|
[cases.test_mtree_one_mroot_many_commits]
|
|
defines.N = [5, 5000]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, &alphas[i % 26], 1))) => 0;
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &lfs.mroot,
|
|
-1, LFSR_TAG_UATTR(1), buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &lfs.mroot,
|
|
-1, LFSR_TAG_UATTR(1), buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[(N-1) % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
## Splitting operations ##
|
|
|
|
# specific split corner cases
|
|
[cases.test_mtree_uninline]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// prepare mroot with a large attr so the next entry can not fit
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
// create a large entry that needs to be uninlined (but not split!)
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
// assert that our entry is still in the mtree
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_uninline_split]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create 2 large entries that needs to be uninlined and split
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdirs were unininlined and split
|
|
assert(lfsr_mtree_weight(&lfs) == 2);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our entries are still in the mtree
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
lfsr_mtree_lookup(&lfs, 1, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_split]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// now add another large entry to the mdir, forcing a split
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
memset(buffer, alphas[2 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mdir to compact
|
|
mdir.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was split correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 2);
|
|
// assert mroot still has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
// assert that our entries are still in the mtree
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
|
|
|
lfsr_mtree_lookup(&lfs, 1, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[2 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
|
|
# TODO test many mroots
|
|
|
|
# try creating a range of entries that may or may not split our mtree
|
|
[cases.test_mtree_split_many]
|
|
defines.N = [5, 10, 20, 40, 80, 160, 320]
|
|
defines.FORCE_COMPACTION = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create entries
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0;
|
|
|
|
lfs_ssize_t rid = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// force a compaction?
|
|
if (FORCE_COMPACTION) {
|
|
mdir.rbyd.off = cfg->block_size;
|
|
lfs.mroot.rbyd.off = cfg->block_size;
|
|
}
|
|
|
|
lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS(
|
|
LFSR_ATTR(rid, MKINLINED, +1, &alphas[i % 26], 1))) => 0;
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
|
buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
|
|
rid += 1;
|
|
}
|
|
|
|
// try looking up each entry
|
|
lfs_size_t i = 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)lfsr_mdir_weight(&mdir);
|
|
rid++) {
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
|
buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
i += 1;
|
|
}
|
|
}
|
|
assert(i == N);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# create random entries
|
|
[cases.test_mtree_split_fuzz]
|
|
defines.N = [5, 10, 20, 40, 80, 160]
|
|
defines.FORCE_COMPACTION = [false, true]
|
|
defines.SAMPLES = 100
|
|
# -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) % (lfsr_mdir_weight(&mdir)+1);
|
|
|
|
// force a compaction?
|
|
if (FORCE_COMPACTION) {
|
|
mdir.rbyd.off = cfg->block_size;
|
|
lfs.mroot.rbyd.off = cfg->block_size;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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)lfsr_mdir_weight(&mdir);
|
|
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;
|
|
}
|
|
'''
|
|
|
|
## Dropping operations ##
|
|
|
|
# specific drop corner cases
|
|
[cases.test_mtree_drop]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// remove the entry, forcing the mdir to be dropped
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 0);
|
|
// assert mroot still has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_compact]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// remove the entry, forcing the mdir to be dropped
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
// force mdir to compact while we're removing
|
|
mdir.rbyd.off = BLOCK_SIZE;
|
|
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 0);
|
|
// assert mroot still has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_uninline]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove the entry as we compact, forcing the mdir to be dropped
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 0);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_uninline_split_l]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an mdir that needs to be split
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove the left entry as we compact, forcing the left
|
|
// mdir to be dropped
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that one entry is still in the mtree
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_uninline_split_r]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an mdir that needs to be split
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove the right entry as we compact, forcing the right mdir
|
|
// to be dropped
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that one entry is still in the mtree
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_uninline_split_both]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an mdir that needs to be split
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove both entries as we compact, forcing both mdirs to be dropped
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0),
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 0);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_split_l]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// now add another large entry to the mdir, forcing a split
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
memset(buffer, alphas[2 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mdir to compact
|
|
mdir.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove the left entry as we compact, forcing the left
|
|
// mdir to be dropped
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){0}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
// assert that one entry is still in the mtree
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[2 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_split_r]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// now add another large entry to the mdir, forcing a split
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
memset(buffer, alphas[2 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mdir to compact
|
|
mdir.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove the right entry as we compact, forcing the right
|
|
// mdir to be dropped
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
// assert that one entry is still in the mtree
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
lfsr_mdir_get(&lfs, &mdir, 0, LFSR_TAG_INLINED,
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[1 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_split_both]
|
|
# this should be set so only one entry can fit in a metadata block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create an uninlined mdir
|
|
uint8_t buffer[SIZE];
|
|
memset(buffer, alphas[0 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(-1, UATTR(1), 0, buffer, SIZE))) => 0;
|
|
|
|
memset(buffer, alphas[1 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mroot to compact
|
|
lfs.mroot.rbyd.off = BLOCK_SIZE;
|
|
lfsr_mdir_commit(&lfs, &lfs.mroot, &(lfs_ssize_t){-1}, NULL, 0) => 0;
|
|
|
|
// assert mdir was unininlined correctly
|
|
assert(lfsr_mtree_weight(&lfs) == 1);
|
|
// assert mroot now has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// now add another large entry to the mdir, forcing a split
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, 0, &mdir) => 0;
|
|
assert(mdir.rbyd.weight == 1);
|
|
|
|
memset(buffer, alphas[2 % 26], SIZE);
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(1, MKINLINED, +1, buffer, SIZE))) => 0;
|
|
|
|
// force mdir to compact
|
|
mdir.rbyd.off = BLOCK_SIZE;
|
|
|
|
// remove both entries as we compact, forcing both mdirs to be dropped
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){1}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0),
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
// assert mdir was dropped
|
|
assert(lfsr_mtree_weight(&lfs) == 0);
|
|
// assert mroot has no entries
|
|
assert(lfs.mroot.rbyd.weight == 0);
|
|
|
|
// assert that our attr is still in the mroot
|
|
lfsr_mdir_get(&lfs, &lfs.mroot, -1, LFSR_TAG_UATTR(1),
|
|
buffer, SIZE) => SIZE;
|
|
assert(memcmp(buffer, &alphas[0 % 26], 1) == 0);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# try creating an mtree and then dropping mdirs
|
|
[cases.test_mtree_drop_many]
|
|
defines.N = [5, 10, 20, 40, 80, 160, 320]
|
|
defines.REMAINING = [20, 5, 1, 0]
|
|
if = 'N > REMAINING'
|
|
defines.FORCE_COMPACTION = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
// create entries
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0;
|
|
|
|
lfs_ssize_t rid = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS(
|
|
LFSR_ATTR(rid, MKINLINED, +1, &alphas[i % 26], 1))) => 0;
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
|
buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
|
|
rid += 1;
|
|
}
|
|
|
|
// remove entries
|
|
for (lfs_size_t i = 0; i < N - REMAINING; i++) {
|
|
lfs_ssize_t mid = (lfsr_mtree_isinlined(&lfs) ? -1 : 0);
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, mid, &mdir) => 0;
|
|
|
|
// drop should make sure we never have empty mdirs
|
|
assert(mdir.mid == -1 || mdir.rbyd.weight > 0);
|
|
|
|
// force a compaction?
|
|
if (FORCE_COMPACTION) {
|
|
mdir.rbyd.off = cfg->block_size;
|
|
lfs.mroot.rbyd.off = cfg->block_size;
|
|
}
|
|
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){0}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
}
|
|
|
|
// try looking up each entry
|
|
lfs_size_t i = N - REMAINING;
|
|
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)lfsr_mdir_weight(&mdir);
|
|
rid++) {
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
|
buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
i += 1;
|
|
}
|
|
}
|
|
assert(i == N);
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
# this one has some pretty nasty corner cases
|
|
[cases.test_mtree_repeated_drop]
|
|
defines.N = [5, 10, 20, 40]
|
|
defines.FORCE_COMPACTION = [false, true]
|
|
defines.CYCLES = 10
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_t lfs;
|
|
lfsr_format(&lfs, cfg) => 0;
|
|
lfsr_mount(&lfs, cfg) => 0;
|
|
|
|
for (lfs_size_t cycle = 0; cycle < CYCLES; cycle++) {
|
|
// create entries
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, lfsr_mtree_weight(&lfs)-1, &mdir) => 0;
|
|
|
|
lfs_ssize_t rid = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS(
|
|
LFSR_ATTR(rid, MKINLINED, +1, &alphas[i % 26], 1))) => 0;
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
|
buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
|
|
rid += 1;
|
|
}
|
|
|
|
// try looking up each entry
|
|
lfs_size_t i = 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)lfsr_mdir_weight(&mdir);
|
|
rid++) {
|
|
uint8_t buffer[4];
|
|
lfsr_mdir_get(&lfs, &mdir, rid, LFSR_TAG_INLINED,
|
|
buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
i += 1;
|
|
}
|
|
}
|
|
assert(i == N);
|
|
|
|
// remove entries
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfs_ssize_t mid = (lfsr_mtree_isinlined(&lfs) ? -1 : 0);
|
|
lfsr_mdir_t mdir;
|
|
lfsr_mtree_lookup(&lfs, mid, &mdir) => 0;
|
|
|
|
// drop should make sure we never have empty mdirs
|
|
assert(mdir.mid == -1 || mdir.rbyd.weight > 0);
|
|
|
|
// force a compaction?
|
|
if (FORCE_COMPACTION) {
|
|
mdir.rbyd.off = cfg->block_size;
|
|
lfs.mroot.rbyd.off = cfg->block_size;
|
|
}
|
|
|
|
lfsr_mdir_commit(&lfs, &mdir, &(lfs_ssize_t){0}, LFSR_ATTRS(
|
|
LFSR_ATTR(0, MKUNR, -1, NULL, 0))) => 0;
|
|
}
|
|
|
|
assert(lfsr_mtree_weight(&lfs) == 0);
|
|
assert(lfsr_mdir_weight(&lfs.mroot) == 0);
|
|
}
|
|
|
|
lfsr_unmount(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_mtree_drop_fuzz]
|
|
defines.N = [5, 10, 20, 40, 80, 160]
|
|
defines.FORCE_COMPACTION = [false, true]
|
|
defines.SAMPLES = 100
|
|
# -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) % (lfsr_mdir_weight(&mdir)+1);
|
|
// choose to create or delete
|
|
uint8_t op = (lfs_size_t)rid == lfsr_mdir_weight(&mdir)
|
|
? 0
|
|
: TEST_PRNG(&prng) % 2;
|
|
|
|
// force a compaction?
|
|
if (FORCE_COMPACTION) {
|
|
mdir.rbyd.off = cfg->block_size;
|
|
lfs.mroot.rbyd.off = cfg->block_size;
|
|
}
|
|
|
|
// create
|
|
if (op == 0) {
|
|
// 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;
|
|
|
|
// delete
|
|
} else {
|
|
lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS(
|
|
LFSR_ATTR(rid, MKUNR, -1, NULL, 0))) => 0;
|
|
|
|
count -= 1;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
// drop should make sure we never have empty mdirs
|
|
assert(mdir.mid == -1 || mdir.rbyd.weight > 0);
|
|
|
|
for (lfs_ssize_t rid = 0;
|
|
rid < (lfs_ssize_t)lfsr_mdir_weight(&mdir);
|
|
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;
|
|
}
|
|
'''
|