Beispiel #1
0
def test_MutateAST_visit_index_neg(
    i_order, lineno, col_offset, mut, index_file, index_expected_locs
):
    """Test mutation for Index: i[0], i[1], i[-1]."""
    tree = get_ast_from_src(index_file)
    test_mutation = mut

    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=index_expected_locs[i_order], mutation=test_mutation).visit(
        testing_tree
    )

    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    assert len(mast.locs) == 4

    for l in mast.locs:
        # spot check on mutation from Index_NumNeg to Index_NumPos
        if l.lineno == lineno and l.col_offset == col_offset:
            assert l.op_type == test_mutation

        # spot check on not-mutated location still being None
        if l.lineno == 4 and l.col_offset == 23:
            assert l.op_type == "Index_NumPos"
Beispiel #2
0
def test_MutateAST_visit_nameconst(nameconst_file, nameconst_expected_locs):
    """Test mutation for nameconst: True, False, None."""
    tree = get_ast_from_src(nameconst_file)
    test_mutation = False

    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=nameconst_expected_locs[0], mutation=test_mutation).visit(
        testing_tree
    )

    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    # if statement is included with this file that will be picked up
    nc_locs = [l for l in mast.locs if l.ast_class == "NameConstant"]
    assert len(nc_locs) == 4

    for l in nc_locs:
        # spot check on mutation from True to False
        if l.lineno == 1 and l.col_offset == 14:
            assert l.op_type == test_mutation

        # spot check on not-mutated location still being None
        if l.lineno == 7 and l.col_offset == 22:
            assert l.op_type is None
Beispiel #3
0
def test_MutateAST_visit_binop_37(binop_file):
    """Read only test to ensure locations are aggregated."""
    tree = Genome(binop_file).ast

    # Py 3.7 vs. Py 3.8
    end_lineno = None if sys.version_info < (3, 8) else 6
    end_col_offset = None if sys.version_info < (3, 8) else 17

    test_idx = LocIndex(
        ast_class="BinOp",
        lineno=6,
        col_offset=11,
        op_type=ast.Add,
        end_lineno=end_lineno,
        end_col_offset=end_col_offset,
    )
    test_mutation = ast.Pow

    # apply the mutation to the original tree copy
    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=test_idx,
                             mutation=test_mutation).visit(testing_tree)

    # revisit in read-only mode to gather the locations of the new nodes
    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    # four locations from the binary operations in binop_file
    assert len(mast.locs) == 4

    # locs is an unordered set, cycle through to thd target and check the mutation
    for l in mast.locs:
        if (l.lineno == 6 and l.col_offset == 11 and l.end_lineno == end_lineno
                and l.end_col_offset == end_col_offset):
            assert l.op_type == test_mutation
Beispiel #4
0
def test_MutateAST_visit_if(if_file, if_expected_locs):
    """Test mutation for nameconst: True, False, None."""
    tree = get_ast_from_src(if_file)
    test_mutation = "If_True"

    testing_tree = deepcopy(tree)
    # change from If_Statement to If_True
    mutated_tree = MutateAST(target_idx=if_expected_locs[0], mutation=test_mutation).visit(
        testing_tree
    )

    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    # named constants will also be picked up, filter just to if_ operations
    if_locs = [l for l in mast.locs if l.ast_class == "If"]
    assert len(if_locs) == 4

    for l in if_locs:
        # spot check on mutation from True to False
        if l.lineno == 2 and l.col_offset == 4:
            print(l)
            assert l.op_type == test_mutation

        # spot check on not-mutated location still being None
        if l.lineno == 13 and l.col_offset == 4:
            assert l.op_type == "If_False"
Beispiel #5
0
def test_MutateAST_visit_read_only(binop_file):
    """Read only test to ensure locations are aggregated."""
    tree = get_ast_from_src(binop_file)
    mast = MutateAST(readonly=True)
    testing_tree = deepcopy(tree)
    mast.visit(testing_tree)

    # four locations from the binary operations in binop_file
    assert len(mast.locs) == 4

    # tree should be unmodified
    assert ast.dump(tree) == ast.dump(testing_tree)
Beispiel #6
0
def get_mutation_targets(tree: ast.Module, src_file: Path) -> Set[LocIndex]:
    """Run the mutatest AST search with no targets or mutations to bring back target indicies.

    Args:
        tree: the source file AST
        src_file: source file name, used in logging

    Returns:
        Set of potential mutatest targets within AST
    """
    ro_mast = MutateAST(target_idx=None,
                        mutation=None,
                        readonly=True,
                        src_file=src_file)
    ro_mast.visit(tree)
    return ro_mast.locs
Beispiel #7
0
    def targets(self) -> Set[LocIndex]:
        """Viable mutation targets within the AST of the ``source_file``.

        This is cached locally and updated if the source_file is changed. Filtering is not
        cached and applies any time the ``filter_codes`` are changed.

        Returns:
             The set of the location index objects from the transformer that could be
             potential mutation targets.
        """
        if self._targets is None:
            ro_mast = MutateAST(
                target_idx=None, mutation=None, readonly=True, src_file=self.source_file
            )
            ro_mast.visit(self.ast)
            self._targets = ro_mast.locs

        return CategoryCodeFilter(codes=self.filter_codes).filter(self._targets)
