Esempio n. 1
0
def create_mutation_run_trial(genome: Genome, target_idx: LocIndex,
                              mutation_op: Any,
                              test_cmds: List[str]) -> MutantTrialResult:
    """Run a single mutation trial by creating a new mutated cache file, running the
    test commands, and then removing the mutated cache file.

    Args:
        genome: the genome to mutate
        target_idx: the mutation location
        mutation_op: the mutation operation
        test_cmds: the test commands to execute with the mutated code

    Returns:
        The mutation trial result
    """

    LOGGER.debug("Running trial for %s", mutation_op)
    mutant = genome.mutate(target_idx, mutation_op, write_cache=True)
    mutant_trial = subprocess.run(test_cmds,
                                  capture_output=capture_output(
                                      LOGGER.getEffectiveLevel()))
    cache.remove_existing_cache_files(mutant.src_file)

    return MutantTrialResult(mutant=mutant,
                             return_code=mutant_trial.returncode)
Esempio n. 2
0
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
Esempio n. 3
0
def test_mutate_TypeError_source_file(mock_LocIdx):
    """Mutate with a NoneType source_file property raises a TypeError."""
    genome = Genome()
    with pytest.raises(TypeError):
        _ = genome.mutate(target_idx=mock_LocIdx,
                          mutation_op=ast.Div,
                          write_cache=False)
Esempio n. 4
0
def test_mutate_ValueError_target(binop_file, mock_LocIdx):
    """Mutate with a target_idx not in the targets raises a ValueError."""
    genome = Genome(binop_file)
    with pytest.raises(ValueError):
        _ = genome.mutate(target_idx=mock_LocIdx,
                          mutation_op=ast.Div,
                          write_cache=False)
Esempio n. 5
0
def test_mutate_MutationException(binop_file, mock_LocIdx):
    """Mutate with an invalid operation raises a mutation exception."""
    genome = Genome(binop_file)
    with pytest.raises(MutationException):
        _ = genome.mutate(target_idx=mock_LocIdx,
                          mutation_op="badoperation",
                          write_cache=False)
Esempio n. 6
0
def add_five_to_mult_mutant(binop_file, stdoutIO, binop_Add_LocIdx):
    """Mutant that takes add_five op ADD to MULT. Fails if mutation code does not work."""
    genome = Genome(source_file=binop_file)

    mutation_op = ast.Mult
    mutant = genome.mutate(binop_Add_LocIdx, 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
Esempio n. 7
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."""
    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
Esempio n. 8
0
def create_mutation_run_trial(genome: Genome, target_idx: LocIndex,
                              mutation_op: Any, test_cmds: List[str],
                              max_runtime: float) -> MutantTrialResult:
    """Run a single mutation trial by creating a new mutated cache file, running the
    test commands, and then removing the mutated cache file.

    Args:
        genome: the genome to mutate
        target_idx: the mutation location
        mutation_op: the mutation operation
        test_cmds: the test commands to execute with the mutated code
        max_runtime: timeout for the trial

    Returns:
        The mutation trial result
    """
    LOGGER.debug("Running trial for %s", mutation_op)

    mutant = genome.mutate(target_idx, mutation_op, write_cache=True)

    try:
        mutant_trial = subprocess.run(
            test_cmds,
            capture_output=capture_output(LOGGER.getEffectiveLevel()),
            timeout=max_runtime,
        )
        return_code = mutant_trial.returncode

    except subprocess.TimeoutExpired:
        return_code = 3

    cache.remove_existing_cache_files(mutant.src_file)

    return MutantTrialResult(
        mutant=MutantReport(src_file=mutant.src_file,
                            src_idx=mutant.src_idx,
                            mutation=mutant.mutation),
        return_code=return_code,
    )
Esempio n. 9
0
def create_mutation_run_parallelcache_trial(
        genome: Genome, target_idx: LocIndex, mutation_op: Any,
        test_cmds: List[str], max_runtime: float) -> MutantTrialResult:
    """Similar to run.create_mutation_run_trial() but using the parallel cache directory settings.

    This function requires Python 3.8 and does not run with Python 3.7. Importantly, it has the
    identical signature to run.create_mutation_run_trial() and is substituted in the
    run.mutation_sample_dispatch().

    Args:
        genome: the genome to mutate
        target_idx: the mutation location
        mutation_op: the mutation operation
        test_cmds: the test commands to execute with the mutated code
        max_runtime: timeout for the subprocess trial

    Returns:
        MutantTrialResult

    Raises:
        EnvironmentError: if Python version is less than 3.8
    """

    if sys.version_info < (3, 8):
        raise EnvironmentError(
            "Python 3.8 is required to use PYTHONPYCACHEPREFIX.")

    # Note in coverage reports this shows as untested code due to the subprocess dispatching
    # the 'slow' tests in `test_run.py` cover this.
    cache.check_cache_invalidation_mode()

    # create the mutant without writing the cache
    mutant = genome.mutate(target_idx, mutation_op, write_cache=False)

    # set up parallel cache structure
    parallel_cache = Path.cwd() / PARALLEL_PYCACHE_DIR / uuid.uuid4().hex
    resolved_source_parts = genome.source_file.resolve().parent.parts[
        1:]  # type: ignore
    parallel_cfile = parallel_cache.joinpath(
        *resolved_source_parts) / mutant.cfile.name

    bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(  # type: ignore
        mutant.mutant_code, mutant.source_stats["mtime"],
        mutant.source_stats["size"])

    LOGGER.debug("Writing parallel mutant cache file: %s", parallel_cfile)
    cache.create_cache_dirs(parallel_cfile)
    importlib._bootstrap_external._write_atomic(  # type: ignore
        parallel_cfile,
        bytecode,
        mutant.mode,
    )

    copy_env = os.environ.copy()
    copy_env["PYTHONPYCACHEPREFIX"] = str(parallel_cache)
    try:
        mutant_trial = subprocess.run(
            test_cmds,
            env=copy_env,
            capture_output=capture_output(LOGGER.getEffectiveLevel()),
            timeout=max_runtime + MULTI_PROC_TIMEOUT_BUFFER,
        )
        return_code = mutant_trial.returncode

    except subprocess.TimeoutExpired:
        return_code = 3

    LOGGER.debug("Removing parallel cache file: %s", parallel_cache.parts[-1])
    shutil.rmtree(parallel_cache)

    return MutantTrialResult(
        mutant=MutantReport(src_file=mutant.src_file,
                            src_idx=mutant.src_idx,
                            mutation=mutant.mutation),
        return_code=return_code,
    )