コード例 #1
0
def test_create_mutant(binop_file, stdoutIO):
    """Basic mutant creation to modify the add_five() function from add to mult."""
    tree = get_ast_from_src(binop_file)

    # this target is the add_five() function, changing add to mult
    target_idx = LocIndex(ast_class="BinOp",
                          lineno=10,
                          col_offset=11,
                          op_type=ast.Add)
    mutation_op = ast.Mult

    mutant = create_mutant(tree=tree,
                           src_file=binop_file,
                           target_idx=target_idx,
                           mutation_op=mutation_op)

    # uses the redirection for stdout to capture the value from the final output of binop_file
    with stdoutIO() as s:
        exec(mutant.mutant_code)
        assert int(s.getvalue()) == 25

    tag = sys.implementation.cache_tag
    expected_cfile = binop_file.parent / "__pycache__" / ".".join(
        [binop_file.stem, tag, "pyc"])

    assert mutant.src_file == binop_file
    assert mutant.cfile == expected_cfile
    assert mutant.src_idx == target_idx
コード例 #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
コード例 #3
0
def test_get_mutation_targets(binop_file, binop_expected_locs):
    """Test mutation target retrieval from the bin_op test fixture."""
    tree = get_ast_from_src(binop_file)
    targets = get_mutation_targets(tree, binop_file)

    assert len(targets) == 4
    assert targets == binop_expected_locs
コード例 #4
0
def test_create_mutation_and_run_trial(returncode, expected_status,
                                       monkeypatch, binop_file):
    """Mocked trial to ensure mutated cache files are removed after running."""
    tree = get_ast_from_src(binop_file)

    # this target is the add_five() function, changing add to mult
    target_idx = LocIndex(ast_class="BinOp",
                          lineno=10,
                          col_offset=11,
                          op_type=ast.Add)
    mutation_op = ast.Mult

    tag = sys.implementation.cache_tag
    expected_cfile = binop_file.parent / "__pycache__" / ".".join(
        [binop_file.stem, tag, "pyc"])

    def mock_subprocess_run(*args, **kwargs):
        return CompletedProcess(args="pytest", returncode=returncode)

    monkeypatch.setattr(subprocess, "run", mock_subprocess_run)

    trial = create_mutation_and_run_trial(
        src_tree=tree,
        src_file=binop_file,
        target_idx=target_idx,
        mutation_op=mutation_op,
        test_cmds=["pytest"],
        tree_inplace=False,
    )

    # mutated cache files should be removed after trial run
    assert not expected_cfile.exists()
    assert trial.status == expected_status
コード例 #5
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"
コード例 #6
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"
コード例 #7
0
def test_get_ast_from_src(binop_file):
    """Basic assurance that the AST matches expectations in type and body size."""
    tree = get_ast_from_src(binop_file)

    assert type(tree) == ast.Module

    # 4 functions will be 4 body entries in the AST, plus 1 for print statement, 5 total
    assert len(tree.body) == 5
コード例 #8
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)
コード例 #9
0
def add_five_to_mult_mutant(binop_file, stdoutIO):
    """Mutant that takes add_five op ADD to MULT. Fails if mutation code does not work."""
    tree = get_ast_from_src(binop_file)

    # this target is the add_five() function, changing add to mult
    target_idx = LocIndex(ast_class="BinOp",
                          lineno=10,
                          col_offset=11,
                          op_type=ast.Add)
    mutation_op = ast.Mult

    mutant = create_mutant(tree=tree,
                           src_file=binop_file,
                           target_idx=target_idx,
                           mutation_op=mutation_op)
    # uses the redirection for stdout to capture the value from the final output of binop_file
    with stdoutIO() as s:
        exec(mutant.mutant_code)
        assert int(s.getvalue()) == 25

    return mutant
コード例 #10
0
def build_src_trees_and_targets(
    src_loc: Union[str, Path],
    exclude_files: Optional[List[Path]] = None
) -> Tuple[Dict[str, ast.Module], Dict[str, List[LocIndex]]]:
    """Build the source AST references and find all mutatest target locations for each.

    Args:
        src_loc: the source code package directory to scan or file location

    Returns:
        Tuple(source trees, source targets)
    """
    src_trees: Dict[str, ast.Module] = {}
    src_targets: Dict[str, List[LocIndex]] = {}

    for src_file in get_py_files(src_loc):

        # if the src_file is in the exclusion list then reset to the next iteration
        if exclude_files:
            if src_file in exclude_files:
                LOGGER.info(
                    "%s", colorize_output(f"Exclusion: {src_file}", "yellow"))
                continue

        tree = get_ast_from_src(src_file)
        targets = get_mutation_targets(tree, src_file)
        LOGGER.info(
            "%s",
            colorize_output(
                f"{len(targets)} mutation targets found in {src_file} AST.",
                "green" if len(targets) > 0 else "yellow",
            ),
        )

        # only add files that have at least one valid target for mutatest
        if targets:
            src_trees[str(src_file)] = tree
            src_targets[str(src_file)] = [tgt for tgt in targets]

    return src_trees, src_targets
コード例 #11
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
コード例 #12
0
def test_MutateAST_visit_augassign(augassign_file, augassign_expected_locs):
    """Test mutation for AugAssign: +=, -=, /=, *=."""
    tree = get_ast_from_src(augassign_file)
    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"
コード例 #13
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"
コード例 #14
0
def test_MutateAST_visit_compare(compare_file, compare_expected_loc):
    """Test mutation of the == to != in the compare op."""
    tree = get_ast_from_src(compare_file)
    test_mutation = ast.NotEq

    # apply the mutation to the original tree copy
    testing_tree = deepcopy(tree)
    mutated_tree = MutateAST(target_idx=compare_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