Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
26b8f02
Fix local download
Devin-Crawford Feb 6, 2026
c547b03
chore: adding changelog file 7212.fixed.md [dependabot-skip]
pyansys-ci-bot Feb 6, 2026
dd53d4e
Convert test_tmp_dir from a return fixture to a yield fixture.
Devin-Crawford Feb 7, 2026
03a747f
Allow for delay after simulation is started
Devin-Crawford Feb 7, 2026
534beaf
Add logging to help debug test failure on Linux
Devin-Crawford Feb 8, 2026
f60815d
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Feb 8, 2026
fd110e6
Minimal logging in test_3dl_analyze_setup
Devin-Crawford Feb 8, 2026
0b5a9d9
Merge branch 'main' into fix/local_example_download
Devin-Crawford Feb 8, 2026
e8af6ba
Merge remote-tracking branch 'origin/fix/local_example_download' into…
Devin-Crawford Feb 8, 2026
319eceb
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Feb 8, 2026
6390538
Change return to yield before removing test temp dir.
Devin-Crawford Feb 10, 2026
c5f2d9c
Merge branch 'main' into fix/local_example_download
Devin-Crawford Feb 10, 2026
2c78b7c
chore: adding changelog file 7231.fixed.md [dependabot-skip]
pyansys-ci-bot Feb 10, 2026
79e054b
Apply suggestions from code review
Devin-Crawford Feb 10, 2026
d92a431
Merge branch 'fix/7214_temp_dir_test' into fix/local_example_download
Devin-Crawford Feb 10, 2026
2c4acb3
Merge branch 'main' into fix/local_example_download
Devin-Crawford Feb 10, 2026
07e7114
Teardown after yield in conftest
Devin-Crawford Feb 14, 2026
2e0b519
Merge branch 'main' into fix/local_example_download
Devin-Crawford Feb 14, 2026
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/7212.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Local access to file in example-data repo
1 change: 1 addition & 0 deletions doc/changelog.d/7231.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test Temp dir race condition
23 changes: 17 additions & 6 deletions src/ansys/aedt/core/examples/downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,24 @@ def _copy_local_example(
source_relative_path: str,
target_path: str | Path | None = None,
) -> Path: # pragma: no cover
"""Copy a folder from a local copy of the examples repo."""
dst = Path(target_path) / Path(source_relative_path).name
"""Copy a file or folder from a local copy of the examples repo."""
pyaedt_logger.debug(f"Retrieving local example from '{settings.local_example_folder}'")
source = Path(settings.local_example_folder) / source_relative_path
target_path = Path(target_path)

if source.is_file():
target_path.mkdir(parents=True, exist_ok=True)
dst = target_path / source.name
try:
shutil.copy2(source, dst)
except Exception as e:
raise AEDTRuntimeError(f"Failed to copy {str(source)}.") from e
return dst

dst = target_path / Path(source_relative_path).name
dst.mkdir(parents=True, exist_ok=True)
pyaedt_logger.debug(f"Retrieving local folder from '{settings.local_example_folder}'")
source_folder = Path(settings.local_example_folder) / source_relative_path
for p in source_folder.rglob("*"):
target = dst / p.relative_to(source_folder)
for p in source.rglob("*"):
target = dst / p.relative_to(source)
if p.is_dir():
target.mkdir(parents=True, exist_ok=True)
else:
Expand Down
4 changes: 2 additions & 2 deletions src/ansys/aedt/core/visualization/post/solution_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ def __init__(self, aedtdata) -> None:
else:
self._primary_sweep = self._sweeps_names[0]
end = time.time() - start
print(f"Time to initialize solution data:{end}")
settings.logger.debug(f"Time to initialize solution data:{end}")
self.init_solutions_data()
self._ifft = None
end = time.time() - start
print(f"Time to initialize solution data:{end}")
settings.logger.debug(f"Time to initialize solution data:{end}")

@property
def active_variation(self):
Expand Down
7 changes: 4 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,11 @@ def desktop(tmp_path_factory, request):
def test_tmp_dir(file_tmp_root, request):
d = file_tmp_root / request.node.name.split("[", 1)[0]

if d.exists():
yield d
try:
shutil.rmtree(d, ignore_errors=True)
d.mkdir(parents=True, exist_ok=True)
return d
except Exception:
pyaedt_logger.warning(f"Failed to cleanup temporary directory {d}")


@pytest.fixture
Expand Down
51 changes: 47 additions & 4 deletions tests/system/solvers/test_analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,63 @@ def test_3dl_generate_mesh(hfss3dl_solve) -> None:


@pytest.mark.skipif(DESKTOP_VERSION < "2023.2", reason="Working only from 2023 R2")
def test_3dl_analyze_setup(hfss3dl_solve) -> None:
def test_3dl_analyze_setup(hfss3dl_solve):
logger = hfss3dl_solve.logger

