Skip to content

Commit f4f1d24

Browse files
committed
fix cmake use of workspace.view
1 parent f60d2ed commit f4f1d24

5 files changed

Lines changed: 90 additions & 70 deletions

File tree

.github/workflows/workflow.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ jobs:
2828
- name: Pytest
2929
run: |
3030
pip install .[dev]
31-
pytest src/canary_gitlab/tests
3231
pytest src/canary_cmake/tests
33-
pytest src/canary_vvtest/tests
34-
pytest src/canary_pyt/tests
32+
pytest src/canary_gitlab/tests
3533
pytest src/canary_hpc/tests
34+
pytest src/canary_pyt/tests
35+
pytest src/canary_vvtest/tests
3636
pytest tests
3737
# - name: Coverage
3838
# run: |

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ max_supported_python = "3.14"
106106
[tool.pytest.ini_options]
107107
testpaths = [
108108
"tests",
109-
"src/canary_vvtest/tests",
110109
"src/canary_cmake/tests",
110+
"src/canary_gitlab/tests",
111111
"src/canary_hpc/tests",
112-
"src/canary_gitlab/tests"
112+
"src/canary_pyt/tests",
113+
"src/canary_vvtest/tests",
113114
]
114115
norecursedirs = "mock data generators"
115116
addopts = "-ra --durations=10"
@@ -124,7 +125,7 @@ output = "coverage.xml"
124125
branch = true
125126
omit = [ "*/third_party/*" ]
126127
source = [ "_canary" ]
127-
command_line = "-m pytest ./tests"
128+
command_line = "-m pytest ./tests ./src/canary_cmake/tests ./src/canary_gitlab/tests ./src/canary_hpc/tests ./src/canary_pyt/tests ./src/canary_vvtest/tests"
128129

129130
[tool.coverage.html]
130131
directory = "html.cov"

src/_canary/plugins/subcommands/check.py

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
import site
1010
import subprocess
1111
import sys
12+
import time
13+
from concurrent.futures import Future
14+
from concurrent.futures import ProcessPoolExecutor
15+
from concurrent.futures import as_completed
16+
from dataclasses import dataclass
1217
from pathlib import Path
1318
from typing import TYPE_CHECKING
1419
from typing import Any
@@ -31,6 +36,14 @@ def canary_addcommand(parser: "Parser") -> None:
3136
stderr: Any = subprocess.PIPE
3237

3338
logger = logging.get_logger(__name__)
39+
test_paths = (
40+
"tests",
41+
"src/canary_cmake/tests",
42+
"src/canary_gitlab/tests",
43+
"src/canary_hpc/tests",
44+
"src/canary_pyt/tests",
45+
"src/canary_vvtest/tests",
46+
)
3447

3548

