forked from genewildish/Mainline
feat: add pipeline introspection demo mode
- Add PipelineIntrospectionSource that renders live ASCII DAG with metrics - Add PipelineMetricsSensor exposing pipeline performance as sensor values - Add PipelineIntrospectionDemo controller with 3-phase animation: - Phase 1: Toggle effects one at a time (3s each) - Phase 2: LFO drives intensity default→max→min→default - Phase 3: All effects with shared LFO (infinite loop) - Add pipeline-inspect preset - Add get_frame_times() to Pipeline for sparkline data - Add tests for new components - Update mise.toml with pipeline-inspect preset task
This commit is contained in:
156
tests/test_pipeline_introspection.py
Normal file
156
tests/test_pipeline_introspection.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
Tests for PipelineIntrospectionSource.
|
||||
"""
|
||||
|
||||
from engine.pipeline_sources.pipeline_introspection import PipelineIntrospectionSource
|
||||
|
||||
|
||||
class TestPipelineIntrospectionSource:
|
||||
"""Tests for PipelineIntrospectionSource."""
|
||||
|
||||
def test_basic_init(self):
|
||||
"""Source initializes with defaults."""
|
||||
source = PipelineIntrospectionSource()
|
||||
assert source.name == "pipeline-inspect"
|
||||
assert source.is_dynamic is True
|
||||
assert source.frame == 0
|
||||
|
||||
def test_init_with_pipelines(self):
|
||||
"""Source initializes with custom pipelines list."""
|
||||
source = PipelineIntrospectionSource(
|
||||
pipelines=[], viewport_width=100, viewport_height=40
|
||||
)
|
||||
assert source.viewport_width == 100
|
||||
assert source.viewport_height == 40
|
||||
|
||||
def test_inlet_outlet_types(self):
|
||||
"""Source has correct inlet/outlet types."""
|
||||
source = PipelineIntrospectionSource()
|
||||
# inlet should be NONE (source), outlet should be SOURCE_ITEMS
|
||||
from engine.pipeline.core import DataType
|
||||
|
||||
assert DataType.NONE in source.inlet_types
|
||||
assert DataType.SOURCE_ITEMS in source.outlet_types
|
||||
|
||||
def test_fetch_returns_items(self):
|
||||
"""fetch() returns SourceItem list."""
|
||||
source = PipelineIntrospectionSource()
|
||||
items = source.fetch()
|
||||
assert len(items) == 1
|
||||
assert items[0].source == "pipeline-inspect"
|
||||
|
||||
def test_fetch_increments_frame(self):
|
||||
"""fetch() increments frame counter."""
|
||||
source = PipelineIntrospectionSource()
|
||||
assert source.frame == 0
|
||||
source.fetch()
|
||||
assert source.frame == 1
|
||||
source.fetch()
|
||||
assert source.frame == 2
|
||||
|
||||
def test_get_items(self):
|
||||
"""get_items() returns list of SourceItems."""
|
||||
source = PipelineIntrospectionSource()
|
||||
items = source.get_items()
|
||||
assert isinstance(items, list)
|
||||
assert len(items) > 0
|
||||
assert items[0].source == "pipeline-inspect"
|
||||
|
||||
def test_add_pipeline(self):
|
||||
"""add_pipeline() adds pipeline to list."""
|
||||
source = PipelineIntrospectionSource()
|
||||
mock_pipeline = object()
|
||||
source.add_pipeline(mock_pipeline)
|
||||
assert mock_pipeline in source._pipelines
|
||||
|
||||
def test_remove_pipeline(self):
|
||||
"""remove_pipeline() removes pipeline from list."""
|
||||
source = PipelineIntrospectionSource()
|
||||
mock_pipeline = object()
|
||||
source.add_pipeline(mock_pipeline)
|
||||
source.remove_pipeline(mock_pipeline)
|
||||
assert mock_pipeline not in source._pipelines
|
||||
|
||||
|
||||
class TestPipelineIntrospectionRender:
|
||||
"""Tests for rendering methods."""
|
||||
|
||||
def test_render_header_no_pipelines(self):
|
||||
"""_render_header returns default when no pipelines."""
|
||||
source = PipelineIntrospectionSource()
|
||||
lines = source._render_header()
|
||||
assert len(lines) == 1
|
||||
assert "PIPELINE INTROSPECTION" in lines[0]
|
||||
|
||||
def test_render_bar(self):
|
||||
"""_render_bar creates correct bar."""
|
||||
source = PipelineIntrospectionSource()
|
||||
bar = source._render_bar(50, 10)
|
||||
assert len(bar) == 10
|
||||
assert bar.count("█") == 5
|
||||
assert bar.count("░") == 5
|
||||
|
||||
def test_render_bar_zero(self):
|
||||
"""_render_bar handles zero percentage."""
|
||||
source = PipelineIntrospectionSource()
|
||||
bar = source._render_bar(0, 10)
|
||||
assert bar == "░" * 10
|
||||
|
||||
def test_render_bar_full(self):
|
||||
"""_render_bar handles 100%."""
|
||||
source = PipelineIntrospectionSource()
|
||||
bar = source._render_bar(100, 10)
|
||||
assert bar == "█" * 10
|
||||
|
||||
def test_render_sparkline(self):
|
||||
"""_render_sparkline creates sparkline."""
|
||||
source = PipelineIntrospectionSource()
|
||||
values = [1.0, 2.0, 3.0, 4.0, 5.0]
|
||||
sparkline = source._render_sparkline(values, 10)
|
||||
assert len(sparkline) == 10
|
||||
|
||||
def test_render_sparkline_empty(self):
|
||||
"""_render_sparkline handles empty values."""
|
||||
source = PipelineIntrospectionSource()
|
||||
sparkline = source._render_sparkline([], 10)
|
||||
assert sparkline == " " * 10
|
||||
|
||||
def test_render_footer_no_pipelines(self):
|
||||
"""_render_footer shows collecting data when no pipelines."""
|
||||
source = PipelineIntrospectionSource()
|
||||
lines = source._render_footer()
|
||||
assert len(lines) >= 2
|
||||
assert "collecting data" in lines[1] or "Frame Time" in lines[0]
|
||||
|
||||
|
||||
class TestPipelineIntrospectionFull:
|
||||
"""Integration tests."""
|
||||
|
||||
def test_render_empty(self):
|
||||
"""_render works with no pipelines."""
|
||||
source = PipelineIntrospectionSource()
|
||||
lines = source._render()
|
||||
assert len(lines) > 0
|
||||
assert "PIPELINE INTROSPECTION" in lines[0]
|
||||
|
||||
def test_render_with_mock_pipeline(self):
|
||||
"""_render works with mock pipeline."""
|
||||
source = PipelineIntrospectionSource()
|
||||
|
||||
class MockStage:
|
||||
category = "source"
|
||||
name = "test"
|
||||
|
||||
class MockPipeline:
|
||||
stages = {"test": MockStage()}
|
||||
execution_order = ["test"]
|
||||
|
||||
def get_metrics_summary(self):
|
||||
return {"stages": {"test": {"avg_ms": 1.5}}, "avg_ms": 2.0, "fps": 60}
|
||||
|
||||
def get_frame_times(self):
|
||||
return [1.0, 2.0, 3.0]
|
||||
|
||||
source.add_pipeline(MockPipeline())
|
||||
lines = source._render()
|
||||
assert len(lines) > 0
|
||||
Reference in New Issue
Block a user