class Engine(BaseEngine): """Python engine for running tests.""" schema = StorySchema( given={ Optional("files"): MapPattern(Str(), Str()), Optional("symlinks"): MapPattern(Str(), Str()), Optional("permissions"): MapPattern(Str(), Str()), Optional("setup"): Str(), Optional("python version"): Str(), Optional("pathpy version"): Str() }, info={}, ) def __init__(self, keypath, settings): self.path = keypath self.settings = settings def set_up(self): """Set up your applications and the test environment.""" self.path.state = self.path.gen.joinpath("state") if self.path.state.exists(): self.path.state.rmtree(ignore_errors=True) self.path.state.mkdir() if self.path.gen.joinpath("q").exists(): self.path.gen.joinpath("q").remove() for filename, text in self.given.get("files", {}).items(): filepath = self.path.state.joinpath(filename) if not filepath.dirname().exists(): filepath.dirname().makedirs() filepath.write_text(text) for filename, linkto in self.given.get("symlinks", {}).items(): filepath = self.path.state.joinpath(filename) linktopath = self.path.state.joinpath(linkto) linktopath.symlink(filepath) for filename, permission in self.given.get("permissions", {}).items(): filepath = self.path.state.joinpath(filename) filepath.chmod(int(permission, 8)) pylibrary = hitchbuildpy.PyLibrary( name="py3.5.0", base_python=hitchbuildpy.PyenvBuild("3.5.0").with_build_path( self.path.share), module_name="pathquery", library_src=self.path.project, ).with_build_path(self.path.gen) pylibrary.ensure_built() self.python = pylibrary.bin.python self.example_py_code = ExamplePythonCode(self.python, self.path.state)\ .with_code(self.given.get('code', ''))\ .with_setup_code(self.given.get('setup', ''))\ .with_terminal_size(160, 100)\ .with_env(TMPDIR=self.path.gen)\ .with_long_strings( yaml_snippet_1=self.given.get('yaml_snippet_1'), yaml_snippet=self.given.get('yaml_snippet'), yaml_snippet_2=self.given.get('yaml_snippet_2'), modified_yaml_snippet=self.given.get('modified_yaml_snippet'), ) @expected_exception(NonMatching) @expected_exception(HitchRunPyException) @validate( code=Str(), will_output=Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), raises=Map({ Optional("type"): Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), Optional("message"): Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), }), in_interpreter=Bool(), ) def run(self, code, will_output=None, yaml_output=True, raises=None, in_interpreter=False): if in_interpreter: code = '{0}\nprint(repr({1}))'.format( '\n'.join(code.strip().split('\n')[:-1]), code.strip().split('\n')[-1]) to_run = self.example_py_code.with_code(code) if self.settings.get("cprofile"): to_run = to_run.with_cprofile( self.path.profile.joinpath("{0}.dat".format(self.story.slug))) result = to_run.expect_exceptions().run( ) if raises is not None else to_run.run() if will_output is not None: actual_output = '\n'.join( [line.rstrip() for line in result.output.split("\n")]) try: Templex(will_output).assert_match(actual_output) except NonMatching: if self.settings.get("rewrite"): self.current_step.update(**{"will output": actual_output}) else: raise if raises is not None: differential = False # Difference between python 2 and python 3 output? exception_type = raises.get('type') message = raises.get('message') if exception_type is not None: if not isinstance(exception_type, str): differential = True exception_type = exception_type['in python 2']\ if self.given['python version'].startswith("2")\ else exception_type['in python 3'] if message is not None: if not isinstance(message, str): differential = True message = message['in python 2']\ if self.given['python version'].startswith("2")\ else message['in python 3'] try: result = self.example_py_code.expect_exceptions().run() result.exception_was_raised(exception_type, message) except ExpectedExceptionMessageWasDifferent as error: if self.settings.get("rewrite") and not differential: new_raises = raises.copy() new_raises['message'] = result.exception.message self.current_step.update(raises=new_raises) else: raise def output_contains(self, expected_contents, but_not=None): try: output_contents = self.path.state.joinpath( "output.txt").text().strip() except FileNotFoundError: raise AssertionError("Output not found") for expected_item in expected_contents: found = False for output_item in output_contents.split('\n'): if output_item.strip() == str( self.path.state.joinpath( expected_item).abspath()).strip(): found = True if not found: raise AssertionError( "Expected:\n{0}\n\nNot found in output:\n{1}".format( expected_item, output_contents, )) if but_not is not None: for unexpected_item in but_not: found = False for output_item in output_contents.split('\n'): if output_item.strip() == str( self.path.state.joinpath( unexpected_item).abspath()).strip(): found = True if found: raise RuntimeError( "Expected NOT to find:\n{0}\n\nBut found in:\n{1}". format( unexpected_item, output_contents, )) def pause(self, message="Pause"): import IPython IPython.embed() def on_success(self): if self.settings.get("rewrite"): self.new_story.save() if self.settings.get("cprofile"): self.python( self.path.key.joinpath("printstats.py"), self.path.profile.joinpath("{0}.dat".format( self.story.slug))).run() def tear_down(self): if self.path.gen.joinpath("q").exists(): print(self.path.gen.joinpath("q").text())
class Engine(BaseEngine): """Python engine for running tests.""" schema = StorySchema( given={ Optional("yaml_snippet"): Str(), Optional("yaml_snippet_1"): Str(), Optional("yaml_snippet_2"): Str(), Optional("modified_yaml_snippet"): Str(), Optional("python version"): Str(), Optional("ruamel version"): Str(), Optional("setup"): Str(), Optional("code"): Str(), }, info={ Optional("description"): Str(), Optional("importance"): Int(), Optional("experimental"): Bool(), Optional("docs"): Str(), Optional("fails on python 2"): Bool(), }, ) def __init__(self, keypath, settings): self.path = keypath self.settings = settings def set_up(self): """Set up your applications and the test environment.""" self.path.state = self.path.gen.joinpath("state") if self.path.state.exists(): self.path.state.rmtree(ignore_errors=True) self.path.state.mkdir() self.path.profile = self.path.gen.joinpath("profile") if not self.path.profile.exists(): self.path.profile.mkdir() self.python = project_build( self.path, self.given['python version'], self.given['ruamel version'], ).bin.python self.example_py_code = ExamplePythonCode(self.python, self.path.state)\ .with_code(self.given.get('code', ''))\ .with_setup_code(self.given.get('setup', ''))\ .with_terminal_size(160, 100)\ .with_long_strings( yaml_snippet_1=self.given.get('yaml_snippet_1'), yaml_snippet=self.given.get('yaml_snippet'), yaml_snippet_2=self.given.get('yaml_snippet_2'), modified_yaml_snippet=self.given.get('modified_yaml_snippet'), ) @expected_exception(NonMatching) @expected_exception(HitchRunPyException) @validate( code=Str(), will_output=Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), raises=Map({ Optional("type"): Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), Optional("message"): Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), }), in_interpreter=Bool(), ) def run(self, code, will_output=None, yaml_output=True, raises=None, in_interpreter=False): if in_interpreter: code = '{0}\nprint(repr({1}))'.format( '\n'.join(code.strip().split('\n')[:-1]), code.strip().split('\n')[-1]) to_run = self.example_py_code.with_code(code) if self.settings.get("cprofile"): to_run = to_run.with_cprofile( self.path.profile.joinpath("{0}.dat".format(self.story.slug))) result = to_run.expect_exceptions().run( ) if raises is not None else to_run.run() if will_output is not None: actual_output = '\n'.join( [line.rstrip() for line in result.output.split("\n")]) try: Templex(will_output).assert_match(actual_output) except NonMatching: if self.settings.get("rewrite"): self.current_step.update(**{"will output": actual_output}) else: raise if raises is not None: differential = False # Difference between python 2 and python 3 output? exception_type = raises.get('type') message = raises.get('message') if exception_type is not None: if not isinstance(exception_type, str): differential = True exception_type = exception_type['in python 2']\ if self.given['python version'].startswith("2")\ else exception_type['in python 3'] if message is not None: if not isinstance(message, str): differential = True message = message['in python 2']\ if self.given['python version'].startswith("2")\ else message['in python 3'] try: result = self.example_py_code.expect_exceptions().run() result.exception_was_raised(exception_type, message) except ExpectedExceptionMessageWasDifferent as error: if self.settings.get("rewrite") and not differential: new_raises = raises.copy() new_raises['message'] = result.exception.message self.current_step.update(raises=new_raises) else: raise def pause(self, message="Pause"): import IPython IPython.embed() def on_success(self): if self.settings.get("rewrite"): self.new_story.save() if self.settings.get("cprofile"): self.python( self.path.key.joinpath("printstats.py"), self.path.profile.joinpath("{0}.dat".format( self.story.slug))).run()
class Engine(BaseEngine): """Python engine for running tests.""" schema = StorySchema( given={ Optional("yaml_snippet"): Str(), Optional("yaml_snippet_1"): Str(), Optional("yaml_snippet_2"): Str(), Optional("modified_yaml_snippet"): Str(), Optional("python version"): Str(), Optional("ruamel version"): Str(), Optional("setup"): Str(), Optional("code"): Str(), }, info={ Optional("description"): Str(), Optional("importance"): Int(), Optional("docs"): Str(), }, ) def __init__(self, keypath, settings): self.path = keypath self.settings = settings def set_up(self): """Set up your applications and the test environment.""" self.doc = hitchdoc.Recorder( hitchdoc.HitchStory(self), self.path.gen.joinpath('storydb.sqlite'), ) self.path.state = self.path.gen.joinpath("state") if self.path.state.exists(): self.path.state.rmtree(ignore_errors=True) self.path.state.mkdir() self.path.profile = self.path.gen.joinpath("profile") if not self.path.profile.exists(): self.path.profile.mkdir() self.python_package = hitchpython.PythonPackage( self.given['python version'] ) self.python_package.build() self.pip = self.python_package.cmd.pip self.python = self.python_package.cmd.python # Install debugging packages with hitchtest.monitor([self.path.key.joinpath("debugrequirements.txt")]) as changed: if changed: run(self.pip("install", "-r", "debugrequirements.txt").in_dir(self.path.key)) # Uninstall and reinstall with hitchtest.monitor( pathq(self.path.project.joinpath("strictyaml")).ext("py") ) as changed: if changed: run(self.pip("uninstall", "strictyaml", "-y").ignore_errors()) run(self.pip("install", ".").in_dir(self.path.project)) run(self.pip("install", "ruamel.yaml=={0}".format( self.given["ruamel version"] ))) self.example_py_code = ExamplePythonCode(self.python, self.path.state)\ .with_code(self.given.get('code', ''))\ .with_setup_code(self.given.get('setup', ''))\ .with_long_strings( yaml_snippet_1=self.given.get('yaml_snippet_1'), yaml_snippet=self.given.get('yaml_snippet'), yaml_snippet_2=self.given.get('yaml_snippet_2'), modified_yaml_snippet=self.given.get('modified_yaml_snippet'), ) @expected_exception(HitchRunPyException) @validate( exception_type=Map({"in python 2": Str(), "in python 3": Str()}) | Str(), message=Map({"in python 2": Str(), "in python 3": Str()}) | Str(), ) def raises_exception(self, exception_type=None, message=None): """ Expect an exception. """ differential = False if exception_type is not None: if not isinstance(exception_type, str): differential = True exception_type = exception_type['in python 2']\ if self.given['python version'].startswith("2")\ else exception_type['in python 3'] if message is not None: if not isinstance(message, str): differential = True message = message['in python 2']\ if self.given['python version'].startswith("2")\ else message['in python 3'] try: result = self.example_py_code.expect_exceptions().run() result.exception_was_raised(exception_type, message) except ExpectedExceptionMessageWasDifferent as error: if self.settings.get("rewrite") and not differential: self.current_step.update(message=error.actual_message) else: raise @expected_exception(NonMatching) @expected_exception(HitchRunPyException) @validate( code=Str(), will_output=Str(), raises=Map({ Optional("type"): Map({"in python 2": Str(), "in python 3": Str()}) | Str(), Optional("message"): Map({"in python 2": Str(), "in python 3": Str()}) | Str(), }) ) def run(self, code, will_output=None, raises=None): to_run = self.example_py_code.with_code(code) if self.settings.get("cprofile"): to_run = to_run.with_cprofile( self.path.profile.joinpath("{0}.dat".format(self.story.slug)) ) result = to_run.expect_exceptions().run() if raises is not None else to_run.run() if will_output is not None: actual_output = '\n'.join([line.rstrip() for line in result.output.split("\n")]) try: Templex(will_output).assert_match(actual_output) except NonMatching: if self.settings.get("rewrite"): self.current_step.update(**{"will output": actual_output}) else: raise if raises is not None: differential = False # Difference between python 2 and python 3 output? exception_type = raises.get('type') message = raises.get('message') if exception_type is not None: if not isinstance(exception_type, str): differential = True exception_type = exception_type['in python 2']\ if self.given['python version'].startswith("2")\ else exception_type['in python 3'] if message is not None: if not isinstance(message, str): differential = True message = message['in python 2']\ if self.given['python version'].startswith("2")\ else message['in python 3'] try: result = self.example_py_code.expect_exceptions().run() result.exception_was_raised(exception_type, message) except ExpectedExceptionMessageWasDifferent as error: if self.settings.get("rewrite") and not differential: new_raises = raises.copy() new_raises['message'] = result.exception.message self.current_step.update(raises=new_raises) else: raise def pause(self, message="Pause"): import IPython IPython.embed() def on_success(self): if self.settings.get("rewrite"): self.new_story.save() if self.settings.get("cprofile"): self.python( self.path.key.joinpath("printstats.py"), self.path.profile.joinpath("{0}.dat".format(self.story.slug)) ).run()
class Engine(BaseEngine): schema = StorySchema(given={ Optional("files"): MapPattern(Str(), Str()), Optional("variables"): MapPattern(Str(), Str()), Optional("python version"): Str(), Optional("setup"): Str(), Optional("code"): Str(), }, ) def __init__(self, pathgroup, settings): self.path = pathgroup self.settings = settings def set_up(self): self.path.state = self.path.gen.joinpath("state") if self.path.state.exists(): self.path.state.rmtree(ignore_errors=True) self.path.state.mkdir() for filename, text in self.given.get("files", {}).items(): filepath = self.path.state.joinpath(filename) if not filepath.dirname().exists(): filepath.dirname().mkdir() filepath.write_text(str(text)) filepath.chmod("u+x") for filename, text in self.given.get("variables", {}).items(): filepath = self.path.state.joinpath(filename) if not filepath.dirname().exists(): filepath.dirname().mkdir() filepath.write_text(str(text)) self.path.key.joinpath("code_that_does_things.py").copy( self.path.state) self.python_package = hitchpython.PythonPackage( self.given.get('python_version', self.given['python version'])) self.python_package.build() self.pip = self.python_package.cmd.pip self.python = self.python_package.cmd.python with hitchtest.monitor( [self.path.key.joinpath("debugrequirements.txt")]) as changed: if changed: self.pip("install", "-r", "debugrequirements.txt").in_dir(self.path.key).run() with hitchtest.monitor( pathq(self.path.project.joinpath("icommandlib")).ext( "py")) as changed: if changed: self.pip("uninstall", "icommandlib", "-y").ignore_errors().run() self.pip("install", ".").in_dir(self.path.project).run() self.example_py_code = ExamplePythonCode( self.python, self.path.state, ).with_setup_code(self.given.get('setup', '')).with_code( self.given.get('code', '')).with_timeout(4.0) @expected_exception(HitchRunPyException) def run_code(self): self.result = self.example_py_code.run() @expected_exception(HitchRunPyException) def start_code(self): self.running_python = self.example_py_code.running_code() def pause_for_half_a_second(self): import time time.sleep(0.5) def send_signal_and_wait_for_finish(self, signal_name): SIGNAL_NAMES_TO_NUMBERS = { name: getattr(signal, name) for name in dir(signal) if name.startswith('SIG') and '_' not in name } self.running_python.iprocess.psutil._send_signal( int(SIGNAL_NAMES_TO_NUMBERS[signal_name])) self.running_python.iprocess.wait_for_finish() @expected_exception(HitchRunPyException) @validate( exception_type=Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), message=Map({ "in python 2": Str(), "in python 3": Str() }) | Str(), ) def raises_exception(self, exception_type=None, message=None): """ Expect an exception. """ differential = False if exception_type is not None: if not isinstance(exception_type, str): differential = True exception_type = exception_type['in python 2']\ if self.given['python version'].startswith("2")\ else exception_type['in python 3'] if message is not None: if not isinstance(message, str): differential = True message = message['in python 2']\ if self.given['python version'].startswith("2")\ else message['in python 3'] try: result = self.example_py_code.expect_exceptions().run() result.exception_was_raised(exception_type, message) except ExpectedExceptionMessageWasDifferent as error: if self.settings.get("overwrite artefacts") and not differential: self.current_step.update(message=error.actual_message) else: raise @validate(from_filenames=Seq(Str())) def processes_not_alive(self, from_filenames=None): still_alive = [] for from_filename in from_filenames: import psutil pid = int( self.path.state.joinpath(from_filename).bytes().decode( 'utf8').strip()) try: proc = psutil.Process(pid) proc.kill() still_alive.append(from_filename) except psutil.NoSuchProcess: pass if len(still_alive) > 0: raise Exception("Processes from {0} still alive.".format( ', '.join(still_alive))) def touch_file(self, filename): self.path.state.joinpath(filename).write_text("\nfile touched!", append=True) def _will_be(self, content, text, reference, changeable=None): if text is not None: if content.strip() == text.strip(): return else: raise RuntimeError( "Expected to find:\n{0}\n\nActual output:\n{1}".format( text, content, )) artefact = self.path.key.joinpath( "artefacts", "{0}.txt".format(reference.replace(" ", "-").lower())) from simex import DefaultSimex simex = DefaultSimex( open_delimeter="(((", close_delimeter=")))", ) simex_contents = content if changeable is not None: for replacement in changeable: simex_contents = simex.compile(replacement).sub( replacement, simex_contents) if not artefact.exists(): artefact.write_text(simex_contents) else: if self.settings.get('overwrite artefacts'): if artefact.bytes().decode('utf8') != simex_contents: artefact.write_text(simex_contents) print(content) else: if simex.compile(artefact.bytes().decode('utf8')).match( content) is None: raise RuntimeError( "Expected to find:\n{0}\n\nActual output:\n{1}".format( artefact.bytes().decode('utf8'), content, )) def file_contents_will_be(self, filename, text=None, reference=None, changeable=None): output_contents = self.path.state.joinpath(filename).bytes().decode( 'utf8') self._will_be(output_contents, text, reference, changeable) def output_will_be(self, text=None, reference=None, changeable=None): output_contents = self.path.state.joinpath( "output.txt").bytes().decode('utf8') self._will_be(output_contents, text, reference, changeable) @validate(seconds=Float()) def sleep(self, seconds): import time time.sleep(float(seconds)) def on_success(self): if self.settings.get("overwrite artefacts"): self.new_story.save()