1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
import os
import pathlib
import shutil
import subprocess
import sys
import tempfile
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from multiprocessing import Process, Queue
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.support import expected_conditions as EC
# Path to the root of the repo.
REPO = pathlib.Path(__file__).parent.parent.parent
BROWSERS = {
"chrome": (webdriver.Chrome, webdriver.ChromeOptions),
"firefox": (webdriver.Firefox, webdriver.FirefoxOptions),
}
def build_wheel() -> str:
"""Build a wheel package of ``pygls`` and its testsuite.
In order to test pygls under pyodide, we need to load the code for both pygls and its
testsuite. This is done by building a wheel.
To avoid messing with the repo this is all done under a temp directory.
"""
with tempfile.TemporaryDirectory() as tmpdir:
# Copy all required files.
dest = pathlib.Path(tmpdir)
# So that we don't have to fuss with packaging, copy the test suite into `pygls`
# as a sub module.
directories = [("pygls", "pygls"), ("tests", "pygls/tests")]
for src, target in directories:
shutil.copytree(REPO / src, dest / target)
files = ["pyproject.toml", "poetry.lock", "README.md", "ThirdPartyNotices.txt"]
for src in files:
shutil.copy(REPO / src, dest)
# Convert the lock file to requirements.txt.
# Ensures reproducible behavour for testing.
subprocess.run(
[
"poetry",
"export",
"-f",
"requirements.txt",
"--output",
"requirements.txt",
],
cwd=dest,
)
subprocess.run(
["poetry", "run", "pip", "install", "-r", "requirements.txt"], cwd=dest
)
# Build the wheel
subprocess.run(["poetry", "build", "--format", "wheel"], cwd=dest)
whl = list((dest / "dist").glob("*.whl"))[0]
shutil.copy(whl, REPO / "tests/pyodide_testrunner")
return whl.name
def spawn_http_server(q: Queue, directory: str):
"""A http server is needed to serve the files to the browser."""
handler_class = partial(SimpleHTTPRequestHandler, directory=directory)
server = ThreadingHTTPServer(("localhost", 0), handler_class)
q.put(server.server_port)
server.serve_forever()
def main():
exit_code = 1
whl = build_wheel()
q = Queue()
server_process = Process(
target=spawn_http_server,
args=(q, REPO / "tests/pyodide_testrunner"),
daemon=True,
)
server_process.start()
port = q.get()
print("Running tests...")
try:
driver_cls, options_cls = BROWSERS[os.environ.get("BROWSER", "chrome")]
options = options_cls()
if "CI" in os.environ:
options.binary_location = "/usr/bin/google-chrome"
options.add_argument("--headless")
driver = driver_cls(options=options)
driver.get(f"http://localhost:{port}?whl={whl}")
wait = WebDriverWait(driver, 120)
try:
button = wait.until(EC.element_to_be_clickable((By.ID, "exit-code")))
exit_code = int(button.text)
except WebDriverException as e:
print(f"Error while running test: {e!r}")
exit_code = 1
console = driver.find_element(By.ID, "console")
print(console.text)
finally:
if hasattr(server_process, "kill"):
server_process.kill()
else:
server_process.terminate()
return exit_code
if __name__ == "__main__":
sys.exit(main())
|