Skip to content

Commit f8ebb32

Browse files
committed
Add moosepytest module and move common pytest utilities
refs #31834
1 parent bb0a08b commit f8ebb32

File tree

5 files changed

+205
-156
lines changed

5 files changed

+205
-156
lines changed

python/moosecontrol/conftest.py

Lines changed: 5 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -9,138 +9,8 @@
99

1010
"""Configures pytest for the moosecontrol module."""
1111

12-
import os
13-
import shutil
14-
import subprocess
15-
from typing import Optional, Tuple
16-
17-
import pytest
18-
19-
20-
def pytest_addoption(parser):
21-
"""Add custom options to pytest."""
22-
parser.addoption(
23-
"--no-moose",
24-
action="store_true",
25-
default=False,
26-
help="Skip tests that require a moose executable",
27-
)
28-
parser.addoption(
29-
"--moose-exe",
30-
type=str,
31-
help="Specify the path to the moose executable instead of searching",
32-
)
33-
34-
35-
def find_moose_test_exe() -> Tuple[Optional[str], list[str]]:
36-
"""
37-
Try to find the moose_test executable in PATH or in-tree.
38-
39-
Returns
40-
-------
41-
Optional[str]:
42-
The absolute executable path, if found.
43-
list[str]:
44-
The paths that were searched.
45-
46-
"""
47-
this_dir = os.path.dirname(__file__)
48-
test_dir = os.path.abspath(os.path.join(this_dir, "..", "..", "test"))
49-
if not os.path.isdir(test_dir):
50-
test_dir = None
51-
52-
found: Optional[str] = None
53-
searched: list[str] = []
54-
55-
for method in ["dbg", "devel", "oprof", "opt"]:
56-
exe = f"moose_test-{method}"
57-
58-
# Exists in PATH
59-
searched.append(exe)
60-
if which := shutil.which(exe):
61-
found = which
62-
break
63-
64-
# Exists in tree
65-
if test_dir is not None:
66-
in_tree = os.path.join(test_dir, exe)
67-
searched.append(in_tree)
68-
if os.path.exists(in_tree):
69-
found = in_tree
70-
break
71-
72-
return found, searched
73-
74-
75-
def pytest_configure(config):
76-
"""
77-
Configure the tests; ran once before all tests.
78-
79-
If --no-moose is not set, make sure that we have
80-
a working MOOSE executable. If not, automatically
81-
fail the tests that depend on MOOSE. If so, make
82-
the executable path available to tests.
83-
"""
84-
config.moose_exe = None
85-
86-
# --no-moose not set, running with MOOSE
87-
if not config.getoption("--no-moose"):
88-
# Paths searched; used in error handling
89-
searched: Optional[list[str]] = None
90-
91-
moose_exe = config.getoption("--moose-exe")
92-
# User set --moose-exe, make sure it exists
93-
if moose_exe is not None:
94-
if not shutil.which(moose_exe):
95-
pytest.exit(f'--moose-exe: Executable "{moose_exe}" not found')
96-
# --moose-exe not set, do a search
97-
else:
98-
moose_exe, searched = find_moose_test_exe()
99-
100-
# Nothing found
101-
if moose_exe is None:
102-
message = "Failed to find a MOOSE executable.\n\n"
103-
if searched:
104-
prefix = "\n - "
105-
message += f"Searched paths:{prefix}{prefix.join(searched)}\n\n"
106-
message += "Either disable moose tests with --no-moose, "
107-
message += "or specify an exectuable with --moose-exe."
108-
pytest.exit(message)
109-
110-
# Make sure it runs
111-
cmd = [moose_exe, "--help"]
112-
process = subprocess.run(
113-
cmd,
114-
text=True,
115-
stdout=subprocess.PIPE,
116-
stderr=subprocess.STDOUT,
117-
)
118-
if process.returncode != 0:
119-
pytest.exit(
120-
"Failed to run MOOSE executable with "
121-
f"\"{' '.join(cmd)}\":\n\n{process.stdout}"
122-
)
123-
124-
print(f"INFO: Using moose executable {moose_exe}")
125-
# Set in shared state to be used as a fixture
126-
config.moose_exe = moose_exe
127-
# Not using moose
128-
else:
129-
print("INFO: Not running tests that require moose")
130-
131-
132-
def pytest_collection_modifyitems(config, items):
133-
"""Add custom skips to pytest."""
134-
# Skip 'moose' tests if --no-moose is set
135-
if config.getoption("--no-moose"):
136-
marker = pytest.mark.skip(reason="--no-moose")
137-
for item in items:
138-
if "moose" in item.keywords:
139-
item.add_marker(marker)
140-
141-
142-
@pytest.fixture
143-
def moose_exe(pytestconfig) -> Optional[str]:
144-
"""Get the moose executable found during init, if any."""
145-
assert isinstance(pytestconfig.moose_exe, (type(None), str))
146-
return pytestconfig.moose_exe
12+
pytest_plugins = (
13+
# Configures pytest for finding and optionally
14+
# skipping tests that depend on MOOSE.
15+
"moosepytest.mooseexe_plugin",
16+
)

