Пример #1
0
def _git_fetch_for_comparison(remote: str, actual_branch: str,
                              compare_branch: str,
                              verbose: bool) -> prepared_env.PreparedEnv:
    """Fetches two branches including their common ancestor.

    Limits the depth of the fetch to avoid unnecessary work. Scales up the
    depth exponentially and tries again when the initial guess is not deep
    enough.

    Args:
        remote: The location of the remote repository, in a format that the
            git command will understand.
        actual_branch: A remote branch or ref to fetch,
        compare_branch: Another remote branch or ref to fetch,
        verbose: When set, more progress output is produced.

    Returns:
        A ComparableCommits containing the commit id of the actual branch and
        a the id of a commit to compare against (e.g. for when doing incremental
        checks).
    """
    actual_id = ''
    base_id = ''
    optional_quiet = [] if verbose else ['--quiet']
    for depth in [10, 100, 1000, None]:
        optional_depth = [] if depth is None else [f'--depth={depth}']

        shell_tools.run(
            [
                'git', 'fetch', *optional_quiet, remote, actual_branch,
                *optional_depth
            ],
            log_run_to_stderr=verbose,
        )
        actual_id = shell_tools.output_of(['git', 'rev-parse', 'FETCH_HEAD'])

        shell_tools.run(
            [
                'git', 'fetch', *optional_quiet, remote, compare_branch,
                *optional_depth
            ],
            log_run_to_stderr=verbose,
        )
        base_id = shell_tools.output_of(['git', 'rev-parse', 'FETCH_HEAD'])

        try:
            base_id = shell_tools.output_of(
                ['git', 'merge-base', actual_id, base_id])
            break
        except subprocess.CalledProcessError:
            # No common ancestor. We need to dig deeper.
            pass

    return prepared_env.PreparedEnv(None, actual_id, base_id, None, None)
Пример #2
0
def test_run_with_command_logging():
    catch_stderr = io.StringIO()
    kw = {'stdout': subprocess.DEVNULL}
    with contextlib.redirect_stderr(catch_stderr):
        shell_tools.run(['echo', '-n', 'a', 'b'], **kw)
    assert catch_stderr.getvalue() == "run: ('echo', '-n', 'a', 'b')\n"
    catch_stderr = io.StringIO()
    with contextlib.redirect_stderr(catch_stderr):
        shell_tools.run(['echo', '-n', 'a', 'b'],
                        abbreviate_non_option_arguments=True,
                        **kw)
    assert catch_stderr.getvalue() == "run: ('echo', '-n', '[...]')\n"
Пример #3
0
 def _create_base_env(base_dir: Path, pip_install_args: Tuple[str, ...]):
     try:
         create_virtual_env(str(base_dir), [], sys.executable, True)
         with open(base_dir / "testrun.uid", mode="w") as f:
             f.write(testrun_uid)
         if pip_install_args:
             shell_tools.run(
                 [f"{base_dir}/bin/pip", "install", *pip_install_args])
     except BaseException as ex:
         # cleanup on failure
         if base_dir.is_dir():
             print(f"Removing {base_dir}, due to error: {ex}")
             shutil.rmtree(base_dir)
         raise
Пример #4
0
def create_virtual_env(venv_path: str, requirements_paths: Iterable[str],
                       python_path: str, verbose: bool) -> None:
    """Creates a new virtual environment and then installs dependencies.

    Args:
        venv_path: Where to put the virtual environment's state.
        requirements_paths: Location of requirements files to -r install.
        python_path: The python binary to use.
        verbose: When set, more progress output is produced.
    """
    optional_quiet = [] if verbose else ['--quiet']
    shell_tools.run(
        ['virtualenv', *optional_quiet, '-p', python_path, venv_path],
        stdout=sys.stderr)
    pip_path = os.path.join(venv_path, 'bin', 'pip')
    for req_path in requirements_paths:
        shell_tools.run([pip_path, 'install', *optional_quiet, '-r', req_path],
                        stdout=sys.stderr)
Пример #5
0
def test_isolated_env_cloning(cloned_env, param):
    env = cloned_env("test_isolated", "flynt==0.64")
    assert (env / "bin" / "pip").is_file()

    result = shell_tools.run(f"{env}/bin/pip list --format=json".split(),
                             stdout=subprocess.PIPE)
    packages = json.loads(result.stdout)
    assert {"name": "flynt", "version": "0.64"} in packages
    assert {"astor", "flynt", "pip", "setuptools",
            "wheel"} == set(p['name'] for p in packages)
