Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up testing infrastructure #37

Merged
merged 10 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions .github/workflows/ci-workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@ jobs:
# Code style
- linux: codestyle

tests:
needs: initial_checks
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1
with:
display: true
coverage: codecov
libraries: |
apt:
- '^libxcb.*-dev'
- libxkbcommon-x11-0
- libegl1-mesa
- libhdf5-dev
envs: |
- linux: py39-test
- linux: py310-test
- linux: py311-test
- linux: py312-test

- macos: py39-test
- macos: py310-test
- macos: py311-test
- macos: py312-test

- windows: py39-test
- windows: py310-test
- windows: py311-test
- windows: py312-test


deploy-examples:
needs: initial_checks
if: github.event_name != 'pull_request'
Expand Down
10 changes: 10 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
comment: off
coverage:
status:
project:
default:
target: auto
# adjust accordingly based on how flaky your tests are
# this allows a 0.001% drop from the previous base commit coverage
# basically just to prevent counting zero changes as negative
threshold: 0.001%
23 changes: 14 additions & 9 deletions glue_ar/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from glue_ar.common.scatter_export_options import * # noqa
from glue_ar.common.volume_export_options import * # noqa
from pkg_resources import get_distribution, DistributionNotFound
import traceback

try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
pass

Check warning on line 7 in glue_ar/__init__.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/__init__.py#L6-L7

Added lines #L6 - L7 were not covered by tests


def setup_qt():
Expand All @@ -8,7 +13,7 @@
except ImportError:
from glue_vispy_viewers.scatter.scatter_viewer import VispyScatterViewer

from glue_ar.qt.export_tool import QtARExportTool # noqa
from .qt.export_tool import QtARExportTool # noqa

VispyScatterViewer.subtools = {
**VispyScatterViewer.subtools,
Expand All @@ -26,7 +31,7 @@
}

try:
from glue_ar.qt.qr_tool import ARLocalQRTool # noqa
from .qt.qr_tool import ARLocalQRTool # noqa
VispyScatterViewer.tools = [t for t in VispyScatterViewer.tools] + ["ar"]
VispyVolumeViewer.tools = [t for t in VispyVolumeViewer.tools] + ["ar"]
VispyScatterViewer.subtools["ar"] = ["ar:qr"]
Expand All @@ -36,7 +41,7 @@


def setup_jupyter():
from glue_ar.jupyter.export_tool import JupyterARExportTool # noqa
from .jupyter.export_tool import JupyterARExportTool # noqa
from glue_vispy_viewers.scatter.jupyter import JupyterVispyScatterViewer
from glue_vispy_viewers.volume.jupyter import JupyterVispyVolumeViewer
JupyterVispyScatterViewer.tools = [t for t in JupyterVispyScatterViewer.tools] + ["save:ar_jupyter"]
Expand All @@ -46,14 +51,14 @@
def setup():
try:
setup_qt()
except ImportError as e:
except ImportError:

Check warning on line 54 in glue_ar/__init__.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/__init__.py#L54

Added line #L54 was not covered by tests
print("Qt setup error")
print(e)
print(traceback.format_exc())

Check warning on line 56 in glue_ar/__init__.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/__init__.py#L56

Added line #L56 was not covered by tests
pass

try:
setup_jupyter()
except ImportError as e:
except ImportError:

Check warning on line 61 in glue_ar/__init__.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/__init__.py#L61

Added line #L61 was not covered by tests
print("Jupyter setup error")
print(e)
print(traceback.format_exc())

Check warning on line 63 in glue_ar/__init__.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/__init__.py#L63

Added line #L63 was not covered by tests
pass
2 changes: 2 additions & 0 deletions glue_ar/common/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .marching_cubes import add_isosurface_layer_gltf, add_isosurface_layer_usd # noqa: F401
from .scatter import add_scatter_layer_gltf, add_scatter_layer_usd # noqa: F401
from .voxels import add_voxel_layers_gltf, add_voxel_layers_usd # noqa: F401
from .scatter_export_options import ARScatterExportOptions # noqa: F401
from .volume_export_options import ARIsosurfaceExportOptions, ARVoxelExportOptions # noqa: F401
17 changes: 9 additions & 8 deletions glue_ar/common/gltf_builder.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from gltflib import Accessor, AccessorType, AlphaMode, Asset, Attributes, Buffer, \
BufferTarget, BufferView, ComponentType, GLTFModel, \
Material, Mesh, Node, PBRMetallicRoughness, Primitive, PrimitiveMode, Scene
from gltflib.gltf import GLTF
from gltflib.gltf_resource import FileResource

from typing import Iterable, List, Optional, Self, Union
from typing import Iterable, List, Optional, Union


