DazedTL/tests/test_wolf_speakers.py
2026-07-03 16:01:31 -05:00

106 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Unit tests for the WOLF first-line speaker reshaping in util/speakers.py."""
from __future__ import annotations
import os
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
os.chdir(ROOT)
sys.path.insert(0, str(ROOT))
from util import speakers as ws # noqa: E402
ALL_ON = {"literal_line1": True, "literal_line1_lowconf": True}
ALL_OFF = {"literal_line1": False, "literal_line1_lowconf": False}
class TestSplitSource(unittest.TestCase):
def test_splits_lowconf_when_enabled(self):
out = ws.split_source("市民\nおはよう\n元気?", "literal_line1_lowconf", ALL_ON)
self.assertEqual(out, ("", "市民", "おはよう\n元気?"))
def test_splits_highconf_when_enabled(self):
out = ws.split_source("セルリア\nふふふ", "literal_line1", ALL_ON)
self.assertEqual(out, ("", "セルリア", "ふふふ"))
def test_disabled_format_returns_none(self):
self.assertIsNone(ws.split_source("市民\nおはよう", "literal_line1_lowconf", ALL_OFF))
def test_non_firstline_src_returns_none(self):
for src in ("narration", "ui", "choice", "string_var", ""):
self.assertIsNone(ws.split_source("市民\nおはよう", src, ALL_ON))
def test_no_body_returns_none(self):
self.assertIsNone(ws.split_source("市民", "literal_line1", ALL_ON))
def test_preserves_window_option_prefix(self):
out = ws.split_source("@2\n市民\nおはよう", "literal_line1", ALL_ON)
self.assertEqual(out, ("@2\n", "市民", "おはよう"))
class TestPrefixedRoundTrip(unittest.TestCase):
def test_to_prefixed(self):
self.assertEqual(ws.to_prefixed("市民", "おはよう"), "[市民]: おはよう")
def test_parse_prefixed(self):
self.assertEqual(ws.parse_prefixed("[Citizen]: Good morning"), ("Citizen", "Good morning"))
def test_parse_prefixed_multiline_body(self):
spk, body = ws.parse_prefixed("[Celria]: Wave.\nSmile.")
self.assertEqual(spk, "Celria")
self.assertEqual(body, "Wave.\nSmile.")
def test_parse_prefixed_no_prefix(self):
self.assertEqual(ws.parse_prefixed("Just narration"), (None, "Just narration"))
def test_restore_source(self):
self.assertEqual(ws.restore_source("", "Citizen", "Good morning"), "Citizen\nGood morning")
def test_restore_source_with_window_prefix(self):
self.assertEqual(
ws.restore_source("@2\n", "Citizen", "Hi"), "@2\nCitizen\nHi"
)
def test_full_round_trip_structure_preserved(self):
source = "市民\nおはよう\n元気?"
prefix, speaker, body = ws.split_source(source, "literal_line1_lowconf", ALL_ON)
transport = ws.to_prefixed(speaker, body)
self.assertEqual(transport, "[市民]: おはよう\n元気?")
# Simulate a translation that keeps the [Speaker]: format.
spk_en, body_en = ws.parse_prefixed("[Citizen]: Morning\nDoing well?")
restored = ws.restore_source(prefix, spk_en, body_en)
self.assertEqual(restored, "Citizen\nMorning\nDoing well?")
# Same number of newlines as the original layout (speaker on its own line).
self.assertEqual(source.count("\n"), restored.count("\n"))
class TestConfigIO(unittest.TestCase):
def test_load_defaults_when_missing(self):
orig = ws.CONFIG_PATH
try:
ws.CONFIG_PATH = ROOT / "tests" / "_nonexistent_wolf_speakers.json"
self.assertEqual(ws.load_config(), ws.DEFAULT_CONFIG)
finally:
ws.CONFIG_PATH = orig
def test_save_and_load_roundtrip(self):
import tempfile
orig = ws.CONFIG_PATH
try:
with tempfile.TemporaryDirectory() as td:
ws.CONFIG_PATH = Path(td) / "wolf_speakers.json"
ws.save_config({"literal_line1": False, "literal_line1_lowconf": True})
loaded = ws.load_config()
self.assertFalse(loaded["literal_line1"])
self.assertTrue(loaded["literal_line1_lowconf"])
finally:
ws.CONFIG_PATH = orig
if __name__ == "__main__":
unittest.main(verbosity=2)