Something went wrong. Try again.
bayes for days
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157"""The container `sds play` builds, and the watchdogs it has to turn off."""
from __future__ import annotations
import contextlibimport ioimport subprocessimport tempfileimport unittestfrom pathlib import Pathfrom unittest import mock
from sds import clifrom sds import play as play_modulefrom sds.play import PlaySpec
def _invocation(spec: PlaySpec) -> tuple[list[str], str]: """The `docker run` argv and the script it hands the container.
Docker, the display probe and the settings directory are all faked: the thing under test is the command line, and a real one wants an X server and a MegaMek install. """ seen: list[list[str]] = []
def fake_run(command, *popenargs, **kwargs): if command[:2] == ["docker", "run"]: seen.append(command) return subprocess.CompletedProcess(command, 0)
with ( tempfile.TemporaryDirectory(dir=play_module.REPO) as tmp, mock.patch.object(play_module, "preflight", lambda _spec: None), mock.patch.object( play_module, "display_environment", lambda: (":7", "/run/user/1000/cookie") ), mock.patch.object(play_module, "_seed_config", lambda _home: Path("/tmp/mmconf")), mock.patch.object(play_module.subprocess, "run", fake_run), # `play` narrates itself at a person; a test suite is not the audience. contextlib.redirect_stdout(io.StringIO()), ): play_module.play(spec, Path(tmp)) command = seen[0] return command, command[-1]
def _spec(**overrides) -> PlaySpec: fields = { "scenario": play_module.REPO / "scenarios" / "mirror-lance.mms", "human": "North", "sds": "South", "bot": "/work/target/release/sds-bot", } fields.update(overrides) return PlaySpec(**fields)
def _flag(script: str, flag: str) -> str: """The value SdsHost is passed for one flag.
Every host argument is single-quoted into the script, so the quotes come off before either side is compared. """ parts = [word.strip("'") for word in script.split()] return parts[parts.index(flag) + 1]
class CommandLine(unittest.TestCase): def test_every_watchdog_is_off(self): """A person takes minutes per turn; all three of these would end the game.""" _, script = _invocation(_spec()) self.assertEqual(_flag(script, "--stall-seconds"), "0") self.assertEqual(_flag(script, "--wall-clock"), "0") self.assertEqual(_flag(script, "--max-rounds"), "0")
def test_the_seats_say_who_is_who(self): _, script = _invocation(_spec(human="South", sds="North")) self.assertIn("'South=human'", script) self.assertIn("'North=sds'", script) self.assertIn('-player_name "human South"', script)
def test_the_bot_is_named_and_passed_through(self): _, script = _invocation(_spec(bot="python3 /work/bots/random_bot.py")) self.assertIn("'python3 /work/bots/random_bot.py'", script)
def test_the_client_is_not_headless(self): """The host stays headless; the seat a person sits in cannot be.""" _, script = _invocation(_spec()) host, client = script.split("megamek.MegaMek") self.assertIn("-Djava.awt.headless=true", host) self.assertNotIn("-Djava.awt.headless=true", client)
def test_the_display_is_mounted_not_published(self): command, _ = _invocation(_spec()) self.assertIn("DISPLAY=:7", command) self.assertIn("/tmp/.X11-unix:/tmp/.X11-unix", command) self.assertIn("/run/user/1000/cookie:/tmp/.sds-xauth:ro", command) # Nothing is published: the server and the client share the container's # own loopback, which is why two of these cannot collide. self.assertNotIn("-p", command) self.assertNotIn("--publish", command)
def test_the_container_is_not_named_sds(self): """`sds clean` kills every `sds-*` container on the machine.""" command, _ = _invocation(_spec()) name = command[command.index("--name") + 1] self.assertTrue(name.startswith("sdsplay-"), name) self.assertFalse(name.startswith("sds-"), name)
def test_the_server_outlives_the_result(self): """So MegaMek's end-of-game screen is readable rather than a disconnect.""" _, script = _invocation(_spec()) self.assertGreater(int(_flag(script, "--linger-seconds")), 0)
if __name__ == "__main__": unittest.main()
class ASeatThatAnsweredNothing(unittest.TestCase): """A run whose bot never started must stop, not play on.
`_check_bot` covers the default binary only. A wrong `--bot` - a relative path, where the container mounts the repo at `/work` - fails the same way and nothing caught it: the host disabled the seat, every decision took an inert default, and six matches recorded a bot that was never running. """
def seat(self, decisions: int, answered: int) -> dict: return { "players": [ { "name": "sds North", "kind": "sds", "sds": {"decisions": decisions, "answered": answered}, }, {"name": "princess South", "kind": "princess", "sds": {}}, ] }
def test_a_seat_with_decisions_and_no_answers_is_named(self): self.assertEqual(cli._seat_answered_nothing(self.seat(93, 0)), "sds North")
def test_a_seat_that_answered_is_not_flagged(self): self.assertIsNone(cli._seat_answered_nothing(self.seat(93, 90)))
def test_one_answer_is_enough(self): # The guard is about a bot that never ran, not about a bot playing # badly. Anything above zero means the process was there. self.assertIsNone(cli._seat_answered_nothing(self.seat(93, 1)))
def test_a_seat_asked_for_nothing_is_not_flagged(self): # Princess keeps no tally, and a match where our seat was never asked # is not evidence of anything. self.assertIsNone(cli._seat_answered_nothing(self.seat(0, 0)))