-
Notifications
You must be signed in to change notification settings - Fork 164
Convert /tests/policy/plan into pytest-based test #4678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
2b4a017
53491d8
dcd5af2
998705a
4c109a2
da41a85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,20 @@ | ||
| from collections.abc import Mapping | ||
| from typing import IO, Any, Optional, Protocol, Union | ||
| import contextlib | ||
| import functools | ||
| import os | ||
| from collections.abc import Iterable, Iterator, Mapping | ||
| from typing import IO, Any, Callable, Optional, Protocol, TypeVar, Union, cast | ||
|
|
||
| import click.core | ||
| import click.testing | ||
| import jq as _jq | ||
|
|
||
| import tmt.__main__ | ||
| import tmt.cli._root | ||
| from tmt._compat.typing import ParamSpec | ||
| from tmt.utils import Path | ||
|
|
||
| T = TypeVar('T') | ||
| P = ParamSpec('P') | ||
|
|
||
|
|
||
| def reset_common() -> None: | ||
|
|
@@ -30,6 +39,52 @@ def reset_common() -> None: | |
| klass.cli_invocation = None | ||
|
|
||
|
|
||
| @contextlib.contextmanager | ||
| def cwd(path: Path) -> Iterator[Path]: | ||
| """ | ||
| A context manager switching the current working directory to a given path. | ||
|
|
||
| .. warning:: | ||
|
|
||
| Changing the current working directory can have unexpected | ||
| consequences in a multithreaded environment. | ||
| """ | ||
|
|
||
| cwd = Path.cwd() | ||
|
|
||
| os.chdir(path) | ||
|
|
||
| try: | ||
| yield path | ||
|
|
||
| finally: | ||
| os.chdir(cwd) | ||
|
|
||
|
|
||
| def with_cwd(path: Path) -> Callable[[Callable[P, T]], Callable[P, T]]: | ||
| """ | ||
| Decorate a test to have it run in the given path as its CWD. | ||
| """ | ||
|
|
||
| def _with_cwd(fn: Callable[P, T]) -> Callable[P, T]: | ||
| @functools.wraps(fn) | ||
| def __with_cwd(*args: P.args, **kwargs: P.kwargs) -> T: | ||
| with cwd(path): | ||
| return fn(*args, **kwargs) | ||
|
|
||
| return __with_cwd | ||
|
|
||
| return _with_cwd | ||
|
|
||
|
|
||
| def jq_all(data: Any, query: str) -> Iterable[Any]: | ||
| """ | ||
| Apply a jq filter on given data, and return the product. | ||
| """ | ||
|
|
||
| return cast(Iterable[Any], _jq.compile(query).input(data).all()) | ||
happz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| class RunTmt(Protocol): | ||
| """ | ||
| A type representing :py:meth:`CliRunner.invoke`. | ||
|
|
@@ -40,7 +95,7 @@ class RunTmt(Protocol): | |
|
|
||
| def __call__( | ||
| self, | ||
| *args: str, | ||
| *args: Union[str, Path], | ||
| command: Optional[click.BaseCommand] = None, | ||
| input: Optional[Union[str, bytes, IO[Any]]] = None, | ||
| env: Optional[Mapping[str, Optional[str]]] = None, | ||
|
|
@@ -57,7 +112,7 @@ def __init__(self) -> None: | |
|
|
||
| def invoke( # type: ignore[override] | ||
| self, | ||
| *args: str, | ||
| *args: Union[str, Path], | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would refer not to extend this one, at the very least not inside this function |
||
| command: Optional[click.BaseCommand] = None, | ||
| input: Optional[Union[str, bytes, IO[Any]]] = None, | ||
| env: Optional[Mapping[str, Optional[str]]] = None, | ||
|
|
@@ -73,7 +128,7 @@ def invoke( # type: ignore[override] | |
|
|
||
| return super().invoke( | ||
| command, | ||
| args=args, | ||
| args=[str(arg) for arg in args], | ||
| input=input, | ||
| env=env, | ||
| catch_exceptions=catch_exceptions, | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,63 @@ | ||||||||
| from typing import TYPE_CHECKING | ||||||||
|
|
||||||||
| from tests import jq_all, with_cwd | ||||||||
|
|
||||||||
| import tmt.cli | ||||||||
| from tmt.utils import Path, from_yaml | ||||||||
|
|
||||||||
| if TYPE_CHECKING: | ||||||||
| from tests import RunTmt | ||||||||
|
|
||||||||
|
|
||||||||
| TEST_DIR = Path(__file__).absolute().parent | ||||||||
| DATA_DIR = TEST_DIR / 'data/plan' | ||||||||
| POLICIES_DIR = TEST_DIR / 'policies' | ||||||||
|
|
||||||||
|
|
||||||||
| @with_cwd(DATA_DIR) | ||||||||
| def test_export_modified_plan(run_tmt: 'RunTmt') -> None: | ||||||||
| """ | ||||||||
| Verify a plan export is affected by the policy. | ||||||||
|
|
||||||||
| .. note:: | ||||||||
|
|
||||||||
| Not doing anything complex, test-level policy tests cover plenty | ||||||||
| of policy instructions and behavior. Focusing on plan-specific | ||||||||
| modifications only. | ||||||||
| """ | ||||||||
|
|
||||||||
| result = run_tmt( | ||||||||
| '-vv', | ||||||||
| 'plan', | ||||||||
| 'export', | ||||||||
| '--policy-file', | ||||||||
| POLICIES_DIR / 'plan/plan.yaml', | ||||||||
| ) | ||||||||
|
|
||||||||
| assert f"Apply tmt policy '{POLICIES_DIR}/plan/plan.yaml' to plans." in result.stderr | ||||||||
|
|
||||||||
| plans_exported = from_yaml(result.stdout) | ||||||||
|
|
||||||||
| assert jq_all(plans_exported, '.[] | .discover') == [None], "Verify that discover key is empty" | ||||||||
|
|
||||||||
| assert jq_all(plans_exported, '.[] | .prepare | .[] | [.how, .order]') == [ | ||||||||
| ['feature', 17], | ||||||||
| ['shell', None], | ||||||||
| ], 'Verify that prepare step contains two phases' | ||||||||
|
|
||||||||
| assert jq_all(plans_exported, '.[] | .contact') == [["xyzzy"]], ( | ||||||||
| 'Verify that contact key was populated' | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
| @with_cwd(DATA_DIR) | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we maybe avoid this and use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd consider this a different problem. One that can be improved, but I tried to not invent too many new things. |
||||||||
| def test_run_modified_plan(run_tmt: 'RunTmt') -> None: | ||||||||
| """ | ||||||||
| Verify a run is affected by the policy. | ||||||||
| """ | ||||||||
|
|
||||||||
| result = run_tmt('-vv', 'run', '-a', '--policy-file', POLICIES_DIR / 'plan/simple.yaml') | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing exit code verification. The original bash test expected exit code 3 ( result = run_tmt('-vv', 'run', '-a', '--policy-file', POLICIES_DIR / 'plan/simple.yaml')
assert result.exit_code == 3, "Expected exit code 3"
Suggested change
Spotted by Graphite |
||||||||
|
|
||||||||
| assert result.exit_code == tmt.cli.TmtExitCode.NO_RESULTS_FOUND | ||||||||
| assert f"Apply tmt policy '{POLICIES_DIR}/plan/simple.yaml' to plans." in result.stderr | ||||||||
| assert "No tests found, finishing plan." in result.stderr | ||||||||
Uh oh!
There was an error while loading. Please reload this page.