Skip to content

Commit

Permalink
TST Make pyodide-test-runner installable (#2742)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanking13 authored Jul 4, 2022
1 parent bfd364b commit 7d7b7e8
Show file tree
Hide file tree
Showing 17 changed files with 252 additions and 198 deletions.
37 changes: 33 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
name: Test docs
command: |
mkdir test-results
pip install ./pyodide-test-runner
npm install -g node-fetch@2
pytest docs/sphinx_pyodide/tests --junitxml=test-results/junit.xml
- store_test_results:
path: test-results
Expand Down Expand Up @@ -214,6 +216,8 @@ jobs:
command: |
make npm-link
mkdir test-results
pip install ./pyodide-test-runner
npm install -g node-fetch@2
tools/pytest_wrapper.py \
--junitxml=test-results/junit.xml \
--verbose \
Expand All @@ -231,6 +235,8 @@ jobs:
name: stack-size
command: |
make npm-link
pip install ./pyodide-test-runner
npm install -g node-fetch@2
pytest -s benchmark/stack_usage.py | sed -n 's/## //pg'
test-python:
Expand All @@ -243,17 +249,31 @@ jobs:
name: test
command: |
mkdir test-results
python3 -m pip install ./pyodide-test-runner
python3 -m pip install -e ./pyodide-build
npm install -g node-fetch@2
PYODIDE_ROOT=. pytest \
--junitxml=test-results/junit.xml \
--verbose \
-k 'not (chrome or firefox or node)' \
--cov=pyodide_build --cov=pyodide \
src pyodide-build packages/micropip/ pyodide-test-runner
src pyodide-build packages/micropip/
- store_test_results:
path: test-results

test-test-runner:
<<: *defaults
resource_class: medium+
steps:
- attach_workspace:
at: .
- run:
name: test
command: |
npm install -g node-fetch@2
cd pyodide-test-runner && pytest -v .
test-js:
<<: *defaults
resource_class: small
Expand Down Expand Up @@ -291,6 +311,8 @@ jobs:
- run:
name: benchmark
command: |
pip install ./pyodide-test-runner
npm install -g node-fetch@2
python benchmark/benchmark.py all --output dist/benchmarks.json
- store_artifacts:
Expand Down Expand Up @@ -482,7 +504,7 @@ workflows:

- test-main:
name: test-core-chrome
test-params: -k "chrome and not webworker" src packages/micropip packages/fpcast-test packages/sharedlib-test-py/ packages/cpp-exceptions-test/ pyodide-test-runner
test-params: -k "chrome and not webworker" src packages/micropip packages/fpcast-test packages/sharedlib-test-py/ packages/cpp-exceptions-test/
requires:
- build-core
filters:
Expand All @@ -491,7 +513,7 @@ workflows:

- test-main:
name: test-core-firefox
test-params: -k "firefox and not webworker" src packages/micropip packages/fpcast-test packages/sharedlib-test-py/ packages/cpp-exceptions-test/ pyodide-test-runner
test-params: -k "firefox and not webworker" src packages/micropip packages/fpcast-test packages/sharedlib-test-py/ packages/cpp-exceptions-test/
requires:
- build-core
filters:
Expand All @@ -500,7 +522,7 @@ workflows:

- test-main:
name: test-core-node
test-params: -k node src packages/micropip packages/fpcast-test packages/sharedlib-test-py/ packages/cpp-exceptions-test/ pyodide-test-runner
test-params: -k node src packages/micropip packages/fpcast-test packages/sharedlib-test-py/ packages/cpp-exceptions-test/
requires:
- build-core
filters:
Expand Down Expand Up @@ -552,6 +574,13 @@ workflows:
tags:
only: /.*/

- test-test-runner:
requires:
- build-core
filters:
tags:
only: /.*/

- test-js:
requires:
- build-core
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ jobs:
shell: bash -l {0}
run: |
pip install -r requirements.txt
pip install ./pyodide-test-runner
# FIXME: playwright 1.23.0 has unknown performance issue on firefox
pip install "playwright<1.23.0" && python -m playwright install
Expand Down
1 change: 0 additions & 1 deletion benchmark/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from time import time

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
sys.path.append(str(Path(__file__).resolve().parents[1] / "pyodide-test-runner"))

from pyodide_test_runner import ( # noqa: E402
SeleniumChromeWrapper,
Expand Down
79 changes: 0 additions & 79 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,24 @@

import pytest

pytest_plugins = ("pytest_asyncio",)

ROOT_PATH = pathlib.Path(__file__).parents[0].resolve()
DIST_PATH = ROOT_PATH / "dist"

sys.path.append(str(ROOT_PATH / "pyodide-test-runner"))
sys.path.append(str(ROOT_PATH / "pyodide-build"))
sys.path.append(str(ROOT_PATH / "src" / "py"))

from pyodide_test_runner.fixture import ( # noqa: F401
console_html_fixture,
playwright_browsers,
script_type,
selenium,
selenium_common,
selenium_context_manager,
selenium_esm,
selenium_module_scope,
selenium_standalone,
selenium_standalone_noload,
selenium_standalone_noload_common,
selenium_webworker_standalone,
web_server_main,
web_server_secondary,
)
from pyodide_test_runner.utils import maybe_skip_test
from pyodide_test_runner.utils import package_is_built as _package_is_built
from pyodide_test_runner.utils import parse_xfail_browsers


def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption(
"--dist-dir",
action="store",
default=DIST_PATH,
help="Path to the dist directory",
)
group.addoption(
"--run-xfail",
action="store_true",
help="If provided, tests marked as xfail will be run",
)
group.addoption(
"--runner",
default="selenium",
choices=["selenium", "playwright"],
help="Select testing frameworks, selenium or playwright (default: %(default)s)",
)


def pytest_configure(config):
Expand Down Expand Up @@ -165,51 +134,3 @@ def extra_checks_test_wrapper(browser, trace_hiwire_refs, trace_pyproxies):

def package_is_built(package_name):
return _package_is_built(package_name, pytest.pyodide_dist_dir)


import ast
from copy import deepcopy
from typing import Any

from _pytest.assertion.rewrite import AssertionRewritingHook, rewrite_asserts
from _pytest.python import (
pytest_pycollect_makemodule as orig_pytest_pycollect_makemodule,
)

# Handling for pytest assertion rewrites
# First we find the pytest rewrite config. It's an attribute of the pytest
# assertion rewriting meta_path_finder, so we locate that to get the config.


def _get_pytest_rewrite_config() -> Any:
for meta_path_finder in sys.meta_path:
if isinstance(meta_path_finder, AssertionRewritingHook):
break
else:
return None
return meta_path_finder.config


# Now we need to parse the ast of the files, rewrite the ast, and store the
# original and rewritten ast into dictionaries. `run_in_pyodide` will look the
# ast up in the appropriate dictionary depending on whether or not it is using
# pytest assert rewrites.

REWRITE_CONFIG = _get_pytest_rewrite_config()
del _get_pytest_rewrite_config

ORIGINAL_MODULE_ASTS: dict[str, ast.Module] = {}
REWRITTEN_MODULE_ASTS: dict[str, ast.Module] = {}


def pytest_pycollect_makemodule(
module_path: pathlib.Path, path: Any, parent: Any
) -> None:
source = module_path.read_bytes()
strfn = str(module_path)
tree = ast.parse(source, filename=strfn)
ORIGINAL_MODULE_ASTS[strfn] = tree
tree2 = deepcopy(tree)
rewrite_asserts(tree2, source, strfn, REWRITE_CONFIG)
REWRITTEN_MODULE_ASTS[strfn] = tree2
orig_pytest_pycollect_makemodule(module_path, parent)
1 change: 1 addition & 0 deletions pyodide-test-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# pyodide-test-runner
4 changes: 4 additions & 0 deletions pyodide-test-runner/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pytest_plugins = [
"pyodide_test_runner.hook",
"pyodide_test_runner.fixture",
]
10 changes: 4 additions & 6 deletions pyodide-test-runner/pyodide_test_runner/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
from copy import deepcopy
from typing import Any, Callable, Collection

from pyodide_test_runner.utils import package_is_built as _package_is_built
import pytest

from .hook import ORIGINAL_MODULE_ASTS, REWRITTEN_MODULE_ASTS
from .utils import package_is_built as _package_is_built


def package_is_built(package_name):
return _package_is_built(package_name, pytest.pyodide_dist_dir)


import pytest


class SeleniumType:
JavascriptException: type
browser: str
Expand Down Expand Up @@ -152,8 +152,6 @@ def __init__(
when an assertion fails, but requires us to load pytest.
"""

from conftest import ORIGINAL_MODULE_ASTS, REWRITTEN_MODULE_ASTS

self._pkgs = list(packages)
self._pytest_not_built = False
if (
Expand Down
4 changes: 3 additions & 1 deletion pyodide-test-runner/pyodide_test_runner/fixture.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import contextlib
import os
from pathlib import Path

import pytest

Expand Down Expand Up @@ -80,7 +82,7 @@ def selenium_common(
f"Unknown runner or browser: {runner_type} / {request.param}"
)

dist_dir = request.config.getoption("--dist-dir")
dist_dir = Path(os.getcwd(), request.config.getoption("--dist-dir"))
runner = cls(
server_port=server_port,
server_hostname=server_hostname,
Expand Down
91 changes: 91 additions & 0 deletions pyodide-test-runner/pyodide_test_runner/hook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import ast
import sys
from copy import deepcopy
from pathlib import Path
from typing import Any

import pytest
from _pytest.assertion.rewrite import AssertionRewritingHook, rewrite_asserts
from _pytest.python import (
pytest_pycollect_makemodule as orig_pytest_pycollect_makemodule,
)


def pytest_configure(config):

config.addinivalue_line(
"markers",
"skip_refcount_check: Don't run refcount checks",
)

config.addinivalue_line(
"markers",
"skip_pyproxy_check: Don't run pyproxy allocation checks",
)

config.addinivalue_line(
"markers",
"driver_timeout: Set script timeout in WebDriver",
)

config.addinivalue_line(
"markers",
"xfail_browsers: xfail a test in specific browsers",
)

pytest.pyodide_dist_dir = config.getoption("--dist-dir")


@pytest.hookimpl(tryfirst=True)
def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption(
"--dist-dir",
action="store",
default="pyodide",
help="Path to the pyodide dist directory",
type=Path,
)
group.addoption(
"--runner",
default="selenium",
choices=["selenium", "playwright"],
help="Select testing frameworks, selenium or playwright (default: %(default)s)",
)


# Handling for pytest assertion rewrites
# First we find the pytest rewrite config. It's an attribute of the pytest
# assertion rewriting meta_path_finder, so we locate that to get the config.


def _get_pytest_rewrite_config() -> Any:
for meta_path_finder in sys.meta_path:
if isinstance(meta_path_finder, AssertionRewritingHook):
break
else:
return None
return meta_path_finder.config


# Now we need to parse the ast of the files, rewrite the ast, and store the
# original and rewritten ast into dictionaries. `run_in_pyodide` will look the
# ast up in the appropriate dictionary depending on whether or not it is using
# pytest assert rewrites.

REWRITE_CONFIG = _get_pytest_rewrite_config()
del _get_pytest_rewrite_config

ORIGINAL_MODULE_ASTS: dict[str, ast.Module] = {}
REWRITTEN_MODULE_ASTS: dict[str, ast.Module] = {}


def pytest_pycollect_makemodule(module_path: Path, path: Any, parent: Any) -> None:
source = module_path.read_bytes()
strfn = str(module_path)
tree = ast.parse(source, filename=strfn)
ORIGINAL_MODULE_ASTS[strfn] = tree
tree2 = deepcopy(tree)
rewrite_asserts(tree2, source, strfn, REWRITE_CONFIG)
REWRITTEN_MODULE_ASTS[strfn] = tree2
orig_pytest_pycollect_makemodule(module_path, parent)
3 changes: 0 additions & 3 deletions pyodide-test-runner/pyodide_test_runner/node_test_driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const readline = require("readline");
const path = require("path");
const util = require("util");
const node_fetch = require("node-fetch");
const base64 = require("base-64");

let baseUrl = process.argv[2];
let distDir = process.argv[3];
Expand All @@ -26,8 +25,6 @@ const context = {
TextDecoder: util.TextDecoder,
TextEncoder: util.TextEncoder,
URL,
atob: base64.decode,
btoa: base64.encode,
clearInterval,
clearTimeout,
setInterval,
Expand Down
Loading

0 comments on commit 7d7b7e8

Please sign in to comment.