Atlas - driver.py

Home / ext / SDL / test / emscripten Lines: 1 | Size: 6440 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1#!/usr/bin/env python 2 3import argparse 4import contextlib 5import logging 6import os 7import pathlib 8import shlex 9import sys 10import time 11from typing import Optional 12import urllib.parse 13 14from selenium import webdriver 15import selenium.common.exceptions 16from selenium.webdriver.common.by import By 17from selenium.webdriver.support.ui import WebDriverWait 18 19 20logger = logging.getLogger(__name__) 21 22 23class 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 140def 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 183if __name__ == "__main__": 184 raise SystemExit(main()) 185
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.