def compare_expected_locs(): """The compare expected locations based on the fixture""" return [ LocIndex(ast_class="Compare", lineno=2, col_offset=11, op_type=ast.Eq), LocIndex(ast_class="CompareIs", lineno=5, col_offset=11, op_type=ast.Is), LocIndex(ast_class="CompareIn", lineno=8, col_offset=11, op_type=ast.In), ]
def slice_expected_locs(): """The slice expected locations based on the fixture.""" return [ LocIndex(ast_class="Slice_Swap", lineno=2, col_offset=13, op_type="Slice_UnboundLower"), LocIndex(ast_class="Slice_Swap", lineno=4, col_offset=14, op_type="Slice_UnboundUpper"), LocIndex(ast_class="Slice_Swap", lineno=6, col_offset=14, op_type="Slice_UnboundUpper"), ]
def index_expected_locs(): """The index expected location based on the fixture""" return [ LocIndex(ast_class="Index_NumNeg", lineno=2, col_offset=20, op_type="Index_NumNeg"), LocIndex(ast_class="Index_NumZero", lineno=3, col_offset=20, op_type="Index_NumZero"), LocIndex(ast_class="Index_NumPos", lineno=4, col_offset=20, op_type="Index_NumPos"), LocIndex(ast_class="Index_NumPos", lineno=4, col_offset=23, op_type="Index_NumPos"), ]
def if_expected_locs(): """Exepected locations in the if_statement.""" return [ LocIndex(ast_class="If", lineno=2, col_offset=4, op_type="If_Statement"), LocIndex(ast_class="If", lineno=4, col_offset=9, op_type="If_Statement"), LocIndex(ast_class="If", lineno=10, col_offset=4, op_type="If_True"), LocIndex(ast_class="If", lineno=13, col_offset=4, op_type="If_False"), ]
def binop_expected_locs(): """Expected target locations for the binop_file fixture.""" return { LocIndex(ast_class="BinOp", lineno=6, col_offset=11, op_type=ast.Add), LocIndex(ast_class="BinOp", lineno=6, col_offset=18, op_type=ast.Sub), LocIndex(ast_class="BinOp", lineno=10, col_offset=11, op_type=ast.Add), LocIndex(ast_class="BinOp", lineno=15, col_offset=11, op_type=ast.Div), }
def augassign_expected_locs(): """The AugAssign expected location based on the fixture""" return [ LocIndex(ast_class="AugAssign", lineno=2, col_offset=4, op_type="AugAssign_Add"), LocIndex(ast_class="AugAssign", lineno=3, col_offset=4, op_type="AugAssign_Sub"), LocIndex(ast_class="AugAssign", lineno=4, col_offset=4, op_type="AugAssign_Div"), LocIndex(ast_class="AugAssign", lineno=5, col_offset=4, op_type="AugAssign_Mult"), ]
def nameconst_expected_locs(): """The nameconst expected location based on the fixture""" return [ LocIndex(ast_class="NameConstant", lineno=1, col_offset=14, op_type=True), LocIndex(ast_class="NameConstant", lineno=4, col_offset=25, op_type=False), LocIndex(ast_class="NameConstant", lineno=6, col_offset=22, op_type=False), LocIndex(ast_class="NameConstant", lineno=7, col_offset=22, op_type=None), ]
def mock_source_and_targets(): """Mock source file with uncovered/covered targets to use with mock_coverage_file. Covered lines include: 1, 2, 4 """ source_file = Path("/simple_isnot/isnot/run.py") targets = { LocIndex(ast_class="AugAssign", lineno=1, col_offset=1, op_type="o"), LocIndex(ast_class="AugAssign", lineno=2, col_offset=1, op_type="o"), LocIndex(ast_class="AugAssign", lineno=3, col_offset=1, op_type="o"), LocIndex(ast_class="BinOp", lineno=4, col_offset=1, op_type="o"), LocIndex(ast_class="BinOp", lineno=5, col_offset=1, op_type="o"), } return SourceAndTargets(source_file, targets)
def binop_expected_locs(): """Expected target locations for the binop_file fixture in Python 3.7.""" # Python 3.7 if sys.version_info < (3, 8): return { LocIndex(ast_class="BinOp", lineno=6, col_offset=11, op_type=ast.Add), LocIndex(ast_class="BinOp", lineno=6, col_offset=18, op_type=ast.Sub), LocIndex(ast_class="BinOp", lineno=10, col_offset=11, op_type=ast.Add), LocIndex(ast_class="BinOp", lineno=15, col_offset=11, op_type=ast.Div), } # Python 3.8 return { LocIndex( ast_class="BinOp", lineno=15, col_offset=11, op_type=ast.Div, end_lineno=15, end_col_offset=16, ), LocIndex( ast_class="BinOp", lineno=6, col_offset=11, op_type=ast.Add, end_lineno=6, end_col_offset=17, ), LocIndex( ast_class="BinOp", lineno=10, col_offset=11, op_type=ast.Add, end_lineno=10, end_col_offset=16, ), LocIndex( ast_class="BinOp", lineno=6, col_offset=11, op_type=ast.Sub, end_lineno=6, end_col_offset=21, ), }
def if_expected_locs(): """Expected locations in the if_statement.""" # Py 3.7 if sys.version_info < (3, 8): return [ LocIndex(ast_class="If", lineno=2, col_offset=4, op_type="If_Statement"), LocIndex(ast_class="If", lineno=4, col_offset=9, op_type="If_Statement"), LocIndex(ast_class="If", lineno=10, col_offset=4, op_type="If_True"), LocIndex(ast_class="If", lineno=13, col_offset=4, op_type="If_False"), ] # Py 3.8 return [ LocIndex( ast_class="If", lineno=2, col_offset=4, op_type="If_Statement", end_lineno=7, end_col_offset=22, ), LocIndex( ast_class="If", lineno=4, col_offset=9, op_type="If_Statement", end_lineno=7, end_col_offset=22, ), LocIndex( ast_class="If", lineno=10, col_offset=4, op_type="If_True", end_lineno=11, end_col_offset=21, ), LocIndex( ast_class="If", lineno=13, col_offset=4, op_type="If_False", end_lineno=14, end_col_offset=22, ), ]
def index_expected_locs(): """The index expected location based on the fixture""" # Python 3.7 if sys.version_info < (3, 8): return [ LocIndex(ast_class="Index", lineno=2, col_offset=20, op_type="Index_NumNeg"), LocIndex(ast_class="Index", lineno=3, col_offset=20, op_type="Index_NumZero"), LocIndex(ast_class="Index", lineno=4, col_offset=20, op_type="Index_NumPos"), LocIndex(ast_class="Index", lineno=4, col_offset=23, op_type="Index_NumPos"), ] # Python 3.8 return [ LocIndex( ast_class="Index", lineno=2, col_offset=20, op_type="Index_NumNeg", end_lineno=2, end_col_offset=22, ), LocIndex( ast_class="Index", lineno=3, col_offset=20, op_type="Index_NumZero", end_lineno=3, end_col_offset=21, ), LocIndex( ast_class="Index", lineno=4, col_offset=20, op_type="Index_NumPos", end_lineno=4, end_col_offset=21, ), LocIndex( ast_class="Index", lineno=4, col_offset=23, op_type="Index_NumPos", end_lineno=4, end_col_offset=24, ), ]
def nameconst_expected_locs(): """The nameconst expected location based on the fixture""" # Python 3.7 if sys.version_info < (3, 8): return [ LocIndex(ast_class="NameConstant", lineno=1, col_offset=14, op_type=True), LocIndex(ast_class="NameConstant", lineno=4, col_offset=25, op_type=False), LocIndex(ast_class="NameConstant", lineno=6, col_offset=22, op_type=False), LocIndex(ast_class="NameConstant", lineno=7, col_offset=22, op_type=None), ] # Python 3.8 return [ LocIndex( ast_class="NameConstant", lineno=1, col_offset=14, op_type=True, end_lineno=1, end_col_offset=18, ), LocIndex( ast_class="NameConstant", lineno=4, col_offset=25, op_type=False, end_lineno=4, end_col_offset=30, ), LocIndex( ast_class="NameConstant", lineno=6, col_offset=22, op_type=False, end_lineno=6, end_col_offset=27, ), LocIndex( ast_class="NameConstant", lineno=7, col_offset=22, op_type=None, end_lineno=7, end_col_offset=26, ), ]
def test_create_mutant_with_cache(binop_file, stdoutIO): """Change ast.Add to ast.Mult in a mutation including pycache changes.""" genome = Genome(source_file=binop_file) # this target is the add_five() function, changing add to mult end_lineno = None if sys.version_info < (3, 8) else 10 end_col_offset = None if sys.version_info < (3, 8) else 16 target_idx = LocIndex( ast_class="BinOp", lineno=10, col_offset=11, op_type=ast.Add, end_lineno=end_lineno, end_col_offset=end_col_offset, ) mutation_op = ast.Mult mutant = genome.mutate(target_idx, mutation_op, write_cache=True) # 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
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
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
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
def test_get_mutations_for_target_slice(): """Slice is a special case where the op type is returned as the mutation.""" expected = "Slice_UPosToZero" mock_loc_idx = LocIndex(ast_class=expected, lineno=10, col_offset=11, op_type=expected) result = get_mutations_for_target(mock_loc_idx) # there should only be one option in result assert result.pop() == expected
def test_get_mutations_for_target(test_op): """Ensure the expected set is returned for binops""" mock_loc_idx = LocIndex(ast_class="BinOp", lineno=10, col_offset=11, op_type=test_op) expected = TEST_BINOPS.copy() expected.remove(test_op) result = get_mutations_for_target(mock_loc_idx) assert result == expected
def mock_Mutant(): """Mock mutant definition.""" return Mutant( mutant_code=None, src_file=Path("src.py"), cfile=Path("__pycache__") / "src.pyc", loader=None, mode=1, source_stats={"mtime": 1, "size": 1}, src_idx=LocIndex(ast_class="BinOp", lineno=1, col_offset=2, op_type=ast.Add), mutation=ast.Mult, )
def binop_Add_LocIdx(): """Binop Add LocIdx as a target for mutations.""" end_lineno = None if sys.version_info < (3, 8) else 10 end_col_offset = None if sys.version_info < (3, 8) else 16 return LocIndex( ast_class="BinOp", lineno=10, col_offset=11, op_type=ast.Add, end_lineno=end_lineno, end_col_offset=end_col_offset, )
def boolop_expected_loc(): """Expected location index of the boolop fixture""" # Py 3.7 vs 3.8 end_lineno = None if sys.version_info < (3, 8) else 2 end_col_offset = None if sys.version_info < (3, 8) else 18 return LocIndex( ast_class="BoolOp", lineno=2, col_offset=11, op_type=ast.And, end_lineno=end_lineno, end_col_offset=end_col_offset, )
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.""" genome = Genome(source_file=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 = genome.mutate(target_idx, mutation_op, write_cache=True) # 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
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
def compare_expected_locs(): """The compare expected locations based on the fixture""" # Py 3.7 if sys.version_info < (3, 8): return [ LocIndex(ast_class="Compare", lineno=2, col_offset=11, op_type=ast.Eq), LocIndex(ast_class="CompareIs", lineno=5, col_offset=11, op_type=ast.Is), LocIndex(ast_class="CompareIn", lineno=8, col_offset=11, op_type=ast.In), ] # Py 3.8 return [ LocIndex( ast_class="Compare", lineno=2, col_offset=11, op_type=ast.Eq, end_lineno=2, end_col_offset=17, ), LocIndex( ast_class="CompareIs", lineno=5, col_offset=11, op_type=ast.Is, end_lineno=5, end_col_offset=17, ), LocIndex( ast_class="CompareIn", lineno=8, col_offset=11, op_type=ast.In, end_lineno=8, end_col_offset=17, ), ]
def mock_LocIdx(): """Mock Single Location Index, not a valid target member of file.""" return LocIndex(ast_class="BinOp", lineno=1, col_offset=2, op_type=ast.Add)
def boolop_expected_loc(): """Expected location index of the boolop fixture""" return LocIndex(ast_class="BoolOp", lineno=2, col_offset=11, op_type=ast.And)