def assert_targets( rule_runner: RuleRunner, expected: set[str], *, target_type: list[str] | None = None, address_regex: list[str] | None = None, tag_regex: list[str] | None = None, granularity: TargetGranularity = TargetGranularity.all_targets, ) -> None: filter_result = rule_runner.run_goal_rule( FilterGoal, args=[ f"--target-type={target_type or []}", f"--address-regex={address_regex or []}", f"--tag-regex={tag_regex or []}", f"--granularity={granularity.value}", "::", ], ) assert set(filter_result.stdout.splitlines()) == expected # Also run with `list` to prove that the filters work from any goal. list_result = rule_runner.run_goal_rule( List, global_args=[ f"--filter-target-type={target_type or []}", f"--filter-address-regex={address_regex or []}", f"--filter-tag-regex={tag_regex or []}", f"--filter-granularity={granularity.value}", "::", ], ) assert set(list_result.stdout.splitlines()) == expected
def test_goal_check_mode(generic_goal_rule_runner: RuleRunner) -> None: """Checks that we correctly set the exit code and pipe fixers to each other.""" generic_goal_rule_runner.write_files({ "BUILD": "# line\n", "dir/BUILD": "# line 1\n# line 2\n" }) result = generic_goal_rule_runner.run_goal_rule( UpdateBuildFilesGoal, global_args=["--pants-bin-name=./custom_pants"], args=["--check", "::"], ) assert result.exit_code == 1 assert result.stdout == dedent("""\ Would update BUILD: - Add a new line - Reverse lines Would update dir/BUILD: - Add a new line - Reverse lines To fix `update-build-files` failures, run `./custom_pants update-build-files`. """) assert Path(generic_goal_rule_runner.build_root, "BUILD").read_text() == "# line\n" assert (Path(generic_goal_rule_runner.build_root, "dir/BUILD").read_text() == "# line 1\n# line 2\n")
def run_goal(rule_runner: RuleRunner, args: list[str]) -> GoalRuleResult: return rule_runner.run_goal_rule( PyConstraintsGoal, env={"PANTS_PYTHON_SETUP_INTERPRETER_CONSTRAINTS": "['>=3.6']"}, env_inherit={"PATH", "PYENV_ROOT", "HOME"}, args=args, )
def test_coursier_resolve_updates_bogus_lockfile( rule_runner: RuleRunner) -> None: rule_runner.write_files({ "BUILD": dedent("""\ coursier_lockfile( name = 'example-lockfile', maven_requirements = [ 'org.hamcrest:hamcrest-core:1.3', ], ) """), "coursier_resolve.lockfile": "]bad json[", }) result = rule_runner.run_goal_rule(CoursierResolve, args=["::"]) assert result.exit_code == 0 assert result.stderr == "Updated lockfile at: coursier_resolve.lockfile\n" expected_lockfile = CoursierResolvedLockfile(entries=(CoursierLockfileEntry( coord=MavenCoord(coord="org.hamcrest:hamcrest-core:1.3"), file_name="hamcrest-core-1.3.jar", direct_dependencies=MavenCoordinates([]), dependencies=MavenCoordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ), )) assert (Path(rule_runner.build_root, "coursier_resolve.lockfile").read_bytes() == expected_lockfile.to_json())
def test_coursier_resolve_noop_does_not_touch_lockfile( rule_runner: RuleRunner) -> None: expected_lockfile = CoursierResolvedLockfile(entries=(CoursierLockfileEntry( coord=MavenCoord(coord="org.hamcrest:hamcrest-core:1.3"), file_name="hamcrest-core-1.3.jar", direct_dependencies=MavenCoordinates([]), dependencies=MavenCoordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ), )) rule_runner.write_files({ "BUILD": dedent("""\ coursier_lockfile( name = 'example-lockfile', maven_requirements = [ 'org.hamcrest:hamcrest-core:1.3', ], sources = [ "coursier_resolve.lockfile", ], ) """), "coursier_resolve.lockfile": expected_lockfile.to_json().decode("utf-8"), }) result = rule_runner.run_goal_rule(CoursierResolve, args=["::"]) assert result.exit_code == 0 assert result.stderr == ""
def test_count_loc(rule_runner: RuleRunner) -> None: py_dir = "src/py/foo" elixir_dir = "src/elixir/foo" rule_runner.write_files({ f"{py_dir}/foo.py": '# A comment.\n\nprint("some code")\n# Another comment.', f"{py_dir}/bar.py": '# A comment.\n\nprint("some more code")', f"{py_dir}/BUILD": "python_library()", f"{elixir_dir}/foo.ex": 'IO.puts("Some elixir")\n# A comment', f"{elixir_dir}/ignored.ex": "# We do not expect this file to appear in counts.", f"{elixir_dir}/BUILD": "elixir(sources=['foo.ex'])", }) result = rule_runner.run_goal_rule(CountLinesOfCode, args=[py_dir, elixir_dir]) assert result.exit_code == 0 assert_counts(result.stdout, "Python", num_files=2, blank=2, comment=3, code=2) assert_counts(result.stdout, "Elixir", comment=1, code=1)
def run_goal(rule_runner: RuleRunner, args: list[str]) -> GoalRuleResult: return rule_runner.run_goal_rule( PyConstraintsGoal, env={"PANTS_PYTHON_INTERPRETER_CONSTRAINTS": "['>=3.6']"}, args=args, global_args=["--no-python-infer-imports"], )
def test_export_codegen(rule_runner: RuleRunner) -> None: rule_runner.add_to_build_file("", "gen1(name='gen1')\ngen2(name='gen2')\n") result = rule_runner.run_goal_rule(ExportCodegen, args=["::"]) assert result.exit_code == 0 parent_dir = Path(rule_runner.build_root, "dist", "codegen") assert (parent_dir / "assets" / "README.md").read_text() == "Hello!" assert (parent_dir / "src" / "haskell" / "app.hs").read_text() == "10 * 4"
def test_mocked_publish(rule_runner: RuleRunner) -> None: rule_runner.write_files({ "src/BUILD": dedent("""\ python_sources() python_distribution( name="dist", provides=python_artifact( name="my-package", version="0.1.0", ), repositories=["mocked-repo"], ) """), }) result = rule_runner.run_goal_rule( Publish, args=("src:dist", ), env_inherit={"HOME", "PATH", "PYENV_ROOT"}, ) assert result.exit_code == 0 assert "my-package-0.1.0.tar.gz published." in result.stderr assert "my_package-0.1.0-py3-none-any.whl published." in result.stderr assert "mocked-repo" in result.stdout
def run_yapf( build_content: str, *, extra_args: list[str] | None = None) -> tuple[GoalRuleResult, str]: """Returns the Goal's result and contents of the BUILD file after execution.""" rule_runner = RuleRunner( rules=( format_build_file_with_yapf, update_build_files, *config_files.rules(), *pex.rules(), SubsystemRule(Yapf), SubsystemRule(UpdateBuildFilesSubsystem), UnionRule(RewrittenBuildFileRequest, FormatWithYapfRequest), ), target_types=[GenericTarget], ) rule_runner.write_files({"BUILD": build_content}) goal_result = rule_runner.run_goal_rule( UpdateBuildFilesGoal, args=["--update-build-files-formatter=yapf", "::"], global_args=extra_args or (), env_inherit=BLACK_ENV_INHERIT, ) rewritten_build = Path(rule_runner.build_root, "BUILD").read_text() return goal_result, rewritten_build
def test_workspace_in_goal_rule() -> None: class WorkspaceGoalSubsystem(GoalSubsystem): name = "workspace-goal" class WorkspaceGoal(Goal): subsystem_cls = WorkspaceGoalSubsystem @dataclass(frozen=True) class DigestRequest: create_digest: CreateDigest @rule def digest_request_singleton() -> DigestRequest: fc = FileContent(path="a.txt", content=b"hello") return DigestRequest(CreateDigest([fc])) @goal_rule async def workspace_goal_rule( console: Console, workspace: Workspace, digest_request: DigestRequest ) -> WorkspaceGoal: snapshot = await Get(Snapshot, CreateDigest, digest_request.create_digest) workspace.write_digest(snapshot.digest) console.print_stdout(snapshot.files[0], end="") return WorkspaceGoal(exit_code=0) rule_runner = RuleRunner(rules=[workspace_goal_rule, digest_request_singleton]) result = rule_runner.run_goal_rule(WorkspaceGoal) assert result.exit_code == 0 assert result.stdout == "a.txt" assert Path(rule_runner.build_root, "a.txt").read_text() == "hello"
def test_black_fixer_noops(black_rule_runner: RuleRunner) -> None: black_rule_runner.write_files({"BUILD": 'target(name="t")\n'}) result = black_rule_runner.run_goal_rule(UpdateBuildFilesGoal, args=["::"], env_inherit=BLACK_ENV_INHERIT) assert result.exit_code == 0 assert Path(black_rule_runner.build_root, "BUILD").read_text() == 'target(name="t")\n'
def test_passthrough_args(rule_runner: RuleRunner) -> None: rule_runner.create_file("foo.py", "print('hello world!')\n") rule_runner.add_to_build_file("", "python_library(name='foo')") result = rule_runner.run_goal_rule(CountLinesOfCode, args=["//:foo", "--", "--no-cocomo"]) assert result.exit_code == 0 assert_counts(result.stdout, "Python", code=1) assert "Estimated Cost to Develop" not in result.stdout
def test_repl_bogus_repl_name(rule_runner: RuleRunner) -> None: setup_sources(rule_runner) result = rule_runner.run_goal_rule( Repl, global_args=["--backend-packages=pants.backend.python"], args=["--shell=bogus-repl", "src/python/lib.py"], ) assert result.exit_code == -1 assert "'bogus-repl' is not a registered REPL. Available REPLs" in result.stderr
def test_black_fixer_args(black_rule_runner: RuleRunner) -> None: black_rule_runner.write_files({"BUILD": "tgt(name='t')\n"}) result = black_rule_runner.run_goal_rule( UpdateBuildFilesGoal, global_args=["--black-args='--skip-string-normalization'"], env_inherit=BLACK_ENV_INHERIT, ) assert result.exit_code == 0 assert Path(black_rule_runner.build_root, "BUILD").read_text() == "tgt(name='t')\n"
def test_files_without_owners(rule_runner: RuleRunner) -> None: """cloc works on any readable file in the build root, regardless of whether it's declared in a BUILD file.""" rule_runner.create_file("test/foo.ex", 'IO.puts("im a free thinker!")') rule_runner.create_file("test/foo.hs", 'main = putStrLn "Whats Pants, precious?"') result = rule_runner.run_goal_rule(CountLinesOfCode, args=["test/foo.*"]) assert result.exit_code == 0 assert_counts(result.stdout, "Elixir", code=1) assert_counts(result.stdout, "Haskell", code=1)
def test_passthrough_args(rule_runner: RuleRunner) -> None: rule_runner.write_files({ "foo.py": "print('hello world!')\n", "BUILD": "python_sources(name='foo')" }) result = rule_runner.run_goal_rule(CountLinesOfCode, args=["//:foo", "--", "--no-cocomo"]) assert result.exit_code == 0 assert_counts(result.stdout, "Python", code=1) assert "Estimated Cost to Develop" not in result.stdout
def run_fmt( rule_runner: RuleRunner, *, target_specs: List[str], only: list[str] | None = None ) -> str: result = rule_runner.run_goal_rule( Fmt, args=[f"--only={repr(only or [])}", *target_specs], ) assert result.exit_code == 0 assert not result.stdout return result.stderr
def test_no_matches(rule_runner: RuleRunner, caplog) -> None: rule_runner.create_file("f.txt") rule_runner.add_to_build_file("", "files(name='tgt', sources=['f.txt'])") result = rule_runner.run_goal_rule(PyConstraintsGoal, args=["f.txt"]) assert result.exit_code == 0 assert len(caplog.records) == 1 assert ( "No Python files/targets matched for the `py-constraints` goal. All target types with " "Python interpreter constraints: python_library, python_tests" ) in caplog.text
def test_black_config(black_rule_runner: RuleRunner) -> None: black_rule_runner.write_files( { "pyproject.toml": "[tool.black]\nskip-string-normalization = 'true'\n", "BUILD": "tgt(name='t')\n", }, ) result = black_rule_runner.run_goal_rule(UpdateBuildFilesGoal, env_inherit=BLACK_ENV_INHERIT) assert result.exit_code == 0 assert Path(black_rule_runner.build_root, "BUILD").read_text() == "tgt(name='t')\n"
def test_repl_ipython(rule_runner: RuleRunner) -> None: setup_sources(rule_runner) result = rule_runner.run_goal_rule( Repl, global_args=[ "--backend-packages=pants.backend.python", "--backend-packages=pants.backend.codegen.protobuf.python", ], args=["--shell=ipython", "src/python/lib.py"], ) assert result.exit_code == 0
def test_repl_bogus_repl_name(rule_runner: RuleRunner) -> None: setup_sources(rule_runner) with mock_console(rule_runner.options_bootstrapper): result = rule_runner.run_goal_rule( Repl, global_args=["--backend-packages=pants.backend.python"], args=["--shell=bogus-repl", "src/python/lib.py"], env_inherit={"PATH", "PYENV_ROOT", "HOME"}, ) assert result.exit_code == -1 assert "'bogus-repl' is not a registered REPL. Available REPLs" in result.stderr
def test_render_constraints(rule_runner: RuleRunner) -> None: setup_project(rule_runner) result = rule_runner.run_goal_rule(PyConstraintsGoal, args=["app"]) assert result.stdout == dedent("""\ Final merged constraints: CPython==2.7.*,==3.7.*,>=3.6 OR CPython==3.7.*,>=3.5,>=3.6 CPython==3.7.* app CPython>=3.6 lib2/a.py lib2/b.py CPython==2.7.* OR CPython>=3.5 lib1 """) # If we run on >1 input, we include a warning about what the "final merged constraints" mean. result = rule_runner.run_goal_rule(PyConstraintsGoal, args=["app", "lib1"]) assert "Consider using a more precise query" in result.stdout
def test_constraints_summary(rule_runner: RuleRunner) -> None: setup_project(rule_runner) result = rule_runner.run_goal_rule(PyConstraintsGoal, args=["--summary"]) assert result.stdout == dedent("""\ Target,Constraints,Transitive Constraints,# Dependencies,# Dependees\r app,CPython==3.7.*,"CPython==2.7.*,==3.7.*,>=3.6 OR CPython==3.7.*,>=3.5,>=3.6",3,0\r lib1,CPython==2.7.* OR CPython>=3.5,CPython==2.7.* OR CPython>=3.5,0,1\r lib2,CPython>=3.6,CPython>=3.6,2,0\r lib2/a.py,CPython>=3.6,CPython>=3.6,2,3\r lib2/b.py,CPython>=3.6,CPython>=3.6,2,3\r """)
def test_black_fixer_fixes(black_rule_runner: RuleRunner) -> None: black_rule_runner.write_files({"BUILD": "tgt( name = 't' )"}) result = black_rule_runner.run_goal_rule(UpdateBuildFilesGoal, env_inherit=BLACK_ENV_INHERIT) assert result.exit_code == 0 assert result.stdout == dedent( """\ Updated BUILD: - Format with Black """ ) assert Path(black_rule_runner.build_root, "BUILD").read_text() == 'tgt(name="t")\n'
def test_repl_ipython(rule_runner: RuleRunner) -> None: setup_sources(rule_runner) with mock_console(rule_runner.options_bootstrapper): result = rule_runner.run_goal_rule( Repl, global_args=[ "--backend-packages=pants.backend.python", "--backend-packages=pants.backend.codegen.protobuf.python", ], args=["--shell=ipython", "src/python/lib.py"], env_inherit={"PATH", "PYENV_ROOT", "HOME"}, ) assert result.exit_code == 0
def assert_dependencies( rule_runner: RuleRunner, *, specs: List[str], expected: List[str], transitive: bool = False, dependency_type: DependencyType = DependencyType.SOURCE, ) -> None: args = [f"--type={dependency_type.value}"] if transitive: args.append("--transitive") result = rule_runner.run_goal_rule(Dependencies, args=[*args, *specs]) assert result.stdout.splitlines() == expected
def test_raw_output_single_build_file(rule_runner: RuleRunner) -> None: rule_runner.add_to_build_file("project", "# A comment\nfiles(sources=[])") result = rule_runner.run_goal_rule(Peek, args=["--output=raw", "project"]) expected_output = dedent( """\ ------------- project/BUILD ------------- # A comment files(sources=[]) """ ) assert result.stdout == expected_output
def run_repl(rule_runner: RuleRunner, *, extra_args: list[str] | None = None) -> GoalRuleResult: # TODO(#9108): Expand `mock_console` to allow for providing input for the repl to verify # that, e.g., the generated protobuf code is available. Right now this test prepares for # that by including generated code, but cannot actually verify it. with mock_console(rule_runner.options_bootstrapper): return rule_runner.run_goal_rule( Repl, global_args=extra_args or (), args=["src/python/lib.py"], env_inherit={"PATH", "PYENV_ROOT", "HOME"}, )
def test_tailor_rule_write_mode(rule_runner: RuleRunner) -> None: rule_runner.write_files({ "foo/bar1_test.f90": "", "foo/BUILD": "fortran_library()", "baz/qux1.f90": "", "conflict/f1.f90": "", "conflict/f2.f90": "", "conflict/BUILD": "fortran_library(sources=['f1.f90'])", }) result = rule_runner.run_goal_rule( TailorGoal, args=["--alias-mapping={'fortran_library': 'my_fortran_lib'}"]) assert result.exit_code == 0 assert result.stdout == dedent("""\ Created baz/BUILD: - Add my_fortran_lib target baz Updated conflict/BUILD: - Add my_fortran_lib target conflict0 Updated foo/BUILD: - Add fortran_tests target tests """) assert Path(rule_runner.build_root, "foo/BUILD").read_text() == dedent("""\ fortran_library() fortran_tests( name="tests", ) """) assert Path(rule_runner.build_root, "baz/BUILD").read_text() == "my_fortran_lib()\n" assert Path(rule_runner.build_root, "conflict/BUILD").read_text() == dedent("""\ fortran_library(sources=['f1.f90']) # NOTE: Sources restricted from the default for my_fortran_lib due to conflict with # - conflict:conflict my_fortran_lib( name="conflict0", sources=[ "f2.f90", ], ) """)