3649
class Action(argparse.Action):
@@ -54,8 +67,8 @@ def setup_parser(self, parser: "Parser") -> None:
5467
parser.add_argument(
5568
"-b", nargs=0, action=Action, help="run bandit security checks (default)"
5669
)
57-
parser.add_argument("-t", nargs=0, action=Action, help="run pytest")
58-
parser.add_argument("-C", nargs=0, action=Action, help="run coverage (default)")
70+
parser.add_argument("-t", nargs=0, action=Action, help="run pytest (default)")
71+
parser.add_argument("-C", nargs=0, action=Action, help="run coverage")
5972
parser.add_argument("-e", nargs=0, action=Action, help="run examples test")
6073
parser.add_argument("-d", nargs=0, action=Action, help="make docs")
6174
parser.add_argument("-v", action="store_true", help="verbose")
@@ -72,7 +85,7 @@ def execute(self, args: argparse.Namespace) -> int:
7285
raise ValueError("canary check must be run from a editable install of canary")
7386
self.root = os.path.normpath(str(root))
7487
if not hasattr(args, "action"):
75-
args.action = set("fcmbC")
88+
args.action = set("fcmbt")
7689
if shutil.which("ruff") is None and "f" in args.action:
7790
raise ValueError("ruff must be on PATH to format and check code")
7891
if shutil.which("ruff") is None and "c" in args.action:
@@ -179,24 +192,20 @@ def run_tests(self, args: argparse.Namespace):
179192
if "e" in args.action:
180193
os.environ["CANARY_RUN_EXAMPLES_TEST"] = "1"
181194
with working_dir(self.root):
182-
if "C" not in args.action:
183-
pm = logger.progress_monitor(f"Running tests in {self.root}/tests")
184-
pytest("./tests")
185-
pm.done()
186-
pm = logger.progress_monitor(f"Running tests in {self.root}/canary_pyt/tests")
187-
pytest("./src/canary_pyt/tests")
188-
pm.done()
189-
pm = logger.progress_monitor(f"Running tests in {self.root}/canary_cmake/tests")
190-
pytest("./src/canary_cmake/tests")
191-
pm.done()
192-
pm = logger.progress_monitor(f"Running tests in {self.root}/canary_hpc/tests")
193-
pytest("./src/canary_hpc/tests")
194-
pm.done()
195-
pm = logger.progress_monitor(f"Running tests in {self.root}/canary_vvtest/tests")
196-
pytest("./src/canary_vvtest/tests")
197-
pm.done()
195+
if "t" in args.action:
196+
results = run_pytests_parallel(Path(self.root), test_paths)
197+
failed = [r for r in results if not r.ok]
198+
if failed:
199+
for r in failed:
200+
if r.stdout:
201+
sys.stdout.write(r.stdout)
202+
if r.stderr:
203+
sys.stderr.write(r.stderr)
204+
raise ValueError(
205+
f"{len(failed)} pytest runs failed: {', '.join(r.path for r in failed)}"
206+
)
198207
else:
199-
pm = logger.progress_monitor(f"Running coverage in {self.root}/tests")
208+
pm = logger.progress_monitor(f"Running coverage in {self.root}")
200209
coverage("run")
201210
pm.done()
202211
pm = logger.progress_monitor("Creating coverage report")
@@ -302,6 +311,56 @@ def pytest(*args: str, **kwargs: Any) -> subprocess.CompletedProcess:
302311
return cp
303312

304313

314+
@dataclass(frozen=True)
315+
class PytestResult:
316+
path: str
317+
returncode: int
318+
stdout: str
319+
stderr: str
320+
elapsed_s: float
321+
322+
@property
323+
def ok(self) -> bool:
324+
return self.returncode == 0
325+
326+
327+
def run_pytest_one(root: str, relpath: str, pytest_args: tuple[str, ...] = ()) -> PytestResult:
328+
# Runs in a worker process
329+
t0 = time.time()
330+
command = ["pytest", relpath, *pytest_args]
331+
cp = subprocess.run(command, cwd=root, stdout=stdout, stderr=stderr, encoding="utf-8")
332+
return PytestResult(
333+
path=relpath,
334+
returncode=cp.returncode,
335+
stdout=cp.stdout or "",
336+
stderr=cp.stderr or "",
337+
elapsed_s=time.time() - t0,
338+
)
339+
340+
341+
def run_pytests_parallel(
342+
root: Path,
343+
test_paths: tuple[str, ...],
344+
*,
345+
max_workers: int | None = None,
346+
pytest_args: tuple[str, ...] = (),
347+
) -> list[PytestResult]:
348+
results: list[PytestResult] = []
349+
350+
with ProcessPoolExecutor(max_workers=max_workers or os.cpu_count()) as ex:
351+
futures: dict[Future, str] = {}
352+
for p in test_paths:
353+
logger.info(f"Submitting tests in {p} to pytest")
354+
fut = ex.submit(run_pytest_one, str(root), str(p), pytest_args)
355+
futures[fut] = str(p)
356+
for fut in as_completed(futures):
357+
res = fut.result()
358+
results.append(res)
359+
# Print on completion (no interleaving during run)
360+
logger.info(f"pytest finished: {res.path} ({res.elapsed_s:.1f}s) rc={res.returncode}")
361+
return results
362+
363+
305364
def coverage(*args: str, **kwargs: Any) -> subprocess.CompletedProcess:
306365
kwargs["stdout"] = stdout
307366
kwargs["stderr"] = stderr

