Files
mtga-assistant/tests/test_logparse.py
T
Aeth3randClaude Opus 5.5 7dc2ce0da8 Add MTGA Assistant: native Linux deck and game tracker
PySide6 app that follows MTG Arena's Player.log under Steam/Proton and shows
my remaining library with draw odds, my zones, and everything revealed about
the opponent (board, graveyard, exile, known hand/library, seen this match).

- Auto-detects the Steam library, Proton prefix log and MTGA install
- Card names/costs/types from MTGA's local SQLite card database
- Tracks card identity across zone changes, shuffles and Bo3 games
- Scryfall card images with a lazy, size-capped LRU disk cache
- Live log following with restart detection, plus --replay mode
- Flags log gaps where MTGA omits oversized game-state messages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:56:12 -04:00

66 lines
2.3 KiB
Python

from collections import Counter
from pathlib import Path
from mtga_assistant.logparse import ClientDeck, DeckSubmitted, GreMessages, LogParser, MatchRoom
FIXTURE = Path(__file__).parent / "fixtures" / "bo3_match.log"
def parse_file(path: Path) -> list:
p = LogParser()
out = []
with open(path, encoding="utf-8") as f:
for line in f:
out.extend(p.feed(line))
return out
def test_fixture_event_kinds():
events = parse_file(FIXTURE)
kinds = Counter(type(e).__name__ for e in events)
assert kinds["MatchRoom"] == 2
assert kinds["DeckSubmitted"] == 1
assert kinds["ClientDeck"] == 1
assert kinds["GreMessages"] >= 15
rooms = [e for e in events if isinstance(e, MatchRoom)]
assert rooms[0].state == "MatchGameRoomStateType_Playing"
assert rooms[1].state == "MatchGameRoomStateType_MatchCompleted"
assert {p["playerName"] for p in rooms[0].players} == {"Local Player", "Opponent Player"}
def test_deck_submitted():
deck = next(e for e in parse_file(FIXTURE) if isinstance(e, DeckSubmitted))
assert deck.name == "Cruel Tutelage"
assert sum(deck.main.values()) == 60
assert sum(deck.sideboard.values()) == 15
def test_client_deck_from_pretty_printed_body():
deck = next(e for e in parse_file(FIXTURE) if isinstance(e, ClientDeck))
assert sum(deck.main.values()) == 60
assert sum(deck.sideboard.values()) == 15
def test_single_line_gre_event():
p = LogParser()
assert p.feed("[UnityCrossThreadLogger]10/16/2026 12:34:51 AM: Match to X: GreToClientEvent\n") == []
evs = p.feed('{ "greToClientEvent": { "greToClientMessages": [ { "type": "GREMessageType_UIMessage" } ] } }\n')
assert len(evs) == 1 and isinstance(evs[0], GreMessages)
def test_multiline_body_and_abandoned_fragment():
p = LogParser()
assert p.feed("{\n") == []
assert p.feed(' "payload": { "type": "Other" },\n') == []
# A new log entry starts before the body closed: the fragment is dropped.
assert p.feed("[UnityCrossThreadLogger]something\n") == []
evs = p.feed('{ "greToClientEvent": { "greToClientMessages": [ { "type": "T" } ] } }\n')
assert len(evs) == 1
def test_non_json_lines_ignored():
p = LogParser()
for line in ["OnSceneLoaded for DuelScene", "[UnityCrossThreadLogger]STATE CHANGED {\"old\":1}", ""]:
assert p.feed(line) == []