fix(performance): use simple height estimation instead of PIL rendering

- Replace estimate_block_height (PIL-based) with estimate_simple_height (word wrap)
- Update viewport filter tests to match new height-based filtering (~4 items vs 24)
- Fix CI task duplication in mise.toml (remove redundant depends)

Closes #38
Closes #36
This commit is contained in:
2026-03-18 22:33:36 -07:00
parent abe49ba7d7
commit c57617bb3d
26 changed files with 3938 additions and 1956 deletions

View File

@@ -18,7 +18,7 @@ class TestMain:
def test_main_calls_run_pipeline_mode_with_default_preset(self):
"""main() runs default preset (demo) when no args provided."""
with patch("engine.app.run_pipeline_mode") as mock_run:
with patch("engine.app.main.run_pipeline_mode") as mock_run:
sys.argv = ["mainline.py"]
main()
mock_run.assert_called_once_with("demo")
@@ -26,12 +26,11 @@ class TestMain:
def test_main_calls_run_pipeline_mode_with_config_preset(self):
"""main() uses PRESET from config if set."""
with (
patch("engine.app.config") as mock_config,
patch("engine.app.run_pipeline_mode") as mock_run,
patch("engine.config.PIPELINE_DIAGRAM", False),
patch("engine.config.PRESET", "gallery-sources"),
patch("engine.config.PIPELINE_MODE", False),
patch("engine.app.main.run_pipeline_mode") as mock_run,
):
mock_config.PIPELINE_DIAGRAM = False
mock_config.PRESET = "gallery-sources"
mock_config.PIPELINE_MODE = False
sys.argv = ["mainline.py"]
main()
mock_run.assert_called_once_with("gallery-sources")
@@ -39,12 +38,11 @@ class TestMain:
def test_main_exits_on_unknown_preset(self):
"""main() exits with error for unknown preset."""
with (
patch("engine.app.config") as mock_config,
patch("engine.app.list_presets", return_value=["demo", "poetry"]),
patch("engine.config.PIPELINE_DIAGRAM", False),
patch("engine.config.PRESET", "nonexistent"),
patch("engine.config.PIPELINE_MODE", False),
patch("engine.pipeline.list_presets", return_value=["demo", "poetry"]),
):
mock_config.PIPELINE_DIAGRAM = False
mock_config.PRESET = "nonexistent"
mock_config.PIPELINE_MODE = False
sys.argv = ["mainline.py"]
with pytest.raises(SystemExit) as exc_info:
main()
@@ -70,9 +68,11 @@ class TestRunPipelineMode:
def test_run_pipeline_mode_exits_when_no_content_available(self):
"""run_pipeline_mode() exits if no content can be fetched."""
with (
patch("engine.app.load_cache", return_value=None),
patch("engine.app.fetch_all", return_value=([], None, None)),
patch("engine.app.effects_plugins"),
patch("engine.app.pipeline_runner.load_cache", return_value=None),
patch(
"engine.app.pipeline_runner.fetch_all", return_value=([], None, None)
),
patch("engine.effects.plugins.discover_plugins"),
pytest.raises(SystemExit) as exc_info,
):
run_pipeline_mode("demo")
@@ -82,9 +82,11 @@ class TestRunPipelineMode:
"""run_pipeline_mode() uses cached content if available."""
cached = ["cached_item"]
with (
patch("engine.app.load_cache", return_value=cached) as mock_load,
patch("engine.app.fetch_all") as mock_fetch,
patch("engine.app.DisplayRegistry.create") as mock_create,
patch(
"engine.app.pipeline_runner.load_cache", return_value=cached
) as mock_load,
patch("engine.app.pipeline_runner.fetch_all") as mock_fetch,
patch("engine.app.pipeline_runner.DisplayRegistry.create") as mock_create,
):
mock_display = Mock()
mock_display.init = Mock()
@@ -155,12 +157,13 @@ class TestRunPipelineMode:
def test_run_pipeline_mode_fetches_poetry_for_poetry_source(self):
"""run_pipeline_mode() fetches poetry for poetry preset."""
with (
patch("engine.app.load_cache", return_value=None),
patch("engine.app.pipeline_runner.load_cache", return_value=None),
patch(
"engine.app.fetch_poetry", return_value=(["poem"], None, None)
"engine.app.pipeline_runner.fetch_poetry",
return_value=(["poem"], None, None),
) as mock_fetch_poetry,
patch("engine.app.fetch_all") as mock_fetch_all,
patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.pipeline_runner.fetch_all") as mock_fetch_all,
patch("engine.app.pipeline_runner.DisplayRegistry.create") as mock_create,
):
mock_display = Mock()
mock_display.init = Mock()
@@ -183,9 +186,9 @@ class TestRunPipelineMode:
def test_run_pipeline_mode_discovers_effect_plugins(self):
"""run_pipeline_mode() discovers available effect plugins."""
with (
patch("engine.app.load_cache", return_value=["item"]),
patch("engine.app.effects_plugins") as mock_effects,
patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.pipeline_runner.load_cache", return_value=["item"]),
patch("engine.effects.plugins.discover_plugins") as mock_discover,
patch("engine.app.pipeline_runner.DisplayRegistry.create") as mock_create,
):
mock_display = Mock()
mock_display.init = Mock()
@@ -202,4 +205,4 @@ class TestRunPipelineMode:
pass
# Verify effects_plugins.discover_plugins was called
mock_effects.discover_plugins.assert_called_once()
mock_discover.assert_called_once()