2222# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323# SOFTWARE.
2424
25+ import json
26+ import os
27+ from pathlib import Path
28+ import random
29+ import shutil
30+ import string
2531import sys
32+ import tempfile
2633from typing import List
2734from unittest .mock import MagicMock
2835
2936import pytest
3037
38+ from ansys .aedt .core .aedt_logger import pyaedt_logger
39+ from ansys .aedt .core .generic .settings import settings
40+ from ansys .aedt .core .internal .filesystem import Scratch
41+
42+ # Test category prefixes for marker assignment
3143UNIT_TEST_PREFIX = "tests/unit"
3244INTEGRATION_TEST_PREFIX = "tests/integration"
3345SYSTEM_TEST_PREFIX = "tests/system"
3951EMIT_TEST_PREFIX = "tests/system/emit"
4052
4153
54+ DEFAULT_CONFIG = {
55+ "desktopVersion" : "2025.2" ,
56+ "NonGraphical" : True ,
57+ "NewThread" : True ,
58+ "use_grpc" : True ,
59+ "close_desktop" : True ,
60+ "remove_lock" : False ,
61+ "disable_sat_bounding_box" : True ,
62+ "use_local_example_data" : False ,
63+ "local_example_folder" : None ,
64+ "skip_circuits" : False ,
65+ "skip_modelithics" : True ,
66+ }
67+
68+ # Load top-level configuration
69+ local_path = Path (__file__ ).parent
70+ local_config_file = local_path / "local_config.json"
71+
72+ config = DEFAULT_CONFIG .copy ()
73+ if local_config_file .exists ():
74+ try :
75+ with open (local_config_file ) as f :
76+ local_config = json .load (f )
77+ config .update (local_config )
78+ except Exception as e : # pragma: no cover
79+ # Failed to load local_config.json; report error
80+ print (f"Failed to load local_config.json ({ local_config_file } ): { e } " )
81+
82+ desktop_version = config .get ("desktopVersion" , DEFAULT_CONFIG .get ("desktopVersion" ))
83+ NONGRAPHICAL = config .get ("NonGraphical" , DEFAULT_CONFIG .get ("NonGraphical" ))
84+ new_thread = config .get ("NewThread" , DEFAULT_CONFIG .get ("NewThread" ))
85+ settings .use_grpc_api = config .get ("use_grpc" , DEFAULT_CONFIG .get ("use_grpc" ))
86+ close_desktop = config .get ("close_desktop" , DEFAULT_CONFIG .get ("close_desktop" ))
87+ remove_lock = config .get ("remove_lock" , DEFAULT_CONFIG .get ("remove_lock" ))
88+ settings .disable_bounding_box_sat = config .get (
89+ "disable_sat_bounding_box" , DEFAULT_CONFIG .get ("disable_sat_bounding_box" )
90+ )
91+ settings .use_local_example_data = config .get ("use_local_example_data" , DEFAULT_CONFIG .get ("use_local_example_data" ))
92+ if settings .use_local_example_data :
93+ settings .local_example_folder = config .get ("local_example_folder" , DEFAULT_CONFIG .get ("local_example_folder" ))
94+
95+ logger = pyaedt_logger
96+ os .environ ["PYAEDT_SCRIPT_VERSION" ] = config .get ("desktopVersion" , DEFAULT_CONFIG .get ("desktopVersion" ))
97+
98+
99+ # Add current path to sys.path for imports
100+ sys .path .append (str (local_path ))
101+
102+
103+ def generate_random_string (length ):
104+ """Generate a random string of specified length."""
105+ characters = string .ascii_letters + string .digits
106+ random_string = "" .join (random .sample (characters , length ))
107+ return random_string
108+
109+
110+ def generate_random_ident ():
111+ """Generate a random identifier for test folders."""
112+ ident = "-" + generate_random_string (6 ) + "-" + generate_random_string (6 ) + "-" + generate_random_string (6 )
113+ return ident
114+
115+
42116def pytest_collection_modifyitems (config : pytest .Config , items : List [pytest .Item ]):
43117 """Hook used to apply marker on tests."""
44118 for item in items :
@@ -64,8 +138,41 @@ def pytest_collection_modifyitems(config: pytest.Config, items: List[pytest.Item
64138 item .add_marker (pytest .mark .emit )
65139
66140
141+ # ================================
142+ # SHARED FIXTURES
143+ # ================================
144+
145+
146+ @pytest .fixture (scope = "session" , autouse = True )
147+ def init_scratch ():
148+ """Initialize a global scratch directory for all tests."""
149+ test_folder_name = "pyaedt_test" + generate_random_ident ()
150+ test_folder = Path (tempfile .gettempdir ()) / test_folder_name
151+ try :
152+ os .makedirs (test_folder , mode = 0o777 )
153+ except FileExistsError as e :
154+ print (f"Failed to create { test_folder } . Reason: { e } " )
155+
156+ yield test_folder
157+
158+ try :
159+ shutil .rmtree (test_folder , ignore_errors = True )
160+ except Exception as e :
161+ print (f"Failed to delete { test_folder } . Reason: { e } " )
162+
163+
164+ @pytest .fixture (scope = "module" , autouse = True )
165+ def local_scratch (init_scratch ):
166+ """Provide a module-scoped scratch directory."""
167+ tmp_path = init_scratch
168+ scratch = Scratch (tmp_path )
169+ yield scratch
170+ scratch .remove ()
171+
172+
67173@pytest .fixture
68174def touchstone_file (tmp_path ):
175+ """Create a dummy touchstone file for testing."""
69176 file_path = tmp_path / "dummy.s2p"
70177 file_content = """
71178! Terminal data exported
@@ -80,6 +187,7 @@ def touchstone_file(tmp_path):
80187
81188@pytest .fixture ()
82189def patch_graphics_modules (monkeypatch ):
190+ """Patch graphics modules to avoid headless env issues."""
83191 modules = [
84192 "matplotlib" ,
85193 "matplotlib.pyplot" ,
@@ -100,9 +208,9 @@ def patch_graphics_modules(monkeypatch):
100208
101209 # Specific action to make a mock an attribute of another mock
102210 mocks ["matplotlib" ].pyplot = mocks ["matplotlib.pyplot" ]
103- mocks [ "ansys.tools.visualization_interface" ]. backends = mocks ["ansys.tools.visualization_interface.backends " ]
104- mocks ["ansys.tools.visualization_interface.backends" ]. pyvista = mocks [
105- "ansys.tools.visualization_interface. backends.pyvista"
106- ]
211+ viz_interface = mocks ["ansys.tools.visualization_interface" ]
212+ viz_backends = mocks ["ansys.tools.visualization_interface.backends" ]
213+ viz_interface . backends = viz_backends
214+ viz_backends . pyvista = mocks [ "ansys.tools.visualization_interface.backends.pyvista" ]
107215
108216 yield mocks
0 commit comments