diff options
Diffstat (limited to 'contrib/SDL-3.2.8/test/emscripten/driver.py')
| -rwxr-xr-x | contrib/SDL-3.2.8/test/emscripten/driver.py | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/test/emscripten/driver.py b/contrib/SDL-3.2.8/test/emscripten/driver.py new file mode 100755 index 0000000..ee91610 --- /dev/null +++ b/contrib/SDL-3.2.8/test/emscripten/driver.py | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | import argparse | ||
| 4 | import contextlib | ||
| 5 | import logging | ||
| 6 | import os | ||
| 7 | import pathlib | ||
| 8 | import shlex | ||
| 9 | import sys | ||
| 10 | import time | ||
| 11 | from typing import Optional | ||
| 12 | import urllib.parse | ||
| 13 | |||
| 14 | from selenium import webdriver | ||
| 15 | import selenium.common.exceptions | ||
| 16 | from selenium.webdriver.common.by import By | ||
| 17 | from selenium.webdriver.support.ui import WebDriverWait | ||
| 18 | |||
| 19 | |||
| 20 | logger = logging.getLogger(__name__) | ||
| 21 | |||
| 22 | |||
| 23 | class SDLSeleniumTestDriver: | ||
| 24 | def __init__(self, server: str, test: str, arguments: list[str], browser: str, firefox_binary: Optional[str]=None, chrome_binary: Optional[str]=None): | ||
| 25 | self. server = server | ||
| 26 | self.test = test | ||
| 27 | self.arguments = arguments | ||
| 28 | self.chrome_binary = chrome_binary | ||
| 29 | self.firefox_binary = firefox_binary | ||
| 30 | self.driver = None | ||
| 31 | self.stdout_printed = False | ||
| 32 | self.failed_messages: list[str] = [] | ||
| 33 | self.return_code = None | ||
| 34 | |||
| 35 | options = [ | ||
| 36 | "--headless", | ||
| 37 | ] | ||
| 38 | |||
| 39 | driver_contructor = None | ||
| 40 | match browser: | ||
| 41 | case "firefox": | ||
| 42 | driver_contructor = webdriver.Firefox | ||
| 43 | driver_options = webdriver.FirefoxOptions() | ||
| 44 | if self.firefox_binary: | ||
| 45 | driver_options.binary_location = self.firefox_binary | ||
| 46 | case "chrome": | ||
| 47 | driver_contructor = webdriver.Chrome | ||
| 48 | driver_options = webdriver.ChromeOptions() | ||
| 49 | if self.chrome_binary: | ||
| 50 | driver_options.binary_location = self.chrome_binary | ||
| 51 | options.append("--no-sandbox") | ||
| 52 | if driver_contructor is None: | ||
| 53 | raise ValueError(f"Invalid {browser=}") | ||
| 54 | for o in options: | ||
| 55 | driver_options.add_argument(o) | ||
| 56 | logger.debug("About to create driver") | ||
| 57 | self.driver = driver_contructor(options=driver_options) | ||
| 58 | |||
| 59 | @property | ||
| 60 | def finished(self): | ||
| 61 | return len(self.failed_messages) > 0 or self.return_code is not None | ||
| 62 | |||
| 63 | def __del__(self): | ||
| 64 | if self.driver: | ||
| 65 | self.driver.quit() | ||
| 66 | |||
| 67 | @property | ||
| 68 | def url(self): | ||
| 69 | req = { | ||
| 70 | "loghtml": "1", | ||
| 71 | "SDL_ASSERT": "abort", | ||
| 72 | } | ||
| 73 | for key, value in os.environ.items(): | ||
| 74 | if key.startswith("SDL_"): | ||
| 75 | req[key] = value | ||
| 76 | req.update({f"arg_{i}": a for i, a in enumerate(self.arguments, 1) }) | ||
| 77 | req_str = urllib.parse.urlencode(req) | ||
| 78 | return f"{self.server}/{self.test}.html?{req_str}" | ||
| 79 | |||
| 80 | @contextlib.contextmanager | ||
| 81 | def _selenium_catcher(self): | ||
| 82 | try: | ||
| 83 | yield | ||
| 84 | success = True | ||
| 85 | except selenium.common.exceptions.UnexpectedAlertPresentException as e: | ||
| 86 | # FIXME: switch context, verify text of dialog and answer "a" for abort | ||
| 87 | wait = WebDriverWait(self.driver, timeout=2) | ||
| 88 | try: | ||
| 89 | alert = wait.until(lambda d: d.switch_to.alert) | ||
| 90 | except selenium.common.exceptions.NoAlertPresentException: | ||
| 91 | self.failed_messages.append(e.msg) | ||
| 92 | return False | ||
| 93 | self.failed_messages.append(alert) | ||
| 94 | if "Assertion failure" in e.msg and "[ariA]" in e.msg: | ||
| 95 | alert.send_keys("a") | ||
| 96 | alert.accept() | ||
| 97 | else: | ||
| 98 | self.failed_messages.append(e.msg) | ||
| 99 | success = False | ||
| 100 | return success | ||
| 101 | |||
| 102 | def get_stdout_and_print(self): | ||
| 103 | if self.stdout_printed: | ||
| 104 | return | ||
| 105 | with self._selenium_catcher(): | ||
| 106 | div_terminal = self.driver.find_element(by=By.ID, value="terminal") | ||
| 107 | assert div_terminal | ||
| 108 | text = div_terminal.text | ||
| 109 | print(text) | ||
| 110 | self.stdout_printed = True | ||
| 111 | |||
| 112 | def update_return_code(self): | ||
| 113 | with self._selenium_catcher(): | ||
| 114 | div_process_quit = self.driver.find_element(by=By.ID, value="process-quit") | ||
| 115 | if not div_process_quit: | ||
| 116 | return | ||
| 117 | if div_process_quit.text != "": | ||
| 118 | try: | ||
| 119 | self.return_code = int(div_process_quit.text) | ||
| 120 | except ValueError: | ||
| 121 | raise ValueError(f"process-quit element contains invalid data: {div_process_quit.text:r}") | ||
| 122 | |||
| 123 | def loop(self): | ||
| 124 | print(f"Connecting to \"{self.url}\"", file=sys.stderr) | ||
| 125 | self.driver.get(url=self.url) | ||
| 126 | self.driver.implicitly_wait(0.2) | ||
| 127 | |||
| 128 | while True: | ||
| 129 | self.update_return_code() | ||
| 130 | if self.finished: | ||
| 131 | break | ||
| 132 | time.sleep(0.1) | ||
| 133 | |||
| 134 | self.get_stdout_and_print() | ||
| 135 | if not self.stdout_printed: | ||
| 136 | self.failed_messages.append("Failed to get stdout/stderr") | ||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | def main() -> int: | ||
| 141 | parser = argparse.ArgumentParser(allow_abbrev=False, description="Selenium SDL test driver") | ||
| 142 | parser.add_argument("--browser", default="firefox", choices=["firefox", "chrome"], help="browser") | ||
| 143 | parser.add_argument("--server", default="http://localhost:8080", help="Server where SDL tests live") | ||
| 144 | parser.add_argument("--verbose", action="store_true", help="Verbose logging") | ||
| 145 | parser.add_argument("--chrome-binary", help="Chrome binary") | ||
| 146 | parser.add_argument("--firefox-binary", help="Firefox binary") | ||
| 147 | |||
| 148 | index_double_dash = sys.argv.index("--") | ||
| 149 | if index_double_dash < 0: | ||
| 150 | parser.error("Missing test arguments. Need -- <FILENAME> <ARGUMENTS>") | ||
| 151 | driver_arguments = sys.argv[1:index_double_dash] | ||
| 152 | test = pathlib.Path(sys.argv[index_double_dash+1]).name | ||
| 153 | test_arguments = sys.argv[index_double_dash+2:] | ||
| 154 | |||
| 155 | args = parser.parse_args(args=driver_arguments) | ||
| 156 | |||
| 157 | logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) | ||
| 158 | |||
| 159 | logger.debug("driver_arguments=%r test=%r test_arguments=%r", driver_arguments, test, test_arguments) | ||
| 160 | |||
| 161 | sdl_test_driver = SDLSeleniumTestDriver( | ||
| 162 | server=args.server, | ||
| 163 | test=test, | ||
| 164 | arguments=test_arguments, | ||
| 165 | browser=args.browser, | ||
| 166 | chrome_binary=args.chrome_binary, | ||
| 167 | firefox_binary=args.firefox_binary, | ||
| 168 | ) | ||
| 169 | sdl_test_driver.loop() | ||
| 170 | |||
| 171 | rc = sdl_test_driver.return_code | ||
| 172 | if sdl_test_driver.failed_messages: | ||
| 173 | for msg in sdl_test_driver.failed_messages: | ||
| 174 | print(f"FAILURE MESSAGE: {msg}", file=sys.stderr) | ||
| 175 | if rc == 0: | ||
| 176 | print(f"Test signaled success (rc=0) but a failure happened", file=sys.stderr) | ||
| 177 | rc = 1 | ||
| 178 | sys.stdout.flush() | ||
| 179 | logger.info("Exit code = %d", rc) | ||
| 180 | return rc | ||
| 181 | |||
| 182 | |||
| 183 | if __name__ == "__main__": | ||
| 184 | raise SystemExit(main()) | ||
