Skip to content

Commit 72ebc08

Browse files
authored
feat(command): poly test (#330)
* feat: poly test command * wip: collect all bricks * wip: collect all test paths * feat(CLI): add test command * wip: add test command diff report * feat(poetry): add test diff command * bump CLI to 1.27.0 * bump Poetry plugin to 1.37.0
1 parent 7097537 commit 72ebc08

File tree

14 files changed

+281
-19
lines changed

14 files changed

+281
-19
lines changed

bases/polylith/cli/core.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import List, Union
33

44
from polylith import commands, configuration, info, repo
5-
from polylith.cli import build, create, options
5+
from polylith.cli import build, create, options, test
66
from typer import Exit, Option, Typer
77
from typing_extensions import Annotated
88

@@ -21,6 +21,12 @@
2121
help="For Package & Dependency Management tools without support for plugins or build hooks.",
2222
)
2323

24+
app.add_typer(
25+
test.app,
26+
name="test",
27+
help="Commands for tests.",
28+
)
29+
2430

2531
def filtered_projects_data(
2632
projects_data: List[dict], directory: Union[str, None]

bases/polylith/cli/test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from pathlib import Path
2+
3+
from polylith import commands, configuration, diff, repo
4+
from polylith.cli import options
5+
from typer import Option, Typer
6+
from typing_extensions import Annotated
7+
8+
app = Typer()
9+
10+
11+
@app.command("diff")
12+
def diff_command(
13+
since: Annotated[str, Option(help="Changed since a specific tag.")] = "",
14+
short: Annotated[bool, options.short] = False,
15+
bricks: Annotated[bool, Option(help="Bricks affected by changes in tests")] = False,
16+
projects: Annotated[
17+
bool, Option(help="Projects affected by changes in tests")
18+
] = False,
19+
):
20+
"""Shows the Polylith projects and bricks that are affected by changes in tests."""
21+
root = repo.get_workspace_root(Path.cwd())
22+
ns = configuration.get_namespace_from_config(root)
23+
24+
tag = diff.collect.get_latest_tag(root, since) or since
25+
26+
if not tag:
27+
print("No matching tags or commits found in repository.")
28+
return
29+
30+
options = {"short": short, "bricks": bricks, "projects": projects}
31+
32+
commands.test.run(root, ns, tag, options)

bases/polylith/poetry_plugin/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
InfoCommand,
1212
LibsCommand,
1313
SyncCommand,
14+
TestDiffCommand,
1415
)
1516

1617
commands = [
@@ -24,6 +25,7 @@
2425
InfoCommand,
2526
LibsCommand,
2627
SyncCommand,
28+
TestDiffCommand,
2729
]
2830

2931

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from polylith.commands import check, create, deps, diff, info, libs, sync
1+
from polylith.commands import check, create, deps, diff, info, libs, sync, test
22

3-
__all__ = ["check", "create", "deps", "diff", "info", "libs", "sync"]
3+
__all__ = ["check", "create", "deps", "diff", "info", "libs", "sync", "test"]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from pathlib import Path
2+
from typing import List, Set, Tuple, Union
3+
4+
from polylith import bricks, configuration, diff, info, test
5+
6+
7+
def get_imported_bricks_in_tests(
8+
root: Path, ns: str, tag_name: Union[str, None], theme: str
9+
) -> Set[str]:
10+
files = test.get_changed_files(root, tag_name)
11+
imports = test.get_brick_imports_in_tests(root, ns, theme, files)
12+
13+
return set().union(*imports.values())
14+
15+
16+
def extract_brick_names(bricks_data: List[dict], imported_bricks: Set[str]) -> Set[str]:
17+
return {v for b in bricks_data for v in b.values() if v in imported_bricks}
18+
19+
20+
def get_affected_bricks(
21+
root: Path, ns: str, tag_name: Union[str, None], theme: str
22+
) -> Tuple[Set[str], Set[str]]:
23+
found = get_imported_bricks_in_tests(root, ns, tag_name, theme)
24+
25+
bases = extract_brick_names(bricks.get_bases_data(root, ns), found)
26+
components = extract_brick_names(bricks.get_components_data(root, ns), found)
27+
28+
return bases, components
29+
30+
31+
def get_affected_projects(
32+
root: Path, ns: str, bases: Set[str], components: Set[str]
33+
) -> List[dict]:
34+
projects_data = [p for p in info.get_projects_data(root, ns) if info.is_project(p)]
35+
36+
names = diff.collect.get_projects_affected_by_changes(
37+
projects_data, [], list(bases), list(components)
38+
)
39+
40+
return [p for p in projects_data if p["path"].name in names]
41+
42+
43+
def run(root: Path, ns: str, tag: str, options: dict) -> None:
44+
theme = configuration.get_theme_from_config(root)
45+
46+
bases, components = get_affected_bricks(root, ns, tag, theme)
47+
projects_data = get_affected_projects(root, ns, bases, components)
48+
49+
if options.get("bricks"):
50+
test.report.print_detected_changes_affecting_bricks(bases, components, options)
51+
return
52+
53+
if options.get("projects"):
54+
test.report.print_projects_affected_by_changes(projects_data, options)
55+
return
56+
57+
test.report.print_report_summary(projects_data, bases, components, tag)
58+
test.report.print_test_report(projects_data, bases, components, options)

