Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/3703.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test: improving testing performance
24 changes: 13 additions & 11 deletions src/ansys/mapdl/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@

SIGINT_TRACKER: List = []

# Configuration of 'protect_grpc' wrapper
N_ATTEMPTS = 5
INITIAL_BACKOFF = 0.1
MULTIPLIER_BACKOFF = 2


LOCKFILE_MSG: str = """
Another ANSYS job with the same job name is already running in this
Expand Down Expand Up @@ -307,9 +312,9 @@ def wrapper(*args, **kwargs):
old_handler = signal.signal(signal.SIGINT, handler)

# Capture gRPC exceptions
n_attempts = 5
initial_backoff = 0.1
multiplier_backoff = 2
n_attempts = kwargs.get("n_attempts", N_ATTEMPTS)
initial_backoff = kwargs.get("initial_backoff", INITIAL_BACKOFF)
multiplier_backoff = kwargs.get("multiplier_backoff", MULTIPLIER_BACKOFF)

i_attemps = 0

Expand All @@ -321,7 +326,6 @@ def wrapper(*args, **kwargs):
break

except grpc.RpcError as error:

mapdl = retrieve_mapdl_from_args(args)
mapdl._log.debug("A gRPC error has been detected.")

Expand All @@ -331,14 +335,16 @@ def wrapper(*args, **kwargs):
wait = (
initial_backoff * multiplier_backoff**i_attemps
) # Exponential backoff
sleep(wait)

# reconnect
mapdl._log.debug(
f"Re-connection attempt {i_attemps} after waiting {wait:0.3f} seconds"
)

connected = mapdl._connect(timeout=wait)
if not mapdl.is_alive:
connected = mapdl._connect(timeout=wait)
else:
sleep(wait)

# Retry again
continue
Expand Down Expand Up @@ -455,12 +461,8 @@ def handle_generic_grpc_error(

else:
# Making sure we do not keep executing gRPC calls.
mapdl._exited = True
mapdl._exiting = True

# Must close unfinished processes
mapdl._close_process()
mapdl._exiting = False
mapdl.exit()
raise MapdlExitedError(msg)


Expand Down
10 changes: 5 additions & 5 deletions src/ansys/mapdl/core/mapdl_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,10 +637,12 @@ def process_is_alive(self):
"""Check if the MAPDL process is alive"""
return self._is_alive_subprocess()

def _post_mortem_checks(self):
def _post_mortem_checks(self, process=None):
"""Check possible reasons for not having a successful connection."""
# Check early exit
process = self._mapdl_process
if process is None:
process = self._mapdl_process

if process is None or not self.is_grpc:
return

Expand All @@ -651,9 +653,7 @@ def _post_mortem_checks(self):

def _read_stds(self):
"""Read the stdout and stderr from the subprocess."""
from ansys.mapdl.core.launcher import (
_get_std_output, # Avoid circular import error
)
from ansys.mapdl.core.launcher import _get_std_output

if self._mapdl_process is None or not self._mapdl_process.stdout:
return
Expand Down
2 changes: 2 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ def is_exited(mapdl: Mapdl):
try:
# to connect
mapdl = Mapdl(port=port, ip=ip)
warn("MAPDL disconnected during testing, reconnected.")

except MapdlConnectionError as err:
from conftest import DEBUG_TESTING, ON_LOCAL
Expand Down Expand Up @@ -294,6 +295,7 @@ def is_exited(mapdl: Mapdl):
log_apdl="pymapdl.apdl" if DEBUG_TESTING else None,
mapdl_output="apdl.out" if (DEBUG_TESTING and ON_LOCAL) else None,
)
warn("MAPDL died during testing, relaunched.")

LOG.info("Successfully re-connected to MAPDL")

Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,8 @@ def mapdl(request, tmpdir_factory):
with pytest.raises(MapdlExitedError):
mapdl._send_command_stream("/PREP7")

# Delete Mapdl object
del mapdl
# Delete Mapdl object
del mapdl


################################################################
Expand Down
103 changes: 94 additions & 9 deletions tests/test_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import os
import re
import shutil
import subprocess
import sys
from unittest.mock import patch

Expand All @@ -34,6 +35,8 @@
from ansys.mapdl.core.common_grpc import DEFAULT_CHUNKSIZE
from ansys.mapdl.core.errors import (
MapdlCommandIgnoredError,
MapdlConnectionError,
MapdlDidNotStart,
MapdlExitedError,
MapdlgRPCError,
MapdlRuntimeError,
Expand Down Expand Up @@ -577,19 +580,101 @@ def test_input_compatibility_api_change(mapdl, cleared):


@requires("grpc")
@requires("local")
@requires("nostudent")
def test__check_stds():
def test__check_stds(mapdl):
"""Test that the standard input is checked."""
from ansys.mapdl.core import launch_mapdl
from ansys.mapdl.core.launcher import _check_server_is_alive, _get_std_output

cmd = "counter=1; while true; do echo $counter; ((counter++)); sleep 1; done"

process = subprocess.Popen(
["bash", "-c", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE
) # nosec B603 B607

if not hasattr(mapdl, "_stdout_queue"):
mapdl._stdout_queue = None
if not hasattr(mapdl, "_stdout_thread"):
mapdl._stdout_thread = None

with (
# To avoid overwriting the values for the rest of the tests.
patch.object(mapdl, "_stdout_queue"),
patch.object(mapdl, "_stdout_thread"),
patch.object(mapdl, "_mapdl_process"),
patch(
"ansys.mapdl.core.launcher._get_std_output", autospec=True
) as mock_get_std_output,
):

mock_get_std_output.side_effect = _get_std_output

mapdl._mapdl_process = process
mapdl._create_process_stds_queue(process)

# this should raise no issues
mapdl._post_mortem_checks(process)

mapdl = launch_mapdl(port=50058)
with pytest.raises(MapdlDidNotStart):
_check_server_is_alive(mapdl._stdout_queue, 0.5)

mapdl._read_stds()
assert mapdl._stdout is not None
assert mapdl._stderr is not None
assert isinstance(mapdl._stdout, list)
assert isinstance(mapdl._stderr, list)

mapdl.exit(force=True)
assert (
mapdl._stdout
and len(mapdl._stdout) > 0
and mapdl._stdout[-1]
and mapdl._stdout[-1].strip().isdigit()
)

mock_get_std_output.assert_called()


@requires("grpc")
@requires("nowindows") # since we are using bash
def test__post_mortem_checks(mapdl):
"""Test that the standard input is checked."""
from ansys.mapdl.core.launcher import _get_std_output

bash_command = """
counter=1; while true; do
echo $counter;
((counter++));
sleep 0.1;
if [ $counter -eq 7 ]; then
echo "";
echo "ERROR: Expected MapdlConnection error";
echo "";
fi;
done
"""
process = subprocess.Popen(
["bash", "-c", bash_command], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

if not hasattr(mapdl, "_stdout_queue"):
mapdl._stdout_queue = None
if not hasattr(mapdl, "_stdout_thread"):
mapdl._stdout_thread = None

with (
# To avoid overwriting the values for the rest of the tests.
patch.object(mapdl, "_stdout_queue"),
patch.object(mapdl, "_stdout_thread"),
patch.object(mapdl, "_mapdl_process"),
patch(
"ansys.mapdl.core.launcher._get_std_output", autospec=True
) as mock_get_std_output,
):

mock_get_std_output.side_effect = _get_std_output

mapdl._mapdl_process = process
mapdl._create_process_stds_queue(process)

with pytest.raises(
MapdlConnectionError, match="Expected MapdlConnection error"
):
mapdl._post_mortem_checks(process)


def test_subscribe_to_channel(mapdl, cleared):
Expand Down
Loading
Loading