forked from Imagelibrary/littlefs
Aside from reworking the internals of test_.py to work well with inherited TestCase classes, this also provides the two main features that were the main reason for revamping the test framework 1. ./scripts/test_.py --reentrant Runs reentrant tests (tests with reentrant=true in the .toml configuration) under gdb such that the program is killed on every call to lfs_emubd_prog or lfs_emubd_erase. Currently this just increments a number of prog/erases to skip, which means it doesn't necessarily check every possible branch of the test, but this should still provide a good coverage of power-loss tests. 2. ./scripts/test_.py --gdb Run the tests and if a failure is hit, drop into GDB. In theory this will be very useful for reproducing and debugging test failures. Note this can be combined with --reentrant to drop into GDB on the exact cycle of power-loss where the tests fail.
216 lines
6.3 KiB
Python
Executable File
216 lines
6.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import parsy as p
|
|
import re
|
|
import io
|
|
import sys
|
|
|
|
ASSERT_PATTERN = p.string('LFS_ASSERT') | p.string('assert')
|
|
ASSERT_CHARS = 'La'
|
|
ASSERT_TARGET = '__LFS_ASSERT_{TYPE}_{COMP}'
|
|
ASSERT_TESTS = {
|
|
'int': """
|
|
__typeof__({lh}) _lh = {lh};
|
|
__typeof__({lh}) _rh = (__typeof__({lh})){rh};
|
|
if (!(_lh {op} _rh)) {{
|
|
printf("%s:%d:assert: "
|
|
"assert failed with %"PRIiMAX", expected {comp} %"PRIiMAX"\\n",
|
|
{file}, {line}, (intmax_t)_lh, (intmax_t)_rh);
|
|
fflush(NULL);
|
|
raise(SIGABRT);
|
|
}}
|
|
""",
|
|
'str': """
|
|
const char *_lh = {lh};
|
|
const char *_rh = {rh};
|
|
if (!(strcmp(_lh, _rh) {op} 0)) {{
|
|
printf("%s:%d:assert: "
|
|
"assert failed with \\\"%s\\\", expected {comp} \\\"%s\\\"\\n",
|
|
{file}, {line}, _lh, _rh);
|
|
fflush(NULL);
|
|
raise(SIGABRT);
|
|
}}
|
|
""",
|
|
'bool': """
|
|
bool _lh = !!({lh});
|
|
bool _rh = !!({rh});
|
|
if (!(_lh {op} _rh)) {{
|
|
printf("%s:%d:assert: "
|
|
"assert failed with %s, expected {comp} %s\\n",
|
|
{file}, {line}, _lh ? "true" : "false", _rh ? "true" : "false");
|
|
fflush(NULL);
|
|
raise(SIGABRT);
|
|
}}
|
|
""",
|
|
}
|
|
|
|
def mkassert(lh, rh='true', type='bool', comp='eq'):
|
|
return ((ASSERT_TARGET + "({lh}, {rh}, __FILE__, __LINE__, __func__)")
|
|
.format(
|
|
type=type, TYPE=type.upper(),
|
|
comp=comp, COMP=comp.upper(),
|
|
lh=lh.strip(' '),
|
|
rh=rh.strip(' ')))
|
|
|
|
def mkdecl(type, comp, op):
|
|
return ((
|
|
"#define "+ASSERT_TARGET+"(lh, rh, file, line, func)"
|
|
" do {{"+re.sub('\s+', ' ', ASSERT_TESTS[type])+"}} while (0)\n")
|
|
.format(
|
|
type=type, TYPE=type.upper(),
|
|
comp=comp, COMP=comp.upper(),
|
|
lh='lh', rh='rh', op=op,
|
|
file='file', line='line', func='func'))
|
|
|
|
# add custom until combinator
|
|
def until(self, end):
|
|
return end.should_fail('should fail').then(self).many()
|
|
p.Parser.until = until
|
|
|
|
pcomp = (
|
|
p.string('==').tag('eq') |
|
|
p.string('!=').tag('ne') |
|
|
p.string('<=').tag('le') |
|
|
p.string('>=').tag('ge') |
|
|
p.string('<').tag('lt') |
|
|
p.string('>').tag('gt'));
|
|
|
|
plogic = p.string('&&') | p.string('||')
|
|
|
|
@p.generate
|
|
def pstrassert():
|
|
yield ASSERT_PATTERN + p.regex('\s*') + p.string('(') + p.regex('\s*')
|
|
yield p.string('strcmp') + p.regex('\s*') + p.string('(') + p.regex('\s*')
|
|
lh = yield pexpr.until(p.string(',') | p.string(')') | plogic)
|
|
yield p.string(',') + p.regex('\s*')
|
|
rh = yield pexpr.until(p.string(')') | plogic)
|
|
yield p.string(')') + p.regex('\s*')
|
|
op = yield pcomp
|
|
yield p.regex('\s*') + p.string('0') + p.regex('\s*') + p.string(')')
|
|
return mkassert(''.join(lh), ''.join(rh), 'str', op[0])
|
|
|
|
@p.generate
|
|
def pintassert():
|
|
yield ASSERT_PATTERN + p.regex('\s*') + p.string('(') + p.regex('\s*')
|
|
lh = yield pexpr.until(pcomp | p.string(')') | plogic)
|
|
op = yield pcomp
|
|
rh = yield pexpr.until(p.string(')') | plogic)
|
|
yield p.string(')')
|
|
return mkassert(''.join(lh), ''.join(rh), 'int', op[0])
|
|
|
|
@p.generate
|
|
def pboolassert():
|
|
yield ASSERT_PATTERN + p.regex('\s*') + p.string('(') + p.regex('\s*')
|
|
expr = yield pexpr.until(p.string(')'))
|
|
yield p.string(')')
|
|
return mkassert(''.join(expr), 'true', 'bool', 'eq')
|
|
|
|
passert = p.peek(ASSERT_PATTERN) >> (pstrassert | pintassert | pboolassert)
|
|
|
|
@p.generate
|
|
def pcomment1():
|
|
yield p.string('//')
|
|
s = yield p.regex('[^\\n]*')
|
|
yield p.string('\n')
|
|
return '//' + s + '\n'
|
|
|
|
@p.generate
|
|
def pcomment2():
|
|
yield p.string('/*')
|
|
s = yield p.regex('((?!\*/).)*')
|
|
yield p.string('*/')
|
|
return '/*' + ''.join(s) + '*/'
|
|
|
|
@p.generate
|
|
def pcomment3():
|
|
yield p.string('#')
|
|
s = yield p.regex('[^\\n]*')
|
|
yield p.string('\n')
|
|
return '#' + s + '\n'
|
|
|
|
pws = p.regex('\s+') | pcomment1 | pcomment2 | pcomment3
|
|
|
|
@p.generate
|
|
def pstring():
|
|
q = yield p.regex('["\']')
|
|
s = yield (p.string('\\%s' % q) | p.regex('[^%s]' % q)).many()
|
|
yield p.string(q)
|
|
return q + ''.join(s) + q
|
|
|
|
@p.generate
|
|
def pnested():
|
|
l = yield p.string('(')
|
|
n = yield pexpr.until(p.string(')'))
|
|
r = yield p.string(')')
|
|
return l + ''.join(n) + r
|
|
|
|
pexpr = (
|
|
# shortcut for a bit better performance
|
|
p.regex('[^%s/#\'"();{}=><,&|-]+' % ASSERT_CHARS) |
|
|
pws |
|
|
passert |
|
|
pstring |
|
|
pnested |
|
|
p.string('->') |
|
|
p.regex('.', re.DOTALL))
|
|
|
|
@p.generate
|
|
def pstmt():
|
|
ws = yield pws.many()
|
|
lh = yield pexpr.until(p.string('=>') | p.regex('[;{}]'))
|
|
op = yield p.string('=>').optional()
|
|
if op == '=>':
|
|
rh = yield pstmt
|
|
return ''.join(ws) + mkassert(''.join(lh), rh, 'int', 'eq')
|
|
else:
|
|
return ''.join(ws) + ''.join(lh)
|
|
|
|
@p.generate
|
|
def pstmts():
|
|
a = yield pstmt
|
|
b = yield (p.regex('[;{}]') + pstmt).many()
|
|
return [a] + b
|
|
|
|
def main(args):
|
|
inf = open(args.input, 'r') if args.input else sys.stdin
|
|
outf = open(args.output, 'w') if args.output else sys.stdout
|
|
|
|
# parse C code
|
|
input = inf.read()
|
|
stmts = pstmts.parse(input)
|
|
|
|
# write extra verbose asserts
|
|
outf.write("#include <stdbool.h>\n")
|
|
outf.write("#include <stdint.h>\n")
|
|
outf.write("#include <inttypes.h>\n")
|
|
outf.write("#include <signal.h>\n")
|
|
outf.write(mkdecl('int', 'eq', '=='))
|
|
outf.write(mkdecl('int', 'ne', '!='))
|
|
outf.write(mkdecl('int', 'lt', '<'))
|
|
outf.write(mkdecl('int', 'gt', '>'))
|
|
outf.write(mkdecl('int', 'le', '<='))
|
|
outf.write(mkdecl('int', 'ge', '>='))
|
|
outf.write(mkdecl('str', 'eq', '=='))
|
|
outf.write(mkdecl('str', 'ne', '!='))
|
|
outf.write(mkdecl('str', 'lt', '<'))
|
|
outf.write(mkdecl('str', 'gt', '>'))
|
|
outf.write(mkdecl('str', 'le', '<='))
|
|
outf.write(mkdecl('str', 'ge', '>='))
|
|
outf.write(mkdecl('bool', 'eq', '=='))
|
|
if args.input:
|
|
outf.write("#line %d \"%s\"\n" % (1, args.input))
|
|
|
|
# write parsed statements
|
|
for stmt in stmts:
|
|
outf.write(stmt)
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
parser = argparse.ArgumentParser(
|
|
description="Cpp step that increases assert verbosity")
|
|
parser.add_argument('input', nargs='?',
|
|
help="Input C file after cpp.")
|
|
parser.add_argument('-o', '--output',
|
|
help="Output C file.")
|
|
main(parser.parse_args())
|