Beispiel #8
0
def test_MutateAST_visit_binop(binop_file):
    """Read only test to ensure locations are aggregated."""
    tree = get_ast_from_src(binop_file)

    test_idx = LocIndex(ast_class="BinOp", lineno=6, col_offset=11, op_type=ast.Add)
    test_mutation = ast.Pow

    # apply the mutation to the original tree copy
    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=test_idx, mutation=test_mutation).visit(testing_tree)

    # revisit in read-only mode to gather the locations of the new nodes
    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    # four locations from the binary operations in binop_file
    assert len(mast.locs) == 4

    # locs is an unordered set, cycle through to thd target and check the mutation
    for l in mast.locs:
        if l.lineno == 6 and l.col_offset == 11:
            assert l.op_type == test_mutation
Beispiel #9
0
def test_MutateAST_visit_augassign(augassign_file, augassign_expected_locs):
    """Test mutation for AugAssign: +=, -=, /=, *=."""
    tree = Genome(augassign_file).ast
    test_mutation = "AugAssign_Div"

    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=augassign_expected_locs[0],
                             mutation=test_mutation).visit(testing_tree)

    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    assert len(mast.locs) == 4

    for l in mast.locs:
        # spot check on mutation from Add tp Div
        if l.lineno == 1 and l.col_offset == 4:
            assert l.op_type == test_mutation

        # spot check on not-mutated location still being Mult
        if l.lineno == 5 and l.col_offset == 4:
            assert l.op_type == "AugAssign_Mult"
Beispiel #10
0
def test_MutateAST_visit_boolop(boolop_file, boolop_expected_loc):
    """Test mutation of AND to OR in the boolop."""
    tree = Genome(boolop_file).ast
    test_mutation = ast.Or

    # apply the mutation to the original tree copy
    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=boolop_expected_loc,
                             mutation=test_mutation).visit(testing_tree)

    # revisit in read-only mode to gather the locations of the new nodes
    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    # four locations from the binary operations in binop_file
    assert len(mast.locs) == 1

    # there will only be one loc, but this still works
    # basedon the col and line offset in the fixture for compare_expected_loc
    for l in mast.locs:
        if l.lineno == 2 and l.col_offset == 11:
            assert l.op_type == test_mutation
def test_MutateAST_visit_compare(idx, mut_op, lineno, compare_file, compare_expected_locs):
    """Test mutation of the == to != in the compare op."""
    tree = Genome(compare_file).ast

    # apply the mutation to the original tree copy
    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=compare_expected_locs[idx], mutation=mut_op).visit(
        testing_tree
    )

    # revisit in read-only mode to gather the locations of the new nodes
    mast = MutateAST(readonly=True)
    mast.visit(mutated_tree)

    assert len(mast.locs) == 3

    # check that the lineno marked for mutation is changed, otherwise original ops should
    # still be present without modification
    for loc in mast.locs:
        if loc.lineno == lineno and loc.col_offset == 11:
            assert loc.op_type == mut_op
        else:
            assert loc.op_type in {ast.Eq, ast.Is, ast.In}  # based on compare_file fixture
Beispiel #12
0
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_MutateAST_visit_subscript(slice_file, slice_expected_locs):
    """Test Slice references within subscript."""
    tree = Genome(slice_file).ast
    mast = MutateAST(readonly=True)
    mast.visit(tree)
    assert len(mast.locs) == len(slice_expected_locs)

    test_mutation = "Slice_UNegToZero"

    mutated_tree = MutateAST(target_idx=slice_expected_locs[2], mutation=test_mutation).visit(tree)

    mast.visit(mutated_tree)
    assert len(mast.locs) == len(slice_expected_locs)

    for loc in mast.locs:

        if loc.lineno == 5 and loc.col_offset == 15:
            assert loc.op_type == test_mutation

        # test one unmodified location
        if loc.lineno == 4 and loc.col_offset == 14:
            assert loc.op_type == "Slice_UnboundUpper"
Beispiel #14
0
def test_MutateAST_visit_subscript(slice_file, slice_expected_locs):
    """Test Slice references within subscript."""
    tree = get_ast_from_src(slice_file)
    mast = MutateAST(readonly=True)
    mast.visit(tree)
    assert len(mast.locs) == len(slice_expected_locs)

    test_mutation = "Slice_UNegToZero"

    # loc index 3 is the Slice_NegShrink operation in the fixture
    mutated_tree = MutateAST(target_idx=slice_expected_locs[3], mutation=test_mutation).visit(tree)

    mast.visit(mutated_tree)
    assert len(mast.locs) == len(slice_expected_locs)

    for l in mast.locs:

        if l.lineno == 5 and l.col_offset == 15:
            assert l.op_type == test_mutation

        # test one unmodified location
        if l.lineno == 4 and l.col_offset == 14:
            assert l.op_type == "Slice_UnboundUpper"
Beispiel #15
0
    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