From ecc2857c0e900d1ae1f49de507fb9851345e6ca7 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 14 Jan 2020 12:04:20 -0600 Subject: [PATCH] Migrated bad-block tests Even with adding better reentrance testing, the bad-block tests are still very useful at isolating the block eviction logic. This also required rewriting a bit of the internal testing wirework to allow custom block devices which opens up quite a bit more straegies for testing. --- scripts/test_.py | 124 +++++++++--- tests_/test_badblocks.toml | 375 +++++++++++++++++++++++++++++++++++++ 2 files changed, 470 insertions(+), 29 deletions(-) create mode 100644 tests_/test_badblocks.toml diff --git a/scripts/test_.py b/scripts/test_.py index 760f64e..2b78510 100755 --- a/scripts/test_.py +++ b/scripts/test_.py @@ -19,6 +19,7 @@ # x config chaining correct # - why can't gdb see my defines? # - say no to internal? +# - buffering stdout issues? import toml import glob @@ -55,8 +56,79 @@ GLOBALS = """ #include "filebd/lfs_filebd.h" #include "rambd/lfs_rambd.h" #include + +extern const char *lfs_testbd_disk; +typedef union { + lfs_filebd_t filebd; + lfs_rambd_t rambd; +} lfs_testbd_t; +struct lfs_testbd_config { + struct lfs_filebd_config filecfg; + struct lfs_rambd_config ramcfg; +}; + +__attribute__((unused)) +static int lfs_testbd_createcfg(const struct lfs_config *cfg, + const struct lfs_testbd_config *bdcfg) { + if (lfs_testbd_disk) { + return lfs_filebd_createcfg(cfg, lfs_testbd_disk, &bdcfg->filecfg); + } else { + return lfs_rambd_createcfg(cfg, &bdcfg->ramcfg); + } +} + +__attribute__((unused)) +static void lfs_testbd_destroy(const struct lfs_config *cfg) { + if (lfs_testbd_disk) { + lfs_filebd_destroy(cfg); + } else { + lfs_rambd_destroy(cfg); + } +} + +__attribute__((unused)) +static int lfs_testbd_read(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) { + if (lfs_testbd_disk) { + return lfs_filebd_read(cfg, block, off, buffer, size); + } else { + return lfs_rambd_read(cfg, block, off, buffer, size); + } +} + +__attribute__((unused)) +static int lfs_testbd_prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) { + if (lfs_testbd_disk) { + return lfs_filebd_prog(cfg, block, off, buffer, size); + } else { + return lfs_rambd_prog(cfg, block, off, buffer, size); + } +} + +__attribute__((unused)) +static int lfs_testbd_erase(const struct lfs_config *cfg, lfs_block_t block) { + if (lfs_testbd_disk) { + return lfs_filebd_erase(cfg, block); + } else { + return lfs_rambd_erase(cfg, block); + } +} + +__attribute__((unused)) +static int lfs_testbd_sync(const struct lfs_config *cfg) { + if (lfs_testbd_disk) { + return lfs_filebd_sync(cfg); + } else { + return lfs_rambd_sync(cfg); + } +} """ DEFINES = { + "LFS_BD_READ": "lfs_testbd_read", + "LFS_BD_PROG": "lfs_testbd_prog", + "LFS_BD_ERASE": "lfs_testbd_erase", + "LFS_BD_SYNC": "lfs_testbd_sync", "LFS_READ_SIZE": 16, "LFS_PROG_SIZE": "LFS_READ_SIZE", "LFS_BLOCK_SIZE": 512, @@ -68,11 +140,8 @@ DEFINES = { } PROLOGUE = """ // prologue - extern const char *LFS_DISK; - __attribute__((unused)) lfs_t lfs; - __attribute__((unused)) lfs_filebd_t filebd; - __attribute__((unused)) lfs_rambd_t rambd; + __attribute__((unused)) lfs_testbd_t bd; __attribute__((unused)) lfs_file_t file; __attribute__((unused)) lfs_dir_t dir; __attribute__((unused)) struct lfs_info info; @@ -82,12 +151,11 @@ PROLOGUE = """ __attribute__((unused)) int err; __attribute__((unused)) const struct lfs_config cfg = { - .context = LFS_DISK ? (void*)&filebd : (void*)&rambd, - .read = LFS_DISK ? &lfs_filebd_read : &lfs_rambd_read, - .prog = LFS_DISK ? &lfs_filebd_prog : &lfs_rambd_prog, - .erase = LFS_DISK ? &lfs_filebd_erase : &lfs_rambd_erase, - .sync = LFS_DISK ? &lfs_filebd_sync : &lfs_rambd_sync, - + .context = &bd, + .read = LFS_BD_READ, + .prog = LFS_BD_PROG, + .erase = LFS_BD_ERASE, + .sync = LFS_BD_SYNC, .read_size = LFS_READ_SIZE, .prog_size = LFS_PROG_SIZE, .block_size = LFS_BLOCK_SIZE, @@ -97,26 +165,16 @@ PROLOGUE = """ .lookahead_size = LFS_LOOKAHEAD_SIZE, }; - __attribute__((unused)) const struct lfs_filebd_config filecfg = { - .erase_value = LFS_ERASE_VALUE, - }; - __attribute__((unused)) const struct lfs_rambd_config ramcfg = { - .erase_value = LFS_ERASE_VALUE, + __attribute__((unused)) const struct lfs_testbd_config bdcfg = { + .filecfg.erase_value = LFS_ERASE_VALUE, + .ramcfg.erase_value = LFS_ERASE_VALUE, }; - if (LFS_DISK) { - lfs_filebd_createcfg(&cfg, LFS_DISK, &filecfg); - } else { - lfs_rambd_createcfg(&cfg, &ramcfg); - } + lfs_testbd_createcfg(&cfg, &bdcfg) => 0; """ EPILOGUE = """ // epilogue - if (LFS_DISK) { - lfs_filebd_destroy(&cfg); - } else { - lfs_rambd_destroy(&cfg); - } + lfs_testbd_destroy(&cfg); """ PASS = '\033[32m✓\033[0m' FAIL = '\033[31m✗\033[0m' @@ -363,15 +421,20 @@ class TestSuite: if re.match(r'code\s*=\s*(\'\'\'|""")', line): code_linenos.append(i+2) + code_linenos.reverse() + # grab global config self.defines = config.get('define', {}) + self.code = config.get('code', None) + if self.code is not None: + self.code_lineno = code_linenos.pop() # create initial test cases self.cases = [] for i, (case, lineno) in enumerate(zip(config['case'], linenos)): # code lineno? if 'code' in case: - case['code_lineno'] = code_linenos.pop(0) + case['code_lineno'] = code_linenos.pop() # give our case's config a copy of our "global" config for k, v in config.items(): if k not in case: @@ -452,8 +515,11 @@ class TestSuite: # build test files tf = open(self.path + '.test.c.t', 'w') tf.write(GLOBALS) - tfs = {None: tf} + if self.code is not None: + tf.write('#line %d "%s"\n' % (self.code_lineno, self.path)) + tf.write(self.code) + tfs = {None: tf} for case in self.cases: if case.in_ not in tfs: tfs[case.in_] = open(self.path+'.'+ @@ -469,11 +535,11 @@ class TestSuite: case.build(tfs[case.in_], **args) tf.write('\n') - tf.write('const char *LFS_DISK = NULL;\n') + tf.write('const char *lfs_testbd_disk;\n') tf.write('int main(int argc, char **argv) {\n') tf.write(4*' '+'int case_ = (argc >= 2) ? atoi(argv[1]) : 0;\n') tf.write(4*' '+'int perm = (argc >= 3) ? atoi(argv[2]) : 0;\n') - tf.write(4*' '+'LFS_DISK = (argc >= 4) ? argv[3] : NULL;\n') + tf.write(4*' '+'lfs_testbd_disk = (argc >= 4) ? argv[3] : NULL;\n') for perm in self.perms: # test declaration tf.write(4*' '+'extern void test_case%d(%s);\n' % ( diff --git a/tests_/test_badblocks.toml b/tests_/test_badblocks.toml new file mode 100644 index 0000000..7599647 --- /dev/null +++ b/tests_/test_badblocks.toml @@ -0,0 +1,375 @@ +# special bad-block block device hook for simulating blocks +# that are no longer writable +code = ''' +lfs_block_t *bbbd_badblocks; +size_t bbbd_badblocks_count; + +int bbbd_read(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) { + for (size_t i = 0; i < bbbd_badblocks_count; i++) { + if (bbbd_badblocks[i] == block) { + return LFS_ERR_CORRUPT; + } + } + + return lfs_testbd_read(c, block, off, buffer, size); +} + +int bbbd_prog(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) { + for (size_t i = 0; i < bbbd_badblocks_count; i++) { + if (bbbd_badblocks[i] == block) { + return LFS_ERR_CORRUPT; + } + } + + return lfs_testbd_prog(c, block, off, buffer, size); +} + +int bbbd_erase(const struct lfs_config *c, lfs_block_t block) { + for (size_t i = 0; i < bbbd_badblocks_count; i++) { + if (bbbd_badblocks[i] == block) { + return LFS_ERR_CORRUPT; + } + } + + return lfs_testbd_erase(c, block); +} +''' + +[[case]] # single bad blocks +define.LFS_BD_PROG = 'bbbd_prog' +define.NAMEMULT = 64 +define.FILEMULT = 1 +code = ''' + for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) { + bbbd_badblocks = &(lfs_block_t){badblock}; + bbbd_badblocks_count = 1; + + lfs_format(&lfs, &cfg) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_mkdir(&lfs, (char*)buffer) => 0; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, + LFS_O_WRONLY | LFS_O_CREAT) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + lfs_file_write(&lfs, &file, buffer, size) => size; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_stat(&lfs, (char*)buffer, &info) => 0; + info.type => LFS_TYPE_DIR; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + uint8_t rbuffer[1024]; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(buffer, rbuffer, size) => 0; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + } +''' + +[[case]] # single persistent blocks (can't erase) +define.LFS_BD_ERASE = 'bbbd_erase' +define.NAMEMULT = 64 +define.FILEMULT = 1 +code = ''' + for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) { + bbbd_badblocks = &(lfs_block_t){badblock}; + bbbd_badblocks_count = 1; + + lfs_format(&lfs, &cfg) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_mkdir(&lfs, (char*)buffer) => 0; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, + LFS_O_WRONLY | LFS_O_CREAT) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + lfs_file_write(&lfs, &file, buffer, size) => size; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_stat(&lfs, (char*)buffer, &info) => 0; + info.type => LFS_TYPE_DIR; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + uint8_t rbuffer[1024]; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(buffer, rbuffer, size) => 0; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + } +''' + +[[case]] # single unreadable blocks (can't read) +define.LFS_BD_READ = 'bbbd_read' +define.NAMEMULT = 64 +define.FILEMULT = 1 +code = ''' + for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) { + bbbd_badblocks = &(lfs_block_t){badblock}; + bbbd_badblocks_count = 1; + + lfs_format(&lfs, &cfg) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_mkdir(&lfs, (char*)buffer) => 0; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, + LFS_O_WRONLY | LFS_O_CREAT) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + lfs_file_write(&lfs, &file, buffer, size) => size; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_stat(&lfs, (char*)buffer, &info) => 0; + info.type => LFS_TYPE_DIR; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + uint8_t rbuffer[1024]; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(buffer, rbuffer, size) => 0; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + } +''' + +[[case]] # region corruption (causes cascading failures) +define.LFS_BD_PROG = '"BADTYPE == 0 ? bbbd_prog : lfs_testbd_prog "' +define.LFS_BD_ERASE = '"BADTYPE == 1 ? bbbd_erase : lfs_testbd_erase"' +define.LFS_BD_READ = '"BADTYPE == 2 ? bbbd_read : lfs_testbd_read "' +define.BADTYPE = 'range(3)' +define.NAMEMULT = 64 +define.FILEMULT = 1 +code = ''' + bbbd_badblocks_count = LFS_BLOCK_COUNT/2; + bbbd_badblocks = malloc(bbbd_badblocks_count*sizeof(lfs_block_t)); + for (size_t i = 0; i < bbbd_badblocks_count; i++) { + bbbd_badblocks[i] = i+2; + } + + lfs_format(&lfs, &cfg) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_mkdir(&lfs, (char*)buffer) => 0; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, + LFS_O_WRONLY | LFS_O_CREAT) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + lfs_file_write(&lfs, &file, buffer, size) => size; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_stat(&lfs, (char*)buffer, &info) => 0; + info.type => LFS_TYPE_DIR; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + uint8_t rbuffer[1024]; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(buffer, rbuffer, size) => 0; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + free(bbbd_badblocks); +''' + +[[case]] # alternating corruption (causes cascading failures) +define.LFS_BD_PROG = '"BADTYPE == 0 ? bbbd_prog : lfs_testbd_prog "' +define.LFS_BD_ERASE = '"BADTYPE == 1 ? bbbd_erase : lfs_testbd_erase"' +define.LFS_BD_READ = '"BADTYPE == 2 ? bbbd_read : lfs_testbd_read "' +define.BADTYPE = 'range(3)' +define.NAMEMULT = 64 +define.FILEMULT = 1 +code = ''' + bbbd_badblocks_count = LFS_BLOCK_COUNT/2; + bbbd_badblocks = malloc(bbbd_badblocks_count*sizeof(lfs_block_t)); + for (size_t i = 0; i < bbbd_badblocks_count; i++) { + bbbd_badblocks[i] = (2*i) + 2; + } + + lfs_format(&lfs, &cfg) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_mkdir(&lfs, (char*)buffer) => 0; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, + LFS_O_WRONLY | LFS_O_CREAT) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + lfs_file_write(&lfs, &file, buffer, size) => size; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + lfs_mount(&lfs, &cfg) => 0; + for (int i = 1; i < 10; i++) { + for (int j = 0; j < NAMEMULT; j++) { + buffer[j] = '0'+i; + } + buffer[NAMEMULT] = '\0'; + lfs_stat(&lfs, (char*)buffer, &info) => 0; + info.type => LFS_TYPE_DIR; + + buffer[NAMEMULT] = '/'; + for (int j = 0; j < NAMEMULT; j++) { + buffer[j+NAMEMULT+1] = '0'+i; + } + buffer[2*NAMEMULT+1] = '\0'; + lfs_file_open(&lfs, &file, (char*)buffer, LFS_O_RDONLY) => 0; + + size = NAMEMULT; + for (int j = 0; j < i*FILEMULT; j++) { + uint8_t rbuffer[1024]; + lfs_file_read(&lfs, &file, rbuffer, size) => size; + memcmp(buffer, rbuffer, size) => 0; + } + + lfs_file_close(&lfs, &file) => 0; + } + lfs_unmount(&lfs) => 0; + + free(bbbd_badblocks); +'''