use super::*;
#[test]
fn send_v20_matches_response_by_header_ignoring_unrelated_messages() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let header = v20::MessageHeader {
device_index: 0x01,
feature_index: 0x05,
function_id: U4::from_lo(0x2),
software_id: U4::from_lo(0x3),
};
let request = v20::Message::Short(header, [0x00, 0x00, 0x00]);
let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
let wrong_device = v20::Message::Short(
v20::MessageHeader {
device_index: 0x02,
..header
},
[0, 0, 0],
);
let wrong_feature = v20::Message::Short(
v20::MessageHeader {
feature_index: 0x06,
..header
},
[0, 0, 0],
);
let wrong_sw_id = v20::Message::Short(
v20::MessageHeader {
software_id: U4::from_lo(0x4),
..header
},
[0, 0, 0],
);
let send_fut = channel.send_v20(request);
let feed_fut = async {
handle.send_incoming(wrong_device.into()).await;
handle.send_incoming(wrong_feature.into()).await;
handle.send_incoming(wrong_sw_id.into()).await;
handle.send_incoming(response.into()).await;
};
let (result, ()) = futures::join!(send_fut, feed_fut);
assert_eq!(result.unwrap(), response);
assert_pending_empty(&channel);
});
}
#[test]
fn send_v20_broadcast_event_does_not_resolve_pending_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 header = v20::MessageHeader {
device_index: 0x01,
feature_index: 0x05,
function_id: U4::from_lo(0x2),
software_id: U4::from_lo(0x3),
};
let request = v20::Message::Short(header, [0, 0, 0]);
let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
let event = v20::Message::Short(
v20::MessageHeader {
software_id: U4::from_lo(0x0),
..header
},
[0x01, 0x02, 0x03],
);
let send_fut = channel.send_v20(request);
let feed_fut = async {
handle.send_incoming(event.into()).await;
wait_for_event_count(&events, 1).await;
handle.send_incoming(response.into()).await;
};
let (result, ()) = futures::join!(send_fut, feed_fut);
assert_eq!(result.unwrap(), response);
wait_for_event_count(&events, 2).await;
let recorded = events.lock().unwrap().clone();
assert_eq!(
recorded,
vec![
(HidppMessage::from(event), false),
(HidppMessage::from(response), true),
]
);
assert_pending_empty(&channel);
});
}
#[test]
fn send_v20_response_may_arrive_as_a_different_report_width() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let header = v20::MessageHeader {
device_index: 0x01,
feature_index: 0x05,
function_id: U4::from_lo(0x2),
software_id: U4::from_lo(0x3),
};
let request = v20::Message::Short(header, [0, 0, 0]);
let response = v20::Message::Long(header, [0xaa; 16]);
handle.queue_response(response.into());
let result = channel.send_v20(request).await.unwrap();
assert_eq!(result, response);
assert_pending_empty(&channel);
});
}
#[test]
fn send_v20_error_frame_resolves_to_feature_error() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let header = v20::MessageHeader {
device_index: 0x01,
feature_index: 0x05,
function_id: U4::from_lo(0x2),
software_id: U4::from_lo(0x3),
};
let request = v20::Message::Short(header, [0, 0, 0]);
let error_response = v20_error_frame(header, ErrorType::InvalidArgument.into());
handle.queue_response(error_response.into());
let err = channel.send_v20(request).await.unwrap_err();
assert!(matches!(
err,
Hidpp20Error::Feature(ErrorType::InvalidArgument)
));
assert_pending_empty(&channel);
});
}
#[test]
fn send_v20_error_frame_with_unmapped_code_is_unsupported_response() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
let channel = channel_with_reader(raw).await;
let header = v20::MessageHeader {
device_index: 0x01,
feature_index: 0x05,
function_id: U4::from_lo(0x2),
software_id: U4::from_lo(0x3),
};
let request = v20::Message::Short(header, [0, 0, 0]);
let error_response = v20_error_frame(header, 0xfe);
handle.queue_response(error_response.into());
let err = channel.send_v20(request).await.unwrap_err();
assert!(matches!(err, Hidpp20Error::UnsupportedResponse));
assert_pending_empty(&channel);
});
}
#[test]
fn send_v20_write_through_preserves_typed_feature_errors() {
futures::executor::block_on(async {
let (raw, handle) = MockRawHidChannel::new();
handle.park_writes();
let channel = channel_with_reader(raw).await;
let header = v20::MessageHeader {
device_index: 0x01,
feature_index: 0x05,
function_id: U4::from_lo(0x2),
software_id: U4::from_lo(0x3),
};
let request = v20::Message::Short(header, [0, 0, 0]);
let error_response = v20_error_frame(header, ErrorType::Busy.into());
handle.queue_response(error_response.into());
let mut send = Box::pin(channel.send_v20_write_through(request, |_| false));
assert!(futures::poll!(send.as_mut()).is_pending());
handle.release_writes();
let error = send.await.unwrap_err();
assert!(matches!(error, Hidpp20Error::Feature(ErrorType::Busy)));
assert_pending_empty(&channel);
});
}
fn v20_error_frame(request_header: v20::MessageHeader, error_code: u8) -> v20::Message {
let error_header = v20::MessageHeader {
device_index: request_header.device_index,
feature_index: 0xff,
function_id: U4::from_hi(request_header.feature_index),
software_id: U4::from_lo(request_header.feature_index),
};
let mut payload = [0u8; 3];
payload[0] = nibble::combine(request_header.function_id, request_header.software_id);
payload[1] = error_code;
v20::Message::Short(error_header, payload)
}