python/moosecontrol/tests/__init__.py

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../moosepytest/init_moose_python.py

python/moosepytest/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file is part of the MOOSE framework
2+
# https://mooseframework.inl.gov
3+
#
4+
# All rights reserved, see COPYRIGHT for full restrictions
5+
# https://github.com/idaholab/moose/blob/master/COPYRIGHT
6+
#
7+
# Licensed under LGPL 2.1, please see LICENSE for details
8+
# https://www.gnu.org/licenses/lgpl-2.1.html
9+
10+
"""Contains pytest plugins for testing MOOSE python packages."""
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# This file is part of the MOOSE framework
2+
# https://mooseframework.inl.gov
3+
#
4+
# All rights reserved, see COPYRIGHT for full restrictions
5+
# https://github.com/idaholab/moose/blob/master/COPYRIGHT
6+
#
7+
# Licensed under LGPL 2.1, please see LICENSE for details
8+
# https://www.gnu.org/licenses/lgpl-2.1.html
9+
10+
"""
11+
Helper __init__ that adds the in-tree MOOSE python to PATH for tests.
12+
13+
In the cases when we're testing in-tree, this file should be
14+
symlinked in the test root for the package as the __init__.py
15+
script. It removes the need to set PYTHONPATH when running tests.
16+
"""
17+
18+
import os
19+
import sys
20+
from importlib.util import find_spec
21+
22+
if find_spec("moosepytest") is None:
23+
this_dir = os.path.dirname(__file__)
24+
moose_python = os.path.abspath(os.path.join(this_dir, "..", ".."))
25+
sys.path.append(moose_python)
26+
assert find_spec("moosepytest")
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# This file is part of the MOOSE framework
2+
# https://mooseframework.inl.gov
3+
#
4+
# All rights reserved, see COPYRIGHT for full restrictions
5+
# https://github.com/idaholab/moose/blob/master/COPYRIGHT
6+
#
7+
# Licensed under LGPL 2.1, please see LICENSE for details
8+
# https://www.gnu.org/licenses/lgpl-2.1.html
9+
10+
"""
11+
Add options to pytest to search for and find a moose executable.
12+
13+
Exposes the command line options:
14+
--no-moose: Don't run tests that need a moose executable, in
15+
which tests that are marked with "@pytest.mark.moose" are
16+
skipped.
17+
--moose-exe: Explicitly specify a path to a moose executable
18+
to used instead of trying to find one within an in-tree
19+
build of MOOSE.
20+
21+
For pytest configuration, this does the following:
22+
- Exposes the command line options above.
23+
- Searches for a moose executable and makes sure that
24+
it works during the pytest configuration step.
25+
- Marks tests as skipped when --no-moose is passed
26+
and the test is marked with "moose".
27+
"""
28+
29+
import os
30+
import shutil
31+
import subprocess
32+
from typing import Optional, Tuple
33+
34+
import pytest
35+
36+
37+
def pytest_addoption(parser: pytest.Parser):
38+
"""Add the --no-moose and --moose-exe options to the Parser."""
39+
parser.addoption(
40+
"--no-moose",
41+
action="store_true",
42+
default=False,
43+
help="Skip tests that require a moose executable",
44+
)
45+
parser.addoption(
46+
"--moose-exe",
47+
type=str,
48+
help="Specify the path to the moose executable instead of searching",
49+
)
50+
51+
52+
def find_moose_test_exe() -> Tuple[Optional[str], list[str]]:
53+
"""
54+
Try to find the moose_test executable in PATH or in-tree.
55+
56+
Returns
57+
-------
58+
Optional[str]:
59+
The absolute executable path, if found.
60+
list[str]:
61+
The paths that were searched.
62+
63+
"""
64+
this_dir = os.path.dirname(__file__)
65+
test_dir = os.path.abspath(os.path.join(this_dir, "..", "..", "test"))
66+
if not os.path.isdir(test_dir):
67+
test_dir = None
68+
69+
found: Optional[str] = None
70+
searched: list[str] = []
71+
72+
for method in ["dbg", "devel", "oprof", "opt"]:
73+
exe = f"moose_test-{method}"
74+
75+
# Exists in PATH
76+
searched.append(exe)
77+
if which := shutil.which(exe):
78+
found = which
79+
break
80+
81+
# Exists in tree
82+
if test_dir is not None:
83+
in_tree = os.path.join(test_dir, exe)
84+
searched.append(in_tree)
85+
if os.path.exists(in_tree):
86+
found = in_tree
87+
break
88+
89+
return found, searched
90+
91+
92+
def pytest_configure(config: pytest.Config):
93+
"""
94+
Configure the tests; ran once before all tests.
95+
96+
If --no-moose is not set, make sure that we have
97+
a working MOOSE executable. If not, automatically
98+
fail the tests that depend on MOOSE. If so, make
99+
the executable path available to tests.
100+
"""
101+
config.moose_exe = None
102+
103+
# --no-moose not set, running with MOOSE
104+
if not config.getoption("--no-moose"):
105+
# Paths searched; used in error handling
106+
searched: Optional[list[str]] = None
107+
108+
moose_exe = config.getoption("--moose-exe")
109+
# User set --moose-exe, make sure it exists
110+
if moose_exe is not None:
111+
if not shutil.which(moose_exe):
112+
pytest.exit(f'--moose-exe: Executable "{moose_exe}" not found')
113+
# --moose-exe not set, do a search
114+
else:
115+
moose_exe, searched = find_moose_test_exe()
116+
117+
# Nothing found
118+
if moose_exe is None:
119+
message = "Failed to find a MOOSE executable.\n\n"
120+
if searched:
121+
prefix = "\n - "
122+
message += f"Searched paths:{prefix}{prefix.join(searched)}\n\n"
123+
message += "Either disable moose tests with --no-moose, "
124+
message += "or specify an exectuable with --moose-exe."
125+
pytest.exit(message)
126+
127+
# Make sure it runs
128+
cmd = [moose_exe, "--help"]
129+
process = subprocess.run(
130+
cmd,
131+
text=True,
132+
stdout=subprocess.PIPE,
133+
stderr=subprocess.STDOUT,
134+
)
135+
if process.returncode != 0:
136+
pytest.exit(
137+
"Failed to run MOOSE executable with "
138+
f"\"{' '.join(cmd)}\":\n\n{process.stdout}"
139+
)
140+
141+
print(f"INFO: Using moose executable {moose_exe}")
142+
# Set in shared state to be used as a fixture
143+
config.moose_exe = moose_exe
144+
# Not using moose
145+
else:
146+
print("INFO: Not running tests that require moose")
147+
148+
149+
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]):
150+
"""Modify items to skip "moose" tests if --no-moose is set."""
151+
# Skip 'moose' tests if --no-moose is set
152+
if config.getoption("--no-moose"):
153+
marker = pytest.mark.skip(reason="--no-moose")
154+
for item in items:
155+
if "moose" in item.keywords:
156+
item.add_marker(marker)
157+
158+
159+
@pytest.fixture
160+
def moose_exe(pytestconfig: pytest.Config) -> Optional[str]:
161+
"""Get the moose executable found during configure, if any."""
162+
assert isinstance(pytestconfig.moose_exe, (type(None), str))
163+
return pytestconfig.moose_exe

0 commit comments

Comments
 (0)