Files
littlefs/tests/test_gbmap.toml
T
Christopher Haster c07a69c2a6 preerase: Expanded preerase testing to include non-0xff erase values
I just noticed we weren't testing preerase with non-0xff ecksums at all!

Added to relevant tests:

  # test with a number of different erase values
  defines.ERASE_VALUE = [0xff, 0x00, -1]

The most important non-0xff value being -1 (noop erases), which should
usually result in fragmented ecksums.

Note this was copied from test_rbyd, where we do something similar to
test non-0xff ecksums in rbyd logs.
2026-01-09 00:03:57 -06:00

2015 lines
62 KiB
TOML

# Test some low-level block-map operations
#
# Note these is very much not-exhaustive, but the entire test suite can
# be run with the gbmap if you define LFS3_YES_GBMAP:
#
# LFS3_YES_GBMAP=1 make test -j
#
after = ['test_btree', 'test_mtree']
ifdef = 'LFS3_GBMAP'
# test low-level gbmap operations
# test simple set operations
[cases.test_gbmap_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 6);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 8);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 10);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0;
// replace those blocks as bad, this tests deleting ranges
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMBAD, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMBAD, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMBAD, NULL) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
&bid_, &weight_, NULL) => LFS3_TAG_BMBAD;
assert(bid_ == 6);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, NULL) => LFS3_TAG_BMBAD;
assert(bid_ == 8);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
&bid_, &weight_, NULL) => LFS3_TAG_BMBAD;
assert(bid_ == 10);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 12, LFS3_TAG_BMINUSE, NULL) => 0;
// set neighboring blocks as in-use, triggering merges
lfs3_gbmap_set(&lfs3, &gbmap, 5, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 9, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 13, LFS3_TAG_BMINUSE, NULL) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 4);
assert(weight_ == 5);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 6);
assert(weight_ == 2);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 10);
assert(weight_ == 3);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 11);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 13);
assert(weight_ == 2);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-14);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as in-use, avoid any weird merges for now
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0;
// test a bunch of noops
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 7, LFS3_TAG_BMFREE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 9, LFS3_TAG_BMFREE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMINUSE, NULL) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 6);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 8);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 10);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// test that setting the first and last blocks don't break anything
//
// though in theory blocks 0x{0,1} are immutable...
lfs3_gbmap_set(&lfs3, &gbmap, 0, LFS3_TAG_BMINUSE, NULL) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, BLOCK_COUNT-1, LFS3_TAG_BMINUSE, NULL) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == 0);
assert(weight_ == 1);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 1,
&bid_, &weight_, NULL) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-2);
assert(weight_ == BLOCK_COUNT-2);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, BLOCK_COUNT-1,
&bid_, &weight_, NULL) => LFS3_TAG_BMINUSE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == 1);
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 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 gbmap type
lfs3_tag_t tag = LFS3_TAG_BMRANGE + (TEST_PRNG(&prng) % TYPES);
// set in gbmap
lfs3_gbmap_set(&lfs3, &gbmap, block, tag, NULL) => 0;
// and set in sim
sim[block] = tag;
}
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// check if gbmap 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 gbmap 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 gbmap contain the optimal range?
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, i,
&bid_, &weight_, NULL) => tag;
assert(bid_ == i+(weight_-1));
assert(weight_ == d);
i += d;
}
free(sim);
lfs3_unmount(&lfs3) => 0;
'''
# set operations with ecksums are a bit more complicated
[cases.test_gbmap_set_ecksum_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as erased with ecksums, avoid any weird merges
// for now
lfs3_ecksum_t ecksum = {.cksize=CFG->prog_size, .cksum=0x12345678};
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_ecksum_t ecksum_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 6);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 8);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 10);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_set_ecksum_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as erased with ecksums, avoid any weird merges
// for now
lfs3_ecksum_t ecksum_a = {.cksize=CFG->prog_size, .cksum=0x12345678};
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum_a) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum_a) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum_a) => 0;
// replace those blocks with different ecksums
lfs3_ecksum_t ecksum_b = {.cksize=CFG->prog_size, .cksum=0x9abcdef0};
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum_b) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum_b) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum_b) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_ecksum_t ecksum_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 5);
assert(weight_ == 6);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 6);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 8);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 9);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 10);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-11);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_set_ecksum_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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as erased with ecksums, avoid any weird merges
// for now
lfs3_ecksum_t ecksum = {.cksize=CFG->prog_size, .cksum=0x12345678};
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 12, LFS3_TAG_BMERASED, &ecksum) => 0;
// set neighboring blocks as erased with ecksums, triggering merges
lfs3_gbmap_set(&lfs3, &gbmap, 5, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 9, LFS3_TAG_BMERASED, &ecksum) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 13, LFS3_TAG_BMERASED, &ecksum) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_ecksum_t ecksum_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 4);
assert(weight_ == 5);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 6);
assert(weight_ == 2);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 10);
assert(weight_ == 3);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 11);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 13);
assert(weight_ == 2);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-14);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_set_ecksum_nomerge]
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 gbmap
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(2, LFS3_TAG_BMFREE, -2),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_NULL)) => 0;
// set some blocks as erased with ecksums, avoid any weird merges
// for now
lfs3_ecksum_t ecksum_a = {.cksize=CFG->prog_size, .cksum=0x12345678};
lfs3_gbmap_set(&lfs3, &gbmap, 6, LFS3_TAG_BMERASED, &ecksum_a) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 8, LFS3_TAG_BMERASED, &ecksum_a) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 10, LFS3_TAG_BMERASED, &ecksum_a) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 12, LFS3_TAG_BMERASED, &ecksum_a) => 0;
// set neighboring blocks as erased with _different_ ecksums, this
// should _not_ trigger a merge!
lfs3_ecksum_t ecksum_b = {.cksize=CFG->prog_size, .cksum=0x9abcdef0};
lfs3_gbmap_set(&lfs3, &gbmap, 5, LFS3_TAG_BMERASED, &ecksum_b) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 9, LFS3_TAG_BMERASED, &ecksum_b) => 0;
lfs3_gbmap_set(&lfs3, &gbmap, 13, LFS3_TAG_BMERASED, &ecksum_b) => 0;
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// weight should stay the same
assert(gbmap.r.weight == BLOCK_COUNT);
// check ranges
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_ecksum_t ecksum_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 0,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 4);
assert(weight_ == 5);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 5,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 5);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 6,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 6);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 7,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 7);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 8,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 8);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 9,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 9);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 10,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 10);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 11,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == 11);
assert(weight_ == 1);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 12,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 12);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_a) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 13,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == 13);
assert(weight_ == 1);
assert(lfs3_ecksum_cmp(&ecksum_, &ecksum_b) == 0);
lfs3_gbmap_lookupnext(&lfs3, &gbmap, 14,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMFREE;
assert(bid_ == BLOCK_COUNT-1);
assert(weight_ == BLOCK_COUNT-14);
assert(!lfs3_ecksum_isecksum(&ecksum_));
lfs3_unmount(&lfs3) => 0;
'''
[cases.test_gbmap_set_ecksum_fuzz]
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
defines.MASK = [0x1, 0x3, 0xf, 0xffff]
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 gbmap
lfs3_ecksum_t ecksum = {.cksize=CFG->prog_size, .cksum=0x00000000};
lfs3_btree_t gbmap;
lfs3_btree_init(&gbmap);
lfs3_gbmap_commit(&lfs3, &gbmap, 0, LFS3_RATTRS(
LFS3_RATTR(3, LFS3_TAG_BMERASED, -2, LFS3_FROM_ECKSUM),
LFS3_RATTR_WEIGHT(+BLOCK_COUNT),
LFS3_RATTR_ARG(&ecksum),
LFS3_RATTR_NULL)) => 0;
// create a simulation to compare against
lfs3_tag_t *sim = malloc(BLOCK_COUNT*sizeof(uint32_t));
for (lfs3_size_t i = 0; i < BLOCK_COUNT; i++) {
sim[i] = 0x00000000;
}
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 ecksum
ecksum.cksum = TEST_PRNG(&prng) & MASK;
// set in gbmap
lfs3_gbmap_set(&lfs3, &gbmap,
block, LFS3_TAG_BMERASED, &ecksum) => 0;
// and set in sim
sim[block] = ecksum.cksum;
}
printf("gbmap: w%d 0x%x.%x\n",
gbmap.r.weight,
gbmap.r.blocks[0],
gbmap.r.trunk);
// check if gbmap 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 gbmap is optimal
uint32_t cksum = sim[i];
lfs3_size_t d = 1;
while (i+d < BLOCK_COUNT && sim[i+d] == cksum) {
d += 1;
}
// does our gbmap contain the optimal range?
lfs3_bid_t bid_;
lfs3_bid_t weight_;
lfs3_ecksum_t ecksum_;
lfs3_gbmap_lookupnext(&lfs3, &gbmap, i,
&bid_, &weight_, &ecksum_) => LFS3_TAG_BMERASED;
assert(bid_ == i+(weight_-1));
assert(weight_ == d);
assert(ecksum_.cksize == CFG->prog_size);
assert(ecksum_.cksum == cksum);
i += d;
}
free(sim);
lfs3_unmount(&lfs3) => 0;
'''
# test high-level gbmap operations
# test that the gbmap generally works
[cases.test_gbmap_files]
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted with the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that the gbmap with gc generally works
[cases.test_gbmap_gc_files]
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
defines.PREERASE = [false, true]
defines.GC_FLAGS = '''
LFS3_GC_LOOKAHEAD
| ((PREERASE) ? LFS3_IFDEF_PREERASE(LFS3_GC_PREERASE, -1) : 0)
'''
defines.GC_STEPS = -1
defines.GC_PREERASE_COUNT = ['0', '4', 'COUNT/2', 'COUNT-4', '-1']
# test with a number of different erase values
defines.ERASE_VALUE = [0xff, 0x00, -1]
ifdef = 'LFS3_GC'
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
'LFS3_IFDEF_PREERASE(true, !PREERASE)',
# this is just to reduce useless permutations
'PREERASE || GC_PREERASE_COUNT == 0',
'PREERASE || ERASE_VALUE == 0xff',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the gbmap flag
lfs3_format(&lfs3,
LFS3_F_RDWR
// note preerasing needs revperturb
| ((PREERASE)
? LFS3_IFDEF_PREERASE(LFS3_F_REVPERTURB, -1)
: 0)
| LFS3_F_GBMAP,
&cfg) => 0;
lfs3_mount(&lfs3,
LFS3_M_RDWR
// note preerasing needs revperturb
| ((PREERASE)
? LFS3_IFDEF_PREERASE(LFS3_M_REVPERTURB, -1)
: 0),
&cfg) => 0;
// check that we were formatted with the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// run gc to prebuild gbmap, preerase blocks, etc
lfs3_fs_gc(&lfs3) => 0;
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test high-high-level gbmap operations
# test that we can remove the gbmap and things still work
[cases.test_gbmap_rmgbmap]
ifndef = 'LFS3_YES_GBMAP'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after
# REMOUNT=2 => remount before
# REMOUNT=3 => remount both after and before
defines.REMOUNT = [0, 1, 2, 3]
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted with the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before?
if (REMOUNT & 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now remove the gbmap
lfs3_fs_rmgbmap(&lfs3) => 0;
// remount after?
if (REMOUNT & 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should no longer have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can make the gbmap and things still work
[cases.test_gbmap_mkgbmap]
ifndef = 'LFS3_YES_GBMAP'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after
# REMOUNT=2 => remount before
# REMOUNT=3 => remount both after and before
defines.REMOUNT = [0, 1, 2, 3]
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the lack of gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted without the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before?
if (REMOUNT & 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now make the gbmap
lfs3_fs_mkgbmap(&lfs3) => 0;
// remount after?
if (REMOUNT & 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should now have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can remove and then remake the gbmap and things still
# work
#
# this leaves the disk with garbage gstate that mkgbmap needs to clean
# up
[cases.test_gbmap_rmmkgbmap]
ifndef = 'LFS3_YES_GBMAP'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
# REMOUNT=0x1 => remount after
# REMOUNT=0x2 => remount before make
# REMOUNT=0x4 => remount after remove
# REMOUNT=0x8 => remount before
defines.REMOUNT = 'range(0x10)'
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted with the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before?
if (REMOUNT & 8) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now remove the gbmap
lfs3_fs_rmgbmap(&lfs3) => 0;
// remount after remove?
if (REMOUNT & 4) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should no longer have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before make?
if (REMOUNT & 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now remake the gbmap
lfs3_fs_mkgbmap(&lfs3) => 0;
// remount after?
if (REMOUNT & 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should now have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can make and then remove the gbmap and things still
# work
#
# not exactly sure what this tests, but including for completeness
[cases.test_gbmap_mkrmgbmap]
ifndef = 'LFS3_YES_GBMAP'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
# REMOUNT=0x1 => remount after
# REMOUNT=0x2 => remount before remove
# REMOUNT=0x4 => remount after make
# REMOUNT=0x8 => remount before
defines.REMOUNT = 'range(0x10)'
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the lack of gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted without the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before?
if (REMOUNT & 8) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now make the gbmap
lfs3_fs_mkgbmap(&lfs3) => 0;
// remount after make?
if (REMOUNT & 4) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should now have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before remove?
if (REMOUNT & 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now remove the gbmap
lfs3_fs_rmgbmap(&lfs3) => 0;
// remount after?
if (REMOUNT & 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should no longer have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that removing a non-existant gbmap errors
[cases.test_gbmap_rmgbmap_noent]
ifndef = 'LFS3_YES_GBMAP'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after
# REMOUNT=2 => remount before
# REMOUNT=3 => remount both after and before
defines.REMOUNT = [0, 1, 2, 3]
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the lack of gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted without the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before?
if (REMOUNT & 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now try to remove the gbmap
lfs3_fs_rmgbmap(&lfs3) => LFS3_ERR_NOENT;
// remount after?
if (REMOUNT & 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should no longer have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(!(fsinfo.flags & LFS3_I_GBMAP));
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that making an existant gbmap errors
[cases.test_gbmap_mkgbmap_exist]
ifndef = 'LFS3_YES_GBMAP'
defines.COUNT = [
'BLOCK_COUNT',
'BLOCK_COUNT-1',
'BLOCK_COUNT/2',
'BLOCK_COUNT/4',
'5',
'3',
]
defines.SIZE = [
'BLOCK_SIZE',
'2*BLOCK_SIZE',
'8*BLOCK_SIZE',
]
defines.N = ['5', 'BLOCK_COUNT/2']
defines.WRAPAROUND = 3
# REMOUNT=0 => don't remount
# REMOUNT=1 => remount after
# REMOUNT=2 => remount before
# REMOUNT=3 => remount both after and before
defines.REMOUNT = [0, 1, 2, 3]
if = [
'COUNT >= 3',
'COUNT >= 2*N*SIZE/BLOCK_SIZE',
]
code = '''
// test various block counts
struct lfs3_cfg cfg = *CFG;
cfg.block_count = COUNT;
lfs3_t lfs3;
// note the gbmap flag
lfs3_format(&lfs3, LFS3_F_RDWR | LFS3_F_GBMAP, &cfg) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
// check that we were formatted with the gbmap
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
uint32_t prng = 42;
uint32_t prng_ = prng;
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
// remount before?
if (REMOUNT & 2) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// now make the gbmap
lfs3_fs_mkgbmap(&lfs3) => LFS3_ERR_EXIST;
// remount after?
if (REMOUNT & 1) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// we should now have a gbmap
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
assert(fsinfo.flags & LFS3_I_GBMAP);
// create n files repeatedly until we're sure we've wrapped around
// a few times
for (lfs3_size_t i = 0;
i < (WRAPAROUND*COUNT)
/ (N*(SIZE/BLOCK_SIZE));
i++) {
prng = prng_;
for (lfs3_size_t n = 0; n < N; n++) {
char name[256];
sprintf(name, "file%08d", n);
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, &cfg) => 0;
}
// check that our file writes worked
prng_ = prng;
for (lfs3_size_t i = 0; i < N; i++) {
// check with stat
char name[256];
sprintf(name, "file%08d", i);
struct lfs3_info info;
lfs3_stat(&lfs3, name, &info) => 0;
assert(strcmp(info.name, name) == 0);
assert(info.type == LFS3_TYPE_REG);
assert(info.size == SIZE);
// try reading the file, note we reset prng above
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26);
}
lfs3_file_t file;
uint8_t rbuf[SIZE];
lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''