components/polylith/imports/parser.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@ def extract_imports(path: Path) -> List[str]:
4444
return [i for node in ast.walk(tree) for i in parse_imports(node) if i is not None]
4545

4646

47+
def is_python_file(path: Path) -> bool:
48+
return path.is_file() and path.suffix == ".py"
49+
50+
4751
def list_imports(path: Path) -> Set[str]:
48-
py_modules = path.rglob("*.py")
52+
py_modules = [path] if is_python_file(path) else path.rglob("*.py")
4953

5054
extracted = (extract_imports(m) for m in py_modules)
5155
flattened = (i for imports in extracted for i in imports)

components/polylith/poetry/commands/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from polylith.poetry.commands.info import InfoCommand
99
from polylith.poetry.commands.libs import LibsCommand
1010
from polylith.poetry.commands.sync import SyncCommand
11+
from polylith.poetry.commands.test import TestDiffCommand
1112

1213
__all__ = [
1314
"CheckCommand",
@@ -20,4 +21,5 @@
2021
"InfoCommand",
2122
"LibsCommand",
2223
"SyncCommand",
24+
"TestDiffCommand",
2325
]

components/polylith/poetry/commands/diff.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,26 @@
22
from poetry.console.commands.command import Command
33
from polylith import commands
44

5+
command_options = [
6+
option(
7+
long_name="short",
8+
short_name="s",
9+
description="Print short view",
10+
flag=True,
11+
),
12+
option(
13+
long_name="since",
14+
description="Changed since a specific tag",
15+
flag=False,
16+
),
17+
]
18+
519

620
class DiffCommand(Command):
721
name = "poly diff"
822
description = "Shows changed bricks compared to the latest git tag."
923

10-
options = [
11-
option(
12-
long_name="short",
13-
short_name="s",
14-
description="Print short view",
15-
flag=True,
16-
),
24+
options = command_options + [
1725
option(
1826
long_name="bricks",
1927
description="Print changed bricks",
@@ -24,11 +32,6 @@ class DiffCommand(Command):
2432
description="Print bricks that depend on the changes. Use with --bricks.",
2533
flag=True,
2634
),
27-
option(
28-
long_name="since",
29-
description="Changed since a specific tag",
30-
flag=False,
31-
),
3235
]
3336

3437
def handle(self) -> int:
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from pathlib import Path
2+
3+
from cleo.helpers import option
4+
from poetry.console.commands.command import Command
5+
from polylith import commands, configuration, diff, repo
6+
from polylith.poetry.commands.diff import command_options
7+
8+
9+
class TestDiffCommand(Command):
10+
name = "poly test diff"
11+
description = (
12+
"Shows the Polylith projects and bricks that are affected by changes in tests."
13+
)
14+
15+
options = command_options + [
16+
option(
17+
long_name="bricks",
18+
description="Bricks affected by changes in tests",
19+
flag=True,
20+
),
21+
option(
22+
long_name="projects",
23+
description="Projects affected by changes in tests",
24+
flag=True,
25+
),
26+
]
27+
28+
def handle(self) -> int:
29+
since = self.option("since")
30+
31+
options = {
32+
"short": self.option("short"),
33+
"bricks": self.option("bricks"),
34+
"projects": self.option("projects"),
35+
}
36+
37+
root = repo.get_workspace_root(Path.cwd())
38+
ns = configuration.get_namespace_from_config(root)
39+
40+
tag = diff.collect.get_latest_tag(root, since) or since
41+
42+
if tag:
43+
commands.test.run(root, ns, tag, options)
44+
else:
45+
self.line("No matching tags or commits found in repository.")
46+
47+
return 0
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from polylith.test import report
2+
from polylith.test.core import get_brick_imports_in_tests, get_changed_files
13
from polylith.test.tests import create_test
24

3-
__all__ = ["create_test"]
5+
__all__ = ["report", "create_test", "get_brick_imports_in_tests", "get_changed_files"]

0 commit comments

Comments
 (0)