assert hfss3dl_solve.export_touchstone_on_completion(export=False)
assert hfss3dl_solve.export_touchstone_on_completion(export=True)
if DESKTOP_VERSION > "2024.2":
assert hfss3dl_solve.set_export_touchstone()
else:
with pytest.raises(AEDTRuntimeError):
hfss3dl_solve.set_export_touchstone()
assert hfss3dl_solve.analyze_setup("Setup1", cores=4, blocking=False)
assert hfss3dl_solve.are_there_simulations_running
assert hfss3dl_solve.stop_simulations()

logger.info("test_3dl_analyze_setup: calling analyze_setup('Setup1', cores=4, blocking=False)")
analyze_result = hfss3dl_solve.analyze_setup("Setup1", cores=4, blocking=False)
logger.info(f"test_3dl_analyze_setup: analyze_setup returned {analyze_result}")
assert analyze_result

running = hfss3dl_solve.are_there_simulations_running
logger.info(f"test_3dl_analyze_setup: initial are_there_simulations_running={running}")
start = time.time()
poll_count = 0
while not running and time.time() - start < 10:
time.sleep(0.5)
running = hfss3dl_solve.are_there_simulations_running
poll_count += 1
logger.info(
f"test_3dl_analyze_setup: poll #{poll_count} at {time.time() - start:.1f}s, "
f"are_there_simulations_running={running}"
)
elapsed_poll = time.time() - start
logger.info(
f"test_3dl_analyze_setup: polling loop done after {elapsed_poll:.1f}s, "
f"poll_count={poll_count}, running={running}"
)

if not running:
setup_solved = hfss3dl_solve.setups[0].is_solved if hfss3dl_solve.setups else None
logger.warning(
f"test_3dl_analyze_setup: simulations NOT detected as running. "
f"setup_solved={setup_solved}, num_setups={len(hfss3dl_solve.setups)}"
)
assert running, (
f"Simulations not detected as running after {elapsed_poll:.1f}s "
f"(poll_count={poll_count}). Check AEDT logs for errors."
)

stop_result = hfss3dl_solve.stop_simulations()
logger.info(f"test_3dl_analyze_setup: stop_simulations returned {stop_result}")
assert stop_result

wait_start = time.time()
wait_count = 0
while hfss3dl_solve.are_there_simulations_running:
time.sleep(1)
wait_count += 1

profile = hfss3dl_solve.setups[0].get_profile()
logger.info(f"test_3dl_analyze_setup: profile keys={list(profile.keys()) if profile else None}")
key0 = list(profile.keys())[0]
assert key0 == "Setup1"
assert isinstance(profile[key0], SimulationProfile)
Expand Down
190 changes: 190 additions & 0 deletions tests/unit/test_downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@
# SOFTWARE.

from pathlib import Path
from unittest.mock import patch

import pytest

from ansys.aedt.core.examples import downloads
from ansys.aedt.core.examples.downloads import _copy_local_example
from ansys.aedt.core.generic.file_utils import generate_unique_name
from ansys.aedt.core.generic.settings import is_linux
from ansys.aedt.core.internal.errors import AEDTRuntimeError


@pytest.fixture(scope="module", autouse=True)
Expand Down Expand Up @@ -120,3 +123,190 @@ def test_download_icepak_3d_component(test_tmp_dir) -> None:
def test_download_fss_file(test_tmp_dir) -> None:
example_folder = downloads.download_fss_3dcomponent(local_path=test_tmp_dir)
assert Path(example_folder).exists()


# ================================
# _copy_local_example unit tests
# ================================


@pytest.fixture
def local_example_folder(test_tmp_dir):
"""Create a mock local example folder structure for testing."""
example_root = test_tmp_dir / "mock_example_data"
example_root.mkdir(parents=True, exist_ok=True)
return example_root


@pytest.fixture
def mock_settings(local_example_folder):
"""Patch settings.local_example_folder to use the mock folder."""
with patch("ansys.aedt.core.examples.downloads.settings") as mock_settings:
mock_settings.local_example_folder = str(local_example_folder)
yield mock_settings


class TestCopyLocalExampleFile:
"""Tests for _copy_local_example when source is a file."""

