use super::*;
use crate::component::Component;
use crate::input::{Event, Key};
use ratatui::style::Color;
#[test]
fn test_flame_node_new() {
let node = FlameNode::new("main()", 500);
assert_eq!(node.label(), "main()");
assert_eq!(node.value(), 500);
assert_eq!(node.color(), Color::Cyan);
assert!(node.children().is_empty());
assert!(!node.has_children());
}
#[test]
fn test_flame_node_with_color() {
let node = FlameNode::new("main()", 500).with_color(Color::Red);
assert_eq!(node.color(), Color::Red);
}
#[test]
fn test_flame_node_with_child() {
let node = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
assert_eq!(node.children().len(), 1);
assert!(node.has_children());
assert_eq!(node.children()[0].label(), "compute()");
}
#[test]
fn test_flame_node_with_children() {
let children = vec![
FlameNode::new("a()", 100),
FlameNode::new("b()", 200),
FlameNode::new("c()", 150),
];
let node = FlameNode::new("main()", 500).with_children(children);
assert_eq!(node.children().len(), 3);
assert!(node.has_children());
}
#[test]
fn test_flame_node_total_value() {
let node = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
assert_eq!(node.total_value(), 500);
}
#[test]
fn test_flame_node_self_value() {
let node = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300))
.with_child(FlameNode::new("io()", 100));
assert_eq!(node.self_value(), 100); }
#[test]
fn test_flame_node_self_value_no_children() {
let node = FlameNode::new("leaf()", 100);
assert_eq!(node.self_value(), 100);
}
#[test]
fn test_flame_node_self_value_children_exceed_parent() {
let node = FlameNode::new("main()", 100)
.with_child(FlameNode::new("compute()", 80))
.with_child(FlameNode::new("io()", 80));
assert_eq!(node.self_value(), 0); }
#[test]
fn test_state_new() {
let state = FlameGraphState::new();
assert!(state.root().is_none());
assert!(state.zoom_stack().is_empty());
assert_eq!(state.selected_depth(), 0);
assert_eq!(state.selected_index(), 0);
assert_eq!(state.search_query(), "");
assert_eq!(state.title(), None);
}
#[test]
fn test_state_with_root() {
let root = FlameNode::new("main()", 500);
let state = FlameGraphState::with_root(root);
assert!(state.root().is_some());
assert_eq!(state.root().unwrap().label(), "main()");
}
#[test]
fn test_state_with_title() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500)).with_title("CPU Profile");
assert_eq!(state.title(), Some("CPU Profile"));
}
#[test]
fn test_state_default() {
let state = FlameGraphState::default();
assert!(state.root().is_none());
}
#[test]
fn test_set_root() {
let mut state = FlameGraphState::new();
state.set_root(FlameNode::new("main()", 500));
assert!(state.root().is_some());
assert_eq!(state.root().unwrap().label(), "main()");
}
#[test]
fn test_set_root_resets_zoom() {
let mut state = FlameGraphState::with_root(
FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300).with_child(FlameNode::new("sort()", 200))),
);
state.select_down();
state.zoom_in();
assert!(!state.zoom_stack().is_empty());
state.set_root(FlameNode::new("new_main()", 1000));
assert!(state.zoom_stack().is_empty());
assert_eq!(state.selected_depth(), 0);
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_clear() {
let mut state = FlameGraphState::with_root(FlameNode::new("main()", 500));
state.set_search("test".to_string());
state.clear();
assert!(state.root().is_none());
assert_eq!(state.search_query(), "");
assert!(state.zoom_stack().is_empty());
}
#[test]
fn test_select_down() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
assert_eq!(state.selected_depth(), 0);
assert!(state.select_down());
assert_eq!(state.selected_depth(), 1);
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_select_down_no_children() {
let root = FlameNode::new("main()", 500);
let mut state = FlameGraphState::with_root(root);
assert!(!state.select_down());
assert_eq!(state.selected_depth(), 0);
}
#[test]
fn test_select_down_empty() {
let mut state = FlameGraphState::new();
assert!(!state.select_down());
}
#[test]
fn test_select_up() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
state.select_down();
assert_eq!(state.selected_depth(), 1);
assert!(state.select_up());
assert_eq!(state.selected_depth(), 0);
}
#[test]
fn test_select_up_at_root() {
let root = FlameNode::new("main()", 500);
let mut state = FlameGraphState::with_root(root);
assert!(!state.select_up());
assert_eq!(state.selected_depth(), 0);
}
#[test]
fn test_select_up_empty() {
let mut state = FlameGraphState::new();
assert!(!state.select_up());
}
#[test]
fn test_select_left() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300))
.with_child(FlameNode::new("io()", 100));
let mut state = FlameGraphState::with_root(root);
state.select_down(); state.select_right(); assert_eq!(state.selected_index(), 1);
assert!(state.select_left());
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_select_left_at_first() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
state.select_down();
assert!(!state.select_left());
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_select_left_empty() {
let mut state = FlameGraphState::new();
assert!(!state.select_left());
}
#[test]
fn test_select_right() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300))
.with_child(FlameNode::new("io()", 100));
let mut state = FlameGraphState::with_root(root);
state.select_down(); assert!(state.select_right());
assert_eq!(state.selected_index(), 1);
}
#[test]
fn test_select_right_at_last() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
state.select_down();
assert!(!state.select_right());
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_select_right_empty() {
let mut state = FlameGraphState::new();
assert!(!state.select_right());
}
#[test]
fn test_navigation_deep() {
let root = FlameNode::new("main()", 500).with_child(
FlameNode::new("compute()", 300)
.with_child(FlameNode::new("sort()", 200).with_child(FlameNode::new("merge()", 100))),
);
let mut state = FlameGraphState::with_root(root);
assert!(state.select_down()); assert!(state.select_down()); assert!(state.select_down()); assert!(!state.select_down()); assert_eq!(state.selected_depth(), 3);
assert!(state.select_up()); assert_eq!(state.selected_depth(), 2);
}
#[test]
fn test_zoom_in() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300).with_child(FlameNode::new("sort()", 200)));
let mut state = FlameGraphState::with_root(root);
state.select_down();
assert!(state.zoom_in());
assert_eq!(state.zoom_stack(), &["compute()".to_string()]);
assert_eq!(state.current_view_root().unwrap().label(), "compute()");
assert_eq!(state.selected_depth(), 0);
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_zoom_in_leaf_fails() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("leaf()", 100));
let mut state = FlameGraphState::with_root(root);
state.select_down();
assert!(!state.zoom_in());
assert!(state.zoom_stack().is_empty());
}
#[test]
fn test_zoom_out() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300).with_child(FlameNode::new("sort()", 200)));
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.zoom_in();
assert!(state.zoom_out());
assert!(state.zoom_stack().is_empty());
assert_eq!(state.current_view_root().unwrap().label(), "main()");
}
#[test]
fn test_zoom_out_at_root() {
let root = FlameNode::new("main()", 500);
let mut state = FlameGraphState::with_root(root);
assert!(!state.zoom_out());
}
#[test]
fn test_reset_zoom() {
let root = FlameNode::new("main()", 500).with_child(
FlameNode::new("compute()", 300)
.with_child(FlameNode::new("sort()", 200).with_child(FlameNode::new("merge()", 100))),
);
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.zoom_in();
state.select_down();
state.zoom_in();
assert_eq!(state.zoom_stack().len(), 2);
state.reset_zoom();
assert!(state.zoom_stack().is_empty());
assert_eq!(state.selected_depth(), 0);
assert_eq!(state.selected_index(), 0);
}
#[test]
fn test_zoom_stack_deep() {
let root = FlameNode::new("main()", 500).with_child(
FlameNode::new("a()", 400)
.with_child(FlameNode::new("b()", 300).with_child(FlameNode::new("c()", 200))),
);
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.zoom_in();
assert_eq!(state.current_view_root().unwrap().label(), "a()");
state.select_down();
state.zoom_in();
assert_eq!(state.current_view_root().unwrap().label(), "b()");
state.zoom_out();
assert_eq!(state.current_view_root().unwrap().label(), "a()");
}
#[test]
fn test_set_search() {
let mut state = FlameGraphState::with_root(FlameNode::new("main()", 500));
state.set_search("compute".to_string());
assert_eq!(state.search_query(), "compute");
}
#[test]
fn test_clear_search_via_set() {
let mut state = FlameGraphState::with_root(FlameNode::new("main()", 500));
state.set_search("compute".to_string());
state.set_search(String::new());
assert_eq!(state.search_query(), "");
}
#[test]
fn test_current_view_root_no_zoom() {
let root = FlameNode::new("main()", 500);
let state = FlameGraphState::with_root(root);
assert_eq!(state.current_view_root().unwrap().label(), "main()");
}
#[test]
fn test_current_view_root_empty() {
let state = FlameGraphState::new();
assert!(state.current_view_root().is_none());
}
#[test]
fn test_current_view_root_with_zoom() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300).with_child(FlameNode::new("sort()", 200)));
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.zoom_in();
assert_eq!(state.current_view_root().unwrap().label(), "compute()");
}
#[test]
fn test_selected_frame_at_root() {
let root = FlameNode::new("main()", 500);
let state = FlameGraphState::with_root(root);
assert_eq!(state.selected_frame().unwrap().label(), "main()");
}
#[test]
fn test_selected_frame_after_navigation() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300))
.with_child(FlameNode::new("io()", 100));
let mut state = FlameGraphState::with_root(root);
state.select_down();
assert_eq!(state.selected_frame().unwrap().label(), "compute()");
state.select_right();
assert_eq!(state.selected_frame().unwrap().label(), "io()");
}
#[test]
fn test_selected_frame_empty() {
let state = FlameGraphState::new();
assert!(state.selected_frame().is_none());
}
#[test]
fn test_max_depth_single() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(state.max_depth(), 0);
}
#[test]
fn test_max_depth_deep() {
let root = FlameNode::new("main()", 500).with_child(
FlameNode::new("a()", 300)
.with_child(FlameNode::new("b()", 200).with_child(FlameNode::new("c()", 100))),
);
let state = FlameGraphState::with_root(root);
assert_eq!(state.max_depth(), 3);
}
#[test]
fn test_max_depth_empty() {
let state = FlameGraphState::new();
assert_eq!(state.max_depth(), 0);
}
#[test]
fn test_handle_event_not_focused() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
let msg = FlameGraph::handle_event(&state, &Event::key(Key::Down), &EventContext::default());
assert_eq!(msg, None);
}
#[test]
fn test_handle_event_disabled() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
let msg = FlameGraph::handle_event(
&state,
&Event::key(Key::Down),
&EventContext::new().focused(true).disabled(true),
);
assert_eq!(msg, None);
}
#[test]
fn test_handle_event_down() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Down),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectDown)
);
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::char('j'),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectDown)
);
}
#[test]
fn test_handle_event_up() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Up),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectUp)
);
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::char('k'),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectUp)
);
}
#[test]
fn test_handle_event_left() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Left),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectLeft)
);
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::char('h'),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectLeft)
);
}
#[test]
fn test_handle_event_right() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Right),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectRight)
);
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::char('l'),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SelectRight)
);
}
#[test]
fn test_handle_event_enter_zoom_in() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Enter),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::ZoomIn)
);
}
#[test]
fn test_handle_event_escape_zoom_out() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Esc),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::ZoomOut)
);
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Backspace),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::ZoomOut)
);
}
#[test]
fn test_handle_event_home_reset_zoom() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::key(Key::Home),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::ResetZoom)
);
}
#[test]
fn test_handle_event_slash_search() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::char('/'),
&EventContext::new().focused(true)
),
Some(FlameGraphMessage::SetSearch(String::new()))
);
}
#[test]
fn test_handle_event_unknown_key() {
let state = FlameGraphState::with_root(FlameNode::new("main()", 500));
assert_eq!(
FlameGraph::handle_event(
&state,
&Event::char('x'),
&EventContext::new().focused(true)
),
None
);
}
#[test]
fn test_update_set_root() {
let mut state = FlameGraphState::new();
let output = FlameGraph::update(
&mut state,
FlameGraphMessage::SetRoot(FlameNode::new("main()", 500)),
);
assert_eq!(output, None);
assert!(state.root().is_some());
}
#[test]
fn test_update_clear() {
let mut state = FlameGraphState::with_root(FlameNode::new("main()", 500));
let output = FlameGraph::update(&mut state, FlameGraphMessage::Clear);
assert_eq!(output, None);
assert!(state.root().is_none());
}
#[test]
fn test_update_set_search() {
let mut state = FlameGraphState::with_root(FlameNode::new("main()", 500));
FlameGraph::update(&mut state, FlameGraphMessage::SetSearch("test".to_string()));
assert_eq!(state.search_query(), "test");
}
#[test]
fn test_update_clear_search() {
let mut state = FlameGraphState::with_root(FlameNode::new("main()", 500));
state.set_search("test".to_string());
FlameGraph::update(&mut state, FlameGraphMessage::ClearSearch);
assert_eq!(state.search_query(), "");
}
#[test]
fn test_update_select_down_output() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
let output = FlameGraph::update(&mut state, FlameGraphMessage::SelectDown);
assert_eq!(
output,
Some(FlameGraphOutput::FrameSelected {
label: "compute()".to_string(),
value: 300,
self_value: 300,
})
);
}
#[test]
fn test_update_select_up_output() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
state.select_down();
let output = FlameGraph::update(&mut state, FlameGraphMessage::SelectUp);
assert_eq!(
output,
Some(FlameGraphOutput::FrameSelected {
label: "main()".to_string(),
value: 500,
self_value: 200,
})
);
}
#[test]
fn test_update_zoom_in_output() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300).with_child(FlameNode::new("sort()", 200)));
let mut state = FlameGraphState::with_root(root);
state.select_down();
let output = FlameGraph::update(&mut state, FlameGraphMessage::ZoomIn);
assert_eq!(
output,
Some(FlameGraphOutput::ZoomedIn("compute()".to_string()))
);
}
#[test]
fn test_update_zoom_out_output() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("compute()", 300).with_child(FlameNode::new("sort()", 200)));
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.zoom_in();
let output = FlameGraph::update(&mut state, FlameGraphMessage::ZoomOut);
assert_eq!(output, Some(FlameGraphOutput::ZoomedOut));
}
#[test]
fn test_update_reset_zoom() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.zoom_in();
let output = FlameGraph::update(&mut state, FlameGraphMessage::ResetZoom);
assert_eq!(output, None);
assert!(state.zoom_stack().is_empty());
}
#[test]
fn test_update_empty_graph_ignores_navigation() {
let mut state = FlameGraphState::new();
let output = FlameGraph::update(&mut state, FlameGraphMessage::SelectDown);
assert_eq!(output, None);
}
#[test]
fn test_instance_update() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("compute()", 300));
let mut state = FlameGraphState::with_root(root);
let output = state.update(FlameGraphMessage::SelectDown);
assert!(output.is_some());
}
#[test]
fn test_init() {
let state = FlameGraph::init();
assert!(state.root().is_none());
}
#[test]
fn test_zoom_in_on_root_with_children() {
let root = FlameNode::new("main()", 500).with_child(FlameNode::new("a()", 300));
let mut state = FlameGraphState::with_root(root);
assert!(state.zoom_in());
assert_eq!(state.current_view_root().unwrap().label(), "main()");
}
#[test]
fn test_select_down_multiple_children() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("a()", 200))
.with_child(FlameNode::new("b()", 150))
.with_child(FlameNode::new("c()", 100));
let mut state = FlameGraphState::with_root(root);
state.select_down(); assert_eq!(state.selected_frame().unwrap().label(), "a()");
state.select_right();
assert_eq!(state.selected_frame().unwrap().label(), "b()");
state.select_right();
assert_eq!(state.selected_frame().unwrap().label(), "c()");
assert!(!state.select_right());
}
#[test]
fn test_navigation_sibling_at_depth_zero() {
let root = FlameNode::new("main()", 500);
let mut state = FlameGraphState::with_root(root);
assert!(!state.select_left());
assert!(!state.select_right());
}
#[test]
fn test_deep_nesting_navigation() {
let root = FlameNode::new("d0", 1000).with_child(
FlameNode::new("d1", 800).with_child(
FlameNode::new("d2", 600)
.with_child(FlameNode::new("d3", 400).with_child(FlameNode::new("d4", 200))),
),
);
let mut state = FlameGraphState::with_root(root);
for depth in 1..=4 {
assert!(state.select_down());
assert_eq!(state.selected_depth(), depth);
}
assert!(!state.select_down());
for depth in (0..4).rev() {
assert!(state.select_up());
assert_eq!(state.selected_depth(), depth);
}
assert!(!state.select_up());
}
#[test]
fn test_zoom_in_no_children_returns_false() {
let root = FlameNode::new("leaf()", 100);
let mut state = FlameGraphState::with_root(root);
assert!(!state.zoom_in());
}
#[test]
fn test_select_up_finds_correct_parent() {
let root = FlameNode::new("main()", 500)
.with_child(FlameNode::new("a()", 200).with_child(FlameNode::new("a1()", 100)))
.with_child(FlameNode::new("b()", 200).with_child(FlameNode::new("b1()", 100)));
let mut state = FlameGraphState::with_root(root);
state.select_down();
state.select_right();
assert_eq!(state.selected_frame().unwrap().label(), "b()");
state.select_down();
assert_eq!(state.selected_frame().unwrap().label(), "b1()");
state.select_up();
assert_eq!(state.selected_frame().unwrap().label(), "b()");
}