WIP #35

Closed
david wants to merge 135 commits from klubhaus:feature/capability-based-deps into main
2 changed files with 76 additions and 130 deletions
Showing only changes of commit 8d066edcca - Show all commits

View File

@@ -5,13 +5,24 @@ Application orchestrator — pipeline mode entry point.
import sys import sys
import time import time
import effects_plugins
from engine import config from engine import config
from engine.display import DisplayRegistry
from engine.effects import PerformanceMonitor, get_registry, set_monitor
from engine.fetch import fetch_all, fetch_poetry, load_cache
from engine.pipeline import ( from engine.pipeline import (
Pipeline, Pipeline,
PipelineConfig, PipelineConfig,
get_preset, get_preset,
list_presets, list_presets,
) )
from engine.pipeline.adapters import (
RenderStage,
SourceItemsToBufferStage,
create_items_stage,
create_stage_from_display,
create_stage_from_effect,
)
def main(): def main():
@@ -45,18 +56,6 @@ def main():
def run_pipeline_mode(preset_name: str = "demo"): def run_pipeline_mode(preset_name: str = "demo"):
"""Run using the new unified pipeline architecture.""" """Run using the new unified pipeline architecture."""
import effects_plugins
from engine.display import DisplayRegistry
from engine.effects import PerformanceMonitor, get_registry, set_monitor
from engine.fetch import fetch_all, fetch_poetry, load_cache
from engine.pipeline.adapters import (
RenderStage,
SourceItemsToBufferStage,
create_items_stage,
create_stage_from_display,
create_stage_from_effect,
)
print(" \033[1;38;5;46mPIPELINE MODE\033[0m") print(" \033[1;38;5;46mPIPELINE MODE\033[0m")
print(" \033[38;5;245mUsing unified pipeline architecture\033[0m") print(" \033[38;5;245mUsing unified pipeline architecture\033[0m")

View File

