feat: build CouchOS Melee appliance
Some checks failed
ci / verify (push) Has been cancelled

This commit is contained in:
jackmhny
2026-08-03 02:52:28 +02:00
commit 04ef2a0d74
42 changed files with 3671 additions and 0 deletions

163
tests/test_launcher.py Normal file
View File

@@ -0,0 +1,163 @@
import json
import tempfile
import unittest
from pathlib import Path
from unittest import mock
from couchd.launcher import (
LaunchRequest,
ProcessRegistry,
RunningProcess,
default_kill,
default_terminate,
)
class DuplicateLaunchTests(unittest.TestCase):
def test_refuses_second_launch_when_pid_is_still_alive(self) -> None:
launches: list[LaunchRequest] = []
registry = ProcessRegistry(
is_pid_running=lambda pid: pid == 4321,
spawn=lambda request: launches.append(request) or 9876,
process_probe=lambda: None,
process_identity=lambda pid: "proc-4321" if pid == 4321 else None,
)
registry.record_running(pid=4321, state="melee")
result = registry.launch(LaunchRequest(kind="melee", argv=["/bin/true"]))
self.assertEqual(result["state"], "already_running")
self.assertEqual(result["pid"], 4321)
self.assertEqual(launches, [])
def test_stop_escalates_from_term_to_kill_after_timeout(self) -> None:
signals: list[tuple[str, int]] = []
checks = iter([True, True, True, False])
registry = ProcessRegistry(
is_pid_running=lambda pid: next(checks),
spawn=lambda request: 9876,
terminate=lambda pid: signals.append(("TERM", pid)),
kill=lambda pid: signals.append(("KILL", pid)),
sleep=lambda seconds: None,
process_probe=lambda: None,
process_identity=lambda pid: "proc-4321" if pid == 4321 else None,
)
registry.record_running(pid=4321, state="melee")
result = registry.stop(timeout_seconds=0.0)
self.assertEqual(result, {"state": "stopped", "pid": None})
self.assertEqual(signals, [("TERM", 4321), ("KILL", 4321)])
def test_adopts_running_process_from_probe_when_state_file_is_stale(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
state_file = Path(temp_dir) / "state.json"
state_file.write_text(json.dumps({"pid": 9999, "state": "melee"}), encoding="utf-8")
launches: list[LaunchRequest] = []
registry = ProcessRegistry(
is_pid_running=lambda pid: False,
spawn=lambda request: launches.append(request) or 7777,
state_file=state_file,
process_probe=lambda: RunningProcess(pid=4321, state="melee", identity="proc-a"),
process_identity=lambda pid: "proc-a" if pid == 4321 else None,
)
result = registry.launch(LaunchRequest(kind="melee", argv=["/bin/true"]))
self.assertEqual(result, {"state": "already_running", "pid": 4321})
self.assertEqual(launches, [])
def test_ignores_corrupt_state_file_and_recovers(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
state_file = Path(temp_dir) / "state.json"
state_file.write_text("{not-json", encoding="utf-8")
registry = ProcessRegistry(
is_pid_running=lambda pid: False,
spawn=lambda request: 9876,
state_file=state_file,
process_probe=lambda: None,
process_identity=lambda pid: None,
)
self.assertEqual(registry.current_state(), {"state": "idle", "pid": None})
def test_refuses_to_signal_reused_pid_with_different_identity(self) -> None:
signals: list[tuple[str, int]] = []
with tempfile.TemporaryDirectory() as temp_dir:
state_file = Path(temp_dir) / "state.json"
state_file.write_text(
json.dumps({"pid": 4321, "state": "melee", "identity": "old-proc"}),
encoding="utf-8",
)
registry = ProcessRegistry(
is_pid_running=lambda pid: True,
spawn=lambda request: 9876,
terminate=lambda pid: signals.append(("TERM", pid)),
kill=lambda pid: signals.append(("KILL", pid)),
state_file=state_file,
process_probe=lambda: None,
process_identity=lambda pid: "new-proc",
)
result = registry.stop(timeout_seconds=0.0)
self.assertEqual(result, {"state": "stopped", "pid": None})
self.assertEqual(signals, [])
def test_refuses_to_signal_same_executable_pid_reuse_when_start_time_changes(self) -> None:
signals: list[tuple[str, int]] = []
with tempfile.TemporaryDirectory() as temp_dir:
state_file = Path(temp_dir) / "state.json"
state_file.write_text(
json.dumps(
{
"pid": 4321,
"state": "melee",
"identity": "1000:/home/couch/Applications/Dolphin-x86_64.AppImage:111",
}
),
encoding="utf-8",
)
registry = ProcessRegistry(
is_pid_running=lambda pid: True,
spawn=lambda request: 9876,
terminate=lambda pid: signals.append(("TERM", pid)),
kill=lambda pid: signals.append(("KILL", pid)),
state_file=state_file,
process_probe=lambda: None,
process_identity=lambda pid: "1000:/home/couch/Applications/Dolphin-x86_64.AppImage:222",
)
result = registry.stop(timeout_seconds=0.0)
self.assertEqual(result, {"state": "stopped", "pid": None})
self.assertEqual(signals, [])
class DefaultSignalTests(unittest.TestCase):
def test_terminate_signals_non_group_leader_process_directly(self) -> None:
with mock.patch("couchd.launcher.os.getpgid", return_value=9999), mock.patch(
"couchd.launcher.os.kill"
) as kill, mock.patch("couchd.launcher.os.killpg") as killpg:
default_terminate(4321)
kill.assert_called_once()
self.assertEqual(kill.call_args.args[0], 4321)
killpg.assert_not_called()
def test_kill_signals_group_leader_process_group(self) -> None:
with mock.patch("couchd.launcher.os.getpgid", return_value=4321), mock.patch(
"couchd.launcher.os.kill"
) as kill, mock.patch("couchd.launcher.os.killpg") as killpg:
default_kill(4321)
kill.assert_not_called()
killpg.assert_called_once()