diff --git a/tests/comparison_capture.py b/tests/comparison_capture.py index 1692a00..d54f87a 100644 --- a/tests/comparison_capture.py +++ b/tests/comparison_capture.py @@ -108,7 +108,88 @@ def capture_frames( ) ctx.params = params - # Add message overlay stage if enabled + # Add stages based on source type (similar to pipeline_runner) + from engine.display import DisplayRegistry + from engine.pipeline.adapters import create_stage_from_display + from engine.data_sources.sources import EmptyDataSource + from engine.pipeline.adapters import DataSourceStage + + # Add source stage + if preset.source == "empty": + source_stage = DataSourceStage( + EmptyDataSource(width=preset.viewport_width, height=preset.viewport_height), + name="empty", + ) + else: + # For headlines/poetry, use the actual source + from engine.data_sources.sources import HeadlinesDataSource, PoetryDataSource + + if preset.source == "headlines": + source_stage = DataSourceStage(HeadlinesDataSource(), name="headlines") + elif preset.source == "poetry": + source_stage = DataSourceStage(PoetryDataSource(), name="poetry") + else: + # Fallback to empty + source_stage = DataSourceStage( + EmptyDataSource( + width=preset.viewport_width, height=preset.viewport_height + ), + name="empty", + ) + pipeline.add_stage("source", source_stage) + + # Add font stage for headlines/poetry (with viewport filter) + if preset.source in ["headlines", "poetry"]: + from engine.pipeline.adapters import FontStage, ViewportFilterStage + + # Add viewport filter to prevent rendering all items + pipeline.add_stage( + "viewport_filter", ViewportFilterStage(name="viewport-filter") + ) + # Add font stage for block character rendering + pipeline.add_stage("font", FontStage(name="font")) + else: + # Fallback to simple conversion for empty/other sources + from engine.pipeline.adapters import SourceItemsToBufferStage + + pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer")) + + # Add camera stage + from engine.camera import Camera + from engine.pipeline.adapters import CameraStage, CameraClockStage + + # Create camera based on preset + if preset.camera == "feed": + camera = Camera.feed() + elif preset.camera == "scroll": + camera = Camera.scroll(speed=0.1) + elif preset.camera == "horizontal": + camera = Camera.horizontal(speed=0.1) + else: + camera = Camera.feed() + + camera.set_canvas_size(preset.viewport_width, preset.viewport_height * 2) + + # Add camera update (for animation) + pipeline.add_stage("camera_update", CameraClockStage(camera, name="camera-clock")) + # Add camera stage + pipeline.add_stage("camera", CameraStage(camera, name=preset.camera)) + + # Add effects + if preset.effects: + from engine.effects.registry import EffectRegistry + from engine.pipeline.adapters import create_stage_from_effect + + effect_registry = EffectRegistry() + for effect_name in preset.effects: + effect = effect_registry.get(effect_name) + if effect: + pipeline.add_stage( + f"effect_{effect_name}", + create_stage_from_effect(effect, effect_name), + ) + + # Add message overlay stage if enabled (BEFORE display) if getattr(preset, "enable_message_overlay", False): from engine.pipeline.adapters import MessageOverlayConfig, MessageOverlayStage @@ -120,9 +201,21 @@ def capture_frames( "message_overlay", MessageOverlayStage(config=overlay_config) ) + # Add null display stage (LAST) + null_display = DisplayRegistry.create("null") + if null_display: + pipeline.add_stage("display", create_stage_from_display(null_display, "null")) + # Build pipeline pipeline.build() + # Enable recording on null display if available + display_stage = pipeline._stages.get("display") + if display_stage and hasattr(display_stage, "_display"): + backend = display_stage._display + if hasattr(backend, "start_recording"): + backend.start_recording() + # Capture frames frames = [] start_time = time.time() @@ -132,18 +225,36 @@ def capture_frames( stage_result = pipeline.execute() frame_time = time.time() - frame_start - # Extract buffer from result - buffer = stage_result.data if stage_result.success else [] + # Get frames from display recording + display_stage = pipeline._stages.get("display") + if display_stage and hasattr(display_stage, "_display"): + backend = display_stage._display + if hasattr(backend, "get_recorded_data"): + recorded_frames = backend.get_recorded_data() + # Add render_time_ms to each frame + for frame in recorded_frames: + frame["render_time_ms"] = frame_time * 1000 + frames = recorded_frames - frames.append( - { - "frame_number": i, - "buffer": buffer, - "width": preset.viewport_width, - "height": preset.viewport_height, - "render_time_ms": frame_time * 1000, - } - ) + # Fallback: create empty frames if no recording + if not frames: + for i in range(frame_count): + frames.append( + { + "frame_number": i, + "buffer": [], + "width": preset.viewport_width, + "height": preset.viewport_height, + "render_time_ms": frame_time * 1000, + } + ) + + # Stop recording on null display + display_stage = pipeline._stages.get("display") + if display_stage and hasattr(display_stage, "_display"): + backend = display_stage._display + if hasattr(backend, "stop_recording"): + backend.stop_recording() total_time = time.time() - start_time