@@ -38,31 +38,16 @@ class TestMain:
def test_main_exits_on_unknown_preset(self): def test_main_exits_on_unknown_preset(self):
"""main() exits with error message for unknown preset.""" """main() exits with error message for unknown preset."""
sys.argv = ["mainline.py"]
with ( with (
patch("engine.app.run_pipeline_mode"), patch("engine.app.config") as mock_config,
patch("engine.app.list_presets", return_value=["demo", "poetry"]), patch("engine.app.list_presets", return_value=["demo", "poetry"]),
pytest.raises(SystemExit) as exc_info, pytest.raises(SystemExit) as exc_info,
patch("engine.app.config") as mock_config,
): ):
sys.argv = ["mainline.py"]
mock_config.PRESET = "nonexistent" mock_config.PRESET = "nonexistent"
main() main()
assert exc_info.value.code == 1 assert exc_info.value.code == 1
def test_main_handles_pipeline_diagram_flag(self):
"""main() generates pipeline diagram if PIPELINE_DIAGRAM is set."""
with (
patch("engine.app.config") as mock_config,
patch(
"engine.app.generate_pipeline_diagram", return_value="diagram"
) as mock_gen,
):
mock_config.PIPELINE_DIAGRAM = True
with patch("builtins.print") as mock_print:
main()
mock_gen.assert_called_once()
mock_print.assert_called_with("diagram")
class TestRunPipelineMode: class TestRunPipelineMode:
"""Test run_pipeline_mode() pipeline setup and execution.""" """Test run_pipeline_mode() pipeline setup and execution."""
@@ -94,18 +79,16 @@ class TestRunPipelineMode:
), ),
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
with (
patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
pytest.raises((StopIteration, AttributeError)),
):
mock_pipeline = MagicMock() mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline mock_pipeline_class.return_value = mock_pipeline
# Will timeout after first iteration
# Will timeout after first iteration due to KeyboardInterrupt
with pytest.raises((StopIteration, AttributeError)):
run_pipeline_mode("demo") run_pipeline_mode("demo")
def test_run_pipeline_mode_handles_empty_source(self): def test_run_pipeline_mode_handles_empty_source(self):
@@ -114,14 +97,14 @@ class TestRunPipelineMode:
patch("engine.app.fetch_all") as mock_fetch, patch("engine.app.fetch_all") as mock_fetch,
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
with patch("engine.app.Pipeline") as mock_pipeline_class:
mock_pipeline = MagicMock() mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try: try:
run_pipeline_mode("border-test") run_pipeline_mode("border-test")
except (StopIteration, AttributeError): except (StopIteration, AttributeError):
@@ -137,14 +120,14 @@ class TestRunPipelineMode:
patch("engine.app.fetch_all") as mock_fetch, patch("engine.app.fetch_all") as mock_fetch,
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
with patch("engine.app.Pipeline") as mock_pipeline_class:
mock_pipeline = MagicMock() mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try: try:
run_pipeline_mode("demo") run_pipeline_mode("demo")
except (StopIteration, AttributeError): except (StopIteration, AttributeError):
@@ -160,14 +143,14 @@ class TestRunPipelineMode:
patch("engine.app.fetch_all") as mock_fetch_all, patch("engine.app.fetch_all") as mock_fetch_all,
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
with patch("engine.app.Pipeline") as mock_pipeline_class:
mock_pipeline = MagicMock() mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try: try:
run_pipeline_mode("poetry") run_pipeline_mode("poetry")
except (StopIteration, AttributeError): except (StopIteration, AttributeError):
@@ -181,8 +164,8 @@ class TestRunPipelineMode:
patch("engine.app.load_cache", return_value=None), patch("engine.app.load_cache", return_value=None),
patch("engine.app.fetch_all", return_value=([], None, None)), patch("engine.app.fetch_all", return_value=([], None, None)),
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
pytest.raises(SystemExit) as exc_info,
): ):
with pytest.raises(SystemExit) as exc_info:
run_pipeline_mode("demo") run_pipeline_mode("demo")
assert exc_info.value.code == 1 assert exc_info.value.code == 1
@@ -194,14 +177,14 @@ class TestRunPipelineMode:
patch("engine.app.fetch_all", return_value=(["item"], None, None)), patch("engine.app.fetch_all", return_value=(["item"], None, None)),
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
with patch("engine.app.Pipeline") as mock_pipeline_class:
mock_pipeline = MagicMock() mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try: try:
run_pipeline_mode("border-test") run_pipeline_mode("border-test")
except (StopIteration, AttributeError): except (StopIteration, AttributeError):
@@ -216,12 +199,13 @@ class TestRunPipelineMode:
patch("engine.app.load_cache", return_value=["item"]), patch("engine.app.load_cache", return_value=["item"]),
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.Pipeline") as mock_pipeline_class, patch("engine.app.Pipeline") as mock_pipeline_class,
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
mock_pipeline = MagicMock() mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try: try:
run_pipeline_mode("demo") run_pipeline_mode("demo")
except (StopIteration, AttributeError): except (StopIteration, AttributeError):
@@ -229,26 +213,6 @@ class TestRunPipelineMode:
# Should call discover_plugins # Should call discover_plugins
mock_effects.discover_plugins.assert_called_once() mock_effects.discover_plugins.assert_called_once()
def test_run_pipeline_mode_creates_pipeline_with_preset_config(self):
"""run_pipeline_mode() creates pipeline with preset configuration."""
with (
patch("engine.app.load_cache", return_value=["item"]),
patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
):
mock_display = MagicMock()
mock_create.return_value = mock_display
mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try:
run_pipeline_mode("demo")
except (StopIteration, AttributeError):
pass
# Verify Pipeline was created (call may vary, but it should be called)
assert mock_pipeline_class.called
def test_run_pipeline_mode_initializes_display(self): def test_run_pipeline_mode_initializes_display(self):
"""run_pipeline_mode() initializes display with dimensions.""" """run_pipeline_mode() initializes display with dimensions."""
with ( with (
@@ -256,31 +220,14 @@ class TestRunPipelineMode:
patch("engine.app.DisplayRegistry.create") as mock_create, patch("engine.app.DisplayRegistry.create") as mock_create,
patch("engine.app.effects_plugins"), patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline"), patch("engine.app.Pipeline"),
patch("engine.app.time.sleep"),
): ):
mock_display = MagicMock() mock_display = MagicMock()
mock_create.return_value = mock_display mock_create.return_value = mock_display
with patch("engine.app.time.sleep"):
try: try:
run_pipeline_mode("demo") run_pipeline_mode("demo")
except (StopIteration, AttributeError): except (StopIteration, AttributeError):
pass pass
# Display should be initialized with dimensions # Display should be initialized with dimensions
mock_display.init.assert_called() mock_display.init.assert_called()
def test_run_pipeline_mode_builds_pipeline_before_initialize(self):
"""run_pipeline_mode() calls pipeline.build() before initialize()."""
with (
patch("engine.app.load_cache", return_value=["item"]),
patch("engine.app.DisplayRegistry.create"),
patch("engine.app.effects_plugins"),
patch("engine.app.Pipeline") as mock_pipeline_class,
):
mock_pipeline = MagicMock()
mock_pipeline_class.return_value = mock_pipeline
with patch("engine.app.time.sleep"):
try:
run_pipeline_mode("demo")
except (StopIteration, AttributeError):
pass
# Build should be called
assert mock_pipeline.build.called