def test_copy_single_file(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test copying a single file to target directory."""
# Create a source file
source_file = local_example_folder / "test_file.txt"
source_file.write_text("test content")

# Copy the file
target_dir = test_tmp_dir / "target"
result = _copy_local_example("test_file.txt", target_dir)

# Verify
assert result.exists()
assert result.is_file()
assert result.name == "test_file.txt"
assert result.parent == target_dir
assert result.read_text() == "test content"

def test_copy_file_in_subdirectory(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test copying a file from a subdirectory."""
# Create a source file in a subdirectory
subdir = local_example_folder / "pyaedt" / "sbr"
subdir.mkdir(parents=True)
source_file = subdir / "Cassegrain.aedt"
source_file.write_text("aedt file content")

# Copy the file
target_dir = test_tmp_dir / "target"
result = _copy_local_example("pyaedt/sbr/Cassegrain.aedt", target_dir)

# Verify
assert result.exists()
assert result.is_file()
assert result.name == "Cassegrain.aedt"
assert result.parent == target_dir
assert result.read_text() == "aedt file content"

def test_copy_file_creates_target_directory(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test that target directory is created if it doesn't exist."""
# Create a source file
source_file = local_example_folder / "test_file.txt"
source_file.write_text("test content")

# Copy to a non-existent nested target directory
target_dir = test_tmp_dir / "nested" / "target" / "dir"
assert not target_dir.exists()

result = _copy_local_example("test_file.txt", target_dir)

# Verify target directory was created
assert target_dir.exists()
assert result.exists()


class TestCopyLocalExampleFolder:
"""Tests for _copy_local_example when source is a folder."""

def test_copy_folder_with_files(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test copying a folder containing multiple files."""
# Create a source folder with files
source_folder = local_example_folder / "test_folder"
source_folder.mkdir()
(source_folder / "file1.txt").write_text("content 1")
(source_folder / "file2.txt").write_text("content 2")

# Copy the folder
target_dir = test_tmp_dir / "target"
result = _copy_local_example("test_folder", target_dir)

# Verify
assert result.exists()
assert result.is_dir()
assert result.name == "test_folder"
assert (result / "file1.txt").read_text() == "content 1"
assert (result / "file2.txt").read_text() == "content 2"

def test_copy_folder_with_nested_structure(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test copying a folder with nested subdirectories."""
# Create a source folder with nested structure
source_folder = local_example_folder / "parent_folder"
source_folder.mkdir()
(source_folder / "root_file.txt").write_text("root content")

nested = source_folder / "subdir1" / "subdir2"
nested.mkdir(parents=True)
(nested / "nested_file.txt").write_text("nested content")

# Copy the folder
target_dir = test_tmp_dir / "target"
result = _copy_local_example("parent_folder", target_dir)

# Verify
assert result.exists()
assert (result / "root_file.txt").read_text() == "root content"
assert (result / "subdir1" / "subdir2" / "nested_file.txt").read_text() == "nested content"

def test_copy_folder_preserves_empty_subdirectories(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test that empty subdirectories are preserved when copying."""
# Create a source folder with an empty subdirectory
source_folder = local_example_folder / "folder_with_empty"
source_folder.mkdir()
(source_folder / "empty_subdir").mkdir()
(source_folder / "file.txt").write_text("content")

# Copy the folder
target_dir = test_tmp_dir / "target"
result = _copy_local_example("folder_with_empty", target_dir)

# Verify empty subdirectory exists
assert (result / "empty_subdir").exists()
assert (result / "empty_subdir").is_dir()

def test_copy_folder_from_subdirectory(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test copying a folder from a subdirectory path."""
# Create nested source folder
pyaedt = local_example_folder / "pyaedt"
pyaedt.mkdir()
source_folder = pyaedt / "custom_reports"
source_folder.mkdir()
(source_folder / "report.json").write_text('{"key": "value"}')

# Copy the folder
target_dir = test_tmp_dir / "target"
result = _copy_local_example("pyaedt/custom_reports", target_dir)

# Verify
assert result.exists()
assert result.name == "custom_reports"
assert (result / "report.json").read_text() == '{"key": "value"}'


class TestCopyLocalExampleErrors:
"""Tests for error handling in _copy_local_example."""

def test_copy_file_raises_error_on_failure(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test that AEDTRuntimeError is raised when file copy fails."""
# Create a source file
source_file = local_example_folder / "test_file.txt"
source_file.write_text("test content")

target_dir = test_tmp_dir / "target"

# Mock shutil.copy2 to raise an exception
with patch("ansys.aedt.core.examples.downloads.shutil.copy2") as mock_copy:
mock_copy.side_effect = PermissionError("Access denied")
with pytest.raises(AEDTRuntimeError, match="Failed to copy"):
_copy_local_example("test_file.txt", target_dir)

def test_copy_folder_raises_error_on_failure(self, test_tmp_dir, local_example_folder, mock_settings):
"""Test that AEDTRuntimeError is raised when folder file copy fails."""
# Create a source folder with a file
source_folder = local_example_folder / "test_folder"
source_folder.mkdir()
(source_folder / "file.txt").write_text("content")

target_dir = test_tmp_dir / "target"

# Mock shutil.copy2 to raise an exception
with patch("ansys.aedt.core.examples.downloads.shutil.copy2") as mock_copy:
mock_copy.side_effect = PermissionError("Access denied")
with pytest.raises(AEDTRuntimeError, match="Failed to copy"):
_copy_local_example("test_folder", target_dir)
Loading