Skip to content

Commit

Permalink
tests: run on pyodide
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
  • Loading branch information
henryiii committed Jun 26, 2024
1 parent 2e35470 commit 4a6c18a
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 6 deletions.
58 changes: 58 additions & 0 deletions .github/workflows/emscripten.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: WASM

on:
workflow_dispatch:
pull_request:
branches:
- master

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-wasm-emscripten:
name: Pyodide wheel
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0

- uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Install pyodide-build
run: pip install pyodide-build==0.23.4

- name: Compute emsdk version
id: compute-emsdk-version
run: |
# Prepare xbuild environment (side-effect)
pyodide config list
# Save EMSDK version
EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
echo "emsdk-version=$EMSCRIPTEN_VERSION" >> $GITHUB_OUTPUT
- uses: mymindstorm/setup-emsdk@v12
with:
version: ${{ steps.compute-emsdk-version.outputs.emsdk-version }}
actions-cache-folder: emsdk-cache

- name: Build
run: PYODIDE_BUILD_EXPORTS=whole_archive CFLAGS=-fexceptions LDFLAGS=-fexceptions pyodide build
working-directory: tests

- uses: actions/setup-node@v3
with:
node-version: 18

- name: Set up Pyodide virtual environment
run: |
pyodide venv .venv-pyodide
.venv-pyodide/bin/pip install $(echo -n tests/dist/*.whl)
- name: Test
run: .venv-pyodide/bin/pytest -o timeout=0 tests/test_*.py
10 changes: 9 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ set(PYBIND11_TEST_FILTER
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
# We're being loaded directly, i.e. not via add_subdirectory, so make this
# work as its own project and load the pybind11Config to get the tools we need
find_package(pybind11 REQUIRED CONFIG)

if(SKBUILD)
add_subdirectory(.. pybind11_src)
else()
find_package(pybind11 REQUIRED CONFIG)
endif()
endif()

if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
Expand Down Expand Up @@ -489,6 +494,9 @@ foreach(target ${test_targets})
endforeach()
endif()
endif()
if(SKBUILD)
install(TARGETS ${target} LIBRARY DESTINATION .)
endif()
endforeach()

# Provide nice organisation in IDEs
Expand Down
11 changes: 11 additions & 0 deletions tests/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Warning: this is currently used for pyodide, and is not a general out-of-tree
# builder for the tests (yet). Specifically, wheels can't be built from SDists.

[build-system]
requires = ["scikit-build-core[pyproject]"]
build-backend = "scikit_build_core.build"

[project]
name = "pybind11_tests"
version = "0.0.1"
dependencies = ["pytest", "pytest-timeout", "numpy", "scipy"]
5 changes: 5 additions & 0 deletions tests/test_async.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from __future__ import annotations

import sys

import pytest

asyncio = pytest.importorskip("asyncio")
m = pytest.importorskip("pybind11_tests.async_module")

if sys.platform.startswith("emscripten"):
pytest.skip("Can't run a new event_loop in pyodide", allow_module_level=True)


@pytest.fixture()
def event_loop():
Expand Down
3 changes: 3 additions & 0 deletions tests/test_callbacks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
import time
from threading import Thread

Expand Down Expand Up @@ -153,6 +154,7 @@ def test_python_builtins():
assert m.test_sum_builtin(sum, []) == 0


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_async_callbacks():
# serves as state for async callback
class Item:
Expand All @@ -176,6 +178,7 @@ def gen_f():
assert sum(res) == sum(x + 3 for x in work)


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_async_async_callbacks():
t = Thread(target=test_async_callbacks)
t.start()
Expand Down
7 changes: 7 additions & 0 deletions tests/test_embed/test_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

import sys

import pytest

if sys.platform.startswith("emscripten"):
pytest.skip(
"Test not implemented from single wheel on Pyodide", allow_module_level=True
)

from widget_module import Widget


Expand Down
9 changes: 9 additions & 0 deletions tests/test_embed/test_trampoline.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
from __future__ import annotations

import sys

import pytest

if sys.platform.startswith("emscripten"):
pytest.skip(
"Test not implemented from single wheel on Pyodide", allow_module_level=True
)

import trampoline_module


Expand Down
2 changes: 1 addition & 1 deletion tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def test_cross_module_exceptions(msg):

# TODO: FIXME
@pytest.mark.xfail(
"env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang'))",
"env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang')) or sys.platform.startswith('emscripten')",
raises=RuntimeError,
reason="See Issue #2847, PR #2999, PR #4324",
)
Expand Down
13 changes: 9 additions & 4 deletions tests/test_gil_scoped.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,28 @@ def test_cross_module_gil_inner_pybind11_acquired():
m.test_cross_module_gil_inner_pybind11_acquired()


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_custom_released():
"""Makes sure that the GIL can be nested acquired/released by another module
from a GIL-released state using custom locking logic."""
m.test_cross_module_gil_nested_custom_released()


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_custom_acquired():
"""Makes sure that the GIL can be nested acquired/acquired by another module
from a GIL-acquired state using custom locking logic."""
m.test_cross_module_gil_nested_custom_acquired()


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_pybind11_released():
"""Makes sure that the GIL can be nested acquired/released by another module
from a GIL-released state using pybind11 locking logic."""
m.test_cross_module_gil_nested_pybind11_released()


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_pybind11_acquired():
"""Makes sure that the GIL can be nested acquired/acquired by another module
from a GIL-acquired state using pybind11 locking logic."""
Expand All @@ -103,6 +107,7 @@ def test_nested_acquire():
assert m.test_nested_acquire(0xAB) == "171"


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_multi_acquire_release_cross_module():
for bits in range(16 * 8):
internals_ids = m.test_multi_acquire_release_cross_module(bits)
Expand Down Expand Up @@ -204,7 +209,7 @@ def _run_in_threads(test_fn, num_threads, parallel):
thread.join()


# TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_one_thread(test_fn):
"""Makes sure there is no GIL deadlock when running in a thread.
Expand All @@ -214,7 +219,7 @@ def test_run_in_process_one_thread(test_fn):
assert _run_in_process(_run_in_threads, test_fn, num_threads=1, parallel=False) == 0


# TODO: FIXME on macOS Python 3.9
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_multiple_threads_parallel(test_fn):
"""Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
Expand All @@ -224,7 +229,7 @@ def test_run_in_process_multiple_threads_parallel(test_fn):
assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=True) == 0


# TODO: FIXME on macOS Python 3.9
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_multiple_threads_sequential(test_fn):
"""Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
Expand All @@ -234,7 +239,7 @@ def test_run_in_process_multiple_threads_sequential(test_fn):
assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=False) == 0


# TODO: FIXME on macOS Python 3.9
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_direct(test_fn):
"""Makes sure there is no GIL deadlock when using processes.
Expand Down
4 changes: 4 additions & 0 deletions tests/test_iostream.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import annotations

import sys
from contextlib import redirect_stderr, redirect_stdout
from io import StringIO

import pytest

from pybind11_tests import iostream as m


Expand Down Expand Up @@ -270,6 +273,7 @@ def test_redirect_both(capfd):
assert stream2.getvalue() == msg2


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_threading():
with m.ostream_redirect(stdout=True, stderr=False):
# start some threads
Expand Down
5 changes: 5 additions & 0 deletions tests/test_thread.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import sys
import threading

import pytest

from pybind11_tests import thread as m


Expand All @@ -24,6 +27,7 @@ def join(self):
raise self.e


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_implicit_conversion():
a = Thread(m.test)
b = Thread(m.test)
Expand All @@ -34,6 +38,7 @@ def test_implicit_conversion():
x.join()


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_implicit_conversion_no_gil():
a = Thread(m.test_no_gil)
b = Thread(m.test_no_gil)
Expand Down
3 changes: 3 additions & 0 deletions tests/test_virtual_functions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import sys

import pytest

import env # noqa: F401
Expand Down Expand Up @@ -435,6 +437,7 @@ def lucky_number(self):
assert obj.say_everything() == "BT -7"


@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_issue_1454():
# Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
m.test_gil()
Expand Down

0 comments on commit 4a6c18a

Please sign in to comment.