src/canary_cmake/cdash/__init__.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,6 @@ def setup_parser(self, parser: "canary.Parser"):
5252
metavar="directory",
5353
help="Write reports to this directory [default: $session/_reports/cdash]",
5454
)
55-
p.add_argument(
56-
"-j",
57-
dest="json",
58-
metavar="file",
59-
help="Create reports from this JSON file [default: $session/_reports/cdash]",
60-
)
6155
group = p.add_mutually_exclusive_group()
6256
group.add_argument(
6357
"--track",
@@ -232,11 +226,7 @@ def make_gitlab_issues(self, session: "canary.Session | None" = None, **kwargs:
232226
return
233227

234228
def create(self, **kwargs: Any) -> None:
235-
reporter: CDashXMLReporter
236-
if kwargs.get("json"):
237-
reporter = CDashXMLReporter.from_json(file=kwargs["json"], dest=kwargs["dest"])
238-
else:
239-
reporter = CDashXMLReporter.from_workspace(dest=kwargs["dest"])
229+
reporter: CDashXMLReporter = CDashXMLReporter.from_workspace(dest=kwargs["dest"])
240230
if kwargs["f"]:
241231
opts = dict(
242232
buildstamp=kwargs["buildstamp"],

src/canary_cmake/cdash/xmlreporter.py

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,48 +34,18 @@ def __init__(self, dest: str | None = None) -> None:
3434

3535
@classmethod
3636
def from_workspace(cls, dest: str | None = None) -> "CDashXMLReporter":
37-
workspace = canary.Workspace.load()
37+
workspace: canary.Workspace = canary.Workspace.load()
3838
jobs = workspace.load_jobs()
3939
if not jobs:
4040
raise ValueError(f"No results found in {workspace.root}")
4141
if dest is None:
42-
dest = str((workspace.view or workspace.sessions_dir) / "CDASH")
42+
view = workspace.latest_view()
43+
dest = str((workspace.sessions_dir if view is None else view.dir) / "CDASH")
4344
self = cls(dest=dest)
4445
for job in jobs:
4546
self.data.add_job(job)
4647
return self
4748

48-
@classmethod
49-
def from_json(cls, file: str, dest: str | None = None) -> "CDashXMLReporter":
50-
"""Create an xml report from a json report"""
51-
raise NotImplementedError("No way of loading the job directly from lock yet")
52-
53-
# from _canary.testcase import factory as testcase_factory
54-
#
55-
# dest = dest or os.path.join(os.path.dirname(file), "CDASH")
56-
# self = cls(dest=dest)
57-
# data = json.load(open(file))
58-
# ts: TopologicalSorter = TopologicalSorter()
59-
# for id, state in data.items():
60-
# for name, value in state["properties"].items():
61-
# if name == "dependencies":
62-
# dependencies = value
63-
# dep_ids = [d["properties"]["id"] for d in dependencies]
64-
# ts.add(id, *dep_ids)
65-
# break
66-
# jobs: dict[str, canary.Job] = {}
67-
# for id in ts.static_order():
68-
# state = data[id]
69-
# job = testcase_factory(state.pop("type"))
70-
# job.setstate(state)
71-
# for i, dep in enumerate(job.dependencies):
72-
# job.dependencies[i] = jobs[dep.id]
73-
# jobs[id] = job
74-
# for job in jobs.values():
75-
# # job.refresh()
76-
# self.data.add_job(job)
77-
# return self
78-
7949
def create(
8050
self,
8151
buildname: str,

0 commit comments

Comments
 (0)