Пример #6
0
def test_isolated_packages(cloned_env, module):
    env = cloned_env("isolated_packages", *PACKAGES)

    if str(module.root) != "cirq-core":
        assert f'cirq-core=={module.version}' in module.install_requires

    result = shell_tools.run(
        f"{env}/bin/pip install ./{module.root} ./cirq-core".split(),
        stderr=subprocess.PIPE,
        check=False,
    )
    assert result.returncode == 0, f"Failed to install {module.name}:\n{result.stderr}"

    result = shell_tools.run(
        f"{env}/bin/pytest ./{module.root} --ignore ./cirq-core/cirq/contrib".
        split(),
        capture_output=True,
        check=False,
    )
    assert result.returncode == 0, f"Failed isolated tests for {module.name}:\n{result.stdout}"
Пример #7
0
    def base_env_creator(env_dir_name: str, *pip_install_args: str) -> Path:
        """The function to create a cloned base environment."""
        # get/create a temp directory shared by all workers
        base_temp_path = Path(tempfile.gettempdir()) / "cirq-pytest"
        os.makedirs(name=base_temp_path, exist_ok=True)
        nonlocal base_dir
        base_dir = base_temp_path / env_dir_name
        with FileLock(str(base_dir) + ".lock"):
            if _check_for_reuse_or_recreate(base_dir):
                print(
                    f"Pytest worker [{worker_id}] is reusing {base_dir} for '{env_dir_name}'."
                )
            else:
                print(
                    f"Pytest worker [{worker_id}] is creating {base_dir} for '{env_dir_name}'."
                )
                _create_base_env(base_dir, pip_install_args)

        clone_dir = base_temp_path / str(uuid.uuid4())
        shell_tools.run(["virtualenv-clone", str(base_dir), str(clone_dir)])
        return clone_dir
Пример #8
0
def run(
    *,
    script_file: str,
    tmpdir_factory: pytest.TempdirFactory,
    arg: str = '',
    setup: str = '',
    additional_intercepts: Iterable[str] = (),
) -> subprocess.CompletedProcess:
    """Invokes the given script within a temporary test environment."""

    with open(script_file) as f:
        script_lines = f.readlines()

    # Create a unique temporary directory
    dir_path = tmpdir_factory.mktemp('tmp', numbered=True)
    file_path = os.path.join(dir_path, 'test-script.sh')

    intercepted = [
        'python',
        'python3',
        'pylint',
        'env',
        'pytest',
        'mypy',
        'black',
        *additional_intercepts,
    ]
    assert script_lines[0] == '#!/usr/bin/env bash\n'
    for e in intercepted:
        script_lines.insert(1, e + '() {\n  echo INTERCEPTED ' + e + ' $@\n}\n')

    with open(file_path, 'w') as f:
        f.writelines(script_lines)

    cmd = r"""
dir=$(git rev-parse --show-toplevel)
cd {}
git init --quiet --initial-branch master
git config --local user.name 'Me'
git config --local user.email '<>'
git commit -m init --allow-empty --quiet --no-gpg-sign
{}
mkdir -p dev_tools
touch dev_tools/pypath
chmod +x ./test-script.sh
./test-script.sh {}
""".format(
        dir_path, setup, arg
    )
    return shell_tools.run(
        cmd, log_run_to_stderr=False, shell=True, check=False, capture_output=True
    )
Пример #9
0
def fetch_github_pull_request(
    destination_directory: str,
    repository: github_repository.GithubRepository,
    pull_request_number: int,
    verbose: bool,
) -> prepared_env.PreparedEnv:
    """Uses content from github to create a dir for testing and comparisons.

    Args:
        destination_directory: The location to fetch the contents into.
        repository: The github repository that the commit lives under.
        pull_request_number: The id of the pull request to clone. If None, then
            the master branch is cloned instead.
        verbose: When set, more progress output is produced.

    Returns:
        Commit ids corresponding to content to test/compare.
    """

    branch = f'pull/{pull_request_number}/head'
    os.chdir(destination_directory)
    print('chdir', destination_directory, file=sys.stderr)

    optional_quiet = [] if verbose else ['--quiet']
    shell_tools.run(['git', 'init', *optional_quiet], stdout=sys.stderr)
    result = _git_fetch_for_comparison(
        remote=repository.as_remote(),
        actual_branch=branch,
        compare_branch='master',
        verbose=verbose,
    )
    optional_actual_commit_id = [] if result.actual_commit_id is None else [
        result.actual_commit_id
    ]
    shell_tools.run(
        [
            'git', 'branch', *optional_quiet, 'compare_commit',
            result.compare_commit_id
        ],
        log_run_to_stderr=verbose,
    )
    shell_tools.run(
        [
            'git', 'checkout', *optional_quiet, '-b', 'actual_commit',
            *optional_actual_commit_id
        ],
        log_run_to_stderr=verbose,
    )
    return prepared_env.PreparedEnv(
        github_repo=repository,
        actual_commit_id=result.actual_commit_id,
        compare_commit_id=result.compare_commit_id,
        destination_directory=destination_directory,
        virtual_env_path=None,
    )
