From 528f104cb448bb5d113d384a7d1fe879d2fb2beb Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 15 Aug 2023 02:08:46 -0500 Subject: [PATCH] Enabled internal test code at the suite-level Test suites already had the ability to provide suite-level code via the "code" attribute, but this was placed in the suite's generated source file, making it inaccessbile to internal tests. This change allows suite code to be placed in the same place as internal tests, via the "in" attribute, though this has some caveats: 1. Suite-level code generally declares helper functions in global scope. We don't parse this code or anything, so name collisions between helper functions across different test suites is up to the developer to resolve. 2. Internal suite-level code has access to internal functions/variables/ etc, this means we can't place a copy in our suite's generate source and expect it to compile. For this reason, internal suite-level code is unavailable for non-internal tests in the suite. This also means you only get to place internal suite-level code in a single source file. Though this is not really an issue since littlefs is basically a single file... --- scripts/bench.py | 112 +++++++++++++++++++++++++++++------------------ scripts/test.py | 112 +++++++++++++++++++++++++++++------------------ 2 files changed, 138 insertions(+), 86 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index cea7b075..8a5c07d5 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -150,6 +150,11 @@ class BenchCase: return ((self.suite, self.lineno, self.name) < (other.suite, other.lineno, other.name)) + def isin(self, path): + return (self.in_ is not None + and os.path.normpath(self.in_) + == os.path.normpath(path)) + class BenchSuite: # create a BenchSuite object from a toml file @@ -200,6 +205,7 @@ class BenchSuite: (l for l in code_linenos if not case_linenos or l < case_linenos[0][0]), default=None) + self.in_ = config.pop('in', None) self.after = config.pop('after', []) if not isinstance(self.after, list): @@ -207,7 +213,6 @@ class BenchSuite: # a couple of these we just forward to all cases defines = config.pop('defines', {}) - in_ = config.pop('in', None) self.cases = [] for name, case in cases.items(): @@ -217,7 +222,7 @@ class BenchSuite: if 'lineno' in case else ''), 'suite': self.name, 'suite_defines': defines, - 'suite_in': in_, + 'suite_in': self.in_, **case}, args=args)) @@ -248,6 +253,11 @@ class BenchSuite: # note we override this with a topological sort during compilation return self.name < other.name + def isin(self, path): + return (self.in_ is not None + and os.path.normpath(self.in_) + == os.path.normpath(path)) + def compile(bench_paths, **args): # find .toml files @@ -419,16 +429,7 @@ def compile(bench_paths, **args): f.writeln() if not args.get('source'): - if suite.code is not None: - if suite.code_lineno is not None: - f.writeln('#line %d "%s"' - % (suite.code_lineno, suite.path)) - f.write(suite.code) - if suite.code_lineno is not None: - f.writeln('#line %d "%s"' - % (f.lineno+1, args['output'])) - f.writeln() - + # write any suite defines if suite.defines: for i, define in enumerate(sorted(suite.defines)): f.writeln('#ifndef %s' % define) @@ -439,6 +440,17 @@ def compile(bench_paths, **args): f.writeln('#endif') f.writeln() + # write any suite code + if suite.code is not None and suite.in_ is None: + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (suite.code_lineno, suite.path)) + f.write(suite.code) + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (f.lineno+1, args['output'])) + f.writeln() + # create case functions for case in suite.cases: if case.in_ is None: @@ -529,40 +541,54 @@ def compile(bench_paths, **args): # write any internal benches for suite in suites: - for case in suite.cases: - if (case.in_ is not None - and os.path.normpath(case.in_) - == os.path.normpath(args['source'])): - # write defines, but note we need to undef any - # new defines since we're in someone else's file - if suite.defines: - for i, define in enumerate( - sorted(suite.defines)): - f.writeln('#ifndef %s' % define) - f.writeln('#define %-24s ' - 'BENCH_IMPLICIT_DEFINE_COUNT+%d' % ( - define+'_i', i)) - f.writeln('#define %-24s ' - 'BENCH_DEFINE(%s)' % ( - define, define+'_i')) - f.writeln('#define ' - '__BENCH__%s__NEEDS_UNDEF' % ( - define)) - f.writeln('#endif') - f.writeln() + if (suite.isin(args['source']) + or any(case.isin(args['source']) + for case in suite.cases)): + # write defines, but note we need to undef any + # new defines since we're in someone else's file + if suite.defines: + for i, define in enumerate( + sorted(suite.defines)): + f.writeln('#ifndef %s' % define) + f.writeln('#define %-24s ' + 'BENCH_IMPLICIT_DEFINE_COUNT+%d' % ( + define+'_i', i)) + f.writeln('#define %-24s ' + 'BENCH_DEFINE(%s)' % ( + define, define+'_i')) + f.writeln('#define ' + '__BENCH__%s__NEEDS_UNDEF' % ( + define)) + f.writeln('#endif') + f.writeln() + # write any internal suite code + if suite.isin(args['source']): + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (suite.code_lineno, suite.path)) + f.write(suite.code) + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (f.lineno+1, args['output'])) + f.writeln() + + for case in suite.cases: + if case.isin(args['source']): write_case_functions(f, suite, case) - if suite.defines: - for define in sorted(suite.defines): - f.writeln('#ifdef __BENCH__%s__NEEDS_UNDEF' - % define) - f.writeln('#undef __BENCH__%s__NEEDS_UNDEF' - % define) - f.writeln('#undef %s' % define) - f.writeln('#undef %s' % (define+'_i')) - f.writeln('#endif') - f.writeln() + if (suite.isin(args['source']) + or any(case.isin(args['source']) + for case in suite.cases)): + for define in sorted(suite.defines): + f.writeln('#ifdef __BENCH__%s__NEEDS_UNDEF' + % define) + f.writeln('#undef __BENCH__%s__NEEDS_UNDEF' + % define) + f.writeln('#undef %s' % define) + f.writeln('#undef %s' % (define+'_i')) + f.writeln('#endif') + f.writeln() # declare our bench suites # diff --git a/scripts/test.py b/scripts/test.py index c3eea164..c86a30b7 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -152,6 +152,11 @@ class TestCase: return ((self.suite, self.lineno, self.name) < (other.suite, other.lineno, other.name)) + def isin(self, path): + return (self.in_ is not None + and os.path.normpath(self.in_) + == os.path.normpath(path)) + class TestSuite: # create a TestSuite object from a toml file @@ -202,6 +207,7 @@ class TestSuite: (l for l in code_linenos if not case_linenos or l < case_linenos[0][0]), default=None) + self.in_ = config.pop('in', None) self.after = config.pop('after', []) if not isinstance(self.after, list): @@ -209,7 +215,6 @@ class TestSuite: # a couple of these we just forward to all cases defines = config.pop('defines', {}) - in_ = config.pop('in', None) reentrant = config.pop('reentrant', False) self.cases = [] @@ -220,7 +225,7 @@ class TestSuite: if 'lineno' in case else ''), 'suite': self.name, 'suite_defines': defines, - 'suite_in': in_, + 'suite_in': self.in_, 'suite_reentrant': reentrant, **case}, args=args)) @@ -253,6 +258,11 @@ class TestSuite: # note we override this with a topological sort during compilation return self.name < other.name + def isin(self, path): + return (self.in_ is not None + and os.path.normpath(self.in_) + == os.path.normpath(path)) + def compile(test_paths, **args): # find .toml files @@ -424,16 +434,7 @@ def compile(test_paths, **args): f.writeln() if not args.get('source'): - if suite.code is not None: - if suite.code_lineno is not None: - f.writeln('#line %d "%s"' - % (suite.code_lineno, suite.path)) - f.write(suite.code) - if suite.code_lineno is not None: - f.writeln('#line %d "%s"' - % (f.lineno+1, args['output'])) - f.writeln() - + # write any suite defines if suite.defines: for i, define in enumerate(sorted(suite.defines)): f.writeln('#ifndef %s' % define) @@ -444,6 +445,17 @@ def compile(test_paths, **args): f.writeln('#endif') f.writeln() + # write any suite code + if suite.code is not None and suite.in_ is None: + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (suite.code_lineno, suite.path)) + f.write(suite.code) + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (f.lineno+1, args['output'])) + f.writeln() + # create case functions for case in suite.cases: if case.in_ is None: @@ -536,40 +548,54 @@ def compile(test_paths, **args): # write any internal tests for suite in suites: - for case in suite.cases: - if (case.in_ is not None - and os.path.normpath(case.in_) - == os.path.normpath(args['source'])): - # write defines, but note we need to undef any - # new defines since we're in someone else's file - if suite.defines: - for i, define in enumerate( - sorted(suite.defines)): - f.writeln('#ifndef %s' % define) - f.writeln('#define %-24s ' - 'TEST_IMPLICIT_DEFINE_COUNT+%d' % ( - define+'_i', i)) - f.writeln('#define %-24s ' - 'TEST_DEFINE(%s)' % ( - define, define+'_i')) - f.writeln('#define ' - '__TEST__%s__NEEDS_UNDEF' % ( - define)) - f.writeln('#endif') - f.writeln() + if (suite.isin(args['source']) + or any(case.isin(args['source']) + for case in suite.cases)): + # write defines, but note we need to undef any + # new defines since we're in someone else's file + if suite.defines: + for i, define in enumerate( + sorted(suite.defines)): + f.writeln('#ifndef %s' % define) + f.writeln('#define %-24s ' + 'TEST_IMPLICIT_DEFINE_COUNT+%d' % ( + define+'_i', i)) + f.writeln('#define %-24s ' + 'TEST_DEFINE(%s)' % ( + define, define+'_i')) + f.writeln('#define ' + '__TEST__%s__NEEDS_UNDEF' % ( + define)) + f.writeln('#endif') + f.writeln() + # write any internal suite code + if suite.isin(args['source']): + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (suite.code_lineno, suite.path)) + f.write(suite.code) + if suite.code_lineno is not None: + f.writeln('#line %d "%s"' + % (f.lineno+1, args['output'])) + f.writeln() + + for case in suite.cases: + if case.isin(args['source']): write_case_functions(f, suite, case) - if suite.defines: - for define in sorted(suite.defines): - f.writeln('#ifdef __TEST__%s__NEEDS_UNDEF' - % define) - f.writeln('#undef __TEST__%s__NEEDS_UNDEF' - % define) - f.writeln('#undef %s' % define) - f.writeln('#undef %s' % (define+'_i')) - f.writeln('#endif') - f.writeln() + if (suite.isin(args['source']) + or any(case.isin(args['source']) + for case in suite.cases)): + for define in sorted(suite.defines): + f.writeln('#ifdef __TEST__%s__NEEDS_UNDEF' + % define) + f.writeln('#undef __TEST__%s__NEEDS_UNDEF' + % define) + f.writeln('#undef %s' % define) + f.writeln('#undef %s' % (define+'_i')) + f.writeln('#endif') + f.writeln() # declare our test suites #