diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/explode_asserts.py | 6 | ||||
-rwxr-xr-x | scripts/test_.py | 227 |
2 files changed, 87 insertions, 146 deletions
diff --git a/scripts/explode_asserts.py b/scripts/explode_asserts.py index 7c24c63..ff3f260 100755 --- a/scripts/explode_asserts.py +++ b/scripts/explode_asserts.py @@ -146,7 +146,7 @@ def pnested(): pexpr = ( # shortcut for a bit better performance - p.regex('[^%s/#\'"();{}=><,&|-]+' % ASSERT_CHARS) | + p.regex('[^%s/#\'"():;{}=><,&|-]+' % ASSERT_CHARS) | pws | passert | pstring | @@ -157,7 +157,7 @@ pexpr = ( @p.generate def pstmt(): ws = yield pws.many() - lh = yield pexpr.until(p.string('=>') | p.regex('[;{}]')) + lh = yield pexpr.until(p.string('=>') | p.regex('[:;{}]')) op = yield p.string('=>').optional() if op == '=>': rh = yield pstmt @@ -168,7 +168,7 @@ def pstmt(): @p.generate def pstmts(): a = yield pstmt - b = yield (p.regex('[;{}]') + pstmt).many() + b = yield (p.regex('[:;{}]') + pstmt).many() return [a] + b def main(args): diff --git a/scripts/test_.py b/scripts/test_.py index 2b78510..c4e44a1 100755 --- a/scripts/test_.py +++ b/scripts/test_.py @@ -19,7 +19,7 @@ # x config chaining correct # - why can't gdb see my defines? # - say no to internal? -# - buffering stdout issues? +# x buffering stdout issues? import toml import glob @@ -33,6 +33,9 @@ import base64 import sys import copy import shlex +import pty +import errno +import signal TESTDIR = 'tests_' RULES = """ @@ -45,98 +48,31 @@ $(foreach target,$(SRC),$(eval $(FLATTEN))) -include tests_/*.d .SECONDARY: -%.test: override CFLAGS += -fdiagnostics-color=always -%.test: override CFLAGS += -ggdb +%.test: override CFLAGS += -gdwarf-2 +%.test: override CFLAGS += -ggdb3 +%.test: override CFLAGS += -g3 %.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ """ GLOBALS = """ //////////////// AUTOGENERATED TEST //////////////// #include "lfs.h" -#include "filebd/lfs_filebd.h" -#include "rambd/lfs_rambd.h" +#include "testbd/lfs_testbd.h" #include <stdio.h> - -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); - } -} +extern const char *lfs_testbd_path; +extern uint32_t lfs_testbd_cycles; """ 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, - "LFS_BLOCK_COUNT": 1024, - "LFS_BLOCK_CYCLES": -1, - "LFS_CACHE_SIZE": "(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)", - "LFS_LOOKAHEAD_SIZE": 16, - "LFS_ERASE_VALUE": 0xff, + 'LFS_READ_SIZE': 16, + 'LFS_PROG_SIZE': 'LFS_READ_SIZE', + 'LFS_BLOCK_SIZE': 512, + 'LFS_BLOCK_COUNT': 1024, + 'LFS_BLOCK_CYCLES': -1, + 'LFS_CACHE_SIZE': '(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)', + 'LFS_LOOKAHEAD_SIZE': 16, + 'LFS_ERASE_VALUE': 0xff, + 'LFS_ERASE_CYCLES': 0, + 'LFS_BADBLOCK_BEHAVIOR': 'LFS_TESTBD_BADBLOCK_NOPROG', } PROLOGUE = """ // prologue @@ -152,10 +88,10 @@ PROLOGUE = """ __attribute__((unused)) const struct lfs_config cfg = { .context = &bd, - .read = LFS_BD_READ, - .prog = LFS_BD_PROG, - .erase = LFS_BD_ERASE, - .sync = LFS_BD_SYNC, + .read = lfs_testbd_read, + .prog = lfs_testbd_prog, + .erase = lfs_testbd_erase, + .sync = lfs_testbd_sync, .read_size = LFS_READ_SIZE, .prog_size = LFS_PROG_SIZE, .block_size = LFS_BLOCK_SIZE, @@ -166,15 +102,17 @@ PROLOGUE = """ }; __attribute__((unused)) const struct lfs_testbd_config bdcfg = { - .filecfg.erase_value = LFS_ERASE_VALUE, - .ramcfg.erase_value = LFS_ERASE_VALUE, + .erase_value = LFS_ERASE_VALUE, + .erase_cycles = LFS_ERASE_CYCLES, + .badblock_behavior = LFS_BADBLOCK_BEHAVIOR, + .power_cycles = lfs_testbd_cycles, }; - lfs_testbd_createcfg(&cfg, &bdcfg) => 0; + lfs_testbd_createcfg(&cfg, lfs_testbd_path, &bdcfg) => 0; """ EPILOGUE = """ // epilogue - lfs_testbd_destroy(&cfg); + lfs_testbd_destroy(&cfg) => 0; """ PASS = '\033[32m✓\033[0m' FAIL = '\033[31m✗\033[0m' @@ -224,15 +162,15 @@ class TestCase: def build(self, f, **_): # prologue - f.write('void test_case%d(%s) {\n' % (self.caseno, ','.join( + for k, v in sorted(self.defines.items()): + if k not in self.suite.defines: + f.write('#define %s %s\n' % (k, v)) + + f.write('void test_case%d(%s) {' % (self.caseno, ','.join( '\n'+8*' '+'__attribute__((unused)) intmax_t %s' % k for k in sorted(self.perms[0].defines) if k not in self.defines))) - for k, v in sorted(self.defines.items()): - if k not in self.suite.defines: - f.write(4*' '+'#define %s %s\n' % (k, v)) - f.write(PROLOGUE) f.write('\n') f.write(4*' '+'// test case %d\n' % self.caseno) @@ -243,13 +181,11 @@ class TestCase: # epilogue f.write(EPILOGUE) - f.write('\n') + f.write('}\n') for k, v in sorted(self.defines.items()): if k not in self.suite.defines: - f.write(4*' '+'#undef %s\n' % k) - - f.write('}\n') + f.write('#undef %s\n' % k) def shouldtest(self, **args): if (self.filter is not None and @@ -265,7 +201,8 @@ class TestCase: else: return True - def test(self, exec=[], persist=False, gdb=False, failure=None, **args): + def test(self, exec=[], persist=False, cycles=None, + gdb=False, failure=None, **args): # build command cmd = exec + ['./%s.test' % self.suite.path, repr(self.caseno), repr(self.permno)] @@ -280,6 +217,10 @@ class TestCase: cmd.append(self.suite.path + '.disk') + # simulate power-loss after n cycles? + if cycles: + cmd.append(str(cycles)) + # failed? drop into debugger? if gdb and failure: ncmd = ['gdb'] @@ -295,19 +236,25 @@ class TestCase: if args.get('verbose', False): print(' '.join(shlex.quote(c) for c in ncmd)) + signal.signal(signal.SIGINT, signal.SIG_IGN) sys.exit(sp.call(ncmd)) # run test case! - stdout = [] - assert_ = None + mpty, spty = pty.openpty() if args.get('verbose', False): print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, - universal_newlines=True, - bufsize=1, - stdout=sp.PIPE, - stderr=sp.STDOUT) - for line in iter(proc.stdout.readline, ''): + proc = sp.Popen(cmd, stdout=spty, stderr=spty) + os.close(spty) + mpty = os.fdopen(mpty, 'r', 1) + stdout = [] + assert_ = None + while True: + try: + line = mpty.readline() + except OSError as e: + if e.errno == errno.EIO: + break + raise stdout.append(line) if args.get('verbose', False): sys.stdout.write(line) @@ -361,36 +308,23 @@ class ReentrantTestCase(TestCase): return self.reentrant and super().shouldtest(**args) def test(self, exec=[], persist=False, gdb=False, failure=None, **args): - # clear disk first? - if persist != 'noerase': - try: - os.remove(self.suite.path + '.disk') - except FileNotFoundError: - pass - for cycles in it.count(1): + # clear disk first? + if cycles == 1 and persist != 'noerase': + persist = 'erase' + else: + persist = 'noerase' + # exact cycle we should drop into debugger? if gdb and failure and failure.cycleno == cycles: - return super().test(exec=exec, persist='noerase', - gdb=gdb, failure=failure, **args) + return super().test(gdb=gdb, + persist=persist, failure=failure, **args) # run tests, but kill the program after prog/erase has # been hit n cycles. We exit with a special return code if the # program has not finished, since this isn't a test failure. - nexec = exec + [ - 'gdb', '-batch-silent', - '-ex', 'handle all nostop', - '-ex', 'b lfs_filebd_prog', - '-ex', 'b lfs_filebd_erase', - '-ex', 'r', - ] + cycles*['-ex', 'c'] + [ - '-ex', 'q ' - '!$_isvoid($_exitsignal) ? $_exitsignal : ' - '!$_isvoid($_exitcode) ? $_exitcode : ' - '33', - '--args'] try: - return super().test(exec=nexec, persist='noerase', **args) + return super().test(persist=persist, cycles=cycles, **args) except TestFailure as nfailure: if nfailure.returncode == 33: continue @@ -535,11 +469,13 @@ class TestSuite: case.build(tfs[case.in_], **args) tf.write('\n') - tf.write('const char *lfs_testbd_disk;\n') + tf.write('const char *lfs_testbd_path;\n') + tf.write('uint32_t lfs_testbd_cycles;\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_testbd_disk = (argc >= 4) ? argv[3] : NULL;\n') + tf.write(4*' '+'int case_ = (argc > 1) ? atoi(argv[1]) : 0;\n') + tf.write(4*' '+'int perm = (argc > 2) ? atoi(argv[2]) : 0;\n') + tf.write(4*' '+'lfs_testbd_path = (argc > 3) ? argv[3] : NULL;\n') + tf.write(4*' '+'lfs_testbd_cycles = (argc > 4) ? atoi(argv[4]) : 0;\n') for perm in self.perms: # test declaration tf.write(4*' '+'extern void test_case%d(%s);\n' % ( @@ -671,15 +607,20 @@ def main(**args): cmd = (['make', '-f', 'Makefile'] + list(it.chain.from_iterable(['-f', m] for m in makefiles)) + [target for target in targets]) - stdout = [] + mpty, spty = pty.openpty() if args.get('verbose', False): print(' '.join(shlex.quote(c) for c in cmd)) - proc = sp.Popen(cmd, - universal_newlines=True, - bufsize=1, - stdout=sp.PIPE, - stderr=sp.STDOUT) - for line in iter(proc.stdout.readline, ''): + proc = sp.Popen(cmd, stdout=spty, stderr=spty) + os.close(spty) + mpty = os.fdopen(mpty, 'r', 1) + stdout = [] + while True: + try: + line = mpty.readline() + except OSError as e: + if e.errno == errno.EIO: + break + raise stdout.append(line) if args.get('verbose', False): sys.stdout.write(line) |