chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

184
vendor/sdl-3.2.10/test/emscripten/driver.py vendored Executable file
View file

@ -0,0 +1,184 @@
#!/usr/bin/env python
import argparse
import contextlib
import logging
import os
import pathlib
import shlex
import sys
import time
from typing import Optional
import urllib.parse
from selenium import webdriver
import selenium.common.exceptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
logger = logging.getLogger(__name__)
class SDLSeleniumTestDriver:
def __init__(self, server: str, test: str, arguments: list[str], browser: str, firefox_binary: Optional[str]=None, chrome_binary: Optional[str]=None):
self. server = server
self.test = test
self.arguments = arguments
self.chrome_binary = chrome_binary
self.firefox_binary = firefox_binary
self.driver = None
self.stdout_printed = False
self.failed_messages: list[str] = []
self.return_code = None
options = [
"--headless",
]
driver_contructor = None
match browser:
case "firefox":
driver_contructor = webdriver.Firefox
driver_options = webdriver.FirefoxOptions()
if self.firefox_binary:
driver_options.binary_location = self.firefox_binary
case "chrome":
driver_contructor = webdriver.Chrome
driver_options = webdriver.ChromeOptions()
if self.chrome_binary:
driver_options.binary_location = self.chrome_binary
options.append("--no-sandbox")
if driver_contructor is None:
raise ValueError(f"Invalid {browser=}")
for o in options:
driver_options.add_argument(o)
logger.debug("About to create driver")
self.driver = driver_contructor(options=driver_options)
@property
def finished(self):
return len(self.failed_messages) > 0 or self.return_code is not None
def __del__(self):
if self.driver:
self.driver.quit()
@property
def url(self):
req = {
"loghtml": "1",
"SDL_ASSERT": "abort",
}
for key, value in os.environ.items():
if key.startswith("SDL_"):
req[key] = value
req.update({f"arg_{i}": a for i, a in enumerate(self.arguments, 1) })
req_str = urllib.parse.urlencode(req)
return f"{self.server}/{self.test}.html?{req_str}"
@contextlib.contextmanager
def _selenium_catcher(self):
try:
yield
success = True
except selenium.common.exceptions.UnexpectedAlertPresentException as e:
# FIXME: switch context, verify text of dialog and answer "a" for abort
wait = WebDriverWait(self.driver, timeout=2)
try:
alert = wait.until(lambda d: d.switch_to.alert)
except selenium.common.exceptions.NoAlertPresentException:
self.failed_messages.append(e.msg)
return False
self.failed_messages.append(alert)
if "Assertion failure" in e.msg and "[ariA]" in e.msg:
alert.send_keys("a")
alert.accept()
else:
self.failed_messages.append(e.msg)
success = False
return success
def get_stdout_and_print(self):
if self.stdout_printed:
return
with self._selenium_catcher():
div_terminal = self.driver.find_element(by=By.ID, value="terminal")
assert div_terminal
text = div_terminal.text
print(text)
self.stdout_printed = True
def update_return_code(self):
with self._selenium_catcher():
div_process_quit = self.driver.find_element(by=By.ID, value="process-quit")
if not div_process_quit:
return
if div_process_quit.text != "":
try:
self.return_code = int(div_process_quit.text)
except ValueError:
raise ValueError(f"process-quit element contains invalid data: {div_process_quit.text:r}")
def loop(self):
print(f"Connecting to \"{self.url}\"", file=sys.stderr)
self.driver.get(url=self.url)
self.driver.implicitly_wait(0.2)
while True:
self.update_return_code()
if self.finished:
break
time.sleep(0.1)
self.get_stdout_and_print()
if not self.stdout_printed:
self.failed_messages.append("Failed to get stdout/stderr")
def main() -> int:
parser = argparse.ArgumentParser(allow_abbrev=False, description="Selenium SDL test driver")
parser.add_argument("--browser", default="firefox", choices=["firefox", "chrome"], help="browser")
parser.add_argument("--server", default="http://localhost:8080", help="Server where SDL tests live")
parser.add_argument("--verbose", action="store_true", help="Verbose logging")
parser.add_argument("--chrome-binary", help="Chrome binary")
parser.add_argument("--firefox-binary", help="Firefox binary")
index_double_dash = sys.argv.index("--")
if index_double_dash < 0:
parser.error("Missing test arguments. Need -- <FILENAME> <ARGUMENTS>")
driver_arguments = sys.argv[1:index_double_dash]
test = pathlib.Path(sys.argv[index_double_dash+1]).name
test_arguments = sys.argv[index_double_dash+2:]
args = parser.parse_args(args=driver_arguments)
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
logger.debug("driver_arguments=%r test=%r test_arguments=%r", driver_arguments, test, test_arguments)
sdl_test_driver = SDLSeleniumTestDriver(
server=args.server,
test=test,
arguments=test_arguments,
browser=args.browser,
chrome_binary=args.chrome_binary,
firefox_binary=args.firefox_binary,
)
sdl_test_driver.loop()
rc = sdl_test_driver.return_code
if sdl_test_driver.failed_messages:
for msg in sdl_test_driver.failed_messages:
print(f"FAILURE MESSAGE: {msg}", file=sys.stderr)
if rc == 0:
print(f"Test signaled success (rc=0) but a failure happened", file=sys.stderr)
rc = 1
sys.stdout.flush()
logger.info("Exit code = %d", rc)
return rc
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,25 @@
Module['arguments'] = ['0'];
//Gamepads don't appear until a button is pressed and the joystick/gamepad tests expect one to be connected
Module['preRun'].push(function()
{
Module['print']("Waiting for gamepad...");
Module['addRunDependency']("gamepad");
window.addEventListener('gamepadconnected', function()
{
//OK, got one
Module['removeRunDependency']("gamepad");
}, false);
//chrome
if(!!navigator.webkitGetGamepads)
{
var timeout = function()
{
if(navigator.webkitGetGamepads()[0] !== undefined)
Module['removeRunDependency']("gamepad");
else
setTimeout(timeout, 100);
}
setTimeout(timeout, 100);
}
});

