コード例 #1
0
ファイル: __main__.py プロジェクト: XPerianer/mutmut
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,
    )
コード例 #2
0
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))
    context = Context(
        mutation_id=mutation_id,
        filename=filename,
        dict_synonyms=dict_synonyms,
    )
    mutate_file(
        backup=backup,
        context=context,
    )
    if context.number_of_performed_mutations == 0:
        raise RuntimeError(
            'No mutations performed. Are you sure the index is not too big?')
コード例 #3
0
def run_mutation(config: Config, filename: str, mutation_id: MutationID,
                 callback) -> str:
    """
    :return: (computed or cached) status of the tested mutant, one of mutant_statuses
    """
    context = Context(
        mutation_id=mutation_id,
        filename=filename,
        dict_synonyms=config.dict_synonyms,
        config=config,
    )

    cached_status = cached_mutation_status(filename, mutation_id,
                                           config.hash_of_tests)

    if cached_status != UNTESTED:
        return cached_status

    if config.pre_mutation:
        result = subprocess.check_output(config.pre_mutation,
                                         shell=True).decode().strip()
        if result and not config.swallow_output:
            print(result)

    try:
        mutate_file(backup=True, context=context)
        start = time()
        try:
            survived = tests_pass(config=config, callback=callback)
        except TimeoutError:
            return BAD_TIMEOUT

        time_elapsed = time() - start
        if not survived and time_elapsed > config.test_time_base + (
                config.baseline_time_elapsed * config.test_time_multipler):
            return OK_SUSPICIOUS

        if survived:
            return BAD_SURVIVED
        else:
            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 and not config.swallow_output:
                print(result)
コード例 #4
0
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

    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)
コード例 #5
0
ファイル: __main__.py プロジェクト: timb07/mutmut
def main(paths_to_mutate, apply, mutation, backup, runner, tests_dir, s,
         use_coverage, dict_synonyms, show_times):
    if paths_to_mutate is None:
        # Guess path with code
        this_dir = os.getcwd().split(os.sep)[-1]
        if isdir('lib'):
            paths_to_mutate = 'lib'
        elif isdir('src'):
            paths_to_mutate = 'src'
        elif isdir(this_dir):
            paths_to_mutate = this_dir
        else:
            print(
                'Could not figure out where the code to mutate is. Please specify it on the command line like "mutmut code_dir" or by adding "paths_to_mutate=code_dir" in setup.cfg under the section [mutmut]'
            )
            return

    if not isinstance(paths_to_mutate, (list, tuple)):
        paths_to_mutate = [x.strip() for x in paths_to_mutate.split(',')]

    dict_synonyms = [x.strip() for x in dict_synonyms.split(',')]

    if not paths_to_mutate:
        print(
            'You must specify a list of paths to mutate. Either as a command line argument, or by setting paths_to_mutate under the section [mutmut] in setup.cfg'
        )
        return

    os.environ['PYTHONDONTWRITEBYTECODE'] = '1'
    if apply:
        assert mutation is not None
        assert len(paths_to_mutate) == 1
        mutations_performed = mutate_file(backup, mutation, paths_to_mutate[0])
        if mutations_performed == 0:
            print(
                'ERROR: no mutations performed. Are you sure the index is not too big?'
            )
        return

    null_stdout = open(os.devnull, 'w') if not s else None
    null_stderr = open(os.devnull, 'w') if not s else None

    test_command = '%s %s' % (runner, tests_dir)

    using_testmon = '--testmon' in test_command

    def run_tests():
        if using_testmon:
            copy('.testmondata-initial', '.testmondata')
        check_call(test_command,
                   shell=True,
                   stdout=null_stdout,
                   stderr=null_stderr)

    start_time = datetime.now()
    try:
        check_output(test_command, shell=True)
        baseline_time_elapsed = datetime.now() - start_time
    except CalledProcessError as e:
        if using_testmon and e.returncode == 5:
            baseline_time_elapsed = datetime.now() - start_time
        else:
            print(
                "Tests don't run cleanly without mutations. Test command was: %s"
                % test_command)
            print(e.output.decode())
            return

    if using_testmon:
        copy('.testmondata', '.testmondata-initial')

    coverage_data = None
    if use_coverage:
        print('Using coverage data from .coverage file')
        # noinspection PyPackageRequirements
        import coverage
        coverage_data = coverage.CoverageData()
        coverage_data.read_file('.coverage')

    def exclude(context):
        if use_coverage:
            measured_lines = coverage_data.lines(
                os.path.abspath(context.filename))
            if measured_lines is None:
                return True
            if context.current_line not in measured_lines:
                return True

        return False

    mutations_by_file = {}

    for path in paths_to_mutate:
        for filename in python_source_files(path):
            mutations_by_file[filename] = count_mutations(
                open(filename).read(),
                context__filename=filename,
                context__exclude=exclude)

    total = sum(mutations_by_file.values())

    print('--- starting mutation ---')
    progress = 0
    for filename, mutations in mutations_by_file.items():
        for mutation_index in range(mutations):
            if mutation is not None and mutation != mutation_index:
                continue
            start_time = datetime.now()
            progress += 1
            print_status('%s out of %s  (file: %s, mutation: %s)' %
                         (progress, total, filename, mutation_index))
            try:
                apply_line = 'mutmut %s --mutation %s --apply' % (
                    filename, mutation_index)
                assert mutate_file(
                    backup=True,
                    mutation=mutation_index,
                    filename=filename,
                    context__exclude=exclude,
                    context__dict_synonyms=dict_synonyms,
                )
                try:
                    run_tests()
                    print_status('')
                    time_elapsed = (datetime.now() - start_time)
                    print('\rFAILED: %s' % apply_line)
                except CalledProcessError as e:
                    if using_testmon and e.returncode == 5:
                        print(
                            '\rFAILED (all tests skipped, uncovered line?): %s'
                            % apply_line)
                    time_elapsed = (datetime.now() - start_time)

                if time_elapsed > baseline_time_elapsed * 2:
                    print('\nSUSPICIOUS LONG TIME: %s > expected %s (%s)' %
                          (time_elapsed, baseline_time_elapsed, apply_line))
                os.remove(filename)
                try:
                    os.remove(filename + 'c')  # remove .pyc file
                except OSError:
                    pass
            finally:
                try:
                    move(filename + '.bak', filename)

                    if show_times:
                        print('time: %s' % time_elapsed)
                except IOError:
                    pass