Added setup guide and launch tools
This commit is contained in:
1 parent
5c1739c6bf
commit
27b12beff8
15 files changed
+612
-5
No files matched your search
@@ -0,0 +1,98 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
spec = importlib.util.spec_from_file_location('arcade_launch', ROOT / 'tools/launch/launch.py')
|
||||
launch = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(launch)
|
||||
|
||||
|
||||
class LaunchTests(unittest.TestCase):
|
||||
def config(self, folder, gpio='false'):
|
||||
filename = Path(folder) / 'launcher.ini'
|
||||
filename.write_text(f'''[controller]
|
||||
operator_token = operator-test-token-1234
|
||||
cabinet_tokens = {json.dumps({'cabinet_01':'cabinet-test-token-5678'})}
|
||||
catalog = {ROOT / 'tools/room_controller/catalog.json'}
|
||||
database = state/test.sqlite3
|
||||
[cabinet]
|
||||
executable = {sys.executable}
|
||||
id = cabinet_01
|
||||
gpio = {gpio}
|
||||
''')
|
||||
return filename
|
||||
|
||||
def test_combined_plan_and_relative_paths(self):
|
||||
with tempfile.TemporaryDirectory() as folder, patch.dict(os.environ, {}, clear=True):
|
||||
jobs, logs = launch.plan(self.config(folder), 'all')
|
||||
self.assertEqual([j['name'] for j in jobs], ['controller', 'cabinet'])
|
||||
self.assertEqual(jobs[1]['env']['ARCADE_CABINET_TOKEN'], 'cabinet-test-token-5678')
|
||||
self.assertEqual(jobs[1]['env']['ARCADE_ROOM_URL'], 'http://127.0.0.1:8766')
|
||||
self.assertEqual(logs, Path(folder) / 'logs')
|
||||
self.assertNotIn('cabinet-test-token-5678', ' '.join(jobs[1]['command']))
|
||||
self.assertNotIn('--gpio=real', jobs[1]['command'])
|
||||
|
||||
def test_cabinet_preserves_godot_config_fallback(self):
|
||||
with tempfile.TemporaryDirectory() as folder, patch.dict(os.environ, {}, clear=True):
|
||||
jobs, _ = launch.plan(self.config(folder), 'cabinet')
|
||||
self.assertNotIn('ARCADE_CABINET_TOKEN', jobs[0]['env'])
|
||||
self.assertNotIn('ARCADE_ROOM_URL', jobs[0]['env'])
|
||||
|
||||
def test_mismatched_combined_credentials_rejected(self):
|
||||
with tempfile.TemporaryDirectory() as folder, patch.dict(os.environ, {'ARCADE_CABINET_TOKEN':'different-token-1234'}, clear=True):
|
||||
with self.assertRaisesRegex(ValueError, 'does not match'):
|
||||
launch.plan(self.config(folder), 'all')
|
||||
|
||||
def test_real_gpio_rejected_on_windows(self):
|
||||
with tempfile.TemporaryDirectory() as folder, patch.dict(os.environ, {}, clear=True), patch.object(sys, 'platform', 'win32'):
|
||||
with self.assertRaisesRegex(ValueError, 'Linux Pi'):
|
||||
launch.plan(self.config(folder, 'true'), 'cabinet')
|
||||
|
||||
def test_pi_plan_starts_bridge_before_real_game(self):
|
||||
with tempfile.TemporaryDirectory() as folder, patch.dict(os.environ, {}, clear=True), patch.object(sys, 'platform', 'linux'), patch.object(launch.importlib.util, 'find_spec', return_value=object()):
|
||||
jobs, _ = launch.plan(self.config(folder, 'true'), 'cabinet')
|
||||
self.assertEqual([j['name'] for j in jobs], ['gpio', 'cabinet'])
|
||||
self.assertEqual(jobs[0]['env']['GPIOZERO_PIN_FACTORY'], 'lgpio')
|
||||
self.assertIn('cabinet_01_gpio', jobs[0]['command'])
|
||||
self.assertIn('--gpio=real', jobs[1]['command'])
|
||||
self.assertEqual(jobs[0]['port'], ('127.0.0.1', 8765))
|
||||
|
||||
def test_occupied_port_does_not_get_reused_or_killed(self):
|
||||
with socket.socket() as existing:
|
||||
existing.bind(('127.0.0.1', 0))
|
||||
existing.listen()
|
||||
with self.assertRaisesRegex(ValueError, 'unavailable'):
|
||||
launch.check_ports([dict(name='test', port=existing.getsockname())])
|
||||
self.assertGreater(existing.fileno(), -1)
|
||||
|
||||
def test_game_exit_stops_owned_server(self):
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
with socket.socket() as probe:
|
||||
probe.bind(('127.0.0.1', 0))
|
||||
port = probe.getsockname()[1]
|
||||
service = dict(name='controller', command=[sys.executable, '-u', '-m', 'http.server', str(port), '--bind', '127.0.0.1'], env=dict(os.environ), directory=Path(folder), port=('127.0.0.1',port), ready=f'http://127.0.0.1:{port}/')
|
||||
game = dict(name='cabinet', command=[sys.executable,'-c','import time; time.sleep(.3)'], env=dict(os.environ), directory=Path(folder), port=None, ready=None)
|
||||
self.assertEqual(launch.run([service,game], Path(folder)/'logs'), 0)
|
||||
with socket.socket() as probe:
|
||||
self.assertNotEqual(probe.connect_ex(('127.0.0.1',port)), 0)
|
||||
|
||||
def test_startup_failure_cleans_up_previous_child(self):
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
with socket.socket() as probe:
|
||||
probe.bind(('127.0.0.1', 0))
|
||||
port = probe.getsockname()[1]
|
||||
service = dict(name='controller', command=[sys.executable, '-u', '-m', 'http.server', str(port), '--bind', '127.0.0.1'], env=dict(os.environ), directory=Path(folder), port=('127.0.0.1',port), ready=f'http://127.0.0.1:{port}/')
|
||||
broken = dict(name='cabinet', command=[str(Path(folder)/'missing-executable')], env=dict(os.environ), directory=Path(folder), port=None, ready=None)
|
||||
with self.assertRaises(OSError): launch.run([service,broken], Path(folder)/'logs')
|
||||
with socket.socket() as probe:
|
||||
self.assertNotEqual(probe.connect_ex(('127.0.0.1',port)), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__': unittest.main()
|
||||
Reference in new issue
Block a user