use super::*;
#[test]
fn a_request_waits_while_the_same_header_is_in_flight() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
handle.park_writes();
let channel = channel_with_reader(raw).await;
let first_reply = short_msg(0x11);
let mut first =
Box::pin(channel.send(short_msg(0x10), move |candidate| *candidate == first_reply));
assert!(futures::poll!(first.as_mut()).is_pending());
assert_eq!(handle.written_reports().len(), 1);
assert_eq!(pending_len(&channel), 1);
let mut same_header = Box::pin(channel.send(same_header_msg(0x10, 0xa2), |_| true));
for _ in 0..5 {
assert!(futures::poll!(same_header.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(
handle.written_reports().len(),
1,
"the second request went out early"
);
assert_eq!(pending_len(&channel), 1);
let other_reply = short_msg(0x21);
let mut other =
Box::pin(channel.send(short_msg(0x20), move |candidate| *candidate == other_reply));
assert!(futures::poll!(other.as_mut()).is_pending());
assert_eq!(handle.written_reports().len(), 2);
assert_eq!(pending_len(&channel), 2);
drop(first);
assert_eq!(pending_len(&channel), 1);
assert_eq!(stale_len(&channel), 1);
assert!(futures::poll!(same_header.as_mut()).is_pending());
assert_eq!(
handle.written_reports().len(),
2,
"the parked request went out while a reply with its header was outstanding"
);
handle.send_incoming(first_reply).await;
for _ in 0..20 {
if handle.written_reports().len() == 3 {
break;
}
assert!(futures::poll!(same_header.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(handle.written_reports().len(), 3);
assert_eq!(stale_len(&channel), 0);
assert_eq!(pending_len(&channel), 2);
});
}
#[test]
fn a_reply_landing_after_a_timeout_cannot_answer_the_next_same_header_request() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let events = Arc::new(Mutex::new(Vec::new()));
let listener_events = Arc::clone(&events);
channel.add_msg_listener(move |msg, matched| {
listener_events.lock().unwrap().push((msg, matched));
});
let err = channel
.send_with_timeout(short_msg(0x10), |_| true, Duration::from_millis(25))
.await
.unwrap_err();
assert!(matches!(err, ChannelError::Timeout));
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 1);
let mut second = Box::pin(channel.send(same_header_msg(0x10, 0xa2), |_| true));
for _ in 0..5 {
assert!(futures::poll!(second.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(
handle.written_reports().len(),
1,
"the second request went out with a reply to the first still expected"
);
let late_reply = short_msg(0x11);
handle.send_incoming(late_reply).await;
wait_for_event_count(&events, 1).await;
assert_eq!(events.lock().unwrap()[0], (late_reply, false));
let second_reply = short_msg(0x12);
handle.queue_response(second_reply);
assert_eq!(second.await.unwrap(), second_reply);
assert_eq!(handle.written_reports().len(), 2);
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 0);
});
}
#[test]
fn an_unanswered_header_frees_after_the_grace() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let abandoned = Instant::now();
let err = channel
.send_with_timeout(short_msg(0x10), |_| true, Duration::from_millis(25))
.await
.unwrap_err();
assert!(matches!(err, ChannelError::Timeout));
assert_eq!(stale_len(&channel), 1);
let second_reply = short_msg(0x12);
handle.queue_response(second_reply);
let actual = channel
.send_with_timeout(
same_header_msg(0x10, 0xa2),
|_| true,
STALE_REPLY_GRACE + Duration::from_secs(2),
)
.await
.unwrap();
assert_eq!(actual, second_reply);
let waited = abandoned.elapsed();
assert!(
waited >= STALE_REPLY_GRACE,
"the second request went out {waited:?} after the first was abandoned"
);
assert_eq!(handle.written_reports().len(), 2);
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 0);
});
}
#[test]
fn an_identical_re_ask_waits_for_the_quarantined_reply() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let events = Arc::new(Mutex::new(Vec::new()));
let listener_events = Arc::clone(&events);
channel.add_msg_listener(move |msg, matched| {
listener_events.lock().unwrap().push((msg, matched));
});
let err = channel
.send_with_timeout(short_msg(0x10), |_| true, Duration::from_millis(25))
.await
.unwrap_err();
assert!(matches!(err, ChannelError::Timeout));
assert_eq!(stale_len(&channel), 1);
let mut retry = Box::pin(channel.send(short_msg(0x10), |_| true));
for _ in 0..5 {
assert!(futures::poll!(retry.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(
handle.written_reports().len(),
1,
"the re-ask went out with the first ask's reply still owed"
);
assert_eq!(pending_len(&channel), 0);
let first_reply = short_msg(0x11);
handle.send_incoming(first_reply).await;
wait_for_event_count(&events, 1).await;
assert_eq!(events.lock().unwrap()[0], (first_reply, false));
let retry_reply = short_msg(0x12);
handle.queue_response(retry_reply);
assert_eq!(retry.await.unwrap(), retry_reply);
assert_eq!(handle.written_reports().len(), 2);
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 0);
});
}
#[test]
fn an_identical_re_ask_may_adopt_the_outstanding_reply_when_it_says_so() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let events = Arc::new(Mutex::new(Vec::new()));
let listener_events = Arc::clone(&events);
channel.add_msg_listener(move |msg, matched| {
listener_events.lock().unwrap().push((msg, matched));
});
let err = channel
.send_with_timeout(short_msg(0x10), |_| true, Duration::from_millis(25))
.await
.unwrap_err();
assert!(matches!(err, ChannelError::Timeout));
assert_eq!(stale_len(&channel), 1);
let mut retry = Box::pin(channel.send_with(
short_msg(0x10),
|_| true,
SEND_RESPONSE_TIMEOUT,
AbandonedReply::AdoptIdentical,
));
assert!(futures::poll!(retry.as_mut()).is_pending());
assert_eq!(handle.written_reports().len(), 2, "the re-ask waited");
assert_eq!(pending_len(&channel), 1);
assert_eq!(stale_len(&channel), 0);
let first_reply = short_msg(0x11);
handle.send_incoming(first_reply).await;
assert_eq!(retry.await.unwrap(), first_reply);
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 1);
let mut other = Box::pin(channel.send(same_header_msg(0x10, 0xa2), |_| true));
for _ in 0..5 {
assert!(futures::poll!(other.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(
handle.written_reports().len(),
2,
"the other request went out early"
);
let retry_reply = short_msg(0x12);
handle.send_incoming(retry_reply).await;
wait_for_event_count(&events, 2).await;
assert_eq!(events.lock().unwrap()[1], (retry_reply, false));
let other_reply = short_msg(0x13);
handle.queue_response(other_reply);
for _ in 0..20 {
if handle.written_reports().len() == 3 {
break;
}
assert!(futures::poll!(other.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(other.await.unwrap(), other_reply);
assert_eq!(handle.written_reports().len(), 3);
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 0);
});
}
#[test]
fn adoption_is_decided_by_the_re_ask_not_the_abandoned_request() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let err = channel
.send_with(
short_msg(0x10),
|_| true,
Duration::from_millis(25),
AbandonedReply::AdoptIdentical,
)
.await
.unwrap_err();
assert!(matches!(err, ChannelError::Timeout));
assert_eq!(stale_len(&channel), 1);
let mut retry = Box::pin(channel.send(short_msg(0x10), |_| true));
for _ in 0..5 {
assert!(futures::poll!(retry.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(
handle.written_reports().len(),
1,
"a quarantining re-ask went out on the strength of the abandoned request's policy"
);
});
}
const GET_SENSOR_DPI: u8 = 2;
const SET_SENSOR_DPI: u8 = 3;
const BEFORE_WRITE: [u8; 3] = [0x00, 0x03, 0x20];
const AFTER_WRITE: [u8; 3] = [0x00, 0x06, 0x40];
#[test]
fn a_re_asked_read_cannot_adopt_a_reply_from_before_an_intervening_write() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let events = Arc::new(Mutex::new(Vec::new()));
let listener_events = Arc::clone(&events);
channel.add_msg_listener(move |msg, matched| {
listener_events.lock().unwrap().push((msg, matched));
});
let sw_id = channel.get_sw_id();
let dpi = move |function: u8, payload: [u8; 3]| {
v20::Message::Short(
v20::MessageHeader {
device_index: 0x01,
feature_index: 0x0a,
function_id: nibble::U4::from_lo(function),
software_id: sw_id,
},
payload,
)
};
let mut read = Box::pin(channel.send_v20(dpi(GET_SENSOR_DPI, [0; 3])));
assert!(futures::poll!(read.as_mut()).is_pending());
assert_eq!(handle.written_reports().len(), 1);
drop(read);
assert_eq!(stale_len(&channel), 1);
handle.queue_response(dpi(SET_SENSOR_DPI, AFTER_WRITE).into());
channel
.send_v20(dpi(SET_SENSOR_DPI, AFTER_WRITE))
.await
.unwrap();
assert_eq!(handle.written_reports().len(), 2);
wait_for_event_count(&events, 1).await;
let mut reread = Box::pin(channel.send_v20(dpi(GET_SENSOR_DPI, [0; 3])));
for _ in 0..5 {
assert!(futures::poll!(reread.as_mut()).is_pending());
futures_timer::Delay::new(Duration::from_millis(5)).await;
}
assert_eq!(
handle.written_reports().len(),
2,
"the re-asked read went out with the first read's reply still owed"
);
let stale_reply: HidppMessage = dpi(GET_SENSOR_DPI, BEFORE_WRITE).into();
handle.send_incoming(stale_reply).await;
wait_for_event_count(&events, 2).await;
assert_eq!(events.lock().unwrap()[1], (stale_reply, false));
handle.queue_response(dpi(GET_SENSOR_DPI, AFTER_WRITE).into());
let answer = reread.await.unwrap();
assert_eq!(answer.extend_payload()[..3], AFTER_WRITE);
assert_eq!(handle.written_reports().len(), 3);
assert_pending_empty(&channel);
assert_eq!(stale_len(&channel), 0);
});
}
#[test]
fn same_header_requests_are_answered_in_order() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = Arc::new(channel_with_reader(raw).await);
let first_reply = short_msg(0x31);
let second_reply = short_msg(0x32);
handle.queue_response(first_reply);
handle.queue_response(second_reply);
let (first, second) = futures::join!(
channel.send(short_msg(0x30), |_| true),
channel.send(short_msg(0x30), |_| true),
);
assert_eq!(first.unwrap(), first_reply);
assert_eq!(second.unwrap(), second_reply);
assert_eq!(handle.written_reports().len(), 2);
assert_pending_empty(&channel);
});
}