blob: 4ba127e38d5dcabbeeccef735c0a67de43098216 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{
packet_logs::{append_pcap, write_pcap_header},
*,
},
async_utils::PollExt,
fidl::{endpoints::RequestStream, Error as FidlError},
fidl_fuchsia_bluetooth_snoop::{PacketType, SnoopMarker, SnoopProxy, SnoopRequestStream},
fuchsia_async::{Channel, Executor},
fuchsia_inspect::{assert_inspect_tree, Inspector},
fuchsia_zircon as zx,
futures::pin_mut,
std::task::Poll,
};
fn setup() -> (
Executor,
ConcurrentSnooperPacketFutures,
PacketLogs,
SubscriptionManager,
ConcurrentClientRequestFutures,
Inspector,
) {
let inspect = Inspector::new();
(
fasync::Executor::new().unwrap(),
ConcurrentSnooperPacketFutures::new(),
PacketLogs::new(
10,
10,
10,
Duration::new(10, 0),
inspect.root().create_child("packet_log"),
),
SubscriptionManager::new(),
ConcurrentClientRequestFutures::new(),
inspect,
)
}
#[test]
fn test_id_generator() {
let mut id_gen = IdGenerator::new();
assert_eq!(id_gen.next(), ClientId(0));
assert_eq!(id_gen.next(), ClientId(1));
}
#[test]
fn test_register_new_client() {
let (_exec, _snoopers, _logs, _subscribers, mut requests, _inspect) = setup();
assert_eq!(requests.len(), 0);
let (_tx, rx) = zx::Channel::create().unwrap();
let stream = SnoopRequestStream::from_channel(Channel::from_channel(rx).unwrap());
register_new_client(stream, &mut requests, ClientId(0));
assert_eq!(requests.len(), 1);
}
fn fidl_endpoints() -> (SnoopProxy, SnoopRequestStream) {
let (proxy, server) = fidl::endpoints::create_proxy::<SnoopMarker>().unwrap();
let request_stream = server.into_stream().unwrap();
(proxy, request_stream)
}
fn unwrap_request<T, E>(request: Poll<Option<Result<T, E>>>) -> T {
if let Poll::Ready(Some(Ok(request))) = request {
return request;
}
panic!("Failed to receive request");
}
fn unwrap_response<T, E>(response: Poll<Result<T, E>>) -> T {
if let Poll::Ready(Ok(response)) = response {
return response;
}
panic!("Failed to receive response");
}
#[test]
fn test_snoop_default_command_line_args() {
let args = Args::from_args(&["bt-snoop.cmx"], &[]).expect("Args created from empty args");
assert_eq!(args.log_size_soft_kib, 32);
assert_eq!(args.log_size_hard_kib, 256);
assert_eq!(args.log_time_seconds, 60);
assert_eq!(args.max_device_count, 8);
assert_eq!(args.truncate_payload, None);
assert_eq!(args.verbosity, 0);
}
#[test]
fn test_snoop_command_line_args() {
let log_size_kib = 1;
let log_time_seconds = 2;
let max_device_count = 3;
let truncate_payload = 4;
let verbosity = 2;
let raw_args = &[
"--log-size-soft-kib",
&log_size_kib.to_string(),
"--log-size-hard-kib",
&log_size_kib.to_string(),
"--log-time-seconds",
&log_time_seconds.to_string(),
"--max-device-count",
&max_device_count.to_string(),
"--truncate-payload",
&truncate_payload.to_string(),
"-v",
"-v",
];
let args = Args::from_args(&["bt-snoop.cmx"], raw_args).expect("Args created from args");
assert_eq!(args.log_size_soft_kib, log_size_kib);
assert_eq!(args.log_size_hard_kib, log_size_kib);
assert_eq!(args.log_time_seconds, log_time_seconds);
assert_eq!(args.max_device_count, max_device_count);
assert_eq!(args.truncate_payload, Some(truncate_payload));
assert_eq!(args.verbosity, verbosity);
}
#[fasync::run_until_stalled(test)]
async fn test_packet_logs_inspect() {
// This is a test that basic inspect data is plumbed through from the inspect root.
// More comprehensive testing of possible permutations of packet log inspect data
// is found in bounded_queue.rs
let inspect = Inspector::new();
let runtime_metrics_node = inspect.root().create_child("runtime_metrics");
let mut packet_logs =
PacketLogs::new(2, 256, 256, Duration::from_secs(60), runtime_metrics_node);
assert_inspect_tree!(inspect, root: {
runtime_metrics: {
logging_active_for_devices: "",
}
});
let id_1 = String::from("001");
packet_logs.add_device(id_1.clone());
let mut expected_data = vec![];
write_pcap_header(&mut expected_data).expect("write to succeed");
assert_inspect_tree!(inspect, root: {
runtime_metrics: {
logging_active_for_devices: "\"001\"",
device_0: {
hci_device_name: "001",
byte_len: 0u64,
number_of_items: 0u64,
data: expected_data,
},
}
});
let ts = zx::Time::from_nanos(123 * 1_000_000_000);
let packet = snooper::SnoopPacket::new(false, PacketType::Data, ts, vec![3, 2, 1]);
// write pcap header and packet data to expected_data buffer
let mut expected_data = vec![];
write_pcap_header(&mut expected_data).expect("write to succeed");
append_pcap(&mut expected_data, &packet, None).expect("write to succeed");
packet_logs.log_packet(&id_1, packet).await;
assert_inspect_tree!(inspect, root: {
runtime_metrics: {
logging_active_for_devices: "\"001\"",
device_0: {
hci_device_name: "001",
byte_len: 51u64,
number_of_items: 1u64,
data: expected_data,
},
}
});
drop(packet_logs);
}
#[test]
fn test_snoop_config_inspect() {
let args = Args {
log_size_soft_kib: 1,
log_size_hard_kib: 1,
log_time_seconds: 2,
max_device_count: 3,
truncate_payload: Some(4),
verbosity: 5,
};
let inspect = Inspector::new();
let snoop_config_node = inspect.root().create_child("configuration");
let config = SnoopConfig::from_args(args, snoop_config_node);
assert_inspect_tree!(inspect, root: {
configuration: {
log_size_soft_max_bytes: 1024u64,
log_size_hard_max_bytes: "1024",
log_time: 2u64,
max_device_count: 3u64,
truncate_payload: "4 bytes",
hci_dir: HCI_DEVICE_CLASS_PATH,
}
});
drop(config);
}
// Helper that pumps the request stream to get back a single request, panicking if the stream
// stalls before a request is returned.
fn pump_request_stream(
exec: &mut fasync::Executor,
mut request_stream: SnoopRequestStream,
id: ClientId,
) -> ClientRequest {
let request = unwrap_request(exec.run_until_stalled(&mut request_stream.next()));
(id, (Some(Ok(request)), request_stream))
}
// Helper that pumps the the handle_client_request until stalled, panicking if the future
// stalls in a pending state or returns an error.
fn pump_handle_client_request(
exec: &mut fasync::Executor,
request: ClientRequest,
client_requests: &mut ConcurrentClientRequestFutures,
subscribers: &mut SubscriptionManager,
packet_logs: &PacketLogs,
) {
let handler = handle_client_request(request, client_requests, subscribers, packet_logs);
pin_mut!(handler);
exec.run_until_stalled(&mut handler)
.expect("Handler future to complete")
.expect("Client channel to accept response");
}
#[test]
fn test_handle_client_request() {
let (mut exec, mut _snoopers, mut logs, mut subscribers, mut requests, _inspect) = setup();
// unrecognized device returns an error to the client
let (proxy, request_stream) = fidl_endpoints();
let mut client_fut = proxy.start(true, Some(""));
let _ = exec.run_until_stalled(&mut client_fut);
let request = pump_request_stream(&mut exec, request_stream, ClientId(0));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
let response = unwrap_response(exec.run_until_stalled(&mut client_fut));
assert!(response.error.is_some());
assert_eq!(subscribers.number_of_subscribers(), 0);
// valid device returns no errors to a client subscribed to that device
let (proxy, request_stream) = fidl_endpoints();
logs.add_device(String::new());
let mut client_fut = proxy.start(true, Some(""));
let _ = exec.run_until_stalled(&mut client_fut);
let request = pump_request_stream(&mut exec, request_stream, ClientId(1));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
let response = unwrap_response(exec.run_until_stalled(&mut client_fut));
assert!(response.error.is_none());
assert_eq!(subscribers.number_of_subscribers(), 1);
// valid device returns no errors to a client subscribed globally
let (proxy, request_stream) = fidl_endpoints();
let mut client_fut = proxy.start(true, None);
let _ = exec.run_until_stalled(&mut client_fut);
let request = pump_request_stream(&mut exec, request_stream, ClientId(2));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
let response = unwrap_response(exec.run_until_stalled(&mut client_fut));
assert!(response.error.is_none());
assert_eq!(subscribers.number_of_subscribers(), 2);
// second request by the same client returns an error
let (proxy, request_stream) = fidl_endpoints();
let mut client_fut = proxy.start(true, None);
let _ = exec.run_until_stalled(&mut client_fut);
let request = pump_request_stream(&mut exec, request_stream, ClientId(2));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
let response = unwrap_response(exec.run_until_stalled(&mut client_fut));
assert!(response.error.is_some());
assert_eq!(subscribers.number_of_subscribers(), 2);
// valid device returns no errors to a client requesting a dump
let (proxy, request_stream) = fidl_endpoints();
let mut client_fut = proxy.start(false, None);
let _ = exec.run_until_stalled(&mut client_fut);
let request = pump_request_stream(&mut exec, request_stream, ClientId(3));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
let response = unwrap_response(exec.run_until_stalled(&mut client_fut));
assert!(response.error.is_none());
assert_eq!(subscribers.number_of_subscribers(), 2);
}
#[test]
fn test_handle_bad_client_request() {
let (mut exec, mut _snoopers, mut logs, mut subscribers, mut requests, _inspect) = setup();
let id = ClientId(0);
let err = Some(Err(FidlError::Invalid));
let (_proxy, req_stream) = fidl_endpoints();
let handle = req_stream.control_handle();
let request = (id, (err, req_stream));
subscribers.register(id, handle, None, None).unwrap();
assert!(subscribers.is_registered(&id));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
assert!(!subscribers.is_registered(&id));
let id = ClientId(1);
let err = Some(Err(FidlError::Invalid));
let (_proxy, req_stream) = fidl_endpoints();
let handle = req_stream.control_handle();
let request = (id, (err, req_stream));
subscribers.register(id, handle, None, None).unwrap();
assert!(subscribers.is_registered(&id));
pump_handle_client_request(&mut exec, request, &mut requests, &mut subscribers, &mut logs);
assert!(!subscribers.is_registered(&id));
}