Skip to content
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
114 changes: 57 additions & 57 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: 3.14
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -45,7 +45,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
- name: Install JS dependencies
run: npm install -g standard
Expand All @@ -59,11 +59,11 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: 3.14
- name: Install dev dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -75,7 +75,7 @@ jobs:

# Test majority of code for a variety of Python versions and os's
test-unit:
name: 'Test Python ${{ matrix.pyversion }} on ${{ matrix.os }}'
name: 'Test py ${{ matrix.pyversion }} on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand All @@ -89,14 +89,17 @@ jobs:
pyversion: '3.12'
- os: ubuntu-latest
pyversion: '3.13'
- os: ubuntu-latest
pyversion: '3.14'
# platforms
- os: windows-latest
pyversion: '3.13'
pyversion: '3.14'
- os: macos-latest
pyversion: '3.13'
pyversion: '3.14'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.pyversion }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.pyversion }}
- name: Install package and dev dependencies
Expand All @@ -119,9 +122,9 @@ jobs:
- os: ubuntu-latest
pyversion: '3.13'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.pyversion }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.pyversion }}
- name: Install package and dev dependencies
Expand All @@ -141,45 +144,42 @@ jobs:
"
pytest -v tests

# Test for the few GUI frameworks that we support.
test-gui-frameworks:
name: 'Test gui ${{ matrix.backendname }}'
# Test for specific backends. Ran separately, since GUI frameworks don't like to be loaded in the same process.
test-backends:
name: 'Test backend ${{ matrix.backendname }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- guilib: 'glfw'
backendname: 'glfw'
guitestname: 'glfw'
loopname: 'AsyncioLoop'
- backendname: 'glfw'
deps: 'glfw'
os: windows-latest
- guilib: 'PySide6'
backendname: 'pyside6'
guitestname: 'qt'
- backendname: 'pyside6'
deps: 'PySide6'
testname: 'qt'
loopname: 'PySide6Loop'
os: windows-latest
- guilib: 'PyQt6'
backendname: 'pyqt6'
guitestname: 'qt'
- backendname: 'pyqt6'
deps: 'PyQt6'
testname: 'qt'
loopname: 'PyQt6Loop'
os: windows-latest
- guilib: 'PyQt5'
backendname: 'pyqt5'
guitestname: 'qt'
- backendname: 'pyqt5'
deps: 'PyQt5'
testname: 'qt'
loopname: 'PyQt5Loop'
os: windows-latest
- guilib: 'wxPython'
backendname: 'wx'
guitestname: 'wx'
loopname: 'WxLoop'
- backendname: 'wx'
deps: 'wxPython'
# loopname: 'WxLoop' wx's event loop has limitations
os: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- name: Install xvfb and Vulkan drivers
if: matrix.os == 'ubuntu-latest'
run: |
Expand All @@ -190,18 +190,18 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .[tests]
pip install ${{ matrix.guilib }}
pip install ${{ matrix.deps }}
rm -r rendercanvas
- name: Test import
run: |
python -c 'import rendercanvas.${{ matrix.backendname }}'
- name: Test loop
if: matrix.backendname != 'wx'
if: matrix.loopname
run: |
pytest -v tests/test_loop.py -k ${{ matrix.loopname }}
- name: Test GUI canvas
- name: Test backend canvas
run: |
pytest -v tests/test_gui_${{ matrix.guitestname }}.py
pytest -v tests/test_backend_${{ matrix.testname || matrix.backendname }}.py

# Test that the examples run without error, and screenshots match
test-examples:
Expand All @@ -211,16 +211,16 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
pyversion: '3.10'
- os: ubuntu-latest
pyversion: '3.12'
- os: ubuntu-latest
pyversion: '3.14'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: ${{ matrix.pyversion }}
- name: Install llvmpipe and lavapipe for offscreen canvas
run: |
sudo apt update -y -qq
Expand Down Expand Up @@ -248,11 +248,11 @@ jobs:
name: Test pyinstaller
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: 3.14
- name: Install package and dev dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -270,11 +270,11 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: 3.14
- name: Install dev dependencies
run: |
python -m pip install --upgrade pip
Expand Down Expand Up @@ -318,18 +318,18 @@ jobs:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: 3.14
- name: Download assets
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: dist
path: dist
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: |
Expand Down
145 changes: 145 additions & 0 deletions tests/test_backend_glfw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"""
Test the canvas, and parts of the rendering that involves a canvas,
like the canvas context and surface texture.

In contrast to the other test_backend_xx.py tests, this test is run when glfw is
available.
"""

