diff --git a/examples/loft.py b/examples/loft.py index 1204672..a92ad0b 100644 --- a/examples/loft.py +++ b/examples/loft.py @@ -41,7 +41,11 @@ with BuildPart() as art: top_bottom = art.faces().filter_by(GeomType.PLANE) offset(openings=top_bottom, amount=0.5) -assert abs(art.part.volume - 1306.3405290344635) < 1e-3 +want = 1306.3405290344635 +got = art.part.volume +delta = abs(got - want) +tolerance = want * 1e-5 +assert delta < tolerance, f"{delta=} is greater than {tolerance=}; {got=}, {want=}" show(art, names=["art"]) # [End] diff --git a/examples/packed_boxes.py b/examples/packed_boxes.py index f037d2c..e4eacbd 100644 --- a/examples/packed_boxes.py +++ b/examples/packed_boxes.py @@ -12,6 +12,8 @@ import operator import random import build123d as bd +GEN_DOCS = False + random.seed(123456) test_boxes = [bd.Box(random.randint(1, 20), random.randint(1, 20), random.randint(1, 5)) for _ in range(50)] @@ -28,7 +30,8 @@ def export_svg(parts, name): exporter.add_layer("Hidden", line_color=(99, 99, 99), line_type=bd.LineType.ISO_DOT) exporter.add_shape(visible, layer="Visible") exporter.add_shape(hidden, layer="Hidden") - exporter.write(f"../docs/assets/{name}.svg") + if GEN_DOCS: + exporter.write(f"../docs/assets/{name}.svg") export_svg(test_boxes, "packed_boxes_input") export_svg(packed, "packed_boxes_output") diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 0000000..e342f0d --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,86 @@ +""" +build123d Example tests + +name: test_examples.py +by: fischman +date: February 21 2025 + +desc: Unit tests for the build123d examples, ensuring they don't raise. +""" + +from pathlib import Path + +import os +import subprocess +import sys +import tempfile +import unittest + + +_examples_dir = Path(os.path.abspath(os.path.dirname(__file__))).parent / "examples" + +_MOCK_OCP_VSCODE_CONTENTS = """ +from pathlib import Path + +import re +import sys +from unittest.mock import Mock +mock_module = Mock() +mock_module.show = Mock() +mock_module.show_object = Mock() +mock_module.show_all = Mock() +sys.modules["ocp_vscode"] = mock_module +""" + + +def generate_example_test(path: Path): + """Generate and return a function to test the example at `path`.""" + name = path.name + + def assert_example_does_not_raise(self): + with tempfile.TemporaryDirectory( + prefix=f"build123d_test_examples_{name}" + ) as tmpdir: + # More examples emit output files than read input files, + # so default to running with a temporary directory to + # avoid cluttering the git working directory. For + # examples that want to read assets from the examples + # directory, use that. If an example is added in the + # future that wants to both read assets from the examples + # directory and write output files, deal with it then. + cwd = tmpdir if 'benchy' not in path.name else _examples_dir + mock_ocp_vscode = Path(tmpdir) / "_mock_ocp_vscode.py" + with open(mock_ocp_vscode, "w", encoding="utf-8") as f: + f.write(_MOCK_OCP_VSCODE_CONTENTS) + got = subprocess.run( + [ + sys.executable, + "-c", + f"exec(open(r'{mock_ocp_vscode}').read()); exec(open(r'{path}').read())", + ], + capture_output=True, + cwd=cwd, + check=False, + ) + self.assertEqual( + 0, got.returncode, f"stdout/stderr: {got.stdout} / {got.stderr}" + ) + + return assert_example_does_not_raise + + +class TestExamples(unittest.TestCase): + """Tests build123d examples.""" + + +for example in sorted(_examples_dir.iterdir()): + if example.name.startswith("_") or not example.name.endswith(".py"): + continue + setattr( + TestExamples, + f"test_{example.name.replace('.', '_')}", + generate_example_test(example), + ) + +if __name__ == "__main__": + unittest.main()