Пример #10
0
def test_notebooks_against_released_cirq(notebook_path):
    """Test that jupyter notebooks execute.

    In order to speed up the execution of these tests an auxiliary file may be supplied which
    performs substitutions on the notebook to make it faster.

    Specifically for a notebook file notebook.ipynb, one can supply a file notebook.tst which
    contains the substitutes.  The substitutions are provide in the form `pattern->replacement`
    where the pattern is what is matched and replaced. While the pattern is compiled as a
    regular expression, it is considered best practice to not use complicated regular expressions.
    Lines in this file that do not have `->` are ignored.
    """
    notebook_file = os.path.basename(notebook_path)
    notebook_rel_dir = os.path.dirname(os.path.relpath(notebook_path, "."))
    out_path = f"out/{notebook_rel_dir}/{notebook_file[:-6]}.out.ipynb"
    rewritten_notebook_descriptor, rewritten_notebook_path = rewrite_notebook(
        notebook_path)
    papermill_flags = "--no-request-save-on-cell-execute --autosave-cell-every 0"
    cmd = f"""mkdir -p out/{notebook_rel_dir}
papermill {rewritten_notebook_path} {out_path} {papermill_flags}"""

    result = shell_tools.run(cmd,
                             log_run_to_stderr=False,
                             shell=True,
                             check=False,
                             capture_output=True)

    if result.returncode != 0:
        print(result.stderr)
        pytest.fail(
            f"Notebook failure: {notebook_file}, please see {out_path} for the output "
            f"notebook (in Github Actions, you can download it from the workflow artifact"
            f" 'notebook-outputs')")

    if rewritten_notebook_descriptor:
        os.close(rewritten_notebook_descriptor)
Пример #11
0
def fetch_local_files(destination_directory: str,
                      verbose: bool) -> prepared_env.PreparedEnv:
    """Uses local files to create a directory for testing and comparisons.

    Args:
        destination_directory: The directory where the copied files should go.
        verbose: When set, more progress output is produced.

    Returns:
        Commit ids corresponding to content to test/compare.
    """
    staging_dir = destination_directory + '-staging'
    optional_quiet = [] if verbose else ['--quiet']
    try:
        shutil.copytree(get_repo_root(), staging_dir)
        os.chdir(staging_dir)
        if verbose:
            print('chdir', staging_dir, file=sys.stderr)

        shell_tools.run(['git', 'add', '--all'],
                        stdout=sys.stderr,
                        log_run_to_stderr=verbose)

        shell_tools.run(
            [
                'git',
                'commit',
                '-m',
                'working changes',
                '--allow-empty',
                '--no-gpg-sign',
                *optional_quiet,
            ],
            stdout=sys.stderr,
            log_run_to_stderr=verbose,
        )

        cur_commit = shell_tools.output_of(['git', 'rev-parse', 'HEAD'])

        os.chdir(destination_directory)
        if verbose:
            print('chdir', destination_directory, file=sys.stderr)
        shell_tools.run(['git', 'init', *optional_quiet],
                        stdout=sys.stderr,
                        log_run_to_stderr=verbose)
        result = _git_fetch_for_comparison(staging_dir,
                                           cur_commit,
                                           'master',
                                           verbose=verbose)
    finally:
        shutil.rmtree(staging_dir, ignore_errors=True)

    optional_actual_commit_id = [] if result.actual_commit_id is None else [
        result.actual_commit_id
    ]
    shell_tools.run(
        [
            'git', 'branch', *optional_quiet, 'compare_commit',
            result.compare_commit_id
        ],
        log_run_to_stderr=verbose,
    )
    shell_tools.run(
        [
            'git', 'checkout', *optional_quiet, '-b', 'actual_commit',
            *optional_actual_commit_id
        ],
        log_run_to_stderr=verbose,
    )
    return prepared_env.PreparedEnv(
        github_repo=None,
        actual_commit_id=result.actual_commit_id,
        compare_commit_id=result.compare_commit_id,
        destination_directory=destination_directory,
        virtual_env_path=None,
    )
Пример #12
0
def run(*args, **kwargs):
    return shell_tools.run(*args, log_run_to_stderr=False, **kwargs)