diff options
Diffstat (limited to 'tests/pyodide_testrunner/run.py')
-rw-r--r-- | tests/pyodide_testrunner/run.py | 131 |
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()) |