def run_unit_tests(*, remote_execution_enabled: bool) -> None: check_pants_pex_exists() all_targets = get_all_python_tests(tag="-integration") blacklisted_chroot_targets = get_blacklisted_targets( "unit_test_chroot_blacklist.txt") blacklisted_v2_targets = get_blacklisted_targets( "unit_test_v2_blacklist.txt") blacklisted_remote_targets = get_blacklisted_targets( "unit_test_remote_blacklist.txt") v1_no_chroot_targets = blacklisted_chroot_targets v1_chroot_targets = blacklisted_v2_targets v2_local_targets = blacklisted_remote_targets v2_remote_targets = all_targets - v2_local_targets - v1_chroot_targets - v1_no_chroot_targets basic_command = ["./pants.pex", "test.pytest"] v2_command = ["./pants.pex", "--no-v1", "--v2", "test.pytest"] v1_no_chroot_command = basic_command + sorted( v1_no_chroot_targets) + PYTEST_PASSTHRU_ARGS v1_chroot_command = basic_command + [ "--test-pytest-chroot" ] + sorted(v1_chroot_targets) + PYTEST_PASSTHRU_ARGS v2_local_command = v2_command + sorted(v2_local_targets) if not remote_execution_enabled: v2_local_targets = v2_local_targets | v2_remote_targets v2_local_command = v2_command + sorted(v2_local_targets) else: with travis_section( "UnitTestsRemote", "Running unit tests via remote execution" ), get_remote_execution_oauth_token_path() as oauth_token_path: v2_remote_command = v2_command[:-1] + [ "--pants-config-files=pants.remote.ini", # We turn off speculation to reduce the risk of flakiness, where a test passes locally but # fails remoting and we have a race condition for which environment executes first. "--process-execution-speculation-strategy=none", f"--remote-oauth-bearer-token-path={oauth_token_path}", "test.pytest", ] + sorted(v2_remote_targets) try: subprocess.run(v2_remote_command, check=True) except subprocess.CalledProcessError: die("Unit test failure (remote execution)") else: green("Unit tests passed (remote execution)") with travis_section("UnitTestsLocal", "Running unit tests via local execution"): try: subprocess.run(v2_local_command, check=True) subprocess.run(v1_chroot_command, check=True) subprocess.run(v1_no_chroot_command, check=True) except subprocess.CalledProcessError: die("Unit test failure (local execution)") else: green("Unit tests passed (local execution)")
def run_python_tests_v2(*, remote_execution_enabled: bool) -> None: check_pants_pex_exists() blacklisted_v2_targets = get_blacklisted_targets( "unit_test_v2_blacklist.txt") blacklisted_remote_targets = get_blacklisted_targets( "unit_test_remote_blacklist.txt") all_targets = get_all_python_tests(tag="-integration") v2_compatible_targets = all_targets - blacklisted_v2_targets if remote_execution_enabled: remote_execution_targets = v2_compatible_targets - blacklisted_remote_targets local_execution_targets = blacklisted_remote_targets else: remote_execution_targets = set() local_execution_targets = v2_compatible_targets def run_v2_tests(*, targets: Set[str], execution_strategy: str, oauth_token_path: Optional[str] = None) -> None: try: command = (["./pants.pex", "--no-v1", "--v2", "test.pytest"] + sorted(targets) + PYTEST_PASSTHRU_ARGS) if oauth_token_path is not None: command[3:3] = [ "--pants-config-files=pants.remote.ini", # We turn off speculation to reduce the risk of flakiness, where a test passes locally but # fails remoting and we have a race condition for which environment executes first. "--process-execution-speculation-strategy=none", f"--remote-oauth-bearer-token-path={oauth_token_path}" ] subprocess.run(command, check=True) except subprocess.CalledProcessError: die(f"V2 unit tests failure ({execution_strategy} build execution)." ) else: green( f"V2 unit tests passed ({execution_strategy} build execution)." ) if remote_execution_enabled: with travis_section( "PythonTestsV2Remote", "Running Python unit tests with V2 test runner and remote build execution" ), get_remote_execution_oauth_token_path() as oauth_token_path: run_v2_tests(targets=remote_execution_targets, execution_strategy="remote", oauth_token_path=oauth_token_path) with travis_section( "PythonTestsV2Local", "Running Python unit tests with V2 test runner and local build execution" ): run_v2_tests(targets=local_execution_targets, execution_strategy="local")
def run_python_tests_v1() -> None: check_pants_pex_exists() blacklisted_v2_targets = get_blacklisted_targets( "unit_test_v2_blacklist.txt") blacklisted_chroot_targets = get_blacklisted_targets( "unit_test_chroot_blacklist.txt") chrooted_targets = blacklisted_v2_targets - blacklisted_chroot_targets with travis_section("PythonTestsV1", "Running Python unit tests with V1 test runner"): try: subprocess.run( ["./pants.pex", "--test-pytest-chroot", "test.pytest"] + sorted(chrooted_targets) + PYTEST_PASSTHRU_ARGS, check=True) subprocess.run(["./pants.pex", "test.pytest"] + sorted(blacklisted_chroot_targets) + PYTEST_PASSTHRU_ARGS, check=True) except subprocess.CalledProcessError: die("Python unit test failure (V1 test runner") else: green("V1 unit tests passed.")
def build_fs_util() -> None: # See https://www.pantsbuild.org/docs/contributions-rust for a description of fs_util. We include # it in our releases because it can be a useful standalone tool. with travis_section("fs_util", "Building fs_util"): subprocess.run( [ "build-support/bin/native/cargo", "build", "--release", "--manifest-path=src/rust/engine/Cargo.toml", "-p", "fs_util", ], check=True, env={ **os.environ, "RUST_BACKTRACE": "1" }, ) current_os = (subprocess.run(["build-support/bin/get_os.sh"], stdout=subprocess.PIPE, check=True).stdout.decode().strip()) dest_dir = (Path(CONSTANTS.deploy_dir) / "bin" / "fs_util" / current_os / CONSTANTS.pants_unstable_version) dest_dir.mkdir(parents=True, exist_ok=True) shutil.copy("src/rust/engine/target/release/fs_util", dest_dir) green(f"Built fs_util at {dest_dir / 'fs_util'}.")
def run_integration_tests(*, shard: Optional[str]) -> None: main_command = [ "./pants.pex", "--tag=+integration", "test.pytest", "src/python::", "tests/python::", ] contrib_command = [ "./pants.pex", "--tag=+integration", "--exclude-target-regexp=.*/testprojects/.*", "test.pytest", "contrib::", ] if shard is not None: shard_arg = f"--test-pytest-test-shard={shard}" main_command.append(shard_arg) contrib_command.append(shard_arg) main_command.extend(PYTEST_PASSTHRU_ARGS) contrib_command.extend(PYTEST_PASSTHRU_ARGS) with travis_section( "IntegrationTests", f"Running Pants Integration tests{shard if shard is not None else ''}" ): check_pants_pex_exists() try: subprocess.run(main_command, check=True) except subprocess.CalledProcessError: die("Integration test failure.") try: subprocess.run(contrib_command, check=True) except subprocess.CalledProcessError: die("Contrib integration test failure.")
def run_rust_tests() -> None: command = [ "build-support/bin/native/cargo", "test", "--all", # We pass --tests to skip doc tests, because our generated protos contain invalid doc tests in # their comments. "--tests", "--manifest-path=src/rust/engine/Cargo.toml", "--", "--nocapture" ] if platform.system() == "Darwin": # The osx travis environment has a low file descriptors ulimit, so we avoid running too many # tests in parallel. command.append("--test-threads=1") with travis_section("RustTests", "Running Rust tests"): try: subprocess.run(command, env={ **os.environ, "RUST_BACKTRACE": "all" }, check=True) except subprocess.CalledProcessError: die("Rust test failure.")
def run_python_tests_v1() -> None: known_v2_failures_file = "build-support/unit_test_v2_blacklist.txt" with travis_section("PythonTestsV1", "Running Python unit tests with V1 test runner"): check_pants_pex_exists() try: subprocess.run([ "./pants.pex", f"--target-spec-file={known_v2_failures_file}", "test.pytest", "--chroot", ] + PYTEST_PASSTHRU_ARGS, check=True) except subprocess.CalledProcessError: die("Python unit test failure (V1 test runner") else: green("V1 unit tests passed.") try: subprocess.run([ "./pants.pex", "--tag=-integration", "--exclude-target-regexp=./*testprojects/.*", "test.pytest", "contrib::", ] + PYTEST_PASSTHRU_ARGS, check=True) except subprocess.CalledProcessError: die("Contrib Python test failure") else: green("Contrib unit tests passed.")
def run_python_tests_v2() -> None: known_v2_failures_file = "build-support/unit_test_v2_blacklist.txt" with open(known_v2_failures_file, "r") as f: blacklisted_targets = {line.strip() for line in f.readlines()} with travis_section("PythonTestsV1", "Running Python unit tests with V2 test runner"): check_pants_pex_exists() try: all_targets = subprocess.run([ "./pants.pex", "--tag=-integration", "--filter-type=python_tests", "filter", "src/python::", "tests/python::", ], stdout=subprocess.PIPE, encoding="utf-8", check=True).stdout.strip().split("\n") v2_targets = set(all_targets) - blacklisted_targets subprocess.run(["./pants.pex", "--no-v1", "--v2", "test.pytest"] + sorted(v2_targets) + PYTEST_PASSTHRU_ARGS, check=True) except subprocess.CalledProcessError: die("Python unit tests failure (V2 test runner)") else: green("V2 unit tests passed.")
def run_sanity_checks() -> None: def run_check(command: List[str]) -> None: print(f"* Executing `./pants.pex {' '.join(command)}` as a sanity check") try: subprocess.run( ["./pants.pex", *command], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=True, ) except subprocess.CalledProcessError: die(f"Failed to execute `./pants {command}`.") checks = [ ["bash-completion"], ["reference"], ["clean-all"], ["goals"], ["list", "::"], ["roots"], ["targets"], ] with travis_section("SanityCheck", "Sanity checking bootstrapped Pants and repo BUILD files"): check_pants_pex_exists() for check in checks: run_check(check)
def run_cargo_audit() -> None: with travis_section("CargoAudit", "Running Cargo audit on Rust code"): try: subprocess.run( [ "build-support/bin/native/cargo", "ensure-installed", "--package=cargo-audit", "--version=0.6.1", # TODO(John Sirois): Kill --git-url/--git-rev when we upgrade to cargo-audit > 0.6.1. # See: https://github.com/pantsbuild/pants/issues/7760 for context. "--git-url=https://github.com/RustSec/cargo-audit", "--git-rev=1c298bcda2c74f4a1bd8f0d8482b3577ee94fbb3", ], check=True) subprocess.run( [ "build-support/bin/native/cargo", "audit", "-f", "src/rust/engine/Cargo.lock", # TODO(John Sirois): Kill --ignore RUSTSEC-2019-0003 when we can upgrade to an official # released version of protobuf with a fix. # See: https://github.com/pantsbuild/pants/issues/7760 for context. "--ignore", "RUSTSEC-2019-0003" ], check=True) except subprocess.CalledProcessError: die("Cargo audit failure")
def run_cargo_audit() -> None: with travis_section("CargoAudit", "Running Cargo audit on Rust code"): try: subprocess.run(["./cargo", "install", "--version", "0.13.1", "cargo-audit"], check=True) subprocess.run(["./cargo", "audit"], check=True) except subprocess.CalledProcessError: die("Cargo audit failure")
def bootstrap( *, clean: bool, try_to_skip_rust_compilation: bool, python_version: PythonVersion ) -> None: with travis_section("Bootstrap", f"Bootstrapping Pants as a Python {python_version} PEX"): if clean: try: subprocess.run(["./build-support/python/clean.sh"], check=True) except subprocess.CalledProcessError: die("Failed to clean before bootstrapping Pants.") try: skip_native_engine_so_bootstrap = ( try_to_skip_rust_compilation and Path("src/python/pants/engine/native_engine.so").exists() ) subprocess.run( ["./build-support/bin/bootstrap_pants_pex.sh"], check=True, env={ **os.environ, "SKIP_NATIVE_ENGINE_SO_BOOTSTRAP": ( "true" if skip_native_engine_so_bootstrap else "false" ), }, ) except subprocess.CalledProcessError: die("Failed to bootstrap Pants.")
def build_fs_util() -> None: # See https://www.pantsbuild.org/docs/contributions-rust for a description of fs_util. We # include it in our releases because it can be a useful standalone tool. with travis_section("fs_util", "Building fs_util"): command = ["./cargo", "build", "-p", "fs_util"] release_mode = os.environ.get("MODE", "") != "debug" if release_mode: command.append("--release") subprocess.run(command, check=True, env={**os.environ, "RUST_BACKTRACE": "1"}) current_os = ( subprocess.run(["build-support/bin/get_os.sh"], stdout=subprocess.PIPE, check=True) .stdout.decode() .strip() ) dest_dir = ( Path(CONSTANTS.deploy_dir) / "bin" / "fs_util" / current_os / CONSTANTS.pants_unstable_version ) dest_dir.mkdir(parents=True, exist_ok=True) shutil.copy( f"src/rust/engine/target/{'release' if release_mode else 'debug'}/fs_util", dest_dir, ) green(f"Built fs_util at {dest_dir / 'fs_util'}.")
def run_rust_tests() -> None: is_macos = platform.system() == "Darwin" command = [ "./cargo", "test", "--all", # We pass --tests to skip doc tests because our generated protos contain invalid doc tests # in their comments. "--tests", "--", "--nocapture", ] if is_macos: # The osx travis environment has a low file descriptors ulimit, so we avoid running too many # tests in parallel. command.append("--test-threads=1") with travis_section("RustTests", "Running Rust tests"): try: subprocess.run(command, env={ **os.environ, "RUST_BACKTRACE": "all" }, check=True) except subprocess.CalledProcessError: die("Rust test failure.")
def run_tests(*, skip_pantsd_tests: bool) -> None: version_command = ["./pants", "--version"] list_command = ["./pants", "list", "::"] env_with_pantsd = {**os.environ, "PANTS_ENABLE_PANTSD": "True"} with travis_section("PantsVersion", f"Testing `{' '.join(version_command)}`."): subprocess.run(version_command, check=True) with travis_section("PantsList", f"Testing `{' '.join(list_command)}`."): subprocess.run(list_command, check=True) if not skip_pantsd_tests: with travis_section( "PantsVersionDaemon", f"Testing `{' '.join(version_command)}` with pantsd enabled."): subprocess.run(version_command, env=env_with_pantsd, check=True) with travis_section( "PantsListDaemon", f"Testing `{' '.join(list_command)}` with pantsd enabled."): subprocess.run(list_command, env=env_with_pantsd, check=True)
def _run_command(command: List[str], *, slug: str, start_message: str, die_message: str, requires_pex: bool = True) -> None: with travis_section(slug, start_message): if requires_pex: check_pants_pex_exists() try: subprocess.run(command, check=True) except subprocess.CalledProcessError: die(die_message)
def bootstrap(*, clean: bool, python_version: PythonVersion) -> None: with travis_section( "Bootstrap", f"Bootstrapping Pants as a Python {python_version} PEX"): if clean: try: subprocess.run(["./build-support/python/clean.sh"], check=True) except subprocess.CalledProcessError: die("Failed to clean before bootstrapping Pants.") try: subprocess.run(["./build-support/bin/bootstrap_pants_pex.sh"], check=True) except subprocess.CalledProcessError: die("Failed to bootstrap Pants.")
def run_integration_tests(*, shard: Optional[str]) -> None: check_pants_pex_exists() all_targets = get_all_python_tests(tag="+integration") command = ["./pants.pex", "test.pytest"] if shard is not None: command.append(f"--test-pytest-test-shard={shard}") with travis_section( "IntegrationTests", f"Running Pants Integration tests {shard if shard is not None else ''}" ): try: subprocess.run(command + sorted(all_targets) + PYTEST_PASSTHRU_ARGS, check=True) except subprocess.CalledProcessError: die("Integration test failure.")
def bootstrap(*, clean: bool, python_version: PythonVersion) -> None: with travis_section( "Bootstrap", f"Bootstrapping Pants as a Python {python_version} PEX"): if clean: try: subprocess.run(["./build-support/python/clean.sh"], check=True) except subprocess.CalledProcessError: die("Failed to clean before bootstrapping Pants.") try: subprocess.run([ "./pants", "binary", "src/python/pants/bin:pants_local_binary" ], check=True) Path("dist/pants_local_binary.pex").rename("pants.pex") subprocess.run(["./pants.pex", "--version"], check=True) except subprocess.CalledProcessError: die("Failed to bootstrap Pants.")
def run_python_tests_v2(*, remote_execution_enabled: bool) -> None: def get_blacklisted_targets(path: str) -> Set[str]: return {line.strip() for line in Path(path).read_text().splitlines()} blacklisted_v2_targets = get_blacklisted_targets( "build-support/unit_test_v2_blacklist.txt") blacklisted_remote_targets = get_blacklisted_targets( "build-support/unit_test_remote_blacklist.txt") with travis_section("PythonTestsV2Setup", "Setting up Python unit tests with V2 test runner"): all_targets = subprocess.run([ "./pants.pex", "--tag=-integration", "--filter-type=python_tests", "filter", "src/python::", "tests/python::", ], stdout=subprocess.PIPE, encoding="utf-8", check=True).stdout.strip().split("\n") v2_compatible_targets = set(all_targets) - blacklisted_v2_targets if remote_execution_enabled: remote_execution_targets = v2_compatible_targets - blacklisted_remote_targets local_execution_targets = blacklisted_remote_targets else: remote_execution_targets = set() local_execution_targets = v2_compatible_targets check_pants_pex_exists() def run_v2_tests(*, targets: Set[str], execution_strategy: str, oauth_token_path: Optional[str] = None) -> None: try: command = (["./pants.pex", "--no-v1", "--v2", "test.pytest"] + sorted(targets) + PYTEST_PASSTHRU_ARGS) if oauth_token_path is not None: command[3:3] = [ "--pants-config-files=pants.remote.ini", # We turn off speculation to reduce the risk of flakiness, where a test passes locally but # fails remoting and we have a race condition for which environment executes first. "--process-execution-speculation-strategy=none", f"--remote-oauth-bearer-token-path={oauth_token_path}" ] subprocess.run(command, check=True) except subprocess.CalledProcessError: die(f"V2 unit tests failure ({execution_strategy} build execution)." ) else: green( f"V2 unit tests passed ({execution_strategy} build execution)." ) if remote_execution_enabled: with travis_section( "PythonTestsV2Remote", "Running Python unit tests with V2 test runner and remote build execution" ), get_remote_execution_oauth_token_path() as oauth_token_path: run_v2_tests(targets=remote_execution_targets, execution_strategy="remote", oauth_token_path=oauth_token_path) with travis_section( "PythonTestsV2Local", "Running Python unit tests with V2 test runner and local build execution" ): run_v2_tests(targets=local_execution_targets, execution_strategy="local")