class GLTFBuilder:
Expand All @@ -22,7 +23,7 @@ def add_material(self,
opacity: float = 1,
roughness_factor: float = 1,
metallic_factor: float = 0,
alpha_mode: AlphaMode = AlphaMode.BLEND) -> Self:
alpha_mode: AlphaMode = AlphaMode.BLEND) -> GLTFBuilder:
if any(c > 1 for c in color):
color = [c / 256 for c in color[:3]]
self.materials.append(
Expand All @@ -41,7 +42,7 @@ def add_mesh(self,
position_accessor: int,
indices_accessor: Optional[int] = None,
material: Optional[int] = None,
mode: PrimitiveMode = PrimitiveMode.TRIANGLES) -> Self:
mode: PrimitiveMode = PrimitiveMode.TRIANGLES) -> GLTFBuilder:

primitive_kwargs = {
"attributes": Attributes(POSITION=position_accessor),
Expand All @@ -60,7 +61,7 @@ def add_mesh(self,

def add_buffer(self,
byte_length: int,
uri: str) -> Self:
uri: str) -> GLTFBuilder:
self.buffers.append(
Buffer(
byteLength=byte_length,
Expand All @@ -73,7 +74,7 @@ def add_buffer_view(self,
buffer: int,
byte_length: int,
byte_offset: int,
target: BufferTarget) -> Self:
target: BufferTarget) -> GLTFBuilder:
self.buffer_views.append(
BufferView(
buffer=buffer,
Expand All @@ -90,7 +91,7 @@ def add_accessor(self,
count: int,
type: AccessorType,
mins: List[Union[int, float]],
maxes: List[Union[int, float]]) -> Self:
maxes: List[Union[int, float]]) -> GLTFBuilder:
self.accessors.append(
Accessor(
bufferView=buffer_view,
Expand All @@ -105,7 +106,7 @@ def add_accessor(self,

def add_file_resource(self,
filename: str,
data: bytearray) -> Self:
data: bytearray) -> GLTFBuilder:
self.file_resources.append(
FileResource(
filename,
Expand Down
2 changes: 2 additions & 0 deletions glue_ar/common/tests/test_scatter_gltf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class TestScatterGLTF:
pass
79 changes: 77 additions & 2 deletions glue_ar/common/tests/test_shapes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from itertools import product
from math import sqrt
from ..shapes import cone_points, cylinder_points, rectangular_prism_points, sphere_points
import pytest
from glue_ar.common.shapes import cone_points, cone_triangles, \
cylinder_points, cylinder_triangles, \
rectangular_prism_points, rectangular_prism_triangulation, \
sphere_points, sphere_triangles


class TestShapes:
Expand All @@ -20,6 +24,38 @@ def test_rectangular_prism_points(self):
(2.0, 1.5, -0.5),
(2.0, 1.5, 2.5)} == points

def test_rectangular_prism_triangles(self):
triangles = set(rectangular_prism_triangulation())

assert {(0, 2, 1),
(0, 4, 2),
(1, 2, 3),
(4, 0, 1),
(4, 7, 6),
(5, 1, 7),
(5, 4, 1),
(6, 2, 4),
(7, 1, 3),
(7, 2, 6),
(7, 3, 2),
(7, 4, 5)} == triangles

start_index = 5
triangles = set(rectangular_prism_triangulation(start_index=start_index))

assert {(5, 7, 6),
(5, 9, 7),
(6, 7, 8),
(9, 5, 6),
(9, 12, 11),
(10, 6, 12),
(10, 9, 6),
(11, 7, 9),
(12, 6, 8),
(12, 7, 11),
(12, 8, 7),
(12, 9, 10)} == triangles

def test_sphere_points(self):
center = (1, 2, 3)
radius = 2
Expand Down Expand Up @@ -50,6 +86,19 @@ def test_sphere_points(self):

assert points == expected

@pytest.mark.parametrize("theta_resolution,phi_resolution", product((5, 8, 10, 12, 15, 20), repeat=2))
def test_sphere_points_count(self, theta_resolution, phi_resolution):
points = sphere_points(center=(0, 0, 0,), radius=1,
theta_resolution=theta_resolution,
phi_resolution=phi_resolution)
assert len(points) == 2 + (theta_resolution - 2) * phi_resolution

@pytest.mark.parametrize("theta_resolution,phi_resolution", product((5, 8, 10, 12, 15, 20), repeat=2))
def test_sphere_triangles_count(self, theta_resolution, phi_resolution):
triangles = sphere_triangles(theta_resolution=theta_resolution,
phi_resolution=phi_resolution)
assert len(triangles) == 2 * phi_resolution * (theta_resolution - 2)

def test_cylinder_points(self):
center = (-1, 2, -5)
radius = 3
Expand All @@ -70,7 +119,20 @@ def test_cylinder_points(self):

assert points == expected

def test_points(self):
@pytest.mark.parametrize("theta_resolution", (3, 5, 8, 10, 12, 15, 20))
def test_cylinder_points_count(self, theta_resolution):
points = cylinder_points(center=(0, 0, 0,), radius=1,
length=1, central_axis=(0, 0, 1),
theta_resolution=theta_resolution)
assert len(points) == 2 * theta_resolution

@pytest.mark.parametrize("theta_resolution,start_index", product((3, 5, 8, 10, 12, 15), (0, 2, 5, 7, 10)))
def test_cylinder_triangles_count(self, theta_resolution, start_index):
triangles = cylinder_triangles(theta_resolution=theta_resolution,
start_index=start_index)
assert len(triangles) == 2 * (theta_resolution - 2) + 2 * theta_resolution

def test_cone_points(self):
center = (2, 0, -1)
radius = 6
height = 10
Expand All @@ -86,3 +148,16 @@ def test_points(self):
expected = {tuple(round(t, precision) for t in pt) for pt in expected}

assert points == expected

@pytest.mark.parametrize("theta_resolution", (3, 5, 8, 10, 12, 15, 20))
def test_cone_points_count(self, theta_resolution):
points = cone_points(base_center=(0, 0, 0,), radius=1,
height=1, central_axis=(0, 0, 1),
theta_resolution=theta_resolution)
assert len(points) == theta_resolution + 1

@pytest.mark.parametrize("theta_resolution,start_index", product((3, 5, 8, 10, 12, 15), (0, 2, 5, 7, 10)))
def test_cone_triangles_count(self, theta_resolution, start_index):
triangles = cone_triangles(theta_resolution=theta_resolution,
start_index=start_index)
assert len(triangles) == 2 * theta_resolution - 2
4 changes: 2 additions & 2 deletions glue_ar/common/voxels.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
color_components = hex_to_components(color)

for indices in nonempty_indices:
value = data[*indices]
value = data[tuple(indices)]

Check warning on line 96 in glue_ar/common/voxels.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/common/voxels.py#L96

Added line #L96 was not covered by tests
adjusted_opacity = min(max(layer_state.alpha * opacity_factor * (value - isomin) / isorange, 0), 1)
indices_tpl = tuple(indices)
if indices_tpl in occupied_voxels:
Expand Down Expand Up @@ -208,7 +208,7 @@
color_components = hex_to_components(color)

for indices in nonempty_indices:
value = data[*indices]
value = data[tuple(indices)]

Check warning on line 211 in glue_ar/common/voxels.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/common/voxels.py#L211

Added line #L211 was not covered by tests
adjusted_opacity = min(max(layer_state.alpha * opacity_factor * (value - isomin) / isorange, 0), 1)
indices_tpl = tuple(indices)
if indices_tpl in occupied_voxels:
Expand Down
3 changes: 3 additions & 0 deletions glue_ar/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def pytest_configure(config):
from glue_ar import setup
setup()
File renamed without changes.
Empty file added glue_ar/py.typed
Empty file.
Empty file added glue_ar/qt/__init__.py
Empty file.
23 changes: 12 additions & 11 deletions glue_ar/qt/qr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import segno


GLUE_LOGO = os.path.abspath(os.path.join(os.path.dirname(__file__), "logo.png"))
GLUE_LOGO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "logo.png"))

Check warning on line 8 in glue_ar/qt/qr.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/qt/qr.py#L8

Added line #L8 was not covered by tests
GLUE_RED = "#eb1c24"


Expand All @@ -23,19 +23,20 @@
return IP


def create_qr(url):
def create_qr(url, with_logo=True, color=GLUE_RED):

Check warning on line 26 in glue_ar/qt/qr.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/qt/qr.py#L26

Added line #L26 was not covered by tests
qr = segno.make_qr(url)
out = BytesIO()
qr.save(out, kind="png", scale=7, dark=GLUE_RED, light="white")
qr.save(out, kind="png", scale=7, dark=color, light="white")

Check warning on line 29 in glue_ar/qt/qr.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/qt/qr.py#L29

Added line #L29 was not covered by tests
out.seek(0)
img = Image.open(out)
img = img.convert("RGB")
width, height = img.size
logo_max_size = height // 3
logo_img = Image.open(GLUE_LOGO)
# Resize the logo to logo_max_size
logo_img.thumbnail((logo_max_size, logo_max_size), Image.Resampling.LANCZOS)
# Calculate the center of the QR code
box = ((width - logo_img.size[0]) // 2, (height - logo_img.size[1]) // 2)
img.paste(logo_img, box)
if with_logo:
width, height = img.size
logo_max_size = height // 3
logo_img = Image.open(GLUE_LOGO)

Check warning on line 36 in glue_ar/qt/qr.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/qt/qr.py#L33-L36

Added lines #L33 - L36 were not covered by tests
# Resize the logo to logo_max_size
logo_img.thumbnail((logo_max_size, logo_max_size), Image.Resampling.LANCZOS)

Check warning on line 38 in glue_ar/qt/qr.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/qt/qr.py#L38

Added line #L38 was not covered by tests
# Calculate the center of the QR code
box = ((width - logo_img.size[0]) // 2, (height - logo_img.size[1]) // 2)
img.paste(logo_img, box)

Check warning on line 41 in glue_ar/qt/qr.py

View check run for this annotation

Codecov / codecov/patch

glue_ar/qt/qr.py#L40-L41

Added lines #L40 - L41 were not covered by tests
return img
24 changes: 0 additions & 24 deletions pyproject.toml

This file was deleted.

Loading