summaryrefslogtreecommitdiffstats
path: root/tests/pyodide_testrunner/run.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/pyodide_testrunner/run.py')
-rw-r--r--tests/pyodide_testrunner/run.py131
1 files changed, 131 insertions, 0 deletions
diff --git a/tests/pyodide_testrunner/run.py b/tests/pyodide_testrunner/run.py
new file mode 100644
index 0000000..7b56374
--- /dev/null
+++ b/tests/pyodide_testrunner/run.py
@@ -0,0 +1,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())