Files
littlefs/tests/test_bmap.toml
T
Christopher Haster 838a4beee1 bmap: Moved gbmap traversal to the end
This avoids issues with the different traversal paths with an mtree vs
inline-mtree. Previously this was broken when the mtree was inlined.

This order also makes more sense if we want to check mdirs before we
consider the gstate to be trustworthy enough for gbmap traversal.
2025-10-01 17:56:10 -05:00

378 lines
12 KiB
TOML

# Test some low-level block-map operations
after = ['test_btree', 'test_mtree']
ifdef = 'LFS3_BMAP'
# test simple set operations
[cases.test_bmap_set_split]
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_alloc_ckpoint(&lfs3);
// create an initial bmap
lfs3_btree_t bmap;
lfs3_btree_init(&bmap);
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
printf("bmap: w%d 0x%x.%x\n",
bmap.r.weight,
bmap.r.blocks[0],
bmap.r.trunk);
// weight should stay the same
assert(bmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
lfs3_bmap_lookupnext(&lfs3, &bmap, 6,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 6);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 8);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 9,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 10,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 10);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_bmap_set_replace]
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_alloc_ckpoint(&lfs3);
// create an initial bmap
lfs3_btree_t bmap;
lfs3_btree_init(&bmap);
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
// replace those blocks as bad, this tests deleting ranges
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINFLIGHT) => 0;
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINFLIGHT) => 0;
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINFLIGHT) => 0;
printf("bmap: w%d 0x%x.%x\n",
bmap.r.weight,
bmap.r.blocks[0],
bmap.r.trunk);
// weight should stay the same
assert(bmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
lfs3_bmap_lookupnext(&lfs3, &bmap, 6,
&bid_, &weight_) => LFS3_TAG_BMINFLIGHT;
assert(bid_ == 6);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
&bid_, &weight_) => LFS3_TAG_BMINFLIGHT;
assert(bid_ == 8);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 9,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 10,
&bid_, &weight_) => LFS3_TAG_BMINFLIGHT;
assert(bid_ == 10);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_bmap_set_merge]
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_alloc_ckpoint(&lfs3);
// create an initial bmap
lfs3_btree_t bmap;
lfs3_btree_init(&bmap);
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 12, LFS3_TAG_BMINUSE) => 0;
// set neighboring blocks as in-use, triggering merges
lfs3_bmap_set(&lfs3, &bmap, 5, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 9, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 13, LFS3_TAG_BMINUSE) => 0;
printf("bmap: w%d 0x%x.%x\n",
bmap.r.weight,
bmap.r.blocks[0],
bmap.r.trunk);
// weight should stay the same
assert(bmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 4);
assert(weight_ == 5);
lfs3_bmap_lookupnext(&lfs3, &bmap, 5,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 6);
assert(weight_ == 2);
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 10);
assert(weight_ == 3);
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 11);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 12,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 13);
assert(weight_ == 2);
lfs3_bmap_lookupnext(&lfs3, &bmap, 14,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-14);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_bmap_set_noop]
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_alloc_ckpoint(&lfs3);
// create an initial bmap
lfs3_btree_t bmap;
lfs3_btree_init(&bmap);
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
// test a bunch of noops
lfs3_bmap_set(&lfs3, &bmap, 6, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 7, LFS3_TAG_BMFREE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 8, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 9, LFS3_TAG_BMFREE) => 0;
lfs3_bmap_set(&lfs3, &bmap, 10, LFS3_TAG_BMINUSE) => 0;
printf("bmap: w%d 0x%x.%x\n",
bmap.r.weight,
bmap.r.blocks[0],
bmap.r.trunk);
// weight should stay the same
assert(bmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
lfs3_bmap_lookupnext(&lfs3, &bmap, 6,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 6);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 7,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 8,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 8);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 9,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 10,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 10);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 11,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_bmap_set_bounds]
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_alloc_ckpoint(&lfs3);
// create an initial bmap
lfs3_btree_t bmap;
lfs3_btree_init(&bmap);
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
// test that setting the first and last blocks don't break anything
//
// though in theory blocks 0x{0,1} are immutable...
lfs3_bmap_set(&lfs3, &bmap, 0, LFS3_TAG_BMINUSE) => 0;
lfs3_bmap_set(&lfs3, &bmap, BLOCK_COUNT-1, LFS3_TAG_BMINUSE) => 0;
printf("bmap: w%d 0x%x.%x\n",
bmap.r.weight,
bmap.r.blocks[0],
bmap.r.trunk);
// weight should stay the same
assert(bmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_bmap_lookupnext(&lfs3, &bmap, 0,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == 0);
assert(weight_ == 1);
lfs3_bmap_lookupnext(&lfs3, &bmap, 1,
&bid_, &weight_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-2);
assert(weight_ == BLOCK_COUNT-2);
lfs3_bmap_lookupnext(&lfs3, &bmap, BLOCK_COUNT-1,
&bid_, &weight_) => LFS3_TAG_BMINUSE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == 1);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_bmap_set_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.TYPES = [2, 3, 4]
defines.SEED = 'range(20)'
fuzz = 'SEED'
in = 'lfs3.c'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
lfs3_alloc_ckpoint(&lfs3);
// create an initial bmap
lfs3_btree_t bmap;
lfs3_btree_init(&bmap);
lfs3_bmap_commit(&lfs3, &bmap, 0, LFS3_RATTRS(
LFS3_RATTR(LFS3_TAG_BMFREE, +BLOCK_COUNT))) => 0;
// create a simulation to compare against
lfs3_tag_t *sim = malloc(BLOCK_COUNT*sizeof(lfs3_tag_t));
for (lfs3_size_t i = 0; i < BLOCK_COUNT; i++) {
sim[i] = LFS3_TAG_BMFREE;
}
uint32_t prng = SEED;
for (lfs3_size_t i = 0; i < N; i++) {
// choose a pseudo-random block
lfs3_block_t block = TEST_PRNG(&prng) % BLOCK_COUNT;
// and pseudo-random bmap type
lfs3_tag_t tag = LFS3_TAG_BMRANGE + (TEST_PRNG(&prng) % TYPES);
// set in bmap
lfs3_bmap_set(&lfs3, &bmap, block, tag) => 0;
// and set in sim
sim[block] = tag;
}
printf("bmap: w%d 0x%x.%x\n",
bmap.r.weight,
bmap.r.blocks[0],
bmap.r.trunk);
// check if bmap matches sim
lfs3_size_t i = 0;
while (i < BLOCK_COUNT) {
// we need to convert our sim's raw blocks to compressed ranges,
// in theory our bmap is optimal
lfs3_tag_t tag = sim[i];
lfs3_size_t d = 1;
while (i+d < BLOCK_COUNT && sim[i+d] == tag) {
d += 1;
}
// does our bmap contain the optimal range?
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_bmap_lookupnext(&lfs3, &bmap, i,
&bid_, &weight_) => tag;
assert(bid_ == i+(weight_-1));
assert(weight_ == d);
i += d;
}
free(sim);
lfs3_unmount(&lfs3) => 0;
'''