def _set_config(config: Config) -> None: # validate and set IO directories that are relative to project root config.input_glob = [ str(Path(config.project_root).joinpath(str(pattern)).absolute()) for pattern in ensure_list(config.input_glob) ] config.output_dir = str( Path(config.project_root).joinpath(config.output_dir).absolute())
def test_drop_lines(self): config = Config() config.drop_lines = ['# ignore me'] self.go_exact(config, [ start, 'test', newline, A, '# ignore me, this comment is unhelpful\n', B, C, stop, 'other stuff' ])
def test_wrap_raises(self): def x(): raise NotImplementedError() config = Config() config.stop_on_first_failure = True f = [] with self.assertRaises(NotImplementedError): wrap(config, f, None, x)
def test_wrap_wraps(self): def x(): raise NotImplementedError() config = Config() config.stop_on_first_failure = False f = [] default = object() # value to return on failure result = wrap(config, f, None, x, default) self.assertIs(result, default) self.assertEqual(len(f), 1) self.assertIsNone(f[0][0]) self.assertIn("Traceback", f[0][1])
def test_dedent_code(self): with self.assertRaises(exceptions.ValidationFailure): sequence = [ f' {x}' for x in [start + 'test' + newline, A, B, C, stop] ] sequence[-2] = sequence[-2].lstrip() self.go_exact(Config(), sequence)
def test_indent_with_blank_line(self): # left pad the sequence and then have some blank lines sequence = [ f' {x}' for x in [start + 'test' + newline, A, B, C, stop] ] sequence.insert(2, '') self.go_exact(Config(), sequence)
def test_multiple(self): sequence = [ "some other stuff", start, "test 1 ", newline, A, cloak, "something to hide?", uncloak, B, C, stop, "other stuff", "more other stuff", start, " test 2 ", newline, A, B, C, stop, "more stuff", ] for i, parsed in enumerate(self.go(Config(), sequence)): with self.subTest(attempt=i): self.assertEqual(sample_output, parsed)
def test_with_empty_lines(self): self.assertEqual( self.go( Config(), f""" {start} // Listening to device state changes for 2 minutes. Thread.sleep(120000); // Stopping the Wobble. wibbler.stop(); {stop} """, ), [ "".join( [ "\n", "// Listening to device state changes for 2 minutes.\n", "Thread.sleep(120000);\n", "\n", "// Stopping the Wobble.\n", "wibbler.stop();\n", ] ) ], )
def run(config: Config): examples = {} failures = [] # validate and set IO directories that are relative to project root config.input_glob = [ os.path.abspath(os.path.join(config.project_root, pattern)) for pattern in ensure_list(config.input_glob) ] config.output_dir = os.path.abspath(os.path.join(config.project_root, config.output_dir)) paths = file_wrangler.find_files(config) logger.debug('files to parse:\n%s', textwrap.indent('\n'.join(paths), prefix=' ')) for path in paths: # load the file lines = wrap(config, failures, path, partial( file_wrangler.load_file_lines, path ), []) # extract snippets new_examples = wrap(config, failures, path, partial( extract_snippets, config, lines, path ), {}) # store the new examples for analysis examples.update(new_examples) unique_example_names = dict() for (path, line_num, example_name), code_lines in examples.items(): existing = unique_example_names.get(example_name) if existing: raise exceptions.DuplicateName('Example with duplicate name %s %s matches %s' % (path, line_num, existing)) else: unique_example_names[example_name] = (path, line_num, example_name) for (path, line_num, example_name), code_lines in examples.items(): example_block = '\n'.join(code_lines) logger.info('example: %r', example_name) logger.debug('example code: %s', example_block) wrap(config, failures, path, partial( file_wrangler.write_example, config, example_name, example_block )) return examples, paths, failures
def test_multiple(self): sequence = [ 'some other stuff', start, 'test 1 ', newline, A, cloak, 'something to hide?', uncloak, B, C, stop, 'other stuff', 'more other stuff', start, ' test 2 ', newline, A, B, C, stop, 'more stuff' ] for i, parsed in enumerate(self.go(Config(), sequence)): with self.subTest(attempt=i): self.assertEqual(sample_output, parsed)
def test_wrap_passthrough(self): counter = {"a": 0} def x(): counter["a"] += 1 return counter f = [] result = wrap(Config(), f, None, x) self.assertEqual(result, counter) self.assertEqual(counter["a"], 1) self.assertEqual(f, [])
def test_read(self): with open(self.tmp_fp, "w", encoding="utf8") as fh: fh.write(self.text) config = Config() config.stop_on_first_failure = True config.input_glob = self.tmp_fp config.output_dir = self.tmpdir.name examples, paths, failures = workflow.run(config) with self.subTest(part="found the file"): self.assertEqual([self.tmp_fp], paths) with self.subTest(part="no failures"): self.assertEqual([], failures) with self.subTest(part="one example extracted"): self.assertEqual(len(examples), self.expect_examples) for k, v in examples.items(): with self.subTest(part="example matches"): self.assertEqual("\n".join(v), P.sample_output) self.assertIn(self.example_name, k[-1])
def test_cloak(self): self.go_exact( Config(), [ "some other stuff\n", start, "test", newline, A, cloak, "ignore this stuff\n", uncloak, B, C, stop, "other stuff", ], )
def test_trigger_phrase(self): with self.assertRaises(exceptions.ValidationFailure): self.go_exact(Config(), [start, "test", newline, A, B, C, "assert\n", stop])
def test_indent_tabs(self): # left pad the sequence by two tabs, to check result is dedented to depth of start sequence = [f"\t\t{x}" for x in [start + "test" + newline, A, B, C]] sequence.append(stop) self.go_exact(Config(), sequence)
def test_plain(self): self.go_exact(Config(), [start, "test", newline, A, B, C, stop])
def test_replacements(self): config = Config() config.replacements = {'self.': ''} self.go_exact(config, [start, 'test', newline, 'self.' + A, B, C, stop])
def test_empty(self): self.assertEqual(self.go(Config(), [start, "test", newline, stop]), [])
def test_replacements(self): config = Config() config.replacements = {"self.": ""} self.go_exact(config, [start, "test", newline, "self." + A, B, C, stop])
def test_unstarted(self): with self.assertRaises(exceptions.StartEndMismatch): self.go_exact(Config(), [A, B, C, stop])
def test_indent(self): # left pad the sequence by two spaces, to check result is dedented to depth of start sequence = [f' {x}' for x in [start + 'test' + newline, A, B, C]] sequence.append(stop) self.go_exact(Config(), sequence)
def test_cloak_unfinished(self): with self.assertRaises(exceptions.CloakMismatch): self.go_exact(Config(), ["some other stuff", start, "test", newline, A, cloak, B, C, stop, "other stuff"])
def test_prefix(self): self.go_exact(Config(), ["some other stuff", start, "test", newline, A, B, C, stop, "other stuff"])
def test_double_stop(self): with self.assertRaises(exceptions.StartEndMismatch): self.go_exact(Config(), [start, "test", newline, A, stop, B, C, stop])
def test_prefix(self): self.go_exact(Config(), [ 'some other stuff', start, 'test', newline, A, B, C, stop, 'other stuff' ])
def test_cloak_unfinished(self): with self.assertRaises(exceptions.CloakMismatch): self.go_exact(Config(), [ 'some other stuff', start, 'test', newline, A, cloak, B, C, stop, 'other stuff' ])
def test_drop_lines(self): config = Config() config.drop_lines = ["# ignore me"] self.go_exact( config, [start, "test", newline, A, "# ignore me, this comment is unhelpful\n", B, C, stop, "other stuff"] )
def test_unfinished(self): with self.assertRaises(exceptions.StartEndMismatch): self.go_exact(Config(), [start, "test", newline, A, B, C])
def test_cloak(self): self.go_exact(Config(), [ 'some other stuff\n', start, 'test', newline, A, cloak, 'ignore this stuff\n', uncloak, B, C, stop, 'other stuff' ])