def get_unified_diff(argument, dict_synonyms): filename, mutation_id = filename_and_mutation_id_from_pk(argument) with open(filename) as f: source = f.read() context = Context( source=source, filename=filename, mutation_id=mutation_id, dict_synonyms=dict_synonyms, ) mutated_source, number_of_mutations_performed = mutate(context) if not number_of_mutations_performed: return "" output = "" for line in unified_diff(source.split('\n'), mutated_source.split('\n'), fromfile=filename, tofile=filename, lineterm=''): output += line + "\n" return output
def do_apply(mutation_pk, dict_synonyms, backup): """Apply a specified mutant to the source code :param mutation_pk: mutmut cache primary key of the mutant to apply :type mutation_pk: str :param dict_synonyms: list of synonym keywords for a python dictionary :type dict_synonyms: list[str] :param backup: if :obj:`True` create a backup of the source file before applying the mutation :type backup: bool """ filename, mutation_id = filename_and_mutation_id_from_pk(int(mutation_pk)) update_line_numbers(filename) context = Context( mutation_id=mutation_id, filename=filename, dict_synonyms=dict_synonyms, ) mutate_file( backup=backup, context=context, )
def add_mutations_by_file(mutations_by_file, filename, dict_synonyms, config): """ :type mutations_by_file: dict[str, list[MutationID]] :type filename: str :type exclude: Callable[[Context], bool] :type dict_synonyms: list[str] """ with open(filename) as f: source = f.read() context = Context( source=source, filename=filename, config=config, dict_synonyms=dict_synonyms, ) try: mutations_by_file[filename] = list_mutations(context) register_mutants(mutations_by_file) except Exception as e: raise RuntimeError( 'Failed while creating mutations for {}, for line "{}"'.format( context.filename, context.current_source_line)) from e
def test_mutate_dict(): source = "dict(a=b, c=d)" assert mutate(Context(source=source, mutate_id=(source, 1))) == ("dict(a=b, cXX=d)", 1)
def test_performed_mutation_ids(): source = "dict(a=b, c=d)" context = Context(source=source) mutate(context) # we found two mutation points: mutate "a" and "c" assert context.performed_mutation_ids == [(source, 0), (source, 1)]
def test_bug_github_issue_30(): source = """ def from_checker(cls: Type['BaseVisitor'], checker) -> 'BaseVisitor': pass """ assert mutate(Context(source=source)) == (source, 0)
def test_perform_one_indexed_mutation(): assert mutate(Context(source='1+1', mutation_id=MutationID(line='1+1', index=0, line_number=0))) == ('2+1', 1) assert mutate(Context(source='1+1', mutation_id=MutationID('1+1', 1, line_number=0))) == ('1-1', 1) assert mutate(Context(source='1+1', mutation_id=MutationID('1+1', 2, line_number=0))) == ('1+2', 1)
def test_mutate_decorator(): source = """@foo\ndef foo():\n pass\n""" assert mutate(Context(source=source, mutation_id=ALL)) == (source.replace('@foo', ''), 1)
def test_bug_github_issue_19(): source = """key = lambda a: "foo" filters = dict((key(field), False) for field in fields)""" mutate(Context(source=source))
def test_basic_mutations_python36(original, expected): actual = mutate( Context(source=original, mutation_id=ALL, dict_synonyms=['Struct', 'FooBarDict']))[0] assert actual == expected
def test_mutate_all(): assert mutate(Context(source='def foo():\n return 1+1', mutation_id=ALL)) == ('def foo():\n return 2-2', 3)
def test_context_exclude_line(): source = "__import__('pkg_resources').declare_namespace(__name__)\n" assert mutate(Context(source=source)) == (source, 0) source = "__all__ = ['hi']\n" assert mutate(Context(source=source)) == (source, 0)
def test_count_available_mutations(): assert count_mutations(Context(source='def foo():\n return 1+1')) == 3
def killed_mutants_raw(module_under_test_path, project_root, not_failing_node_ids, timeout=None, cache_key=None): logger.debug( "Killed mutants of project {proj} will be saved in folder {f}", proj=project_root, f=cache_location) test_time_multiplier = 2.0 test_time_base = 0.0 swallow_output = True cache_only = False, pre_mutation = None post_mutation = None backup = False save_working_dir = os.getcwd() os.chdir(project_root) os.environ[ 'PYTHONDONTWRITEBYTECODE'] = '1' # stop python from creating .pyc files pytest_args = [] pytest_args += not_failing_node_ids baseline_time_elapsed = time_test_suite(swallow_output=not swallow_output, test_command=pytest_args, working_dir=project_root, using_testmon=False) mutations: List[MutationID] = get_mutants_of(module_under_test_path) total = len(mutations) timed_out_mutants = set() killed = defaultdict(set) st = time.time() prog = 0 for mutation_id in tqdm(mutations): prog += 1 mut = mutation_id.line, mutation_id.index config = Config( swallow_output=not swallow_output, test_command=pytest_args, working_dir=project_root, baseline_time_elapsed=baseline_time_elapsed, backup=backup, dict_synonyms=[], total=total, using_testmon=False, cache_only=cache_only, tests_dirs=[], hash_of_tests=hash_of_tests([]), test_time_multiplier=test_time_multiplier, test_time_base=test_time_base, pre_mutation=pre_mutation, post_mutation=post_mutation, coverage_data=None, covered_lines_by_filename=None, ) context = Context( mutation_id=mutation_id, filename=module_under_test_path, dict_synonyms=config.dict_synonyms, config=config, ) try: mutate_file(backup=True, context=context) try: # those test cases that killed this mutant # they failed - which is good failed_test_cases_ids = tests_pass_expanded( config=config, working_dir=project_root, callback=print) except TimeoutError: failed_test_cases_ids = None if failed_test_cases_ids is None: # This means test cases timed out # we can't figure out which ones exactly # so we don't use this mutant in statistics timed_out_mutants.add(mut) else: for test_case_id in failed_test_cases_ids: killed[test_case_id].add(mut) if timeout and prog > 2 and prog < total / 2: time_per_item = (time.time() - st) / prog estimate = time_per_item * total if estimate > timeout * 1.1: raise TimeoutError("It will take too much time") except Exception: raise finally: move(module_under_test_path + '.bak', module_under_test_path) os.chdir(save_working_dir) return killed, total
def test_bad_mutation_str_type_definition(): source = """ foo: 'SomeType' """ assert mutate(Context(source=source)) == (source, 0)
def test_mutate_dict(): source = "dict(a=b, c=d)" assert mutate( Context(source=source, mutation_id=RelativeMutationID( source, 1, line_number=0))) == ("dict(a=b, cXX=d)", 1)
def test_function_with_annotation(): source = "def capitalize(s : str):\n return s[0].upper() + s[1:] if s else s\n" assert mutate(Context(source=source, mutation_id=MutationID(source.split('\n')[1], 0, line_number=1))) == ("def capitalize(s : str):\n return s[1].upper() + s[1:] if s else s\n", 1)
def test_mutate_dict2(): source = "dict(a=b, c=d, e=f, g=h)" assert mutate(Context(source=source, mutation_id=MutationID(source, 3, line_number=0))) == ("dict(a=b, c=d, e=f, gXX=h)", 1)
def test_perform_one_indexed_mutation(): assert mutate(Context(source='1+1', mutate_id=('1+1', 0))) == ('2+1', 1) assert mutate(Context(source='1+1', mutate_id=('1+1', 1))) == ('1-1', 1) assert mutate(Context(source='1+1', mutate_id=('1+1', 2))) == ('1+2', 1)
def test_simple_apply(): CliRunner().invoke(main, ['foo.py', '--apply', '--mutation', mutation_id_separator.join([file_to_mutate_lines[0], '0'])]) with open('foo.py') as f: assert f.read() == mutate(Context(source=file_to_mutate_contents, mutate_id=(file_to_mutate_lines[0], 0)))[0]
def test_do_not_mutate_python3(source): actual = mutate( Context(source=source, mutation_id=ALL, dict_synonyms=['Struct', 'FooBarDict']))[0] assert actual == source
def test_mutate_dict2(): source = "dict(a=b, c=d, e=f, g=h)" assert mutate(Context(source=source, mutate_id=(source, 3))) == ("dict(a=b, c=d, e=f, gXX=h)", 1)
def test_pragma_no_mutate_and_no_cover(): source = """def foo():\n return 1+1 # pragma: no cover, no mutate\n""" assert mutate(Context(source=source, mutation_id=ALL)) == (source, 0)
def test_mutate_both(): source = 'a = b + c' mutations = list_mutations(Context(source=source)) assert len(mutations) == 2 assert mutate(Context(source=source, mutate_id=mutations[0])) == ('a = b - c', 1) assert mutate(Context(source=source, mutate_id=mutations[1])) == ('a = None', 1)
def test_syntax_error(): with pytest.raises(Exception): mutate(Context(source=':!'))
def test_function(): source = "def capitalize(s):\n return s[0].upper() + s[1:] if s else s\n" assert mutate(Context(source=source, mutate_id=(source.split('\n')[1], 0))) == ("def capitalize(s):\n return s[1].upper() + s[1:] if s else s\n", 1) assert mutate(Context(source=source, mutate_id=(source.split('\n')[1], 1))) == ("def capitalize(s):\n return s[0].upper() - s[1:] if s else s\n", 1) assert mutate(Context(source=source, mutate_id=(source.split('\n')[1], 2))) == ("def capitalize(s):\n return s[0].upper() + s[2:] if s else s\n", 1)
def test_bug_github_issue_26(): source = """ class ConfigurationOptions(Protocol): min_name_length: int """ mutate(Context(source=source))
def run_mutation(config, filename, mutation_id): """ :type config: Config :type filename: str :type mutation_id: MutationID :return: (computed or cached) status of the tested mutant :rtype: str """ context = Context( mutation_id=mutation_id, filename=filename, exclude=config.exclude_callback, dict_synonyms=config.dict_synonyms, config=config, ) cached_status = cached_mutation_status(filename, mutation_id, config.hash_of_tests) if cached_status == BAD_SURVIVED: config.surviving_mutants += 1 elif cached_status == BAD_TIMEOUT: config.surviving_mutants_timeout += 1 elif cached_status == OK_KILLED: config.killed_mutants += 1 elif cached_status == OK_SUSPICIOUS: config.suspicious_mutants += 1 else: assert cached_status == UNTESTED, cached_status config.print_progress() if cached_status != UNTESTED: return cached_status if config.pre_mutation: result = subprocess.check_output(config.pre_mutation, shell=True).decode().strip() if result: print(result) try: number_of_mutations_performed = mutate_file(backup=True, context=context) assert number_of_mutations_performed start = time() try: survived = tests_pass(config) except TimeoutError: context.config.surviving_mutants_timeout += 1 return BAD_TIMEOUT time_elapsed = time() - start if time_elapsed > config.test_time_base + ( config.baseline_time_elapsed * config.test_time_multipler): config.suspicious_mutants += 1 return OK_SUSPICIOUS if survived: context.config.surviving_mutants += 1 return BAD_SURVIVED else: context.config.killed_mutants += 1 return OK_KILLED finally: move(filename + '.bak', filename) if config.post_mutation: result = subprocess.check_output(config.post_mutation, shell=True).decode().strip() if result: print(result)
def test_bug_github_issue_77(): # Don't crash on this Context(source='')
def test_basic_mutations(original, expected): actual, number_of_performed_mutations = mutate(Context(source=original, mutation_id=ALL, dict_synonyms=['Struct', 'FooBarDict'])) assert actual == expected, 'Performed %s mutations for original "%s"' % (number_of_performed_mutations, original)