Skip to content

Commit 53be1a0

Browse files
committed
Improve WASM testing and CI
1 parent 338ac4d commit 53be1a0

File tree

5 files changed

+129
-73
lines changed

5 files changed

+129
-73
lines changed

wasm/demo/src/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,9 @@ function onReady() {
148148
terminalVM = rp.vmStore.init('term_vm');
149149
terminalVM.setStdout(data => localEcho.println(data));
150150
readPrompts().catch(err => console.error(err));
151+
152+
// so that the test knows that we're ready
153+
const readyElement = document.createElement('div');
154+
readyElement.id = 'rp_loaded';
155+
document.head.appendChild(readyElement);
151156
}

wasm/tests/.travis-runner.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ export PATH=$PATH:$PWD/geckodriver
2020
pip install pipenv
2121
(cd wasm/tests; pipenv install)
2222

23-
(cd wasm/demo; npm install; npm run build; npm run ci)
23+
(cd wasm/demo; npm install; npm run test)

wasm/tests/conftest.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import subprocess
2+
import os
3+
import time
4+
import socket
5+
import atexit
6+
import pytest
7+
8+
PORT = 8080
9+
10+
HTTP_SCRIPT = f"""
11+
import mimetypes
12+
mimetypes.add_type("application/wasm", ".wasm")
13+
import http.server
14+
http.server.test(HandlerClass=http.server.SimpleHTTPRequestHandler, port={PORT})
15+
"""
16+
17+
demo_dist = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../demo/dist/")
18+
19+
server_proc = None
20+
21+
22+
def pytest_sessionstart(session):
23+
global server_proc
24+
server_proc = subprocess.Popen(
25+
["python3", "-c", HTTP_SCRIPT],
26+
cwd=demo_dist,
27+
stdout=subprocess.DEVNULL,
28+
stderr=subprocess.DEVNULL,
29+
)
30+
wait_for_port(PORT)
31+
32+
33+
def pytest_sessionfinish(session):
34+
global server_proc
35+
server_proc.terminate()
36+
server_proc = None
37+
38+
39+
atexit.register(lambda: server_proc and server_proc.terminate())
40+
41+
42+
# From https://gist.github.com/butla/2d9a4c0f35ea47b7452156c96a4e7b12
43+
def wait_for_port(port, host="0.0.0.0", timeout=5.0):
44+
"""Wait until a port starts accepting TCP connections.
45+
Args:
46+
port (int): Port number.
47+
host (str): Host address on which the port should exist.
48+
timeout (float): In seconds. How long to wait before raising errors.
49+
Raises:
50+
TimeoutError: The port isn't accepting connection after time specified in `timeout`.
51+
"""
52+
start_time = time.perf_counter()
53+
while True:
54+
try:
55+
with socket.create_connection((host, port), timeout=timeout):
56+
break
57+
except OSError as ex:
58+
time.sleep(0.01)
59+
if time.perf_counter() - start_time >= timeout:
60+
raise TimeoutError(
61+
"Waited too long for the port {} on host {} to start accepting "
62+
"connections.".format(port, host)
63+
) from ex
64+
65+
66+
from selenium import webdriver
67+
from selenium.webdriver.firefox.options import Options
68+
from selenium.webdriver.common.by import By
69+
from selenium.webdriver.support.ui import WebDriverWait
70+
from selenium.webdriver.support import expected_conditions as EC
71+
from selenium.common.exceptions import JavascriptException
72+
73+
74+
class Driver(webdriver.Firefox):
75+
def _print_panic(self):
76+
stack = driver.execute_script(
77+
"return (window.__RUSTPYTHON_ERROR_MSG || '') + '\\n' + (window.__RUSTPYTHON_ERROR_STACK || '')"
78+
)
79+
if stack.strip():
80+
print(f"RustPython panic stack:\n{stack}", file=sys.stderr)
81+
82+
def execute_script(self, *args, **kwargs):
83+
try:
84+
return super().execute_script(*args, **kwargs)
85+
except JavascriptException:
86+
self._print_panic()
87+
raise
88+
89+
90+
@pytest.fixture
91+
def wdriver(request):
92+
options = Options()
93+
options.add_argument("-headless")
94+
driver = Driver(options=options)
95+
try:
96+
driver.get(f"http://0.0.0.0:{PORT}")
97+
WebDriverWait(driver, 5).until(
98+
EC.presence_of_element_located((By.ID, "rp_loaded"))
99+
)
100+
except JavascriptException:
101+
driver._print_panic()
102+
driver.quit()
103+
raise
104+
105+
yield driver
106+
107+
driver.quit()

