mirror of
https://github.com/mihawk-99/PS5_RetroArch.git
synced 2026-10-18 23:09:29 -05:00
The host tests found a free port by binding port 0 and closing it, then started the WebUI's server on it. In between, any connection on the machine can take that port as its own (the same ephemeral range): the server then exits at once, "Failed to bind to port N: Address already in use", and the test waited out its window and failed with "the server did not start" (test_scraper, test_scraper_launchbox) or "HTTP server exited" (test_webui). It happened in about one gate run in three, under JOBS=4 or not. tests/webui_build.py start() starts the server, waits for its status and returns the process, port and session token; a server that exits before answering is started again on another port (five tries). Every harness that started the server itself uses it: test_scraper, test_scraper_launchbox, test_scraper_screenscraper, test_webui (its start and two restarts), test_webui_language, test_webui_transfer_browser. test_scraper's free_port() had no other user and is gone. Evidence: - the cause: 40 sequential starts with each server's stderr kept: 1 failed, "Failed to bind to port 44505: Address already in use" (exit 1, after 0.02 s); through start(), 40 of 40 started. - python3 -m unittest tests.test_scraper tests.test_scraper_launchbox tests.test_scraper_screenscraper: OK; tests.test_webui tests.test_webui_language tests.test_webui_transfer_browser: 21 tests OK. - gates on the tree with the next commit: format, unit (210 tests), build, integration (210 tests), evidence - PASS.
111 lines
5.7 KiB
Python
111 lines
5.7 KiB
Python
"""The WebUI's language: the catalogs (tools/webui-strings.py), the choice kept on the
|
|
console (/api/preferences) and the page in Chromium (tests/webui_language_browser.cjs)."""
|
|
from pathlib import Path
|
|
import http.client
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import webui_build # noqa: E402
|
|
from test_webui_transfer_browser import PLAYWRIGHT # noqa: E402
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
LANGUAGES = ['en', 'es', 'fr', 'de', 'it', 'pt-BR', 'ru', 'ja', 'ko', 'zh-CN', 'zh-TW', 'vi', 'tr', 'pl', 'nl', 'id']
|
|
|
|
|
|
class Catalogs(unittest.TestCase):
|
|
def test_every_language_has_every_string(self):
|
|
run = subprocess.run([sys.executable, 'tools/webui-strings.py', '--check'], cwd=ROOT, capture_output=True, text=True)
|
|
self.assertEqual(run.returncode, 0, run.stdout + run.stderr)
|
|
catalogs = sorted(p.stem for p in (ROOT / 'webui/i18n').glob('*.json') if p.stem != 'strings')
|
|
self.assertEqual(catalogs, sorted(LANGUAGES[1:]))
|
|
script = (ROOT / 'webui/i18n.js').read_text()
|
|
for code in LANGUAGES:
|
|
self.assertIn(f"['{code}',", script)
|
|
flags = {'en': 'us', 'es': 'es', 'fr': 'fr', 'de': 'de', 'it': 'it', 'pt-BR': 'br', 'ru': 'ru', 'ja': 'jp', 'ko': 'kr',
|
|
'zh-CN': 'cn', 'zh-TW': 'tw', 'vi': 'vn', 'tr': 'tr', 'pl': 'pl', 'nl': 'nl', 'id': 'id'}
|
|
source = json.loads((ROOT / 'webui/assets/flags/SOURCE.json').read_text())
|
|
for code, flag in flags.items():
|
|
self.assertTrue((ROOT / f'webui/assets/flags/{flag}.svg').is_file(), flag)
|
|
self.assertIn(flag, source['icons'])
|
|
|
|
|
|
class Server:
|
|
def __init__(self, root):
|
|
subprocess.check_output(['bash', 'tools/build-webui-http.sh', 'host'], cwd=ROOT, text=True)
|
|
subprocess.check_output(['bash', 'tools/build-webui-update.sh', 'host'], cwd=ROOT, text=True)
|
|
self.binary = root.parent / 'server'
|
|
webui_build.build(self.binary, 'tests/webui_server_main.cpp', flags=('-O1',))
|
|
self.process, self.port, self.token = webui_build.start(self.binary, root)
|
|
|
|
def request(self, method, path, token=None):
|
|
conn = http.client.HTTPConnection('127.0.0.1', self.port, timeout=5)
|
|
conn.request(method, path, None, {'X-RetroArch-Token': self.token if token is None else token})
|
|
response = conn.getresponse(); body = response.read(); conn.close()
|
|
return response.status, body
|
|
|
|
def json(self, method, path, token=None):
|
|
status, body = self.request(method, path, token)
|
|
return status, json.loads(body)
|
|
|
|
def stop(self):
|
|
self.process.terminate(); self.process.wait(timeout=5)
|
|
|
|
|
|
class Preference(unittest.TestCase):
|
|
def test_language_is_kept_on_the_console(self):
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root = Path(temp) / 'title'
|
|
for name in ('config', 'content', 'webui'):
|
|
(root / name).mkdir(parents=True)
|
|
(root / 'webui/index.html').write_text('<!doctype html><title>RetroArch</title>')
|
|
server = Server(root)
|
|
try:
|
|
saved = root / 'config/webui-preferences.cfg'
|
|
self.assertEqual(server.json('GET', '/api/status')[1]['language'], '')
|
|
self.assertEqual(server.request('POST', '/api/preferences?language=fr', token='wrong')[0], 403)
|
|
for bad in ('evil', '', 'fr%22%0Ainjected%3D1', '../en', 'FR'):
|
|
self.assertEqual(server.request('POST', '/api/preferences?language=' + bad)[0], 400, bad)
|
|
self.assertFalse(saved.exists())
|
|
for code in ('fr', 'zh-TW', 'pt-BR'):
|
|
self.assertEqual(server.json('POST', '/api/preferences?language=' + code), (200, {'language': code}))
|
|
self.assertEqual(saved.read_text(), f'webui_language = "{code}"\n')
|
|
self.assertEqual(server.json('GET', '/api/status')[1]['language'], code)
|
|
# A value edited by hand to something unknown reads as no choice.
|
|
saved.write_text('webui_language = "xx"\n')
|
|
self.assertEqual(server.json('GET', '/api/status')[1]['language'], '')
|
|
finally:
|
|
server.stop()
|
|
|
|
|
|
@unittest.skipUnless(PLAYWRIGHT and shutil.which('node') and Path('/usr/bin/chromium').exists(), 'needs node, Playwright and Chromium')
|
|
class LanguageBrowser(unittest.TestCase):
|
|
def test_picker_translates_and_follows_the_console(self):
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root = Path(temp) / 'title'
|
|
for name in ('config', 'content', 'webui'):
|
|
(root / name).mkdir(parents=True)
|
|
shutil.copytree(ROOT / 'webui', root / 'webui', dirs_exist_ok=True)
|
|
# A name that reads like a catalog pattern ("{0} of {1}"): shown as it is.
|
|
(root / 'content/Test Disc of the Week.iso').write_bytes(b'fixture')
|
|
server = Server(root)
|
|
try:
|
|
shots = Path(os.environ.get('WEBUI_SCREENSHOTS', ROOT / 'build/tmp/webui-language'))
|
|
run = subprocess.run(['node', 'tests/webui_language_browser.cjs'], cwd=ROOT, capture_output=True, text=True, timeout=300,
|
|
env={**os.environ, 'PLAYWRIGHT_PATH': PLAYWRIGHT, 'WEBUI_TEST_URL': f'http://127.0.0.1:{server.port}',
|
|
'WEBUI_SCREENSHOTS': str(shots)})
|
|
self.assertEqual(run.returncode, 0, run.stdout + run.stderr)
|
|
print(run.stdout.strip())
|
|
self.assertEqual((root / 'config/webui-preferences.cfg').read_text(), 'webui_language = "en"\n')
|
|
finally:
|
|
server.stop()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|