def test_get_cache_file_loc_invariant(s): """Property: 1. Calling cache-file with an empty string raises a value-error. 2. Returned cache files include __pycache__ as the terminal directory. 3. Splitting the returned cache-file stem on the system tag yeids the original file stem. """ if len(s) == 0: with pytest.raises(ValueError): _ = get_cache_file_loc(s) else: result = get_cache_file_loc(s) tag = sys.implementation.cache_tag assert result.parent.parts[-1] == "__pycache__" assert result.stem.split(tag)[0] == s
def test_get_cache_file_loc(): """Expectation for the pycache results based on system tag.""" test_file = "first.py" tag = sys.implementation.cache_tag expected = Path("__pycache__") / ".".join([Path(test_file).stem, tag, "pyc"]) result = get_cache_file_loc(test_file) assert result == expected
def test_get_cache_file_loc_link_exception(monkeypatch): """Symlink existing cache files raise FileExistsError.""" def mock_islink(x): return True monkeypatch.setattr(os.path, "islink", mock_islink) with pytest.raises(FileExistsError): _ = get_cache_file_loc("symlink.py")
def test_get_cache_file_loc_not_file(monkeypatch): """Irregular existing cache files will raise FileExistsError""" def mock_exists(x): return True def mock_isfile(x): return False monkeypatch.setattr(os.path, "exists", mock_exists) monkeypatch.setattr(os.path, "isfile", mock_isfile) with pytest.raises(FileExistsError): _ = get_cache_file_loc("nonregularfile.py")
def create_mutant(tree: ast.Module, src_file: str, target_idx: LocIndex, mutation_op: Any) -> Mutant: """Create a mutatest in the AST of src_file at sample_idx and update the cache. Args: tree: AST for the source file src_file: source file location on disk target_idx: the location to make the mutatest mutation_op: the mutatest to apply Returns: The mutant, and creates the cache file of the mutatest """ # mutate ast and create code binary mutant_ast = MutateAST(target_idx=target_idx, mutation=mutation_op, src_file=src_file, readonly=False).visit(tree) mutant_code = compile(mutant_ast, str(src_file), "exec") # get cache file locations and create directory if needed cfile = get_cache_file_loc(src_file) # generate cache file pyc machinery for writing the cache file loader = importlib.machinery.SourceFileLoader("<py_compile>", src_file) # type: ignore source_stats = loader.path_stats(src_file) mode = importlib._bootstrap_external._calc_mode(src_file) # type: ignore # create the cache files with the mutatest mutant = Mutant( mutant_code=mutant_code, src_file=Path(src_file), cfile=Path(cfile), loader=loader, source_stats=source_stats, mode=mode, src_idx=target_idx, mutation=mutation_op, ) return mutant
def test_get_cache_file_loc_invalid(): """Empty string will raise a ValueError.""" with pytest.raises(ValueError): _ = get_cache_file_loc(src_file="")
def mutate(self, target_idx: LocIndex, mutation_op: Any, write_cache: bool = False) -> Mutant: """Create a mutant from a single LocIndex that is in the Genome. Mutation_op must be a valid mutation for the target_idx operation code type. Optionally, use write_cache to write the mutant to ``__pycache__`` based on the detected location at the time of creation. The Genome AST is unmodified by mutate. Args: target_idx: the target location index (member of .targets) mutation_op: the mutation operation to use write_cache: optional flag to write to ``__pycache__`` Returns: The mutant definition Raises: MutationException: if ``mutation_op`` is not a valid mutation for the location index. TypeError: if the source_file property is not set on the Genome. ValueError: if the target_idx is not a member of Genome targets. """ op_code = CATEGORIES[target_idx.ast_class] valid_mutations = CategoryCodeFilter(codes=(op_code, )).valid_mutations if mutation_op not in valid_mutations: raise MutationException( f"{mutation_op} is not a member of mutation category {op_code}.\n" f"Valid mutations for {op_code}: {valid_mutations}.") if not self.source_file: raise TypeError("Source_file is set to NoneType") if target_idx not in self.targets: raise ValueError(f"{target_idx} is not in the Genome targets.") mutant_ast = MutateAST( target_idx=target_idx, mutation=mutation_op, src_file=self.source_file, readonly=False).visit( deepcopy( self.ast) # deepcopy to avoid in-place modification of AST ) # generate cache file pyc machinery for writing the __pycache__ file loader = importlib.machinery.SourceFileLoader( # type: ignore "<py_compile>", self.source_file) # create the cache files with the mutated AST mutant = Mutant( mutant_code=compile(mutant_ast, str(self.source_file), "exec"), src_file=Path(self.source_file), cfile=Path(cache.get_cache_file_loc(self.source_file)), loader=loader, source_stats=loader.path_stats(self.source_file), mode=importlib._bootstrap_external._calc_mode( self.source_file), # type: ignore src_idx=target_idx, mutation=mutation_op, ) if write_cache: mutant.write_cache() return mutant