Some checks failed
CI / lint (pull_request) Has been cancelled
CI / type (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.12) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.12) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.12) (pull_request) Has been cancelled
CI / test-gui (pull_request) Has been cancelled
CI / test-integration (macos-latest) (pull_request) Has been cancelled
CI / test-integration (ubuntu-latest) (pull_request) Has been cancelled
CI / test-integration (windows-latest) (pull_request) Has been cancelled
Auto-fixes (UP037, UP038, F401, I001, SIM105, RUF100) plus hand-fixes: drop the now-unused `Union` imports, annotate mutable class attributes with ClassVar, raise ValidationError instead of blind Exception in the zero-length test, delete two dead locals, and bind the per-iteration grid-builder closures to their loop variables (B023).
115 lines
3.3 KiB
Python
115 lines
3.3 KiB
Python
"""Unit tests for the AnalysisRunner Qt-thread orchestration.
|
|
|
|
These tests run a tiny analytical model end-to-end (Cantilever +
|
|
linear static), verifying signal flow without mocking. If openseespy
|
|
isn't importable on this platform, the whole module is skipped.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
pytest.importorskip("PySide6")
|
|
pytest.importorskip("openseespy.opensees")
|
|
|
|
from otko.commands import (
|
|
AddAnalysisCasesCommand,
|
|
AddElementsCommand,
|
|
AddNodalLoadsCommand,
|
|
AddNodesCommand,
|
|
AddSectionsCommand,
|
|
)
|
|
from otko.core import (
|
|
ElasticBeamColumn,
|
|
ElasticSection,
|
|
Node,
|
|
StaticCase,
|
|
)
|
|
from otko.services.results import StaticResults
|
|
from otko.viewmodels import AnalysisRunner, ProjectViewModel
|
|
|
|
|
|
@pytest.fixture
|
|
def cantilever_vm() -> ProjectViewModel:
|
|
"""A minimal verifiable model: 2D cantilever beam with tip load."""
|
|
L = 5.0
|
|
vm = ProjectViewModel()
|
|
vm.new_project(ndm=2, ndf=3)
|
|
vm.apply_command(
|
|
AddNodesCommand(
|
|
vm,
|
|
[
|
|
Node(
|
|
id=1, coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)
|
|
),
|
|
Node(id=2, coords=(L, 0.0, 0.0)),
|
|
],
|
|
)
|
|
)
|
|
vm.apply_command(
|
|
AddSectionsCommand(
|
|
vm,
|
|
[
|
|
ElasticSection(id=1, E=200e9, A=0.01, Iz=8.333e-6),
|
|
],
|
|
)
|
|
)
|
|
vm.apply_command(
|
|
AddElementsCommand(
|
|
vm,
|
|
[
|
|
ElasticBeamColumn(id=1, nodes=(1, 2), section_id=1),
|
|
],
|
|
)
|
|
)
|
|
vm.apply_command(AddNodalLoadsCommand(vm, {2}, (0.0, -1000.0, 0.0, 0.0, 0.0, 0.0)))
|
|
vm.apply_command(
|
|
AddAnalysisCasesCommand(
|
|
vm,
|
|
[
|
|
StaticCase(id=1, name="Cantilever", pattern_ids=[1]),
|
|
],
|
|
)
|
|
)
|
|
return vm
|
|
|
|
|
|
@pytest.mark.gui
|
|
def test_runner_emits_finished_with_static_results(qtbot, cantilever_vm) -> None: # type: ignore[no-untyped-def]
|
|
runner = AnalysisRunner()
|
|
case = cantilever_vm.project.analyses[0]
|
|
|
|
with qtbot.waitSignal(runner.finished, timeout=10000) as blocker:
|
|
runner.run(cantilever_vm.project, case)
|
|
|
|
results = blocker.args[0]
|
|
assert isinstance(results, StaticResults)
|
|
assert results.case_id == 1
|
|
|
|
|
|
@pytest.mark.gui
|
|
def test_runner_running_state_toggles(qtbot, cantilever_vm) -> None: # type: ignore[no-untyped-def]
|
|
runner = AnalysisRunner()
|
|
case = cantilever_vm.project.analyses[0]
|
|
assert not runner.is_running
|
|
|
|
with qtbot.waitSignal(runner.runningChanged, timeout=10000) as blocker_start:
|
|
runner.run(cantilever_vm.project, case)
|
|
# First emission: running → True
|
|
assert blocker_start.args[0] is True
|
|
|
|
# Wait for finished + the second runningChanged → False
|
|
with qtbot.waitSignal(runner.runningChanged, timeout=10000) as blocker_end:
|
|
pass
|
|
assert blocker_end.args[0] is False
|
|
assert not runner.is_running
|
|
|
|
|
|
@pytest.mark.gui
|
|
def test_double_run_rejected(qtbot, cantilever_vm) -> None: # type: ignore[no-untyped-def]
|
|
runner = AnalysisRunner()
|
|
case = cantilever_vm.project.analyses[0]
|
|
runner.run(cantilever_vm.project, case)
|
|
with pytest.raises(RuntimeError, match="already running"):
|
|
runner.run(cantilever_vm.project, case)
|
|
qtbot.waitSignal(runner.finished, timeout=10000).wait()
|