mirror of https://github.com/pyodide/pyodide.git
Use hypothesis to test string conversions between js and python (#1339)
Co-authored-by: Roman Yurchak <rth.yurchak@gmail.com>
This commit is contained in:
parent
f4281bf572
commit
9baffb6b7f
|
@ -3,7 +3,7 @@ version: 2
|
|||
defaults: &defaults
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: iodide/pyodide-env:11
|
||||
- image: iodide/pyodide-env:12
|
||||
environment:
|
||||
- EMSDK_NUM_CORES: 4
|
||||
EMCC_CORES: 4
|
||||
|
|
|
@ -11,6 +11,7 @@ firefox/
|
|||
.vscode
|
||||
.idea
|
||||
.mypy_cache/
|
||||
.hypothesis
|
||||
node_modules/
|
||||
|
||||
build
|
||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -9,8 +9,21 @@ RUN apt-get update \
|
|||
libgconf-2-4 chromium \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pip3 --no-cache-dir install pytest pytest-xdist pytest-instafail pytest-rerunfailures \
|
||||
pytest-httpserver pytest-cov selenium PyYAML flake8 black distlib mypy "Cython<3.0"
|
||||
RUN pip3 --no-cache-dir install \
|
||||
black \
|
||||
"cython<3.0" \
|
||||
distlib \
|
||||
flake8 \
|
||||
hypothesis \
|
||||
mypy \
|
||||
pytest \
|
||||
pytest-cov \
|
||||
pytest-httpserver \
|
||||
pytest-instafail \
|
||||
pytest-rerunfailures \
|
||||
pytest-xdist \
|
||||
pyyaml \
|
||||
selenium
|
||||
|
||||
# Get firefox 70.0.1 and geckodriver
|
||||
RUN wget -qO- https://ftp.mozilla.org/pub/firefox/releases/70.0.1/linux-x86_64/en-US/firefox-70.0.1.tar.bz2 | tar jx \
|
||||
|
|
79
conftest.py
79
conftest.py
|
@ -227,53 +227,52 @@ class ChromeWrapper(SeleniumWrapper):
|
|||
|
||||
if pytest is not None:
|
||||
|
||||
@pytest.fixture(params=["firefox", "chrome"])
|
||||
@contextlib.contextmanager
|
||||
def selenium_common(request, web_server_main):
|
||||
server_hostname, server_port, server_log = web_server_main
|
||||
if request.param == "firefox":
|
||||
cls = FirefoxWrapper
|
||||
elif request.param == "chrome":
|
||||
cls = ChromeWrapper
|
||||
selenium = cls(
|
||||
build_dir=request.config.option.build_dir,
|
||||
server_port=server_port,
|
||||
server_hostname=server_hostname,
|
||||
server_log=server_log,
|
||||
)
|
||||
try:
|
||||
yield selenium
|
||||
finally:
|
||||
selenium.driver.quit()
|
||||
|
||||
@pytest.fixture(params=["firefox", "chrome"], scope="function")
|
||||
def selenium_standalone(request, web_server_main):
|
||||
server_hostname, server_port, server_log = web_server_main
|
||||
if request.param == "firefox":
|
||||
cls = FirefoxWrapper
|
||||
elif request.param == "chrome":
|
||||
cls = ChromeWrapper
|
||||
selenium = cls(
|
||||
build_dir=request.config.option.build_dir,
|
||||
server_port=server_port,
|
||||
server_hostname=server_hostname,
|
||||
server_log=server_log,
|
||||
)
|
||||
try:
|
||||
yield selenium
|
||||
finally:
|
||||
print(selenium.logs)
|
||||
selenium.driver.quit()
|
||||
with selenium_common(request, web_server_main) as selenium:
|
||||
try:
|
||||
yield selenium
|
||||
finally:
|
||||
print(selenium.logs)
|
||||
|
||||
# selenium instance cached at the module level
|
||||
@pytest.fixture(params=["firefox", "chrome"], scope="module")
|
||||
def _selenium_cached(request, web_server_main):
|
||||
# Cached selenium instance. This is a copy-paste of
|
||||
# selenium_standalone to avoid fixture scope issues
|
||||
server_hostname, server_port, server_log = web_server_main
|
||||
if request.param == "firefox":
|
||||
cls = FirefoxWrapper
|
||||
elif request.param == "chrome":
|
||||
cls = ChromeWrapper
|
||||
selenium = cls(
|
||||
build_dir=request.config.option.build_dir,
|
||||
server_port=server_port,
|
||||
server_hostname=server_hostname,
|
||||
server_log=server_log,
|
||||
)
|
||||
try:
|
||||
def selenium_module_scope(request, web_server_main):
|
||||
with selenium_common(request, web_server_main) as selenium:
|
||||
yield selenium
|
||||
finally:
|
||||
selenium.driver.quit()
|
||||
|
||||
@pytest.fixture
|
||||
def selenium(_selenium_cached):
|
||||
# selenium instance cached at the module level
|
||||
# We want one version of this decorated as a function-scope fixture and one
|
||||
# version decorated as a context manager.
|
||||
def selenium_per_function(selenium_module_scope):
|
||||
try:
|
||||
_selenium_cached.clean_logs()
|
||||
yield _selenium_cached
|
||||
selenium_module_scope.clean_logs()
|
||||
yield selenium_module_scope
|
||||
finally:
|
||||
print(_selenium_cached.logs)
|
||||
print(selenium_module_scope.logs)
|
||||
|
||||
selenium = pytest.fixture(selenium_per_function)
|
||||
# Hypothesis is unhappy with function scope fixtures. Instead, use the
|
||||
# module scope fixture `selenium_module_scope` and use:
|
||||
# `with selenium_context_manager(selenium_module_scope) as selenium`
|
||||
selenium_context_manager = contextlib.contextmanager(selenium_per_function)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
|
|
|
@ -26,7 +26,7 @@ function error() {
|
|||
}
|
||||
|
||||
|
||||
PYODIDE_IMAGE_TAG="11"
|
||||
PYODIDE_IMAGE_TAG="12"
|
||||
PYODIDE_PREBUILT_IMAGE_TAG="0.16.1"
|
||||
DEFAULT_PYODIDE_DOCKER_IMAGE="iodide/pyodide-env:${PYODIDE_IMAGE_TAG}"
|
||||
DEFAULT_PYODIDE_SYSTEM_PORT="8000"
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
# type: ignore
|
||||
import platform
|
||||
|
||||
if platform.system() == "Emscripten":
|
||||
from _pyodide_core import JsProxy, JsException, JsBuffer
|
||||
from _pyodide_core import JsProxy, JsException
|
||||
else:
|
||||
# Can add shims here if we are so inclined.
|
||||
class JsException(Exception):
|
||||
class JsException(Exception): # type: ignore
|
||||
"""
|
||||
A wrapper around a Javascript Error to allow the Error to be thrown in Python.
|
||||
"""
|
||||
|
||||
# Defined in jsproxy.c
|
||||
|
||||
class JsProxy:
|
||||
class JsProxy: # type: ignore
|
||||
"""A proxy to make a Javascript object behave like a Python object"""
|
||||
|
||||
# Defined in jsproxy.c
|
||||
|
||||
class JsBuffer:
|
||||
"""A proxy to make it possible to call Javascript typed arrays from Python."""
|
||||
|
||||
# Defined in jsproxy.c
|
||||
|
||||
|
||||
__all__ = [JsProxy, JsException]
|
||||
__all__ = ["JsProxy", "JsException"]
|
||||
|
|
|
@ -4,7 +4,7 @@ import time
|
|||
import contextvars
|
||||
|
||||
|
||||
from typing import Awaitable, Callable
|
||||
from typing import Callable
|
||||
|
||||
|
||||
class WebLoop(asyncio.AbstractEventLoop):
|
||||
|
@ -65,7 +65,7 @@ class WebLoop(asyncio.AbstractEventLoop):
|
|||
"""
|
||||
pass
|
||||
|
||||
def run_until_complete(self, future: Awaitable):
|
||||
def run_until_complete(self, future):
|
||||
"""Run until future is done.
|
||||
|
||||
If the argument is a coroutine, it is wrapped in a Task.
|
||||
|
@ -99,7 +99,7 @@ class WebLoop(asyncio.AbstractEventLoop):
|
|||
return self.call_later(delay, callback, *args, context=context)
|
||||
|
||||
def call_soon_threadsafe(
|
||||
callback: Callable, *args, context: contextvars.Context = None
|
||||
self, callback: Callable, *args, context: contextvars.Context = None
|
||||
):
|
||||
"""Like ``call_soon()``, but thread-safe.
|
||||
|
||||
|
@ -223,7 +223,7 @@ class WebLoop(asyncio.AbstractEventLoop):
|
|||
return self._task_factory
|
||||
|
||||
|
||||
class WebLoopPolicy(asyncio.DefaultEventLoopPolicy):
|
||||
class WebLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore
|
||||
"""
|
||||
A simple event loop policy for managing WebLoop based event loops.
|
||||
"""
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
# See also test_pyproxy, test_jsproxy, and test_python.
|
||||
import pytest
|
||||
from hypothesis import given
|
||||
from hypothesis.strategies import text
|
||||
from conftest import selenium_context_manager
|
||||
|
||||
|
||||
@given(s=text())
|
||||
def test_string_conversion(selenium_module_scope, s):
|
||||
with selenium_context_manager(selenium_module_scope) as selenium:
|
||||
# careful string escaping here -- hypothesis will fuzz it.
|
||||
sbytes = list(s.encode())
|
||||
selenium.run_js(
|
||||
f"""
|
||||
window.sjs = (new TextDecoder("utf8")).decode(new Uint8Array({sbytes}));
|
||||
pyodide.runPython('spy = bytes({sbytes}).decode()');
|
||||
"""
|
||||
)
|
||||
assert selenium.run_js(f"""return pyodide.runPython('spy') === sjs;""")
|
||||
assert selenium.run(
|
||||
"""
|
||||
from js import sjs
|
||||
sjs == spy
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_python2js(selenium):
|
||||
|
|
Loading…
Reference in New Issue