261 lines
7.8 KiB
Python
261 lines
7.8 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import logging
|
|
import sys
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
|
|
SRC_ROOT = Path(__file__).resolve().parents[2] / "src"
|
|
|
|
if str(SRC_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(SRC_ROOT))
|
|
|
|
|
|
from ffx import screen_support # noqa: E402
|
|
from ffx.i18n import set_current_language, t # noqa: E402
|
|
|
|
|
|
class StaticConfig:
|
|
def __init__(self, data):
|
|
self._data = data
|
|
|
|
def getData(self):
|
|
return self._data
|
|
|
|
|
|
class FakeTagTable:
|
|
def __init__(self):
|
|
self.rows = {}
|
|
self._next_index = 0
|
|
|
|
def clear(self):
|
|
self.rows.clear()
|
|
|
|
def add_row(self, *values):
|
|
row_key = f"row-{self._next_index}"
|
|
self._next_index += 1
|
|
self.rows[row_key] = tuple(values)
|
|
return row_key
|
|
|
|
|
|
class FakeApp:
|
|
def __init__(self, screen_stack):
|
|
self.screen_stack = list(screen_stack)
|
|
self.pop_called = False
|
|
self.exit_called = False
|
|
|
|
def pop_screen(self):
|
|
self.pop_called = True
|
|
|
|
def exit(self):
|
|
self.exit_called = True
|
|
|
|
|
|
class FakeScreen:
|
|
def __init__(self, screen_stack):
|
|
self.app = FakeApp(screen_stack)
|
|
|
|
|
|
class FakeRichLog:
|
|
def __init__(self):
|
|
self.messages = []
|
|
|
|
def write(self, message):
|
|
self.messages.append(message)
|
|
|
|
|
|
class FakeScreenWithLog:
|
|
def __init__(self):
|
|
self.log_view = FakeRichLog()
|
|
|
|
def query_one(self, selector, _widget_type=None):
|
|
if selector == f"#{screen_support.SCREEN_LOG_VIEW_ID}":
|
|
return self.log_view
|
|
raise LookupError(selector)
|
|
|
|
|
|
class FakeThreadedApp:
|
|
def __init__(self, screen):
|
|
self.screen = screen
|
|
self.calls = []
|
|
|
|
def call_from_thread(self, func, *args):
|
|
self.calls.append((func, args))
|
|
return func(*args)
|
|
|
|
|
|
class ScreenSupportTests(unittest.TestCase):
|
|
def tearDown(self):
|
|
set_current_language("de")
|
|
screen_support.set_screen_log_pane_enabled(False)
|
|
|
|
def make_context(self):
|
|
return {
|
|
"config": StaticConfig(
|
|
{
|
|
"metadata": {
|
|
"signature": {"RECODED_WITH": "FFX"},
|
|
"remove": ["VERSION-eng"],
|
|
"ignore": ["ENCODER"],
|
|
"streams": {
|
|
"remove": ["BPS"],
|
|
"ignore": ["language"],
|
|
},
|
|
}
|
|
}
|
|
),
|
|
"database": {"session": object()},
|
|
}
|
|
|
|
def test_build_screen_bootstrap_extracts_metadata_filters(self):
|
|
context = self.make_context()
|
|
|
|
bootstrap = screen_support.build_screen_bootstrap(context)
|
|
|
|
self.assertIs(context, bootstrap.context)
|
|
self.assertEqual({"RECODED_WITH": "FFX"}, bootstrap.signature_tags)
|
|
self.assertEqual(["VERSION-eng"], bootstrap.remove_global_keys)
|
|
self.assertEqual(["ENCODER"], bootstrap.ignore_global_keys)
|
|
self.assertEqual(["BPS"], bootstrap.remove_track_keys)
|
|
self.assertEqual(["language"], bootstrap.ignore_track_keys)
|
|
|
|
def test_build_screen_controllers_only_creates_requested_instances(self):
|
|
context = self.make_context()
|
|
|
|
with (
|
|
patch.object(screen_support, "PatternController", side_effect=lambda context: ("pattern", context)),
|
|
patch.object(screen_support, "ShowController", side_effect=lambda context: ("show", context)),
|
|
patch.object(screen_support, "TmdbController", side_effect=lambda: "tmdb"),
|
|
patch.object(screen_support, "ShiftedSeasonController", side_effect=lambda context: ("shifted", context)),
|
|
):
|
|
controllers = screen_support.build_screen_controllers(
|
|
context,
|
|
pattern=True,
|
|
show=True,
|
|
tmdb=True,
|
|
shifted_season=True,
|
|
)
|
|
|
|
self.assertEqual(
|
|
{
|
|
"pattern": ("pattern", context),
|
|
"show": ("show", context),
|
|
"tmdb": "tmdb",
|
|
"shifted_season": ("shifted", context),
|
|
},
|
|
controllers,
|
|
)
|
|
|
|
def test_populate_tag_table_keeps_raw_values_outside_display_labels(self):
|
|
table = FakeTagTable()
|
|
|
|
row_data = screen_support.populate_tag_table(
|
|
table,
|
|
{"BPS": 4835, "KEEP": "plain"},
|
|
ignore_keys=["KEEP"],
|
|
remove_keys=["BPS"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
{
|
|
"row-0": ("BPS", "4835"),
|
|
"row-1": ("KEEP", "plain"),
|
|
},
|
|
row_data,
|
|
)
|
|
self.assertEqual(
|
|
("[red]BPS[/red]", "[red]4835[/red]"),
|
|
table.rows["row-0"],
|
|
)
|
|
self.assertEqual(
|
|
("[blue]KEEP[/blue]", "[blue]plain[/blue]"),
|
|
table.rows["row-1"],
|
|
)
|
|
|
|
def test_go_back_or_exit_exits_from_first_pushed_screen(self):
|
|
screen = FakeScreen(screen_stack=["base", "shows"])
|
|
|
|
screen_support.go_back_or_exit(screen)
|
|
|
|
self.assertFalse(screen.app.pop_called)
|
|
self.assertTrue(screen.app.exit_called)
|
|
|
|
def test_go_back_or_exit_pops_nested_screen(self):
|
|
screen = FakeScreen(screen_stack=["base", "shows", "details"])
|
|
|
|
screen_support.go_back_or_exit(screen)
|
|
|
|
self.assertTrue(screen.app.pop_called)
|
|
self.assertFalse(screen.app.exit_called)
|
|
|
|
def test_localized_column_width_handles_combining_character_labels(self):
|
|
set_current_language("ta")
|
|
|
|
translated = t("SubIndex")
|
|
self.assertEqual("துணைச்சுட்டி", translated)
|
|
self.assertGreater(len(translated), 8)
|
|
self.assertEqual(len(translated) + 2, screen_support.localized_column_width(translated, 8))
|
|
|
|
def test_build_screen_log_pane_is_hidden_when_debug_mode_is_disabled(self):
|
|
screen_support.set_screen_log_pane_enabled(False)
|
|
|
|
log_pane = screen_support.build_screen_log_pane()
|
|
|
|
self.assertFalse(log_pane.display)
|
|
|
|
def test_build_screen_log_pane_is_collapsed_when_debug_mode_is_enabled(self):
|
|
screen_support.set_screen_log_pane_enabled(True)
|
|
|
|
log_pane = screen_support.build_screen_log_pane()
|
|
|
|
self.assertIsInstance(log_pane, screen_support.ResizableScreenLogPane)
|
|
self.assertEqual(screen_support.SCREEN_LOG_PANE_ID, log_pane.id)
|
|
self.assertTrue(log_pane.collapsed)
|
|
|
|
def test_resizable_screen_log_pane_clamps_height_to_minimum(self):
|
|
log_pane = screen_support.ResizableScreenLogPane()
|
|
|
|
log_pane.set_log_height(1)
|
|
|
|
self.assertEqual(screen_support.SCREEN_LOG_MIN_HEIGHT, log_pane.get_log_height())
|
|
|
|
def test_configure_screen_log_handler_routes_logger_messages_to_active_screen(self):
|
|
logger_name = "ffx-test-screen-log-handler"
|
|
logger = logging.getLogger(logger_name)
|
|
logger.setLevel(logging.DEBUG)
|
|
logger.propagate = False
|
|
|
|
for handler in list(logger.handlers):
|
|
logger.removeHandler(handler)
|
|
handler.close()
|
|
|
|
screen = FakeScreenWithLog()
|
|
app = FakeThreadedApp(screen)
|
|
|
|
try:
|
|
handler = screen_support.configure_screen_log_handler(
|
|
logger,
|
|
app,
|
|
enabled=True,
|
|
)
|
|
self.assertIsNotNone(handler)
|
|
|
|
logger.info("hello pane")
|
|
|
|
self.assertEqual(1, len(screen.log_view.messages))
|
|
self.assertRegex(
|
|
screen.log_view.messages[0],
|
|
r"^ffx-test-screen-log-handler\s+INFO\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \| hello pane$",
|
|
)
|
|
finally:
|
|
screen_support.configure_screen_log_handler(logger, app, enabled=False)
|
|
for handler in list(logger.handlers):
|
|
logger.removeHandler(handler)
|
|
handler.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|