diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc4feaf..c2dc2ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,12 +8,12 @@ on: workflow_dispatch: jobs: - Test: + PreCommit: name: pre-commit runs-on: ubuntu-latest steps: - name: 💾 Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 🪝 Cache pre-commit hooks uses: actions/cache@v3 @@ -29,6 +29,43 @@ jobs: - name: 🔥 Test run: pre-commit run --show-diff-on-failure --all-files + Test: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - name: 💾 Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install OpenSCAD + run: | + sudo apt-get update + sudo apt-get install openscad + + - name: OpenSCAD Build Information + run: openscad --info + continue-on-error: true + + - name: Create Image Directories + run: mkdir -p base_hole_options baseplate hole_cutouts spiral_vase_base + working-directory: ./images + + - name: Run Unit Tests (Headless) + uses: coactions/setup-xvfb@6b00cf1889f4e1d5a48635647013c0508128ee1a + with: + working-directory: ./tests + run: python3 -m unittest + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: images + path: images/**/*.png + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false diff --git a/tests/openscad_runner.py b/tests/openscad_runner.py index c73987d..ac586d3 100644 --- a/tests/openscad_runner.py +++ b/tests/openscad_runner.py @@ -5,12 +5,15 @@ from __future__ import annotations import json +import logging import subprocess from dataclasses import dataclass, is_dataclass, asdict from pathlib import Path from tempfile import NamedTemporaryFile from typing import NamedTuple, Optional +logger = logging.getLogger(__name__) + class DataClassJSONEncoder(json.JSONEncoder): '''Allow json serialization''' def default(self, o): @@ -99,19 +102,22 @@ class CameraRotations: class OpenScadRunner: '''Helper to run the openscad binary''' + camera_arguments: CameraArguments scad_file_path: Path openscad_binary_path: Path image_folder_base: Path parameters: Optional[dict] '''If set, a temporary parameter file is created, and used with these variables''' - WINDOWS_DEFAULT_PATH = 'C:\\Program Files\\OpenSCAD\\openscad.exe' + LINUX_DEFAULT_PATH = Path('/bin/openscad') + WINDOWS_DEFAULT_PATH = Path('C:\\Program Files\\OpenSCAD\\openscad.exe') TOP_ANGLE_CAMERA = CameraArguments(Vec3(0,0,0),Vec3(45,0,45),150) common_arguments = [ - #'--hardwarnings', // Does not work when setting variables by using functions - '--enable=fast-csg', - '--enable=predictible-output', + #'--hardwarnings', # Does not work when setting variables by using functions + #'--enable=fast-csg', # Requires Beta version of OpenSCAD + #'--enable=predictible-output', # Requires Beta version of OpenSCAD + #'--render=true' # Fully render geometry for images, instead of using fast preview mode. '--imgsize=1280,720', '--view=axes', '--projection=ortho', @@ -121,20 +127,30 @@ class OpenScadRunner: set_variable_argument('$fa', 8) + set_variable_argument('$fs', 0.25) def __init__(self, file_path: Path): - self.openscad_binary_path = self.WINDOWS_DEFAULT_PATH + self.openscad_binary_path = self.find_openscad_binary() self.scad_file_path = file_path self.image_folder_base = Path('.') self.camera_arguments = None self.parameters = None + @classmethod + def find_openscad_binary(cls) -> Path: + if cls.WINDOWS_DEFAULT_PATH.exists(): + return cls.WINDOWS_DEFAULT_PATH + if cls.LINUX_DEFAULT_PATH.exists(): + return cls.LINUX_DEFAULT_PATH + logger.warning("Could not find OpenSCAD binary, defaulting to 'openscad'") + return Path("openscad") + def create_image(self, args: [str], image_file_name: str): """ Run the code, to create an image. @Important The only verification is that no errors occured. There is no verification if the image was created, or the image contents. """ - assert(self.scad_file_path.exists()) - assert(self.image_folder_base.exists()) + assert self.openscad_binary_path.exists(), f"OpenSCAD binary not found at '{self.openscad_binary_path}'" + assert self.scad_file_path.exists(), f"OpenSCAD file not found at '{self.scad_file_path}'" + assert self.image_folder_base.exists(), f"Image folder not found at '{self.image_folder_base}'" image_path = self.image_folder_base.joinpath(image_file_name) command_arguments = self.common_arguments + \ @@ -150,6 +166,6 @@ def create_image(self, args: [str], image_file_name: str): json.dump(params, file, sort_keys=True, indent=2, cls=DataClassJSONEncoder) file.close() command_arguments += ["-p", file.name, "-P", "python_generated"] - return subprocess.run([self.openscad_binary_path]+command_arguments, check=True) + return subprocess.run([str(self.openscad_binary_path)] + command_arguments, check=True) else: - return subprocess.run([self.openscad_binary_path]+command_arguments, check=True) + return subprocess.run([str(self.openscad_binary_path)] + command_arguments, check=True)