mecha10-core 0.6.2

Mecha10 core runtime and foundational types
Documentation
// Tests for mecha10_core::dependency

use mecha10_core::dependency::*;
use std::collections::HashSet;

#[test]
fn test_simple_dependency_chain() {
    let graph = create_graph(&[
        ("camera", vec![]),
        ("vision", vec!["camera"]),
        ("planner", vec!["vision"]),
    ])
    .unwrap();

    let order = graph.topological_sort().unwrap();
    assert_eq!(order, vec!["camera", "vision", "planner"]);

    let reverse = graph.reverse_topological_sort().unwrap();
    assert_eq!(reverse, vec!["planner", "vision", "camera"]);
}

#[test]
fn test_parallel_dependencies() {
    let graph = create_graph(&[
        ("camera", vec![]),
        ("lidar", vec![]),
        ("imu", vec![]),
        ("fusion", vec!["camera", "lidar", "imu"]),
    ])
    .unwrap();

    let plan = graph.startup_plan().unwrap();
    assert_eq!(plan.level_count(), 2);

    // Level 0: camera, lidar, imu (can start in parallel)
    let level0 = plan.get_level(0).unwrap();
    assert_eq!(level0.len(), 3);
    assert!(level0.contains(&"camera".to_string()));
    assert!(level0.contains(&"lidar".to_string()));
    assert!(level0.contains(&"imu".to_string()));

    // Level 1: fusion (must wait for all sensors)
    let level1 = plan.get_level(1).unwrap();
    assert_eq!(level1, &vec!["fusion".to_string()]);
}

#[test]
fn test_circular_dependency_detection() {
    let result = create_graph(&[
        ("node_a", vec!["node_b"]),
        ("node_b", vec!["node_c"]),
        ("node_c", vec!["node_a"]),
    ]);

    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("Circular"));
}

#[test]
fn test_undefined_dependency() {
    let result = create_graph(&[("camera", vec![]), ("vision", vec!["camera", "nonexistent"])]);

    assert!(result.is_err());
}

#[test]
fn test_complex_graph() {
    let graph = create_graph(&[
        ("camera", vec![]),
        ("lidar", vec![]),
        ("imu", vec![]),
        ("odometry", vec!["imu"]),
        ("localization", vec!["lidar", "odometry"]),
        ("perception", vec!["camera", "lidar"]),
        ("planner", vec!["localization", "perception"]),
        ("controller", vec!["planner", "odometry"]),
    ])
    .unwrap();

    let plan = graph.startup_plan().unwrap();

    // Verify dependencies are respected
    let flat = plan.flatten();
    let camera_idx = flat.iter().position(|n| n == "camera").unwrap();
    let lidar_idx = flat.iter().position(|n| n == "lidar").unwrap();
    let perception_idx = flat.iter().position(|n| n == "perception").unwrap();

    assert!(camera_idx < perception_idx);
    assert!(lidar_idx < perception_idx);
}

#[test]
fn test_can_start() {
    let graph = create_graph(&[
        ("camera", vec![]),
        ("vision", vec!["camera"]),
        ("planner", vec!["vision"]),
    ])
    .unwrap();

    let mut running = HashSet::new();

    // camera has no dependencies
    assert!(graph.can_start("camera", &running));

    // vision depends on camera
    assert!(!graph.can_start("vision", &running));

    running.insert("camera".to_string());
    assert!(graph.can_start("vision", &running));

    // planner depends on vision
    assert!(!graph.can_start("planner", &running));

    running.insert("vision".to_string());
    assert!(graph.can_start("planner", &running));
}

#[test]
fn test_can_stop() {
    let graph = create_graph(&[
        ("camera", vec![]),
        ("vision", vec!["camera"]),
        ("planner", vec!["vision"]),
    ])
    .unwrap();

    let mut running: HashSet<String> = ["camera", "vision", "planner"].iter().map(|s| s.to_string()).collect();

    // planner has no dependents
    assert!(graph.can_stop("planner", &running));

    // camera has dependents (vision, planner)
    assert!(!graph.can_stop("camera", &running));

    running.remove("planner");
    running.remove("vision");
    assert!(graph.can_stop("camera", &running));
}

#[test]
fn test_get_all_dependencies() {
    let graph = create_graph(&[("a", vec![]), ("b", vec!["a"]), ("c", vec!["b"]), ("d", vec!["c"])]).unwrap();

    let deps = graph.get_all_dependencies("d");
    assert_eq!(deps.len(), 3);
    assert!(deps.contains("a"));
    assert!(deps.contains("b"));
    assert!(deps.contains("c"));
}

#[test]
fn test_get_all_dependents() {
    let graph = create_graph(&[("a", vec![]), ("b", vec!["a"]), ("c", vec!["b"]), ("d", vec!["c"])]).unwrap();

    let dependents = graph.get_all_dependents("a");
    assert_eq!(dependents.len(), 3);
    assert!(dependents.contains("b"));
    assert!(dependents.contains("c"));
    assert!(dependents.contains("d"));
}