wasm/tests/test_demo.py

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,11 @@
1717
return output;
1818
"""
1919

20-
def print_stack(driver):
21-
stack = driver.execute_script(
22-
"return window.__RUSTPYTHON_ERROR_MSG + '\\n' + window.__RUSTPYTHON_ERROR_STACK"
23-
)
24-
print(f"RustPython error stack:\n{stack}", file=sys.stderr)
2520

26-
27-
@pytest.fixture(scope="module")
28-
def driver(request):
29-
options = Options()
30-
options.add_argument('-headless')
31-
driver = webdriver.Firefox(options=options)
32-
try:
33-
driver.get("http://localhost:8080")
34-
except Exception as e:
35-
print_stack(driver)
36-
raise
37-
time.sleep(5)
38-
yield driver
39-
driver.close()
40-
41-
42-
@pytest.mark.parametrize("script, output",
43-
[
44-
("print(5)", "5"),
45-
("a=5;b=4;print(a+b)", "9")
46-
]
21+
@pytest.mark.parametrize(
22+
"script, output", [("print(5)", "5"), ("a=5;b=4;print(a+b)", "9")]
4723
)
48-
def test_demo(driver, script, output):
49-
script = RUN_CODE_TEMPLATE.format(script)
50-
try:
51-
script_output = driver.execute_script(script)
52-
except Exception as e:
53-
print_stack(driver)
54-
raise
55-
assert script_output.strip() == output
24+
def test_demo(wdriver, script, output):
25+
script = RUN_CODE_TEMPLATE.format(script)
26+
script_output = wdriver.execute_script(script)
27+
assert script_output.strip() == output

wasm/tests/test_exec_mode.py

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,18 @@
1-
import time
2-
import sys
1+
def test_eval_mode(wdriver):
2+
assert wdriver.execute_script("return window.rp.pyEval('1+1')") == 2
33

4-
from selenium import webdriver
5-
from selenium.webdriver.firefox.options import Options
6-
import pytest
4+
def test_exec_mode(wdriver):
5+
assert wdriver.execute_script("return window.rp.pyExec('1+1')") is None
76

8-
def print_stack(driver):
9-
stack = driver.execute_script(
10-
"return window.__RUSTPYTHON_ERROR_MSG + '\\n' + window.__RUSTPYTHON_ERROR_STACK"
11-
)
12-
print(f"RustPython error stack:\n{stack}", file=sys.stderr)
13-
14-
15-
@pytest.fixture(scope="module")
16-
def driver(request):
17-
options = Options()
18-
options.add_argument('-headless')
19-
driver = webdriver.Firefox(options=options)
20-
try:
21-
driver.get("http://localhost:8080")
22-
except Exception as e:
23-
print_stack(driver)
24-
raise
25-
time.sleep(5)
26-
yield driver
27-
driver.close()
28-
29-
30-
def test_eval_mode(driver):
31-
assert driver.execute_script("return window.rp.pyEval('1+1')") == 2
32-
33-
def test_exec_mode(driver):
34-
assert driver.execute_script("return window.rp.pyExec('1+1')") is None
35-
36-
def test_exec_single_mode(driver):
37-
assert driver.execute_script("return window.rp.pyExecSingle('1+1')") == 2
38-
assert driver.execute_script(
7+
def test_exec_single_mode(wdriver):
8+
assert wdriver.execute_script("return window.rp.pyExecSingle('1+1')") == 2
9+
assert wdriver.execute_script(
3910
"""
4011
var output = [];
4112
save_output = function(text) {{
42-
output.push(text)
13+
output.push(text)
4314
}};
4415
window.rp.pyExecSingle('1+1\\n2+2',{stdout: save_output});
4516
return output;
46-
""") == ['2\n', '4\n']
17+
"""
18+
) == ["2\n", "4\n"]

0 commit comments

Comments
 (0)