use super::*;
fn entity(idx: u32) -> Entity {
Entity::from_raw_u32(idx).expect("valid entity index")
}
#[test]
fn test_new_is_empty() {
let acc = DragAccumulator::new();
let result = {
let mut acc = acc;
acc.flush()
};
assert_eq!((result.delta.x, result.delta.y), (0, 0));
assert!(result.transitions.is_empty());
assert!(result.current_dragging_entity.is_none());
assert_eq!(
(result.current_position.x, result.current_position.y),
(0, 0)
);
}
#[test]
fn test_accumulate_delta_sums() {
let mut acc = DragAccumulator::new();
acc.accumulate_delta(PhysicalPoint::new(3, 5));
acc.accumulate_delta(PhysicalPoint::new(-1, 2));
let result = acc.flush();
assert_eq!((result.delta.x, result.delta.y), (2, 7));
}
#[test]
fn test_flush_resets_delta_but_keeps_position_and_entity() {
let mut acc = DragAccumulator::new();
let e = entity(1);
acc.set_transition(DragTransition::Started {
entity: e,
start_pos: PhysicalPoint::new(0, 0),
timestamp: Instant::now(),
});
acc.accumulate_delta(PhysicalPoint::new(10, 20));
acc.update_position(PhysicalPoint::new(100, 200));
let first = acc.flush();
assert_eq!((first.delta.x, first.delta.y), (10, 20));
assert_eq!(first.current_dragging_entity, Some(e));
let second = acc.flush();
assert_eq!((second.delta.x, second.delta.y), (0, 0));
assert!(second.transitions.is_empty());
assert_eq!(second.current_dragging_entity, Some(e));
assert_eq!(
(second.current_position.x, second.current_position.y),
(100, 200)
);
}
#[test]
fn test_set_transition_started_sets_entity_and_is_taken_on_flush() {
let mut acc = DragAccumulator::new();
let e = entity(2);
acc.set_transition(DragTransition::Started {
entity: e,
start_pos: PhysicalPoint::new(1, 1),
timestamp: Instant::now(),
});
let result = acc.flush();
assert!(matches!(
result.transitions.as_slice(),
[DragTransition::Started { .. }]
));
assert_eq!(result.current_dragging_entity, Some(e));
}
#[test]
fn test_set_transition_ended_clears_entity() {
let mut acc = DragAccumulator::new();
let e = entity(3);
acc.set_transition(DragTransition::Started {
entity: e,
start_pos: PhysicalPoint::new(0, 0),
timestamp: Instant::now(),
});
acc.set_transition(DragTransition::Ended {
entity: e,
end_pos: PhysicalPoint::new(5, 5),
cancelled: false,
});
let result = acc.flush();
assert!(matches!(
result.transitions.as_slice(),
[.., DragTransition::Ended { .. }]
));
assert!(
result.current_dragging_entity.is_none(),
"Ended 遷移で current_dragging_entity は None になるべき"
);
}
#[test]
fn test_update_position_overwrites() {
let mut acc = DragAccumulator::new();
acc.update_position(PhysicalPoint::new(10, 10));
acc.update_position(PhysicalPoint::new(50, 60));
let result = acc.flush();
assert_eq!(
(result.current_position.x, result.current_position.y),
(50, 60)
);
}
#[test]
fn test_resource_shares_inner_state_across_clone() {
let res = DragAccumulatorResource::new();
let clone = res.clone();
clone.accumulate_delta(PhysicalPoint::new(7, 8));
clone.update_position(PhysicalPoint::new(70, 80));
let result = res.flush().expect("lock 取得できるはず");
assert_eq!((result.delta.x, result.delta.y), (7, 8));
assert_eq!(
(result.current_position.x, result.current_position.y),
(70, 80)
);
}
#[test]
fn test_resource_set_transition_reflected_in_flush() {
let res = DragAccumulatorResource::new();
let e = entity(4);
res.set_transition(DragTransition::Started {
entity: e,
start_pos: PhysicalPoint::new(2, 2),
timestamp: Instant::now(),
});
let result = res.flush().expect("lock 取得できるはず");
assert!(matches!(
result.transitions.as_slice(),
[DragTransition::Started { .. }]
));
assert_eq!(result.current_dragging_entity, Some(e));
}
fn started(e: Entity) -> DragTransition {
DragTransition::Started {
entity: e,
start_pos: PhysicalPoint::new(0, 0),
timestamp: Instant::now(),
}
}
fn ended(e: Entity, cancelled: bool) -> DragTransition {
DragTransition::Ended {
entity: e,
end_pos: PhysicalPoint::new(5, 5),
cancelled,
}
}
#[test]
fn ended_without_started_is_dropped() {
for cancelled in [false, true] {
let mut acc = DragAccumulator::new();
acc.set_transition(ended(entity(5), cancelled));
let result = acc.flush();
assert!(
result.transitions.is_empty(),
"cancelled={cancelled}: 開始の無い終了は捨てられるはず: {:?}",
result.transitions
);
assert!(result.current_dragging_entity.is_none());
}
}
#[test]
fn started_then_ended_in_one_flush_keeps_both_in_order() {
let mut acc = DragAccumulator::new();
let e = entity(6);
acc.set_transition(started(e));
acc.set_transition(ended(e, false));
let result = acc.flush();
assert!(
matches!(
result.transitions.as_slice(),
[
DragTransition::Started { .. },
DragTransition::Ended {
cancelled: false,
..
}
]
),
"{:?}",
result.transitions
);
assert!(result.current_dragging_entity.is_none());
assert!(acc.flush().transitions.is_empty(), "取り出すと空になる");
}
#[test]
fn ended_then_started_in_one_flush_keeps_order() {
let mut acc = DragAccumulator::new();
let first = entity(7);
let second = entity(8);
acc.set_transition(started(first));
acc.flush();
acc.set_transition(ended(first, false));
acc.set_transition(started(second));
let result = acc.flush();
assert!(
matches!(
result.transitions.as_slice(),
[
DragTransition::Ended { entity: a, .. },
DragTransition::Started { entity: b, .. }
] if *a == first && *b == second
),
"{:?}",
result.transitions
);
assert_eq!(result.current_dragging_entity, Some(second));
}
#[test]
fn push_ended_seed_without_handle_returns_false_and_warns_once() {
let (pushed, events) = std::thread::spawn(|| {
log_capture_kit::capture(|| push_ended_seed(entity(9), PhysicalPoint::new(4, 5), true))
})
.join()
.expect("thread panicked");
assert!(!pushed, "控えが無いのに積めたと返した");
let warns: Vec<_> = events
.iter()
.filter(|e| e.level == tracing::Level::WARN)
.collect();
assert_eq!(warns.len(), 1, "warn! がちょうど 1 件でない: {events:?}");
assert_eq!(
warns[0].field_str("event"),
Some("drag_end_seed_unreachable"),
"{events:?}"
);
}