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 Jul 16, 2023
1 parent b33d06f commit cc090fc
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 7 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
2 changes: 1 addition & 1 deletion include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class cpp_function : public function {

/* Dispatch code which converts function arguments and performs the actual function call */
rec->impl = [](function_call &call) -> handle {
cast_in args_converter;
cast_in args_converter{};

/* Try to cast the function arguments into the C++ domain */
if (!args_converter.load_args(call)) {
Expand Down
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 @@ -488,6 +493,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,8 +1,13 @@
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,3 +1,4 @@
import sys
import time
from threading import Thread

Expand Down Expand Up @@ -151,6 +152,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 @@ -174,6 +176,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
@@ -1,5 +1,12 @@
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,3 +1,12 @@
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 @@ -73,7 +73,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 @@ -69,24 +69,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 @@ -101,6 +105,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 @@ -202,7 +207,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 @@ -212,7 +217,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 @@ -222,7 +227,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 @@ -232,7 +237,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,6 +1,9 @@
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 @@ -268,6 +271,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,5 +1,8 @@
import sys
import threading

import pytest

from pybind11_tests import thread as m


Expand All @@ -22,6 +25,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 @@ -32,6 +36,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,3 +1,5 @@
import sys

import pytest

import env # noqa: F401
Expand Down Expand Up @@ -433,6 +435,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 cc090fc

Please sign in to comment.