use super::*;
#[test]
fn test_hold_down_suppresses_reeval() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_hold_down(60);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
let costs = make_costs(&[(1, 5.0), (2, 1.0)]);
state.set_parent_hysteresis(0.0); let result = state.evaluate_parent(&costs);
assert_eq!(result, None); }
#[test]
fn test_mandatory_switch_bypasses_hold_down() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_hold_down(60);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
state.remove_peer(&peer_a);
let result = state.evaluate_parent(&HashMap::new());
assert_eq!(result, Some(peer_b)); }
#[test]
fn test_heterogeneous_7node_avoids_bottleneck() {
let root = make_node_addr(0);
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
let peer1 = make_node_addr(1); let peer2 = make_node_addr(2);
state.update_peer(
ParentDeclaration::new(peer1, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer2, root, 1, 1000),
make_coords(&[2, 0]),
);
let result_no_cost = state.evaluate_parent(&HashMap::new());
assert_eq!(result_no_cost, Some(peer1));
let costs = make_costs(&[(1, 1.01), (2, 6.0)]);
let result_with_cost = state.evaluate_parent(&costs);
assert_eq!(result_with_cost, Some(peer1));
state.set_parent(peer2, 1, 1000);
state.recompute_coords();
assert_eq!(state.my_coords().depth(), 2);
let result_switch = state.evaluate_parent(&costs);
assert_eq!(result_switch, Some(peer1));
state.set_parent_hysteresis(0.2);
let result_hyst = state.evaluate_parent(&costs);
assert_eq!(result_hyst, Some(peer1));
}
#[test]
fn test_cost_degradation_triggers_switch() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_parent_hysteresis(0.2);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
let initial_costs = make_costs(&[(1, 1.05), (2, 1.08)]);
let result = state.evaluate_parent(&initial_costs);
assert_eq!(result, Some(peer_a));
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
let result = state.evaluate_parent(&initial_costs);
assert_eq!(result, None);
let degraded_costs = make_costs(&[(1, 6.0), (2, 1.08)]);
let result = state.evaluate_parent(°raded_costs);
assert_eq!(result, Some(peer_b));
}
#[test]
fn test_cost_improvement_within_hysteresis_no_switch() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_parent_hysteresis(0.2);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
let costs = make_costs(&[(1, 2.0), (2, 1.5)]);
let result = state.evaluate_parent(&costs);
assert_eq!(result, None);
}
#[test]
fn test_single_peer_no_reeval_benefit() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
let peer_a = make_node_addr(1);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
let costs = make_costs(&[(1, 1.05)]);
let result = state.evaluate_parent(&costs);
assert_eq!(result, Some(peer_a));
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
let bad_costs = make_costs(&[(1, 50.0)]);
let result = state.evaluate_parent(&bad_costs);
assert_eq!(result, None);
}
#[test]
fn test_flap_dampening_engages_after_threshold() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_flap_dampening(3, 60, 3600);
state.set_hold_down(0);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
assert!(!state.is_flap_dampened());
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
assert!(!state.is_flap_dampened());
state.set_parent(peer_b, 2, 2000);
state.recompute_coords();
assert!(!state.is_flap_dampened());
let dampened = state.set_parent(peer_a, 3, 3000);
state.recompute_coords();
assert!(dampened);
assert!(state.is_flap_dampened());
let costs = make_costs(&[(1, 10.0), (2, 1.0)]);
let result = state.evaluate_parent(&costs);
assert_eq!(result, None); }
#[test]
fn test_flap_dampening_allows_mandatory_switches() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_flap_dampening(3, 60, 3600);
state.set_hold_down(0);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
state.set_parent(peer_b, 2, 2000);
state.recompute_coords();
state.set_parent(peer_a, 3, 3000);
state.recompute_coords();
assert!(state.is_flap_dampened());
state.remove_peer(&peer_a);
let result = state.evaluate_parent(&HashMap::new());
assert_eq!(result, Some(peer_b)); }
#[test]
fn test_flap_dampening_expires() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_flap_dampening(3, 60, 0); state.set_hold_down(0);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
state.set_parent(peer_b, 2, 2000);
state.recompute_coords();
let dampened = state.set_parent(peer_a, 3, 3000);
state.recompute_coords();
assert!(dampened);
assert!(!state.is_flap_dampened());
let costs = make_costs(&[(1, 10.0), (2, 1.0)]);
let result = state.evaluate_parent(&costs);
assert_eq!(result, Some(peer_b)); }
#[test]
fn test_flap_dampening_below_threshold() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_flap_dampening(4, 60, 3600); state.set_hold_down(0);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
state.set_parent(peer_b, 2, 2000);
state.recompute_coords();
state.set_parent(peer_a, 3, 3000);
state.recompute_coords();
assert!(!state.is_flap_dampened());
let costs = make_costs(&[(1, 10.0), (2, 1.0)]);
let result = state.evaluate_parent(&costs);
assert_eq!(result, Some(peer_b)); }
#[test]
fn test_flap_dampening_window_reset() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_flap_dampening(3, 0, 3600);
state.set_hold_down(0);
let peer_a = make_node_addr(1);
let peer_b = make_node_addr(2);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.update_peer(
ParentDeclaration::new(peer_b, root, 1, 1000),
make_coords(&[2, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
state.set_parent(peer_b, 2, 2000);
state.recompute_coords();
state.set_parent(peer_a, 3, 3000);
state.recompute_coords();
assert!(!state.is_flap_dampened());
}
#[test]
fn test_flap_dampening_same_parent_no_count() {
let my_node = make_node_addr(5);
let mut state = TreeState::new(my_node);
state.set_flap_dampening(3, 60, 3600);
state.set_hold_down(0);
let peer_a = make_node_addr(1);
let root = make_node_addr(0);
state.update_peer(
ParentDeclaration::new(peer_a, root, 1, 1000),
make_coords(&[1, 0]),
);
state.set_parent(peer_a, 1, 1000);
state.recompute_coords();
state.set_parent(peer_a, 2, 2000);
state.recompute_coords();
state.set_parent(peer_a, 3, 3000);
state.recompute_coords();
state.set_parent(peer_a, 4, 4000);
state.recompute_coords();
state.set_parent(peer_a, 5, 5000);
state.recompute_coords();
assert!(!state.is_flap_dampened());
}