diff options
author | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
commit | 6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch) | |
tree | 34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/emscripten/driver.py | |
parent | 8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff) |
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/emscripten/driver.py')
-rwxr-xr-x | src/contrib/SDL-3.2.20/test/emscripten/driver.py | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/emscripten/driver.py b/src/contrib/SDL-3.2.20/test/emscripten/driver.py new file mode 100755 index 0000000..ee91610 --- /dev/null +++ b/src/contrib/SDL-3.2.20/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()) | ||