From c4fb946b6dc1cdc22feeb4dce4f4442edb1e3dd7 Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:21:30 +1100 Subject: [PATCH 1/7] Move pytest.ini to pyproject.toml --- pyproject.toml | 7 ++++++- tests/pytest.ini | 6 ------ 2 files changed, 6 insertions(+), 7 deletions(-) delete mode 100644 tests/pytest.ini diff --git a/pyproject.toml b/pyproject.toml index b644ed94..f05a4bde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,8 +52,8 @@ pylint = "==4.0.2" mypy = "==1.18.2" black = { version = "==25.11.0", extras = ["jupyter"] } pytest = "==8.4.2" +pytest-cov = "==7.0.0" pytest-describe = "==3.0.0" -pytest-pspec = "==0.0.4" pytest-raises = "==0.11" [tool.poetry.group.docs] @@ -92,6 +92,11 @@ disable = [ max-line-length = 120 py-version = [3.10, 3.11, 3.12, 3.13] +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--cov=anastruct" +required_plugins = ["pytest-raises", "pytest-describe", "pytest-cov"] + [tool.mypy] packages = "anastruct" python_version = "3.10" diff --git a/tests/pytest.ini b/tests/pytest.ini deleted file mode 100644 index 4ba1393e..00000000 --- a/tests/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -describe_prefixes = describe context -# filterwarnings = ignore::DeprecationWarning -# matplotlib throws a useless deprecation warning -required_plugins = "pytest-describe" "pytest-pspec" "pytest-raises" -# in a development environment, strongly recommend pytest-watch or pytest-watcher as well, but neither is required to run pytest \ No newline at end of file From 46adab091ccc071b2ce9ac014ecea4cc982dab3d Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:23:48 +1100 Subject: [PATCH 2/7] Eliminate pspec, split analytical tests to own file --- tests/test_analytical.py | 308 ++++++++++++++++++++++++++ tests/test_e2e.py | 466 +++------------------------------------ 2 files changed, 344 insertions(+), 430 deletions(-) create mode 100644 tests/test_analytical.py diff --git a/tests/test_analytical.py b/tests/test_analytical.py new file mode 100644 index 00000000..78edfc24 --- /dev/null +++ b/tests/test_analytical.py @@ -0,0 +1,308 @@ +import numpy as np +from pytest import approx, raises + +from anastruct import LoadCase, LoadCombination, SystemElements + +from .fixtures.e2e_fixtures import * +from .utils import pspec_context + +""" +NOTE: Several tests in this file validate that the correct numerical engineering results +occur in specific tested scenarios. These results are thereby validated against analytical +results for the same scenarios (which comes from engineering theory). Rather than comment +every single occurrence of every engineering variable, the following general definitions +apply to any tests in which these occur: +- L = total member length (typically in metres) +- l = length of individual member span +- w = peak distributed load magnitude (typically in kN/m) +- W = total distributed load (often w * l, typically in kN) +- P = concentrated / point load magnitude (typically in kN) +- a = location of concentrated / point load from left end +- b = location of concentrated / point load from right end of +- EI = bending stiffness (typically in kN*m^2) +- EA = axial stiffness (typically in kN*m/m) +""" + + +def describe_analytical_validation_tests(): + # Analytical validation tests, ensuring correct results for full FEA runs + + def context_simply_supported_beam_validation(): + # Simply-supported beam with UDL validation + + w = 1 + l = 2 + EI = 10000 + EA = 1000 + + system = SystemElements(EI=EI, EA=EA, mesh=10000) + system.add_element([[0, 0], [l, 0]]) + system.add_support_hinged([1, 2]) + system.q_load(w, element_id=1) + system.solve() + + def it_results_in_correct_moment_shear(): + assert system.get_element_results(1)["Mmax"] == approx(w * l**2 / 8) + assert system.get_element_results(1)["Qmax"] == approx(w * l / 2) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(w * l / 2) + assert system.get_node_results_system(2)["Fy"] == approx(w * l / 2) + + def it_results_in_correct_deflections(): + assert system.get_element_results(1)["wtotmax"] == approx( + -5 * w * l**4 / (384 * EI) + ) + + def context_simply_supported_two_point_loads(): + # Validation tests of results, based upon analytical equations + + p = 80 + l = 5 + a = l / 3 + EI = 14000 + + system = SystemElements(EI=EI, mesh=10000) + system.add_element([[0, 0], [a, 0]]) + system.add_element([[a, 0], [2 * a, 0]]) + system.add_element([[2 * a, 0], [l, 0]]) + system.add_support_hinged([1, 4]) + system.point_load(node_id=2, Fy=p) + system.point_load(node_id=3, Fy=p) + system.solve() + + def it_results_in_correct_moment_shear(): + assert system.get_element_results(2)["Mmax"] == approx(p * a) + assert system.get_element_results(1)["Qmax"] == approx(p) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(p) + assert system.get_node_results_system(4)["Fy"] == approx(p) + + def it_results_in_correct_deflections(): + assert system.get_element_results(2)["wtotmax"] == approx( + -((p * a) / (24 * EI)) * (3 * (l**2) - 4 * (a**2)) + ) + + def context_simply_supported_beam_point_load_validation(): + # Simply-supported beam with point load at center validation + + w = 1 + l = 2 + EI = 10000 + EA = 1000 + p = 1 + + system = SystemElements(EI=EI, EA=EA, mesh=10000) + system.add_element([[0, 0], [l / 2, 0]]) + system.add_element([[l / 2, 0], [l, 0]]) + system.add_support_hinged([1, 3]) + system.point_load(2, Fx=0, Fy=p, rotation=0) + system.solve() + + def it_results_in_correct_moment_shear(): + assert system.get_element_results(1)["Mmax"] == approx(p * l / 4) + assert system.get_element_results(1)["Qmax"] == approx(p / 2) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(p / 2) + assert system.get_node_results_system(3)["Fy"] == approx(p / 2) + + def it_results_in_correct_deflections(): + assert system.get_node_results_system(2)["uy"] == approx( + -p * l**3 / (48 * EI) + ) + + def context_fixed_ends_beam_point_load_validation(): + # Beam fixed at both ends with concentrated load at center + + p = 200 + l = 8 + x = 3 + EI = 23000 + + system = SystemElements(EI=EI, mesh=9000) + system.add_element([[0, 0], [x, 0]]) + system.add_element([[x, 0], [l / 2, 0]]) + system.add_element([[l / 2, 0], [l, 0]]) + system.add_support_fixed([1, 4]) + system.point_load(3, Fx=0, Fy=200, rotation=0) + system.solve() + + def it_results_in_correct_moment_shear(): + assert system.get_element_results(2)["Mmin"] == approx(p / 8 * (4 * x - l)) + assert system.get_element_results(2)["Qmax"] == approx(p / 2) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(p / 2) + assert system.get_node_results_system(4)["Fy"] == approx(p / 2) + + def it_results_in_correct_deflections(): + assert system.get_node_results_system(2)["uy"] == approx( + -p * x**2 / (48 * EI) * (3 * l - 4 * x) + ) + + def context_3_span_continuous_beam_2_UDL_loads_validation(): + # Continuous Beam spanning over three supports with two UDL in the outer spans + + w = 70 + l = 5 + EI = 15000 + + system = SystemElements(EI=EI, mesh=20000) + system.add_element([[0, 0], [l, 0]]) + system.add_element([[l, 0], [2 * l, 0]]) + system.add_element([[2 * l, 0], [3 * l, 0]]) + system.add_support_hinged([1, 2, 3, 4]) + system.q_load(w, element_id=1) + system.q_load(w, element_id=3) + system.solve() + + def it_results_in_correct_moment_shear(): + assert system.get_element_results(1)["Mmax"] == approx(0.10125 * w * l**2) + assert system.get_element_results(2)["Mmax"] == approx(-0.050 * w * l**2) + assert system.get_element_results(1)["Qmin"] == approx(-0.55 * w * l) + assert system.get_element_results(2)["Qmax"] == approx(0) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(0.45 * w * l) + assert system.get_node_results_system(3)["Fy"] == approx(0.55 * w * l) + + def it_results_in_correct_deflections(): + assert system.get_element_results(1)["wtotmax"] == approx( + -0.009917469 * w * l**4 / EI + ) + + def it_results_in_correct_deflections(): + assert system.get_node_results_system(2)["uy"] == approx( + -p * l**3 / (48 * EI) + ) + + def context_fixed1end_simplySupported_UDL_validation(): + # beam fixed at one end and simply supported at the other with UDL + + EA = 3420000 # KN.m/m + EI = 83100 # KN.m^2 + w = 1 # KN/m + l = 2 # m + + system = SystemElements(EA=EA, EI=EI) + system.add_element([[0, 0], [l, 0]]) + system.add_support_hinged(1) + system.add_support_fixed(2) + system.q_load(w, element_id=1) + system.solve() + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(3 * w * l / 8) + assert system.get_node_results_system(2)["Fy"] == approx(5 * w * l / 8) + assert system.get_node_results_system(2)["Tz"] == approx(-w * (l**2) / 8) + + def it_results_in_correct_deflections(): + assert system.get_element_results(1)["wtotmax"] == approx( + -w * (l**4) / (185 * EI), rel=1e-3 + ) + + def context_fixed1end_simplySupported_pointLoad_validation(): + # beam fixed at one end and simply supported on other with point load at the middle + + EA = 3420000 # KN.m/m + EI = 83100 # KN.m^2 + p = 1 # KN + l = 2 # m + + system = SystemElements(EA=EA, EI=EI, mesh=10000) + system.add_element([[0, 0], [l * (1 / 5) ** 0.5, 0]]) + system.add_element([[l * (1 / 5) ** 0.5, 0], [l / 2, 0]]) + system.add_element([[l / 2, 0], [l, 0]]) + system.add_support_hinged(1) + system.add_support_fixed(4) + system.point_load(3, Fx=0, Fy=p, rotation=0) + system.solve() + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(5 * p / 16) + assert system.get_node_results_system(4)["Fy"] == approx(11 * p / 16) + assert system.get_node_results_system(4)["Tz"] == approx(-3 * p * l / 16) + + def it_results_in_correct_deflections(): + assert system.get_node_results_system(2)["uy"] == approx( + -p * (l**3) / (48 * EI * 5**0.5) + ) + + def context_beam_fixed_on_both_ends_UDL(): + # beam with fixed supports at both ends and UDL + + EA = 3420000 # KN.m/m + EI = 83100 # KN.m^2 + w = 1 # KN/m + l = 2 # m + + system = SystemElements(EA=EA, EI=EI, mesh=2000) + system.add_element([[0, 0], [l, 0]]) + system.add_support_fixed([1, 2]) + system.q_load(w, 1) + system.solve() + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(w * l / 2) + assert system.get_node_results_system(2)["Fy"] == approx(w * l / 2) + assert system.get_node_results_system(1)["Tz"] == approx(w * l**2 / 12) + assert system.get_node_results_system(2)["Tz"] == approx(-w * l**2 / 12) + + def it_results_in_correct_deflections(): + assert system.get_element_results(1)["wtotmax"] == approx( + -w * l**4 / (384 * EI) + ) + + def context_cantilever_UDL_validation(): + # Cantilever beam with UDL validation + + w = 10 + l = 3 + EI = 10000 + EA = 1000 + + system = SystemElements(EI=EI, EA=EA, mesh=100) + system.add_element([[0, 0], [l, 0]]) + system.add_support_fixed([2]) + system.q_load(w, element_id=1) + system.solve() + + def it_results_in_correct_moment_shear(): + assert system.get_element_results(1)["Mmin"] == approx(-w * l**2 / 2) + assert system.get_element_results(1)["Qmin"] == approx(-w * l) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(2)["Fy"] == approx(w * l) + + def it_results_in_correct_deflections(): + assert system.get_node_results_system(1)["uy"] == approx( + -w * l**4 / (8 * EI) + ) + + def context_remove_element(): + # Removing an element from a propped beam + + system = SystemElements() + system.add_element([[0, 0], [10, 0]]) + system.add_element([[10, 0], [15, 0]]) + system.add_support_hinged(1) + system.add_support_hinged(2) + system.q_load(q=-1, element_id=1) + system.q_load(q=-1, element_id=2) + system.point_load(node_id=3, Fy=-10) + system.remove_element(2) + system.solve() + + def it_removes_element(): + assert len(system.element_map) == 1 + assert not (2 in system.loads_q) + + def it_removes_orphaned_node(): + assert len(system.node_map) == 2 + assert not (3 in system.loads_point) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(-5) + assert system.get_node_results_system(2)["Fy"] == approx(-5) diff --git a/tests/test_e2e.py b/tests/test_e2e.py index 2a4254e7..d753523c 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -25,14 +25,10 @@ def describe_end_to_end_tests(): - @pspec_context("End-to-End Tests (i.e. original unittest tests)") - def describe(): - pass + # End-to-End Tests (i.e. original unittest tests) def context_example_1(): - @pspec_context("Example 1") - def describe(): - pass + # Example 1 system = SystemElements() system.add_element(location=[[0, 0], [3, 4]], EA=5e9, EI=8000) @@ -52,9 +48,7 @@ def it_results_in_correct_solution(): assert system.solve() == approx(sol) def context_example_2(): - @pspec_context("Example 2") - def describe(): - pass + # Example 2 system = SystemElements() system.add_truss_element(location=[[0, 0], [0, 5]], EA=5000) @@ -76,9 +70,7 @@ def it_results_in_correct_solution(): assert system.solve() == approx(sol) def context_example_3(): - @pspec_context("Example 3") - def describe(): - pass + # Example 3 system = SystemElements() system.add_element(location=[[0, 0], [0, 5]], EA=15000, EI=5000) @@ -105,9 +97,7 @@ def it_results_in_correct_node_3_displacements(): assert system.get_node_displacements(3)["phi_z"] == approx(0.0021605118130) def context_example_4(): - @pspec_context("Example 4") - def describe(): - pass + # Example 4 system = SystemElements() system.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000, spring={2: 0}) @@ -127,9 +117,7 @@ def it_results_in_correct_solution(): assert system.solve() == approx(sol) def context_example_5(): - @pspec_context("Example 5") - def describe(): - pass + # Example 5 system = SystemElements() system.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000) @@ -149,11 +137,7 @@ def it_results_in_correct_solution(): assert system.solve() == approx(sol) def context_example_6_fixed_hinge(): - @pspec_context( - "Example 6: Test the primary force vector when applying a q_load at a hinged element." - ) - def describe(): - pass + # Example 6: Test the primary force vector when applying a q_load at a hinged element. system = SystemElements() system.add_element([[0, 0], [7, 0]], spring={2: 0}) @@ -166,9 +150,7 @@ def it_results_in_correct_in_correct_moment_at_support(): assert system.get_node_results_system(1)["Tz"] == approx(-61.25, rel=1e-3) def context_example_7_rotational_spring(): - @pspec_context("Example 7: Test the rotational springs") - def describe(): - pass + # Example 7: Test the rotational springs def it_results_in_correct_solution(SS_7): sol = np.fromstring( @@ -180,9 +162,7 @@ def it_results_in_correct_solution(SS_7): assert np.allclose(SS_7.solve(), sol) def context_example_8_rotational_spring(): - @pspec_context("Example 8: Test the plastic hinges") - def describe(): - pass + # Example 8: Test the plastic hinges def it_results_in_correct_total_displacement(SS_8): SS_8.solve() @@ -193,9 +173,7 @@ def it_results_in_correct_total_displacement(SS_8): assert u4 == approx(106.45829880642854) def context_example_11(): - @pspec_context("Example 11") - def describe(): - pass + # Example 11 def it_results_in_correct_axial_loads(SS_11): SS_11.solve() @@ -204,11 +182,7 @@ def it_results_in_correct_axial_loads(SS_11): assert SS_11.get_element_results(1)["length"] == approx(5.3851647) def context_example_12(): - @pspec_context( - "Example 12: Moment loads, with system nodes having 0 moment loads" - ) - def describe(): - pass + # Example 12: Moment loads, with system nodes having 0 moment loads" def it_results_in_correct_moment_loads(SS_12): SS_12.solve() @@ -218,9 +192,7 @@ def it_results_in_correct_moment_loads(SS_12): assert SS_12.get_node_results_system(4)["Tz"] == approx(-6.66667) def context_example_13(): - @pspec_context("Example 13: X-axis loads, with system nodes having 0 Fx loads") - def describe(): - pass + # Example 13: X-axis loads, with system nodes having 0 Fx loads def it_results_in_correct_moment_loads(SS_13): SS_13.solve() @@ -230,9 +202,7 @@ def it_results_in_correct_moment_loads(SS_13): assert SS_13.get_node_results_system(4)["Fx"] == approx(-6.66667) def context_example_14(): - @pspec_context("Example 14: Tests dead load and parallel load on axis.") - def describe(): - pass + # Example 14: Tests dead load and parallel load on axis. def it_results_in_correct_axial_loads(SS_14): SS_14.solve() @@ -243,9 +213,7 @@ def it_results_in_correct_axial_loads(SS_14): assert SS_14.get_node_results_system(5)["Fx"] == approx(5.18545) def context_example_15(): - @pspec_context("Example 15: Tests dead load and parallel load on axis.") - def describe(): - pass + # Example 15: Tests dead load and parallel load on axis. def it_results_in_correct_axial_loads(SS_15): SS_15.solve() @@ -256,9 +224,7 @@ def it_results_in_correct_axial_loads(SS_15): assert SS_15.get_node_results_system(5)["Fx"] == approx(9.41394) def context_example_16(): - @pspec_context("Example 16: Tests parallel load on axis.") - def describe(): - pass + # Example 16: Tests parallel load on axis. def it_results_in_correct_axial_loads(SS_16): SS_16.solve() @@ -269,18 +235,14 @@ def it_results_in_correct_axial_loads(SS_16): assert SS_16.get_node_results_system(5)["Fx"] == approx(5.65685) def context_example_18(): - @pspec_context("Example 18: Buckling factor") - def describe(): - pass + # Example 18: Buckling factor def it_results_in_correct_buckling_factor(SS_18): SS_18.solve(geometrical_non_linear=True) assert SS_18.buckling_factor == approx(600) def context_example_19(): - @pspec_context("Example 19: Numerical displacement averaging") - def describe(): - pass + # Example 19: Numerical displacement averaging def it_results_in_correct_deflections(SS_19): SS_19.solve() @@ -289,9 +251,7 @@ def it_results_in_correct_deflections(SS_19): ) def context_example_20(): - @pspec_context("Example 20: Inserting a node") - def describe(): - pass + # Example 20: Inserting a node def it_successfully_inserts_node(SS_20): x, y = SS_20.show_structure(values_only=True) @@ -307,18 +267,14 @@ def it_retains_supports_and_loads(SS_20): assert SS_20.element_map[5].q_load == approx((2.205258, 3)) def context_find_node_id(): - @pspec_context("find_node_id() function using Example 8") - def describe(): - pass + # find_node_id() function using Example 8 def it_finds_the_node_ids(SS_8): assert SS_8.find_node_id([4, 4]) == 6 assert SS_8.find_node_id([3, -3]) is None def context_add_multiple_elements(): - @pspec_context("add_multiple_elements() function") - def describe(): - pass + # add_multiple_elements() function system = SystemElements() system.add_multiple_elements([[0, 0], [10, 10]], n=5) @@ -328,9 +284,7 @@ def it_adds_multiple_elements(): assert node_x_locations == [0, 2.0, 4.0, 6.0, 8.0, 10] def context_no_forces_assertion(): - @pspec_context("Assertion error when no forces are entered") - def describe(): - pass + # Assertion error when no forces are entered system = SystemElements() system.add_element([0, 10]) @@ -340,9 +294,7 @@ def it_raises_an_error_with_no_forces(): system.solve() def context_inclined_horizontal_rollers(): - @pspec_context("Inclined rollers are equal to horizontal rollers") - def describe(): - pass + # Inclined rollers are equal to horizontal rollers system = SystemElements() x = [0, 1, 2] @@ -368,9 +320,7 @@ def it_results_in_same_forces(): assert u1["Tz"] == approx(u2["Tz"]) def context_inclined_roller_reactions(): - @pspec_context("Inclined roller forces are calculated correctly") - def describe(): - pass + # Inclined roller forces are calculated correctly system = SystemElements() x = [0, 1, 2] @@ -386,9 +336,7 @@ def it_results_in_correct_reactions(): assert system.get_node_results_system(3)["Fy"] == approx(-50) def context_inclined_roller_qload(): - @pspec_context("Inclined rollers and q-loads work together") - def describe(): - pass + # Inclined rollers and q-loads work together system = SystemElements(EA=356000, EI=1330) system.add_element(location=[[0, 0], [10, 0]]) @@ -404,9 +352,7 @@ def it_results_in_correct_reactions_forces(): assert system.get_element_results(1)["Nmin"] == approx(-5) def context_deflection_averaging_complex(): - @pspec_context("Deflection averaging test using example 26") - def describe(): - pass + # Deflection averaging test using example 26 def it_results_in_correct_deflections(SS_26): SS_26.solve() @@ -415,9 +361,7 @@ def it_results_in_correct_deflections(SS_26): ) def context_single_hinges_in_trusses(): - @pspec_context("Reducing elements in trusses to single hinges only") - def describe(): - pass + # Reducing elements in trusses to single hinges only system = SystemElements(EA=68300, EI=128, mesh=50) system.add_element( @@ -457,12 +401,7 @@ def it_results_in_correct_reactions(): assert system.get_node_results_system(3)["Tz"] == approx(0) def context_multiple_elements_spacing(): - @pspec_context( - "Tests bug fix for ensuring even spacing of multiple elements " - + "regardless of any floating point roundoff" - ) - def describe(): - pass + # Tests bug fix for ensuring even spacing of multiple elements regardless of any floating point roundoff system = SystemElements(EI=5e3, EA=1e5) system.add_multiple_elements([[0, 0], [10, 0]], 100) @@ -474,9 +413,7 @@ def it_results_in_valid_calculation(): assert system.get_node_results_system(-1)["uy"] == approx(2 / 3) def context_vertical_spring(): - @pspec_context("Test addition of vertical spring supports") - def describe(): - pass + # Test addition of vertical spring supports system = SystemElements(mesh=250) system.add_element( @@ -491,9 +428,7 @@ def it_results_in_correct_nodal_displacement(): assert system.get_node_results_system(2)["uy"] == approx(0.1) def context_rotational_roller_support(): - @pspec_context("Test addition of roller supports with rotational restraint") - def describe(): - pass + # Test addition of roller supports with rotational restraint system = SystemElements() system.add_element(location=[(0, 0), (0, 1)]) @@ -512,9 +447,7 @@ def it_results_in_correct_reaction(): assert system.get_node_results_system(2)["Tz"] == approx(166.6667) def context_rotational_support(): - @pspec_context("Test addition of rotation-only supports") - def describe(): - pass + # Test addition of rotation-only supports system = SystemElements() system.add_element(location=[(0, 0), (1, 0)]) @@ -533,9 +466,7 @@ def it_results_in_correct_reaction(): assert system.get_node_results_system(2)["Tz"] == approx(-166.6667) def context_load_cases(): - @pspec_context("Test a trivial load case") - def describe(): - pass + # Test a trivial load case system = SystemElements() system.add_truss_element(location=[[0, 0], [1000, 0]]) @@ -554,9 +485,7 @@ def it_results_in_correct_reactions(): assert results["dead"].get_node_results_system(2)["Fx"] == approx(14) def context_perpendicular_trapezoidal_load(): - @pspec_context("Test a complex-loaded system with trapezoidal loads") - def describe(): - pass + # Test a complex-loaded system with trapezoidal loads system = SystemElements() system.add_element(location=[(0, 0), (1, 0)]) @@ -582,9 +511,7 @@ def it_results_in_correct_reactions(): assert system.get_node_results_system(2)["Tz"] == approx(-5.8625) def context_internal_hinge_symmetry(): - @pspec_context("Test bug fix for asymmetric results with internal hinges") - def describe(): - pass + # Test bug fix for asymmetric results with internal hinges system = SystemElements() system.add_element(location=[[0, 0], [5, 0]], spring={2: 0}) @@ -606,9 +533,7 @@ def it_results_in_symmetric_element_demands(): ) def context_q_and_q_perp_loads(): - @pspec_context("Test addition of simultaneous q and q_perp loads") - def describe(): - pass + # Test addition of simultaneous q and q_perp loads system = SystemElements() system.add_element([2, 2]) @@ -626,9 +551,7 @@ def it_results_in_correct_element_demands(): assert system.get_element_results(1)["Qmax"] == approx(2 * np.sqrt(2)) def context_parallel_trapezoidal_load(): - @pspec_context("Test additional of parallel trapezoidal loads") - def describe(): - pass + # Test additional of parallel trapezoidal loads system = SystemElements() system.add_element([0, 1]) @@ -647,12 +570,7 @@ def it_results_in_correct_reaction(): assert system.get_node_results_system(1)["Fy"] == approx(-1) def context_load_case_example(): - @pspec_context( - "Test documentation example of load cases from " - + "https://anastruct.readthedocs.io/en/latest/loadcases.html" - ) - def describe(): - pass + # Test documentation example of load cases from https://anastruct.readthedocs.io/en/latest/loadcases.html system = SystemElements() height = 10 @@ -732,315 +650,3 @@ def it_results_in_correct_combination_demands_reactions(): assert results["combination"].get_node_results_system(5)["Fy"] == approx( wind_Fy + cables_Fy ) - - -def describe_analytical_validation_tests(): - @pspec_context("Validation tests of results, based upon analytical equations") - def describe(): - pass - - def context_simply_supported_beam_validation(): - @pspec_context("Simply-supported beam with UDL validation") - def describe(): - pass - - w = 1 - l = 2 - EI = 10000 - EA = 1000 - - system = SystemElements(EI=EI, EA=EA, mesh=10000) - system.add_element([[0, 0], [l, 0]]) - system.add_support_hinged([1, 2]) - system.q_load(w, element_id=1) - system.solve() - - def it_results_in_correct_moment_shear(): - assert system.get_element_results(1)["Mmax"] == approx(w * l**2 / 8) - assert system.get_element_results(1)["Qmax"] == approx(w * l / 2) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(w * l / 2) - assert system.get_node_results_system(2)["Fy"] == approx(w * l / 2) - - def it_results_in_correct_deflections(): - assert system.get_element_results(1)["wtotmax"] == approx( - -5 * w * l**4 / (384 * EI) - ) - - def context_simply_supported_two_point_loads(): - @pspec_context("Validation tests of results, based upon analytical equations") - def describe(): - pass - - p = 80 - l = 5 - a = l / 3 - EI = 14000 - - system = SystemElements(EI=EI, mesh=10000) - system.add_element([[0, 0], [a, 0]]) - system.add_element([[a, 0], [2 * a, 0]]) - system.add_element([[2 * a, 0], [l, 0]]) - system.add_support_hinged([1, 4]) - system.point_load(node_id=2, Fy=p) - system.point_load(node_id=3, Fy=p) - system.solve() - - def it_results_in_correct_moment_shear(): - assert system.get_element_results(2)["Mmax"] == approx(p * a) - assert system.get_element_results(1)["Qmax"] == approx(p) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(p) - assert system.get_node_results_system(4)["Fy"] == approx(p) - - def it_results_in_correct_deflections(): - assert system.get_element_results(2)["wtotmax"] == approx( - -((p * a) / (24 * EI)) * (3 * (l**2) - 4 * (a**2)) - ) - - def context_simply_supported_beam_point_load_validation(): - @pspec_context("Simply-supported beam with point load at center validation") - def describe(): - pass - - w = 1 - l = 2 - EI = 10000 - EA = 1000 - p = 1 - - system = SystemElements(EI=EI, EA=EA, mesh=10000) - system.add_element([[0, 0], [l / 2, 0]]) - system.add_element([[l / 2, 0], [l, 0]]) - system.add_support_hinged([1, 3]) - system.point_load(2, Fx=0, Fy=p, rotation=0) - system.solve() - - def it_results_in_correct_moment_shear(): - assert system.get_element_results(1)["Mmax"] == approx(p * l / 4) - assert system.get_element_results(1)["Qmax"] == approx(p / 2) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(p / 2) - assert system.get_node_results_system(3)["Fy"] == approx(p / 2) - - def it_results_in_correct_deflections(): - assert system.get_node_results_system(2)["uy"] == approx( - -p * l**3 / (48 * EI) - ) - - def context_fixed_ends_beam_point_load_validation(): - @pspec_context("Beam fixed at both ends with concentrated load at center") - def describe(): - pass - - p = 200 - l = 8 - x = 3 - EI = 23000 - - system = SystemElements(EI=EI, mesh=9000) - system.add_element([[0, 0], [x, 0]]) - system.add_element([[x, 0], [l / 2, 0]]) - system.add_element([[l / 2, 0], [l, 0]]) - system.add_support_fixed([1, 4]) - system.point_load(3, Fx=0, Fy=200, rotation=0) - system.solve() - - def it_results_in_correct_moment_shear(): - assert system.get_element_results(2)["Mmin"] == approx(p / 8 * (4 * x - l)) - assert system.get_element_results(2)["Qmax"] == approx(p / 2) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(p / 2) - assert system.get_node_results_system(4)["Fy"] == approx(p / 2) - - def it_results_in_correct_deflections(): - assert system.get_node_results_system(2)["uy"] == approx( - -p * x**2 / (48 * EI) * (3 * l - 4 * x) - ) - - def context_3_span_continuous_beam_2_UDL_loads_validation(): - @pspec_context( - "Continuous Beam spanning over three supports with two UDL in the outer spans" - ) - def describe(): - pass - - w = 70 - l = 5 - EI = 15000 - - system = SystemElements(EI=EI, mesh=20000) - system.add_element([[0, 0], [l, 0]]) - system.add_element([[l, 0], [2 * l, 0]]) - system.add_element([[2 * l, 0], [3 * l, 0]]) - system.add_support_hinged([1, 2, 3, 4]) - system.q_load(w, element_id=1) - system.q_load(w, element_id=3) - system.solve() - - def it_results_in_correct_moment_shear(): - assert system.get_element_results(1)["Mmax"] == approx(0.10125 * w * l**2) - assert system.get_element_results(2)["Mmax"] == approx(-0.050 * w * l**2) - assert system.get_element_results(1)["Qmin"] == approx(-0.55 * w * l) - assert system.get_element_results(2)["Qmax"] == approx(0) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(0.45 * w * l) - assert system.get_node_results_system(3)["Fy"] == approx(0.55 * w * l) - - def it_results_in_correct_deflections(): - assert system.get_element_results(1)["wtotmax"] == approx( - -0.009917469 * w * l**4 / EI - ) - - def it_results_in_correct_deflections(): - assert system.get_node_results_system(2)["uy"] == approx( - -p * l**3 / (48 * EI) - ) - - def context_fixed1end_simplySupported_UDL_validation(): - @pspec_context( - "beam fixed at one end and simply supported at the other with UDL" - ) - def describe(): - pass - - EA = 3420000 # KN.m/m - EI = 83100 # KN.m^2 - w = 1 # KN/m - l = 2 # m - - system = SystemElements(EA=EA, EI=EI) - system.add_element([[0, 0], [l, 0]]) - system.add_support_hinged(1) - system.add_support_fixed(2) - system.q_load(w, element_id=1) - system.solve() - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(3 * w * l / 8) - assert system.get_node_results_system(2)["Fy"] == approx(5 * w * l / 8) - assert system.get_node_results_system(2)["Tz"] == approx(-w * (l**2) / 8) - - def it_results_in_correct_deflections(): - assert system.get_element_results(1)["wtotmax"] == approx( - -w * (l**4) / (185 * EI), rel=1e-3 - ) - - def context_fixed1end_simplySupported_pointLoad_validation(): - @pspec_context( - "beam fixed at one end and simply supported on other with point load at the middle" - ) - def describe(): - pass - - EA = 3420000 # KN.m/m - EI = 83100 # KN.m^2 - p = 1 # KN - l = 2 # m - - system = SystemElements(EA=EA, EI=EI, mesh=10000) - system.add_element([[0, 0], [l * (1 / 5) ** 0.5, 0]]) - system.add_element([[l * (1 / 5) ** 0.5, 0], [l / 2, 0]]) - system.add_element([[l / 2, 0], [l, 0]]) - system.add_support_hinged(1) - system.add_support_fixed(4) - system.point_load(3, Fx=0, Fy=p, rotation=0) - system.solve() - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(5 * p / 16) - assert system.get_node_results_system(4)["Fy"] == approx(11 * p / 16) - assert system.get_node_results_system(4)["Tz"] == approx(-3 * p * l / 16) - - def it_results_in_correct_deflections(): - assert system.get_node_results_system(2)["uy"] == approx( - -p * (l**3) / (48 * EI * 5**0.5) - ) - - def context_beam_fixed_on_both_ends_UDL(): - @pspec_context("beam with fixed supports at both ends and UDL") - def describe(): - pass - - EA = 3420000 # KN.m/m - EI = 83100 # KN.m^2 - w = 1 # KN/m - l = 2 # m - - system = SystemElements(EA=EA, EI=EI, mesh=2000) - system.add_element([[0, 0], [l, 0]]) - system.add_support_fixed([1, 2]) - system.q_load(w, 1) - system.solve() - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(w * l / 2) - assert system.get_node_results_system(2)["Fy"] == approx(w * l / 2) - assert system.get_node_results_system(1)["Tz"] == approx(w * l**2 / 12) - assert system.get_node_results_system(2)["Tz"] == approx(-w * l**2 / 12) - - def it_results_in_correct_deflections(): - assert system.get_element_results(1)["wtotmax"] == approx( - -w * l**4 / (384 * EI) - ) - - def context_cantilever_UDL_validation(): - @pspec_context("Cantilever beam with UDL validation") - def describe(): - pass - - w = 10 - l = 3 - EI = 10000 - EA = 1000 - - system = SystemElements(EI=EI, EA=EA, mesh=100) - system.add_element([[0, 0], [l, 0]]) - system.add_support_fixed([2]) - system.q_load(w, element_id=1) - system.solve() - - def it_results_in_correct_moment_shear(): - assert system.get_element_results(1)["Mmin"] == approx(-w * l**2 / 2) - assert system.get_element_results(1)["Qmin"] == approx(-w * l) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(2)["Fy"] == approx(w * l) - - def it_results_in_correct_deflections(): - assert system.get_node_results_system(1)["uy"] == approx( - -w * l**4 / (8 * EI) - ) - - def context_remove_element(): - @pspec_context("Removing an element from a propped beam") - def describe(): - pass - - system = SystemElements() - system.add_element([[0, 0], [10, 0]]) - system.add_element([[10, 0], [15, 0]]) - system.add_support_hinged(1) - system.add_support_hinged(2) - system.q_load(q=-1, element_id=1) - system.q_load(q=-1, element_id=2) - system.point_load(node_id=3, Fy=-10) - system.remove_element(2) - system.solve() - - def it_removes_element(): - assert len(system.element_map) == 1 - assert not (2 in system.loads_q) - - def it_removes_orphaned_node(): - assert len(system.node_map) == 2 - assert not (3 in system.loads_point) - - def it_results_in_correct_reactions(): - assert system.get_node_results_system(1)["Fy"] == approx(-5) - assert system.get_node_results_system(2)["Fy"] == approx(-5) From d3069baee66657e2f8af499e7a5f3034b460d27c Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:24:27 +1100 Subject: [PATCH 3/7] Move examples to top-level directory --- .../fem/examples => examples}/__init__.py | 0 {anastruct/fem/examples => examples}/ex_1.py | 0 .../examples => examples}/ex_10_dead_load.py | 0 {anastruct/fem/examples => examples}/ex_11.py | 0 {anastruct/fem/examples => examples}/ex_12.py | 0 {anastruct/fem/examples => examples}/ex_13.py | 0 {anastruct/fem/examples => examples}/ex_14.py | 0 {anastruct/fem/examples => examples}/ex_15.py | 0 {anastruct/fem/examples => examples}/ex_16.py | 0 .../fem/examples => examples}/ex_17_gnl.py | 0 .../examples => examples}/ex_18_discretize.py | 0 .../ex_19_num_displacements.py | 0 .../fem/examples => examples}/ex_1_2.py | 0 {anastruct/fem/examples => examples}/ex_2.py | 0 .../ex_20_insert_node.py | 0 .../ex_21_rotate_force.py | 0 .../ex_22_loadcombination_doc.py | 0 .../ex_23_sectionbase.py | 0 .../ex_24_envelope_lines.py | 0 .../ex_25_high_midspan_point.py | 0 .../examples => examples}/ex_26_deflection.py | 0 {anastruct/fem/examples => examples}/ex_3.py | 0 {anastruct/fem/examples => examples}/ex_4.py | 0 {anastruct/fem/examples => examples}/ex_5.py | 0 .../examples => examples}/ex_6_fixed_hinge.py | 0 .../ex_7_rotational_spring.py | 0 .../ex_8_non_linear_portal.py | 0 .../ex_9_vertical_spring.py | 0 .../fem/examples => examples}/water_acc.ipynb | 0 tests/fixtures/e2e_fixtures.py | 29 ++++++++++--------- 30 files changed, 15 insertions(+), 14 deletions(-) rename {anastruct/fem/examples => examples}/__init__.py (100%) rename {anastruct/fem/examples => examples}/ex_1.py (100%) rename {anastruct/fem/examples => examples}/ex_10_dead_load.py (100%) rename {anastruct/fem/examples => examples}/ex_11.py (100%) rename {anastruct/fem/examples => examples}/ex_12.py (100%) rename {anastruct/fem/examples => examples}/ex_13.py (100%) rename {anastruct/fem/examples => examples}/ex_14.py (100%) rename {anastruct/fem/examples => examples}/ex_15.py (100%) rename {anastruct/fem/examples => examples}/ex_16.py (100%) rename {anastruct/fem/examples => examples}/ex_17_gnl.py (100%) rename {anastruct/fem/examples => examples}/ex_18_discretize.py (100%) rename {anastruct/fem/examples => examples}/ex_19_num_displacements.py (100%) rename {anastruct/fem/examples => examples}/ex_1_2.py (100%) rename {anastruct/fem/examples => examples}/ex_2.py (100%) rename {anastruct/fem/examples => examples}/ex_20_insert_node.py (100%) rename {anastruct/fem/examples => examples}/ex_21_rotate_force.py (100%) rename {anastruct/fem/examples => examples}/ex_22_loadcombination_doc.py (100%) rename {anastruct/fem/examples => examples}/ex_23_sectionbase.py (100%) rename {anastruct/fem/examples => examples}/ex_24_envelope_lines.py (100%) rename {anastruct/fem/examples => examples}/ex_25_high_midspan_point.py (100%) rename {anastruct/fem/examples => examples}/ex_26_deflection.py (100%) rename {anastruct/fem/examples => examples}/ex_3.py (100%) rename {anastruct/fem/examples => examples}/ex_4.py (100%) rename {anastruct/fem/examples => examples}/ex_5.py (100%) rename {anastruct/fem/examples => examples}/ex_6_fixed_hinge.py (100%) rename {anastruct/fem/examples => examples}/ex_7_rotational_spring.py (100%) rename {anastruct/fem/examples => examples}/ex_8_non_linear_portal.py (100%) rename {anastruct/fem/examples => examples}/ex_9_vertical_spring.py (100%) rename {anastruct/fem/examples => examples}/water_acc.ipynb (100%) diff --git a/anastruct/fem/examples/__init__.py b/examples/__init__.py similarity index 100% rename from anastruct/fem/examples/__init__.py rename to examples/__init__.py diff --git a/anastruct/fem/examples/ex_1.py b/examples/ex_1.py similarity index 100% rename from anastruct/fem/examples/ex_1.py rename to examples/ex_1.py diff --git a/anastruct/fem/examples/ex_10_dead_load.py b/examples/ex_10_dead_load.py similarity index 100% rename from anastruct/fem/examples/ex_10_dead_load.py rename to examples/ex_10_dead_load.py diff --git a/anastruct/fem/examples/ex_11.py b/examples/ex_11.py similarity index 100% rename from anastruct/fem/examples/ex_11.py rename to examples/ex_11.py diff --git a/anastruct/fem/examples/ex_12.py b/examples/ex_12.py similarity index 100% rename from anastruct/fem/examples/ex_12.py rename to examples/ex_12.py diff --git a/anastruct/fem/examples/ex_13.py b/examples/ex_13.py similarity index 100% rename from anastruct/fem/examples/ex_13.py rename to examples/ex_13.py diff --git a/anastruct/fem/examples/ex_14.py b/examples/ex_14.py similarity index 100% rename from anastruct/fem/examples/ex_14.py rename to examples/ex_14.py diff --git a/anastruct/fem/examples/ex_15.py b/examples/ex_15.py similarity index 100% rename from anastruct/fem/examples/ex_15.py rename to examples/ex_15.py diff --git a/anastruct/fem/examples/ex_16.py b/examples/ex_16.py similarity index 100% rename from anastruct/fem/examples/ex_16.py rename to examples/ex_16.py diff --git a/anastruct/fem/examples/ex_17_gnl.py b/examples/ex_17_gnl.py similarity index 100% rename from anastruct/fem/examples/ex_17_gnl.py rename to examples/ex_17_gnl.py diff --git a/anastruct/fem/examples/ex_18_discretize.py b/examples/ex_18_discretize.py similarity index 100% rename from anastruct/fem/examples/ex_18_discretize.py rename to examples/ex_18_discretize.py diff --git a/anastruct/fem/examples/ex_19_num_displacements.py b/examples/ex_19_num_displacements.py similarity index 100% rename from anastruct/fem/examples/ex_19_num_displacements.py rename to examples/ex_19_num_displacements.py diff --git a/anastruct/fem/examples/ex_1_2.py b/examples/ex_1_2.py similarity index 100% rename from anastruct/fem/examples/ex_1_2.py rename to examples/ex_1_2.py diff --git a/anastruct/fem/examples/ex_2.py b/examples/ex_2.py similarity index 100% rename from anastruct/fem/examples/ex_2.py rename to examples/ex_2.py diff --git a/anastruct/fem/examples/ex_20_insert_node.py b/examples/ex_20_insert_node.py similarity index 100% rename from anastruct/fem/examples/ex_20_insert_node.py rename to examples/ex_20_insert_node.py diff --git a/anastruct/fem/examples/ex_21_rotate_force.py b/examples/ex_21_rotate_force.py similarity index 100% rename from anastruct/fem/examples/ex_21_rotate_force.py rename to examples/ex_21_rotate_force.py diff --git a/anastruct/fem/examples/ex_22_loadcombination_doc.py b/examples/ex_22_loadcombination_doc.py similarity index 100% rename from anastruct/fem/examples/ex_22_loadcombination_doc.py rename to examples/ex_22_loadcombination_doc.py diff --git a/anastruct/fem/examples/ex_23_sectionbase.py b/examples/ex_23_sectionbase.py similarity index 100% rename from anastruct/fem/examples/ex_23_sectionbase.py rename to examples/ex_23_sectionbase.py diff --git a/anastruct/fem/examples/ex_24_envelope_lines.py b/examples/ex_24_envelope_lines.py similarity index 100% rename from anastruct/fem/examples/ex_24_envelope_lines.py rename to examples/ex_24_envelope_lines.py diff --git a/anastruct/fem/examples/ex_25_high_midspan_point.py b/examples/ex_25_high_midspan_point.py similarity index 100% rename from anastruct/fem/examples/ex_25_high_midspan_point.py rename to examples/ex_25_high_midspan_point.py diff --git a/anastruct/fem/examples/ex_26_deflection.py b/examples/ex_26_deflection.py similarity index 100% rename from anastruct/fem/examples/ex_26_deflection.py rename to examples/ex_26_deflection.py diff --git a/anastruct/fem/examples/ex_3.py b/examples/ex_3.py similarity index 100% rename from anastruct/fem/examples/ex_3.py rename to examples/ex_3.py diff --git a/anastruct/fem/examples/ex_4.py b/examples/ex_4.py similarity index 100% rename from anastruct/fem/examples/ex_4.py rename to examples/ex_4.py diff --git a/anastruct/fem/examples/ex_5.py b/examples/ex_5.py similarity index 100% rename from anastruct/fem/examples/ex_5.py rename to examples/ex_5.py diff --git a/anastruct/fem/examples/ex_6_fixed_hinge.py b/examples/ex_6_fixed_hinge.py similarity index 100% rename from anastruct/fem/examples/ex_6_fixed_hinge.py rename to examples/ex_6_fixed_hinge.py diff --git a/anastruct/fem/examples/ex_7_rotational_spring.py b/examples/ex_7_rotational_spring.py similarity index 100% rename from anastruct/fem/examples/ex_7_rotational_spring.py rename to examples/ex_7_rotational_spring.py diff --git a/anastruct/fem/examples/ex_8_non_linear_portal.py b/examples/ex_8_non_linear_portal.py similarity index 100% rename from anastruct/fem/examples/ex_8_non_linear_portal.py rename to examples/ex_8_non_linear_portal.py diff --git a/anastruct/fem/examples/ex_9_vertical_spring.py b/examples/ex_9_vertical_spring.py similarity index 100% rename from anastruct/fem/examples/ex_9_vertical_spring.py rename to examples/ex_9_vertical_spring.py diff --git a/anastruct/fem/examples/water_acc.ipynb b/examples/water_acc.ipynb similarity index 100% rename from anastruct/fem/examples/water_acc.ipynb rename to examples/water_acc.ipynb diff --git a/tests/fixtures/e2e_fixtures.py b/tests/fixtures/e2e_fixtures.py index 5aca504b..bfb4859c 100644 --- a/tests/fixtures/e2e_fixtures.py +++ b/tests/fixtures/e2e_fixtures.py @@ -1,19 +1,20 @@ -import pytest import numpy as np +import pytest + from anastruct import LoadCase, LoadCombination, SystemElements -from anastruct.fem.examples.ex_8_non_linear_portal import ss as SS_ex8 -from anastruct.fem.examples.ex_7_rotational_spring import ss as SS_ex7 -from anastruct.fem.examples.ex_11 import ss as SS_ex11 -from anastruct.fem.examples.ex_12 import ss as SS_ex12 -from anastruct.fem.examples.ex_13 import ss as SS_ex13 -from anastruct.fem.examples.ex_14 import ss as SS_ex14 -from anastruct.fem.examples.ex_15 import ss as SS_ex15 -from anastruct.fem.examples.ex_16 import ss as SS_ex16 -from anastruct.fem.examples.ex_17_gnl import ss as SS_ex17 -from anastruct.fem.examples.ex_18_discretize import ss as SS_ex18 -from anastruct.fem.examples.ex_19_num_displacements import ss as SS_ex19 -from anastruct.fem.examples.ex_20_insert_node import ss as SS_ex20 -from anastruct.fem.examples.ex_26_deflection import ss as SS_ex26 +from examples.ex_7_rotational_spring import ss as SS_ex7 +from examples.ex_8_non_linear_portal import ss as SS_ex8 +from examples.ex_11 import ss as SS_ex11 +from examples.ex_12 import ss as SS_ex12 +from examples.ex_13 import ss as SS_ex13 +from examples.ex_14 import ss as SS_ex14 +from examples.ex_15 import ss as SS_ex15 +from examples.ex_16 import ss as SS_ex16 +from examples.ex_17_gnl import ss as SS_ex17 +from examples.ex_18_discretize import ss as SS_ex18 +from examples.ex_19_num_displacements import ss as SS_ex19 +from examples.ex_20_insert_node import ss as SS_ex20 +from examples.ex_26_deflection import ss as SS_ex26 @pytest.fixture From 1fcf7543611e51e0801aef62ec3e475abec3f1b7 Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:24:43 +1100 Subject: [PATCH 4/7] Enable pytest testing in VS Code --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 422d0e7e..7f88d734 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -44,5 +44,7 @@ "pylint.severity": { "refactor": "Information" }, - "python.analysis.typeCheckingMode": "basic" + "python.analysis.typeCheckingMode": "basic", + "python.testing.pytestEnabled": true, + "testing.coverageToolbarEnabled": true } From c4c96e4ba0b792d3a5ccab00944f86d59e503247 Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:25:02 +1100 Subject: [PATCH 5/7] Don't show plots when auto-running test_plotter --- tests/test_plotter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_plotter.py b/tests/test_plotter.py index e14d2906..7c14a00b 100644 --- a/tests/test_plotter.py +++ b/tests/test_plotter.py @@ -6,7 +6,7 @@ class SimpleTest(unittest.TestCase): - show = True + show = False def test_example_1(self): system = se.SystemElements() From d8bf8dfb8c40d4dd267ced40056ff9de1eedf4cf Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:25:26 +1100 Subject: [PATCH 6/7] Update lock file --- .coverage | Bin 0 -> 241664 bytes poetry.lock | 144 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 .coverage diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..81c5338b57251beb44489a89986d2baa9d9fea7c GIT binary patch literal 241664 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 P@lG*@cqbV{yc7QyOAmMq literal 0 HcmV?d00001 diff --git a/poetry.lock b/poetry.lock index 9111d8de..441831ca 100644 --- a/poetry.lock +++ b/poetry.lock @@ -446,6 +446,114 @@ mypy = ["bokeh", "contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.17.0)", " test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] +[[package]] +name = "coverage" +version = "7.12.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.10" +groups = ["test"] +files = [ + {file = "coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b"}, + {file = "coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d"}, + {file = "coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef"}, + {file = "coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022"}, + {file = "coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f"}, + {file = "coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984"}, + {file = "coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6"}, + {file = "coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4"}, + {file = "coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc"}, + {file = "coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647"}, + {file = "coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937"}, + {file = "coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa"}, + {file = "coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a"}, + {file = "coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c"}, + {file = "coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941"}, + {file = "coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc"}, + {file = "coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8"}, + {file = "coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07"}, + {file = "coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc"}, + {file = "coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87"}, + {file = "coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455"}, + {file = "coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d"}, + {file = "coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c"}, + {file = "coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d"}, + {file = "coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92"}, + {file = "coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17"}, + {file = "coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933"}, + {file = "coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe"}, + {file = "coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d"}, + {file = "coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d"}, + {file = "coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b"}, + {file = "coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a"}, + {file = "coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291"}, + {file = "coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384"}, + {file = "coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a"}, + {file = "coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + [[package]] name = "cycler" version = "0.12.1" @@ -1582,35 +1690,39 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] -name = "pytest-describe" -version = "3.0.0" -description = "Describe-style plugin for pytest" +name = "pytest-cov" +version = "7.0.0" +description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_describe-3.0.0-py3-none-any.whl", hash = "sha256:946d40b5d77faf4eb610b94f2d54ab8b6721cc0b313f5bfb3663fd25c6a3920a"}, - {file = "pytest_describe-3.0.0.tar.gz", hash = "sha256:50e0b7f404decaeb1b5d7bca13cbe369208d1eeffafc2802663777b195f074ff"}, + {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, + {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, ] [package.dependencies] -pytest = ">=6,<9" +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" + +[package.extras] +testing = ["process-tests", "pytest-xdist", "virtualenv"] [[package]] -name = "pytest-pspec" -version = "0.0.4" -description = "A rspec format reporter for Python ptest" +name = "pytest-describe" +version = "3.0.0" +description = "Describe-style plugin for pytest" optional = false -python-versions = "*" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-pspec-0.0.4.tar.gz", hash = "sha256:5c0b0c9e964d5066cc5f2a2e1b296ad1313abbf58c4fd75014553fdf65bfe67a"}, - {file = "pytest_pspec-0.0.4-py2.py3-none-any.whl", hash = "sha256:f80cc46f8896524bfe68750f3a5324bad8d4cb5112e90004bfdaafb7248e5cfd"}, + {file = "pytest_describe-3.0.0-py3-none-any.whl", hash = "sha256:946d40b5d77faf4eb610b94f2d54ab8b6721cc0b313f5bfb3663fd25c6a3920a"}, + {file = "pytest_describe-3.0.0.tar.gz", hash = "sha256:50e0b7f404decaeb1b5d7bca13cbe369208d1eeffafc2802663777b195f074ff"}, ] [package.dependencies] -pytest = ">=3.0.0" -six = ">=1.11.0" +pytest = ">=6,<9" [[package]] name = "pytest-raises" @@ -1752,7 +1864,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "dev", "test"] +groups = ["main", "dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2086,4 +2198,4 @@ plot = ["matplotlib"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "a83a7eb266555cc61fadc6be594492ec62f4a87de79c4b2a39f6a5ad20dd9ccd" +content-hash = "87a9ed1ffb1f3f518a442609a884b7d48039a9c5ea7b1a310e3d5431b15999fe" From ce2b2632c5b2bc263aa67449e9110fb5ab45df72 Mon Sep 17 00:00:00 2001 From: Brooks Smith <42363318+smith120bh@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:28:52 +1100 Subject: [PATCH 7/7] Update workflows to use default pytest options as defined in pyproject.toml --- .github/workflows/release.yaml | 2 +- .github/workflows/test.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f5efa662..cc448951 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -99,7 +99,7 @@ jobs: poetry run mypy - name: Run FEM tests with pytest run: | - poetry run pytest --pspec tests/ + poetry run pytest publish: name: Publish to PyPI needs: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b3b6f9d4..22c268c4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -32,7 +32,7 @@ jobs: poetry run mypy -p anastruct - name: Run FEM tests with pytest run: | - poetry run pytest --pspec tests/ + poetry run pytest run_tests: name: Run FEM Testing Suite on Multiple Python Versions @@ -57,4 +57,4 @@ jobs: - name: Run FEM tests with pytest run: | - poetry run pytest --pspec tests/ + poetry run pytest