View file

@ -0,0 +1,54 @@
const searchParams = new URLSearchParams(window.location.search);
Module.preRun = () => {
};
const arguments = [];
for (let i = 1; true; i++) {
const arg_i = searchParams.get(`arg_${i}`);
if (arg_i == null) {
break;
}
arguments.push(arg_i);
}
Module.arguments = arguments;
if (searchParams.get("loghtml") === "1") {
const divTerm = document.createElement("div");
divTerm.id = "terminal";
document.body.append(divTerm);
function printToStdOut(msg, id) {
const divMsg = document.createElement("div", {class: "stdout"});
divMsg.id = id;
divMsg.append(document.createTextNode(msg));
divTerm.append(divMsg);
return divMsg;
}
Module.print = (msg) => {
console.log(msg);
printToStdOut(msg, "stdout");
}
Module.printErr = (msg) => {
console.error(msg);
const e = printToStdOut(msg, "stderr");
e.style = "color:red";
}
const divQuit = document.createElement("div");
divQuit.id = "process-quit";
document.body.append(divQuit);
Module.quit = (msg) => {
divQuit.innerText = msg;
console.log(`QUIT: ${msg}`)
}
Module.onabort = (msg) => {
printToStdOut(`ABORT: ${msg}`, "stderr");
console.log(`ABORT: ${msg}`);
}
}

102
vendor/sdl-3.2.10/test/emscripten/server.py vendored Executable file
View file

@ -0,0 +1,102 @@
#!/usr/bin/env python
# Based on http/server.py from Python
from argparse import ArgumentParser
import contextlib
from http.server import SimpleHTTPRequestHandler
from http.server import ThreadingHTTPServer
import os
import socket
class MyHTTPRequestHandler(SimpleHTTPRequestHandler):
extensions_map = {
".manifest": "text/cache-manifest",
".html": "text/html",
".png": "image/png",
".jpg": "image/jpg",
".svg": "image/svg+xml",
".css": "text/css",
".js": "application/x-javascript",
".wasm": "application/wasm",
"": "application/octet-stream",
}
def __init__(self, *args, maps=None, **kwargs):
self.maps = maps or []
SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
def end_headers(self):
self.send_my_headers()
SimpleHTTPRequestHandler.end_headers(self)
def send_my_headers(self):
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
self.send_header("Pragma", "no-cache")
self.send_header("Expires", "0")
def translate_path(self, path):
for map_path, map_prefix in self.maps:
if path.startswith(map_prefix):
res = os.path.join(map_path, path.removeprefix(map_prefix).lstrip("/"))
break
else:
res = super().translate_path(path)
return res
def serve_forever(port: int, ServerClass):
handler = MyHTTPRequestHandler
addr = ("0.0.0.0", port)
with ServerClass(addr, handler) as httpd:
host, port = httpd.socket.getsockname()[:2]
url_host = f"[{host}]" if ":" in host else host
print(f"Serving HTTP on {host} port {port} (http://{url_host}:{port}/) ...")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nKeyboard interrupt received, exiting.")
return 0
def main():
parser = ArgumentParser(allow_abbrev=False)
parser.add_argument("port", nargs="?", type=int, default=8080)
parser.add_argument("-d", dest="directory", type=str, default=None)
parser.add_argument("--map", dest="maps", nargs="+", type=str, help="Mappings, used as e.g. \"$HOME/projects/SDL:/sdl\"")
args = parser.parse_args()
maps = []
for m in args.maps:
try:
path, uri = m.split(":", 1)
except ValueError:
parser.error(f"Invalid mapping: \"{m}\"")
maps.append((path, uri))
class DualStackServer(ThreadingHTTPServer):
def server_bind(self):
# suppress exception when protocol is IPv4
with contextlib.suppress(Exception):
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
return super().server_bind()
def finish_request(self, request, client_address):
self.RequestHandlerClass(
request,
client_address,
self,
directory=args.directory,
maps=maps,
)
return serve_forever(
port=args.port,
ServerClass=DualStackServer,
)
if __name__ == "__main__":
raise SystemExit(main())