import os
import weakref
import asyncio
import gc

import pytest
from testutils import run_tests, can_use_glfw, can_use_wgpu_lib, is_pypy
from testutils_backends import BACKEND_TEST_FUNCS, _get_draw_function


if not can_use_glfw:
pytest.skip("Skipping tests that needs glfw", allow_module_level=True)


# def setup_module():
# import glfw
# glfw.init()
#
# def teardown_module():
# import glfw
# from rendercanvas.glfw import poll_glfw_briefly
# poll_glfw_briefly()
# # Terminate; otherwise it gets in the way of tests for the Qt or wx loop.
# glfw.terminate()


def test_is_canvas_classes():
from rendercanvas.base import BaseRenderCanvas
from rendercanvas.glfw import RenderCanvas, GlfwRenderCanvas

assert GlfwRenderCanvas is RenderCanvas
assert issubclass(RenderCanvas, BaseRenderCanvas)


@pytest.mark.parametrize("backend", ["glfw"])
@pytest.mark.parametrize("func", BACKEND_TEST_FUNCS)
def test_backend_generic(func, backend):
func(backend)


def test_glfw_canvas_del():
from rendercanvas.glfw import RenderCanvas, loop

aio_loop = asyncio.new_event_loop()
loop_task = aio_loop.create_task(loop.run_async())

def run_briefly():
aio_loop.run_until_complete(asyncio.sleep(0.5))

canvas = RenderCanvas()
ref = weakref.ref(canvas)

assert ref() is not None
run_briefly()
assert ref() is not None
del canvas
if is_pypy:
gc.collect() # force garbage collection for pypy
assert ref() is None

# Loop shuts down
assert not loop_task.done()
run_briefly()
assert loop_task.done()

aio_loop.close()


def test_glfw_canvas_render():
"""Render an orange square ... in a glfw window."""

if not can_use_wgpu_lib:
pytest.skip("Skipping tests that needs the wgpu lib")

import wgpu
from rendercanvas.glfw import RenderCanvas
from rendercanvas.asyncio import loop

aio_loop = asyncio.new_event_loop()
loop_task = aio_loop.create_task(loop.run_async())

def run_briefly():
aio_loop.run_until_complete(asyncio.sleep(0.5))

canvas = RenderCanvas(max_fps=9999, update_mode="ondemand")

device = wgpu.gpu.request_adapter_sync().request_device_sync()
draw_frame1 = _get_draw_function(device, canvas)

allowed_frames = (1, 2)
if os.getenv("CI"):
allowed_frames = (1, 2, 3)

frame_counter = 0

def draw_frame2():
nonlocal frame_counter
frame_counter += 1
draw_frame1()

canvas.request_draw(draw_frame2)

run_briefly()
# There should have been exactly one draw now
# This assumes ondemand scheduling mode
assert frame_counter in allowed_frames
frame_counter = 0

# Ask for a lot of draws
for i in range(5):
canvas.request_draw()
# Process evens for a while
run_briefly()
# We should have had just one draw
assert frame_counter in allowed_frames
frame_counter = 0

# Change the canvas size
canvas.set_logical_size(300, 200)
canvas.set_logical_size(400, 300)
# We should have had just one draw, but sometimes (more so on CI) we can have more
run_briefly()
assert frame_counter in allowed_frames
frame_counter = 0

# Stopping
assert not loop_task.done()
canvas.close()
assert not loop_task.done()
run_briefly()
assert loop_task.done()

aio_loop.close()


if __name__ == "__main__":
run_tests(globals())
Loading
Loading