diff --git a/doc/changelog.d/3328.dependencies.md b/doc/changelog.d/3328.dependencies.md new file mode 100644 index 0000000000..b1e9f37c9a --- /dev/null +++ b/doc/changelog.d/3328.dependencies.md @@ -0,0 +1 @@ +feat: adding `PYMAPDL_APDL_LOG` env var for testing \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3a8b253c56..454af0565d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,6 +116,7 @@ pymapdl_convert_script = "ansys.mapdl.core.cli:old_pymapdl_convert_script_entry_ pymapdl = "ansys.mapdl.core.cli:main" [tool.pytest.ini_options] +addopts = "-ra -vvv" junit_family = "legacy" filterwarnings = [ "ignore::FutureWarning", diff --git a/src/ansys/mapdl/core/launcher.py b/src/ansys/mapdl/core/launcher.py index f5ed085fab..3f3bdea4b4 100644 --- a/src/ansys/mapdl/core/launcher.py +++ b/src/ansys/mapdl/core/launcher.py @@ -1646,6 +1646,7 @@ def launch_mapdl( loglevel=loglevel, set_no_abort=set_no_abort, use_vtk=use_vtk, + log_apdl=log_apdl, **start_parm, ) if clear_on_connect: diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index 58c0ddb67c..7ad1d2a002 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -160,6 +160,7 @@ VALID_SELECTION_ENTITY_TP = Literal["VOLU", "AREA", "LINE", "KP", "ELEM", "NODE"] GUI_FONT_SIZE = 15 +LOG_APDL_DEFAULT_FILE_NAME = "apdl.log" def parse_to_short_cmd(command): @@ -311,6 +312,9 @@ def __init__( self._componentmanager: ComponentManager = ComponentManager(self) + if isinstance(log_apdl, bool) and log_apdl: + log_apdl = LOG_APDL_DEFAULT_FILE_NAME + if log_apdl: self.open_apdl_log(log_apdl, mode="w") diff --git a/tests/common.py b/tests/common.py index 1fc5f139b0..91fa9b12b1 100644 --- a/tests/common.py +++ b/tests/common.py @@ -137,6 +137,19 @@ def testing_minimal(): return os.environ.get("TESTING_MINIMAL", "NO").upper().strip() in ["YES", "TRUE"] +def log_apdl() -> bool: + if "PYMAPDL_LOG_APDL" in os.environ and os.environ.get("PYMAPDL_LOG_APDL", ""): + log_apdl = os.environ.get("PYMAPDL_LOG_APDL") + + if log_apdl.lower() in ["true", "false", "yes", "no"]: + return log_apdl.lower() in ["true", "yes"] + else: + return log_apdl + + else: + return False + + def is_float(s: str) -> bool: try: float(s) diff --git a/tests/conftest.py b/tests/conftest.py index 8cfaa8095c..5ea5e4e79b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,6 +21,7 @@ # SOFTWARE. from collections import namedtuple +from collections.abc import Generator import os from pathlib import Path from shutil import get_terminal_size @@ -32,6 +33,7 @@ import psutil import pytest +from ansys.mapdl.core.launcher import is_ansys_process from common import ( Element, Node, @@ -44,6 +46,7 @@ is_on_ubuntu, is_running_on_student, is_smp, + log_apdl, support_plotting, testing_minimal, ) @@ -70,6 +73,7 @@ HAS_DPF = has_dpf() SUPPORT_PLOTTING = support_plotting() IS_SMP = is_smp() +LOG_APDL = log_apdl() QUICK_LAUNCH_SWITCHES = "-smp -m 100 -db 100" VALID_PORTS = [] @@ -121,6 +125,16 @@ ) +PROCESS_OK_STATUS = [ + psutil.STATUS_RUNNING, # + psutil.STATUS_SLEEPING, # + psutil.STATUS_DISK_SLEEP, + psutil.STATUS_DEAD, + psutil.STATUS_PARKED, # (Linux) + psutil.STATUS_IDLE, # (Linux, macOS, FreeBSD) +] + + def import_module(requirement): from importlib import import_module @@ -420,11 +434,41 @@ def running_test(): @pytest.fixture(autouse=True, scope="function") -def run_before_and_after_tests(request, mapdl): +def run_before_and_after_tests( + request: pytest.FixtureRequest, mapdl: Mapdl +) -> Generator[Mapdl]: """Fixture to execute asserts before and after a test is run""" - # Setup: fill with any logic you want - def is_exited(mapdl): + # Relaunching MAPDL if dead + mapdl = restart_mapdl(mapdl) + + # Write test info to log_apdl + if LOG_APDL: + log_test_start(mapdl) + + # check if the local/remote state has changed or not + prev = mapdl.is_local + + yield # this is where the testing happens + + assert prev == mapdl.is_local + + make_sure_not_instances_are_left_open() + + # Teardown + if mapdl.is_local and mapdl._exited: + # The test exited MAPDL, so it has failed. + test_name = os.environ.get( + "PYTEST_CURRENT_TEST", "**test id could not get retrieved.**" + ) + + assert ( + False + ), f"Test {test_name} failed at the teardown." # this will fail the test + + +def restart_mapdl(mapdl: Mapdl) -> Mapdl: + def is_exited(mapdl: Mapdl): try: _ = mapdl._ctrl("VERSION") return False @@ -452,44 +496,43 @@ def is_exited(mapdl): override=True, run_location=mapdl._path, cleanup_on_exit=mapdl._cleanup, + log_apdl=LOG_APDL, ) # Restoring the local configuration mapdl._local = local_ - yield # this is where the testing happens + return mapdl - # Teardown : fill with any logic you want - if mapdl.is_local and mapdl._exited: - # The test exited MAPDL, so it is fail. - assert False # this will fail the test +def log_test_start(mapdl: Mapdl) -> None: + test_name = os.environ.get( + "PYTEST_CURRENT_TEST", "**test id could not get retrieved.**" + ) -@pytest.fixture(autouse=True, scope="function") -def run_before_and_after_tests_2(request, mapdl): - """Make sure we are not changing these properties in tests""" - prev = mapdl.is_local + mapdl.run("!") + mapdl.run(f"! PyMAPDL running test: {test_name}") + mapdl.run("!") - yield + # To see it also in MAPDL terminal output + if len(test_name) > 75: + # terminal output is limited to 75 characters + test_name = test_name.split("::") + if len(test_name) > 2: + types_ = ["File path", "Test class", "Method"] + else: + types_ = ["File path", "Test function"] - assert prev == mapdl.is_local + mapdl._run("/com,Running test in:", mute=True) + for type_, name_ in zip(types_, test_name): + mapdl._run(f"/com, {type_}: {name_}", mute=True) + else: + mapdl._run(f"/com,Running test: {test_name}", mute=True) -@pytest.fixture(autouse=True, scope="function") -def run_before_and_after_tests_3(request, mapdl): - """Make sure we leave no MAPDL running behind""" - from ansys.mapdl.core.launcher import is_ansys_process - - PROCESS_OK_STATUS = [ - psutil.STATUS_RUNNING, # - psutil.STATUS_SLEEPING, # - psutil.STATUS_DISK_SLEEP, - psutil.STATUS_DEAD, - psutil.STATUS_PARKED, # (Linux) - psutil.STATUS_IDLE, # (Linux, macOS, FreeBSD) - ] - yield +def make_sure_not_instances_are_left_open() -> None: + """Make sure we leave no MAPDL running behind""" if ON_LOCAL: for proc in psutil.process_iter(): @@ -537,7 +580,7 @@ def mapdl_console(request): "Valid versions are up to 2020R2." ) - mapdl = launch_mapdl(console_path) + mapdl = launch_mapdl(console_path, log_apdl=LOG_APDL) from ansys.mapdl.core.mapdl_console import MapdlConsole assert isinstance(mapdl, MapdlConsole) @@ -568,6 +611,7 @@ def mapdl(request, tmpdir_factory): cleanup_on_exit=cleanup, license_server_check=False, start_timeout=50, + log_apdl=LOG_APDL, ) mapdl._show_matplotlib_figures = False # CI: don't show matplotlib figures MAPDL_VERSION = mapdl.version # Caching version