Files
mtga-assistant/tests/test_background.py
T
Aeth3randClaude Opus 5.5 c4533d12c0 Run in the background and open when MTGA starts
- Closing the window hides it to the system tray; tracking and history
  recording continue. Quit from the tray, the menu or Ctrl+Q.
- Show the window when MTGA starts a new Player.log, detected with OS
  file notifications on the log folder (QFileSystemWatcher), not polling.
- Single instance over a local socket: launching again shows the
  running window (or stays hidden with --background).
- Menu options for background mode, show-on-launch and start at login
  (XDG autostart on Linux, HKCU Run key on Windows); --background flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 09:54:08 -04:00

106 lines
3.0 KiB
Python

import os
import time
import uuid
import pytest
from PySide6.QtCore import QCoreApplication
from mtga_assistant.background import (
LaunchWatcher,
SingleInstance,
autostart_enabled,
desktop_exec,
launch_command,
set_autostart,
)
@pytest.fixture(scope="module")
def app():
return QCoreApplication.instance() or QCoreApplication([])
def wait_for(app, cond, timeout=3.0):
end = time.monotonic() + timeout
while time.monotonic() < end:
app.processEvents()
if cond():
return True
time.sleep(0.01)
app.processEvents()
return cond()
def test_second_launch_messages_the_running_instance(app):
name = f"mtga-assistant-test-{uuid.uuid4().hex[:8]}"
first = SingleInstance(name)
assert not first.send("show") # nobody running yet
assert first.listen()
got = []
first.message.connect(got.append)
second = SingleInstance(name)
try:
assert second.send("show")
assert wait_for(app, lambda: got == ["show"])
finally:
# Let queued socket events run before the objects go away.
first.close()
wait_for(app, lambda: False, timeout=0.2)
def test_launch_watcher_reports_new_log_not_appends(app, tmp_path):
log = tmp_path / "Player.log"
log.write_text("old session\n")
watcher = LaunchWatcher(log)
assert watcher.active
launches = []
watcher.launched.connect(lambda: launches.append(1))
# Normal logging: no launch.
with open(log, "a") as f:
f.write("more\n")
wait_for(app, lambda: False, timeout=0.3)
assert launches == []
# MTGA starts: old log becomes Player-prev.log, a new Player.log appears.
os.replace(log, tmp_path / "Player-prev.log")
log.write_text("new session\n")
assert wait_for(app, lambda: len(launches) >= 1)
# Still watching the new file: a truncate-in-place restart is reported too.
count = len(launches)
with open(log, "a") as f:
f.write("lots of lines\n" * 50)
wait_for(app, lambda: False, timeout=0.3)
assert len(launches) == count
log.write_text("")
assert wait_for(app, lambda: len(launches) > count)
def test_launch_watcher_without_folder_is_inactive(app, tmp_path):
assert not LaunchWatcher(tmp_path / "missing" / "Player.log").active
def test_linux_autostart_entry(monkeypatch, tmp_path):
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path))
assert not autostart_enabled("linux")
set_autostart(True, "linux")
entry = tmp_path / "autostart" / "mtga-assistant.desktop"
text = entry.read_text()
assert autostart_enabled("linux")
assert "--background" in text and "Exec=" in text
set_autostart(False, "linux")
assert not entry.exists() and not autostart_enabled("linux")
def test_launch_command_starts_hidden():
cmd = launch_command()
assert cmd[-1] == "--background"
def test_desktop_exec_quoting():
assert desktop_exec(["/usr/bin/python", "-m", "x"]) == "/usr/bin/python -m x"
assert desktop_exec(["/a b/c"]) == '"/a b/c"'
assert desktop_exec(['/a"b']) == '"/a\\"b"'