nigig-org/crates/apps/map/tests/render_graph_tests.rs
andodeki 0d43d61049 refactor(map): implement trait-based render graph (Phase 3)
- Define RenderPass trait with execute(), z_order(), zoom gating
- Implement 5 concrete passes: Background, Fill, Stroke, Poi, Label
- Create RenderContext to pass state to passes
- Refactor view.rs draw_walk() to use render_graph.execute()
- Reduce draw_walk() from 153 lines to 50 lines (-67%)
- Remove all hardcoded if-statements for pass execution
- Add 13 integration tests for render graph
- Enable true extensibility: new passes require only trait implementation

Benefits:
- Extensible: add custom passes without modifying view.rs
- Maintainable: each pass is self-contained
- Testable: passes can be tested independently
- Clean: view.rs reduced by 150 lines

Architecture:
  NigigMapView.draw_walk()
    -> creates RenderContext
    -> render_graph.execute(&mut ctx)
       -> iterates passes in z_order
       -> calls pass.execute(ctx) for each enabled pass

This completes Phase 3 of the Makepad codebase improvement plan.
2026-07-27 16:17:51 +00:00

205 lines
5.9 KiB
Rust

// Integration tests for the trait-based render graph
use nigig_map::render_graph::{
PassType, RenderGraph, RenderPass, BackgroundPass, FillPass, StrokePass, PoiPass, LabelPass,
};
#[test]
fn test_render_graph_default_passes() {
let graph = RenderGraph::new();
let passes = graph.passes();
assert_eq!(passes.len(), 5, "Should have 5 default passes");
// Verify passes are in z_order
let z_orders: Vec<i32> = passes.iter().map(|p| p.z_order()).collect();
for i in 1..z_orders.len() {
assert!(z_orders[i] > z_orders[i-1], "Passes should be sorted by z_order");
}
}
#[test]
fn test_pass_types() {
let bg = BackgroundPass::default();
assert_eq!(bg.pass_type(), PassType::Background);
assert_eq!(bg.z_order(), 0);
assert!(bg.is_enabled());
let fill = FillPass::default();
assert_eq!(fill.pass_type(), PassType::Fill);
assert_eq!(fill.z_order(), 100);
let stroke = StrokePass::default();
assert_eq!(stroke.pass_type(), PassType::Stroke);
assert_eq!(stroke.z_order(), 200);
let poi = PoiPass::default();
assert_eq!(poi.pass_type(), PassType::Poi);
assert_eq!(poi.z_order(), 300);
assert_eq!(poi.min_zoom(), 13.0);
let label = LabelPass::default();
assert_eq!(label.pass_type(), PassType::Label);
assert_eq!(label.z_order(), 400);
assert_eq!(label.min_zoom(), 13.0);
}
#[test]
fn test_zoom_gating() {
let poi = PoiPass::default();
// POI should not execute below min_zoom
assert!(!poi.should_execute(12.0));
assert!(!poi.should_execute(12.9));
// POI should execute at and above min_zoom
assert!(poi.should_execute(13.0));
assert!(poi.should_execute(14.0));
assert!(poi.should_execute(20.0));
}
#[test]
fn test_pass_enable_disable() {
let mut poi = PoiPass::default();
assert!(poi.is_enabled());
poi.enabled = false;
assert!(!poi.is_enabled());
assert!(!poi.should_execute(15.0)); // Should not execute even at valid zoom
poi.enabled = true;
assert!(poi.is_enabled());
assert!(poi.should_execute(15.0)); // Should execute again
}
#[test]
fn test_render_graph_get_pass() {
let graph = RenderGraph::new();
let bg = graph.get_pass(PassType::Background);
assert!(bg.is_some());
assert_eq!(bg.unwrap().pass_type(), PassType::Background);
let fill = graph.get_pass(PassType::Fill);
assert!(fill.is_some());
// Try to get a pass that doesn't exist
let debug = graph.get_pass(PassType::Debug);
assert!(debug.is_none());
}
#[test]
fn test_render_graph_remove_pass() {
let mut graph = RenderGraph::new();
assert_eq!(graph.passes().len(), 5);
graph.remove_pass(PassType::Background);
assert_eq!(graph.passes().len(), 4);
let bg = graph.get_pass(PassType::Background);
assert!(bg.is_none());
}
#[test]
fn test_custom_pass_ordering() {
let mut graph = RenderGraph::new();
// Create a custom pass with z_order between Fill and Stroke
struct CustomPass;
impl RenderPass for CustomPass {
fn pass_type(&self) -> PassType { PassType::Debug }
fn z_order(&self) -> i32 { 150 }
fn is_enabled(&self) -> bool { true }
fn min_zoom(&self) -> f64 { 0.0 }
fn max_zoom(&self) -> f64 { 30.0 }
fn execute(&self, _ctx: &mut nigig_map::render_graph::RenderContext) -> nigig_map::render_graph::PassStats {
nigig_map::render_graph::PassStats::default()
}
}
graph.add_pass(Box::new(CustomPass));
let passes = graph.passes();
assert_eq!(passes.len(), 6);
// Verify ordering: Background(0), Fill(100), Custom(150), Stroke(200), ...
let z_orders: Vec<i32> = passes.iter().map(|p| p.z_order()).collect();
assert_eq!(z_orders, vec![0, 100, 150, 200, 300, 400]);
}
#[test]
fn test_pass_stats_collection() {
let mut graph = RenderGraph::new();
// Initially no stats
assert_eq!(graph.total_tiles_drawn(), 0);
assert_eq!(graph.total_features_drawn(), 0);
// Note: We can't test execute() without a full RenderContext,
// which requires makepad widgets. This would be tested in
// integration tests with the full UI.
}
#[test]
fn test_pass_zoom_range_customization() {
let mut poi = PoiPass::default();
// Default zoom range
assert_eq!(poi.min_zoom(), 13.0);
assert_eq!(poi.max_zoom(), 30.0);
// Customize zoom range
poi.min_zoom = 10.0;
poi.max_zoom = 20.0;
assert!(poi.should_execute(10.0));
assert!(poi.should_execute(15.0));
assert!(poi.should_execute(20.0));
assert!(!poi.should_execute(9.0));
assert!(!poi.should_execute(21.0));
}
#[test]
fn test_all_pass_types_have_names() {
let types = vec![
PassType::Background,
PassType::Fill,
PassType::Stroke,
PassType::Poi,
PassType::Label,
PassType::Selection,
PassType::Debug,
];
for pass_type in types {
let name = pass_type.name();
assert!(!name.is_empty(), "Pass type {:?} should have a name", pass_type);
}
}
#[test]
fn test_all_pass_types_have_default_z_order() {
let types = vec![
PassType::Background,
PassType::Fill,
PassType::Stroke,
PassType::Poi,
PassType::Label,
PassType::Selection,
PassType::Debug,
];
let z_orders: Vec<i32> = types.iter().map(|t| t.default_z_order()).collect();
// Verify z_orders are unique
let mut sorted = z_orders.clone();
sorted.sort();
sorted.dedup();
assert_eq!(z_orders.len(), sorted.len(), "All pass types should have unique z_orders");
// Verify z_orders are in expected order
assert!(z_orders[0] < z_orders[1]); // Background < Fill
assert!(z_orders[1] < z_orders[2]); // Fill < Stroke
assert!(z_orders[2] < z_orders[3]); // Stroke < Poi
assert!(z_orders[3] < z_orders[4]); // Poi < Label
}