def clean_trial(src_loc: Path, test_cmds: List[str]) -> timedelta: """Remove all existing cache files and run the test suite. Args: src_loc: the directory of the package for cache removal, may be a file test_cmds: test running commands for subprocess.run() Returns: None Raises: BaselineTestException: if the clean trial does not pass from the test run. """ cache.remove_existing_cache_files(src_loc) LOGGER.info("Running clean trial") # clean trial will show output all the time for diagnostic purposes start = datetime.now() clean_run = subprocess.run(test_cmds, capture_output=False) end = datetime.now() if clean_run.returncode != 0: raise BaselineTestException( f"Clean trial does not pass, mutant tests will be meaningless.\n" f"Output: {str(clean_run.stdout)}") return end - start
def create_mutation_run_trial(genome: Genome, target_idx: LocIndex, mutation_op: Any, test_cmds: List[str]) -> MutantTrialResult: """Run a single mutation trial by creating a new mutated cache file, running the test commands, and then removing the mutated cache file. Args: genome: the genome to mutate target_idx: the mutation location mutation_op: the mutation operation test_cmds: the test commands to execute with the mutated code Returns: The mutation trial result """ LOGGER.debug("Running trial for %s", mutation_op) mutant = genome.mutate(target_idx, mutation_op, write_cache=True) mutant_trial = subprocess.run(test_cmds, capture_output=capture_output( LOGGER.getEffectiveLevel())) cache.remove_existing_cache_files(mutant.src_file) return MutantTrialResult(mutant=mutant, return_code=mutant_trial.returncode)
def test_remove_existing_cache_files_from_folder(tmp_path): """Removing multiple cache files based on scanning a directory.""" # structure matches expectation of get_cache_file_loc return tag = sys.implementation.cache_tag test_cache_path = tmp_path / "__pycache__" test_cache_path.mkdir() # create multiple test files in the tmp folder test_files = ["first.py", "second.py", "third.py"] test_cache_files = [] for tf in test_files: with open(tmp_path / tf, "w") as temp_py: temp_py.write("import this") test_cache_file = test_cache_path / ".".join( [Path(tf).stem, tag, "pyc"]) test_cache_file.write_bytes(b"temporary bytes") test_cache_files.append(test_cache_file) for tcf in test_cache_files: assert tcf.exists() remove_existing_cache_files(tmp_path) for tcf in test_cache_files: assert not tcf.exists()
def write_mutant_cache_file(mutant: Mutant) -> None: """Create the cache file for the mutant on disk in __pycache__. Existing target cache files are removed to ensure clean overwrites. Reference: https://github.com/python/cpython/blob/master/Lib/py_compile.py#L157 Args: mutant: the mutant definition to create Returns: None, creates the cache file on disk. """ check_cache_invalidation_mode() bytecode = importlib._bootstrap_external._code_to_timestamp_pyc( # type: ignore mutant.mutant_code, mutant.source_stats["mtime"], mutant.source_stats["size"]) remove_existing_cache_files(mutant.src_file) create_cache_dirs(mutant.cfile) LOGGER.debug("Writing mutant cache file: %s", mutant.cfile) importlib._bootstrap_external._write_atomic(mutant.cfile, bytecode, mutant.mode) # type: ignore
def test_remove_existing_cache_files(tmp_path): """Removing a single existing cache file.""" test_file = tmp_path / "first.py" # structure matches expectation of get_cache_file_loc return tag = sys.implementation.cache_tag test_cache_path = tmp_path / "__pycache__" test_cache_file = test_cache_path / ".".join([Path(test_file).stem, tag, "pyc"]) # create the temp dir and tmp cache file test_cache_path.mkdir() test_cache_file.write_bytes(b"temporary bytes") assert test_cache_file.exists() remove_existing_cache_files(test_file) assert not test_cache_file.exists()
def create_mutation_run_trial(genome: Genome, target_idx: LocIndex, mutation_op: Any, test_cmds: List[str], max_runtime: float) -> MutantTrialResult: """Run a single mutation trial by creating a new mutated cache file, running the test commands, and then removing the mutated cache file. Args: genome: the genome to mutate target_idx: the mutation location mutation_op: the mutation operation test_cmds: the test commands to execute with the mutated code max_runtime: timeout for the trial Returns: The mutation trial result """ LOGGER.debug("Running trial for %s", mutation_op) mutant = genome.mutate(target_idx, mutation_op, write_cache=True) try: mutant_trial = subprocess.run( test_cmds, capture_output=capture_output(LOGGER.getEffectiveLevel()), timeout=max_runtime, ) return_code = mutant_trial.returncode except subprocess.TimeoutExpired: return_code = 3 cache.remove_existing_cache_files(mutant.src_file) return MutantTrialResult( mutant=MutantReport(src_file=mutant.src_file, src_idx=mutant.src_idx, mutation=mutant.mutation), return_code=return_code, )
def create_mutation_and_run_trial( src_tree: ast.Module, src_file: str, target_idx: LocIndex, mutation_op: Any, test_cmds: List[str], tree_inplace: bool = False, ) -> MutantTrialResult: """Write the mutation to the cache location and runs the trial based on test_cmds Args: src_tree: the source AST src_file: the source file location to determine cache location target_idx: the mutation target in the source AST mutation_op: the mutation to apply test_cmds: test command string for running the trial tree_inplace: flag for in-place mutations, default to False Returns: MutationTrialResult from the trial run """ # mutatest requires deep-copy to avoid in-place reference changes to AST tree = src_tree if tree_inplace else deepcopy(src_tree) mutant = create_mutant(tree=tree, src_file=src_file, target_idx=target_idx, mutation_op=mutation_op) write_mutant_cache_file(mutant) mutant_trial = subprocess.run(test_cmds, capture_output=capture_output( LOGGER.getEffectiveLevel())) remove_existing_cache_files(mutant.src_file) return MutantTrialResult(mutant=mutant, return_code=mutant_trial.returncode)