Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion tests/pytest_test/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
load("//tests/support:support.bzl", "SUPPORTS_BZLMOD")
load("//tests/support/pytest_test:pytest_test.bzl", "pytest_test")
load(
"//tests/support/pytest_test:pytest_test.bzl",
"pytest_test",
)
load(":pytest_test_tests.bzl", "pytest_test_test_suite")

pytest_test_test_suite(name = "pytest_test_tests")

pytest_test(
name = "pytest_script_venv_test",
Expand All @@ -20,3 +26,15 @@ pytest_test(
],
target_compatible_with = SUPPORTS_BZLMOD,
)

pytest_test(
name = "pytest_multipy_default_test",
srcs = [
"basic_test.py",
],
python_versions = [
"3.14",
"3.13",
],
Comment on lines +35 to +38

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this - it is quite important to have this sort of thing. Could we create pytest_test macro and accept python_version or python_versions attributes which would do the underlying wiring by themselves?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually originally did that but wasn't sure if I liked it. Since it was also your first instinct, lets go with that.

I also remembered config_settings (because of bootstrap_impl tests) and thought: well, why not accept arbitrary settings?

And then the api looked like this:

pytest_test(
  config_setting_variants = {
    "py3.14_foo": {
      "@rules_python//python/config_settings:python_version": "3.14",
      "//some:flag": "foo",
    }
  }
)

Which, eh...

thoughts?

target_compatible_with = SUPPORTS_BZLMOD,
)
37 changes: 37 additions & 0 deletions tests/pytest_test/pytest_test_tests.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Tests for pytest_test."""

load("@rules_testing//lib:test_suite.bzl", "test_suite")
load(
"//tests/support/pytest_test:pytest_test.bzl",
"get_version_test_name",
)

_tests = []

def _test_get_version_test_name(env):
want = {
("foo_test", "3.14"): "foo_py3.14_test",
("foo_test", "3.10"): "foo_py3.10_test",
("foo_test", "py3.14"): "foo_py3.14_test",
("foo_tests", "3.14"): "foo_py3.14_tests",
("foo", "3.14"): "foo_py3.14",
("test_foo", "3.14"): "test_foo_py3.14",
("basic_test", "3.11"): "basic_py3.11_test",
("pytest_default_test", "3.12"): "pytest_default_py3.12_test",
}

actual = {
(name, ver): get_version_test_name(name, ver)
for (name, ver) in want.keys()
}
env.expect.that_dict(actual).contains_exactly(want)

_tests.append(_test_get_version_test_name)

def pytest_test_test_suite(name):
"""Create the test suite.

Args:
name: The name of the test suite.
"""
test_suite(name = name, basic_tests = _tests)
85 changes: 84 additions & 1 deletion tests/support/pytest_test/pytest_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def pytest_test(
srcs,
pytest = None,
pytest_bazel = None,
python_versions = None,
**kwargs):
"""Run pytest tests.

Expand All @@ -21,9 +22,76 @@ def pytest_test(
pytest: The pytest target to use. Defaults to @pypi//pytest.
pytest_bazel: The pytest-bazel target to use. Defaults to
@pypi//pytest_bazel.
python_versions: List of Python versions to test against. If specified,
a test is created for each version and grouped under a test_suite
named `name`.
**kwargs: Additional arguments passed to py_test. Note that `main` is
not a supported argument.
"""
if python_versions != None:
_multi_pytest_test(
name = name,
srcs = srcs,
pytest = pytest,
pytest_bazel = pytest_bazel,
python_versions = python_versions,
**kwargs
)
else:
_single_pytest_test(
name = name,
srcs = srcs,
pytest = pytest,
pytest_bazel = pytest_bazel,
**kwargs
)

def _multi_pytest_test(
*,
name,
srcs,
pytest = None,
pytest_bazel = None,
python_versions,
**kwargs):
if "python_version" in kwargs:
fail(
"Cannot specify both python_version and python_versions in " +
"pytest_test; use one or the other.",
)
if not python_versions:
fail("python_versions must not be empty for {}".format(name))

tests = []
for python_version in python_versions:
test_name = _get_version_test_name(name, python_version)
_single_pytest_test(
name = test_name,
srcs = srcs,
pytest = pytest,
pytest_bazel = pytest_bazel,
python_version = python_version,
**kwargs
)
tests.append(":" + test_name)

test_suite_kwargs = {}
if "visibility" in kwargs:
test_suite_kwargs["visibility"] = kwargs["visibility"]

native.test_suite(
name = name,
tests = tests,
**test_suite_kwargs
)

def _single_pytest_test(
*,
name,
srcs,
pytest = None,
pytest_bazel = None,
**kwargs):
if pytest == None:
pytest = _DEFAULT_PYTEST
if pytest_bazel == None:
Expand All @@ -37,17 +105,32 @@ def pytest_test(
output_name = main_file,
)

kwargs = dict(kwargs)
deps = kwargs.pop("deps", [])
py_test(
name = name,
main = main_file,
srcs = [bootstrap_target] + srcs,
deps = kwargs.pop("deps", []) + [
deps = deps + [
pytest,
pytest_bazel,
],
**kwargs
)

def _get_version_test_name(name, python_version):
version_str = str(python_version)
if not version_str.startswith("py"):
version_str = "py" + version_str

if name.endswith("_test"):
return "{}_{}_test".format(name[:-len("_test")], version_str)
elif name.endswith("_tests"):
return "{}_{}_tests".format(name[:-len("_tests")], version_str)
return "{}_{}".format(name, version_str)

get_version_test_name = _get_version_test_name

def _write_pytest_bootstrap_impl(ctx):
output = ctx.actions.declare_file(ctx.attr.output_name)
test_files = "\n".join([f.short_path for f in ctx.files.srcs])
Expand Down