| // 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 { |
| fuchsia_zircon::{self as zx, Status}, |
| futures::{executor::block_on, future, StreamExt, TryStreamExt}, |
| std::result, |
| }; |
| |
| use crate::*; |
| |
| // Helper functions |
| |
| pub(crate) fn setup_peer() -> (Peer, zx::Socket) { |
| let (remote, signaling) = zx::Socket::create(zx::SocketOpts::DATAGRAM).unwrap(); |
| |
| let peer = Peer::new(signaling); |
| assert!(peer.is_ok()); |
| (peer.unwrap(), remote) |
| } |
| |
| fn setup_stream_test() -> (RequestStream, Peer, zx::Socket, fasync::Executor) { |
| let exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let stream = peer.take_request_stream(); |
| (stream, peer, remote, exec) |
| } |
| |
| fn next_request(stream: &mut RequestStream, exec: &mut fasync::Executor) -> Request { |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| match complete { |
| Poll::Ready(Some(Ok(r))) => r, |
| _ => panic!("should have a request"), |
| } |
| } |
| |
| pub(crate) fn recv_remote(remote: &zx::Socket) -> result::Result<Vec<u8>, zx::Status> { |
| let waiting = remote.outstanding_read_bytes(); |
| assert!(waiting.is_ok()); |
| let mut response: Vec<u8> = vec![0; waiting.unwrap()]; |
| let response_read = remote.read(response.as_mut_slice())?; |
| assert_eq!(response.len(), response_read); |
| Ok(response) |
| } |
| |
| pub(crate) fn expect_remote_recv(expected: &[u8], remote: &zx::Socket) { |
| let r = recv_remote(&remote); |
| assert!(r.is_ok()); |
| let response = r.unwrap(); |
| if expected.len() != response.len() { |
| panic!( |
| "received wrong length\nexpected: {:?}\nreceived: {:?}", |
| expected, response |
| ); |
| } |
| assert_eq!(expected, &response[0..expected.len()]); |
| } |
| |
| fn stream_request_response( |
| exec: &mut fasync::Executor, stream: &mut RequestStream, remote: &zx::Socket, cmd: &[u8], |
| expect: &[u8], |
| ) { |
| assert!(remote.write(cmd).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received anything on the stream. |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with the expected data. |
| expect_remote_recv(expect, &remote); |
| } |
| |
| #[test] |
| fn closes_socket_when_dropped() { |
| let mut _exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer_sock, signaling) = zx::Socket::create(zx::SocketOpts::DATAGRAM).unwrap(); |
| |
| { |
| let peer = Peer::new(signaling); |
| assert!(peer.is_ok()); |
| let mut _stream = peer.unwrap().take_request_stream(); |
| } |
| |
| // Writing to the sock from the other end should fail. |
| let write_res = peer_sock.write(&[0; 1]); |
| assert!(write_res.is_err()); |
| assert_eq!(Status::PEER_CLOSED, write_res.err().unwrap()); |
| } |
| |
| #[test] |
| #[should_panic] // TODO: can't use catch_unwind here because of PeerInner? |
| fn can_only_take_stream_once() { |
| let mut _exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (_, signaling) = zx::Socket::create(zx::SocketOpts::DATAGRAM).unwrap(); |
| |
| let p = Peer::new(signaling); |
| assert!(p.is_ok()); |
| let peer = p.unwrap(); |
| let mut _stream = peer.take_request_stream(); |
| peer.take_request_stream(); |
| } |
| |
| // Generic Request tests |
| |
| const CMD_DISCOVER: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x01, // RFA (0b00), Discover (0x01) |
| ]; |
| |
| #[test] |
| fn closed_peer_ends_request_stream() { |
| let (stream, _, _, _) = setup_stream_test(); |
| let collected = block_on(stream.collect::<Vec<Result<Request>>>()); |
| assert_eq!(0, collected.len()); |
| } |
| |
| #[test] |
| fn command_not_supported_response() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel 4, DelayReport, which is not implemented |
| assert!(remote.write(&[0x40, 0x0D, 0x40, 0x00, 0x00]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received anything on the future |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with NOT_SUPPORTED_COMMAND |
| expect_remote_recv(&[0x43, 0x0D, 0x19], &remote); |
| } |
| |
| #[test] |
| fn requests_are_queued_if_they_arrive_early() { |
| let (stream, _, remote, mut exec) = setup_stream_test(); |
| assert!(remote.write(&CMD_DISCOVER).is_ok()); |
| let mut collected = Vec::<Request>::new(); |
| |
| let mut fut = stream.try_for_each(|r| { |
| collected.push(r); |
| future::ready(Ok(())) |
| }); |
| |
| let _complete = exec.run_until_stalled(&mut fut); |
| |
| assert_eq!(1, collected.len()); |
| } |
| |
| #[test] |
| fn responds_with_same_tx_id() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_DISCOVER).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::Discover { responder } => { |
| let s = StreamInformation::new( |
| StreamEndpointId(0x0A), // from CMD_DISCOVER |
| false, |
| MediaType::Video, |
| EndpointType::Source, |
| ); |
| responder.send(&[s]) |
| } |
| _ => panic!("should have received a Discover"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| let response: &[u8] = &[ |
| // txlabel (same as CMD_DISCOVER, 4), Single (0b00), Response Accept (0b10) |
| 0x4 << 4 | 0x0 << 2 | 0x2, |
| 0x01, // Discover |
| 0x0A << 2 | 0x0 << 1, // SEID (0A), Not In Use (0b0) |
| 0x01 << 4 | 0x0 << 3, // Video (0x01), Source (0x00) |
| ]; |
| |
| expect_remote_recv(response, &remote); |
| } |
| |
| #[test] |
| fn invalid_signal_id_responds_error() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // This is TxLabel 4, Signal Id 0b110000, which is invalid. |
| assert!(remote.write(&[0x40, 0x30]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received anything on the future |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a General Reject message with the |
| // same TxLabel, and the same (invalid) signal identifier. |
| expect_remote_recv(&[0x41, 0x30], &remote); |
| } |
| |
| #[test] |
| fn exhaust_request_ids() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let mut response_futures = Vec::new(); |
| // There are only 16 labels, so fill up the "outgoing requests pending" buffer |
| for _ in 0..16 { |
| let mut response_fut = Box::pin(peer.discover()); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| response_futures.push(response_fut); |
| } |
| |
| let mut should_fail_fut = Box::pin(peer.discover()); |
| let res = exec.run_until_stalled(&mut should_fail_fut); |
| assert!(res.is_ready()); |
| |
| if let Poll::Ready(x) = res { |
| assert!(x.is_err()); |
| } |
| |
| // Finish some of them. |
| for _ in 0..4 { |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x01, received[1]); // 0x01 = Discover |
| |
| let txlabel_raw = received[0] & 0xF0; |
| let response: &[u8] = &[ |
| txlabel_raw | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x01, // Discover |
| 0x3E << 2 | 0x0 << 1, // SEID (3E), Not In Use (0b0) |
| 0x00 << 4 | 0x1 << 3, // Audio (0x00), Sink (0x01) |
| ]; |
| assert!(remote.write(response).is_ok()); |
| } |
| |
| for idx in 0..4 { |
| assert!(exec |
| .run_until_stalled(&mut response_futures[idx]) |
| .is_ready()); |
| } |
| |
| // We should be able to make new requests now. |
| let mut another_fut = Box::pin(peer.discover()); |
| assert!(exec.run_until_stalled(&mut another_fut).is_pending()); |
| } |
| |
| #[test] |
| fn command_timeout() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let mut response_fut = Box::pin(peer.discover()); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x01, received[1]); // 0x01 = Discover |
| |
| // We should be able to timeout the discover. |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert!(complete.is_pending()); |
| |
| exec.wake_next_timer(); |
| assert_eq!( |
| Poll::Ready(Err(Error::Timeout)), |
| exec.run_until_stalled(&mut response_fut) |
| ); |
| |
| // We should be able to make a new request now. |
| let mut another_fut = Box::pin(peer.discover()); |
| assert!(exec.run_until_stalled(&mut another_fut).is_pending()); |
| |
| // Responding to the first command after it timed out doesn't complete the new command |
| let txlabel_raw = received[0] & 0xF0; |
| let response: &[u8] = &[ |
| txlabel_raw | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x01, // Discover |
| 0x3E << 2 | 0x0 << 1, // SEID (3E), Not In Use (0b0) |
| 0x00 << 4 | 0x1 << 3, // Audio (0x00), Sink (0x01) |
| ]; |
| |
| assert!(remote.write(response).is_ok()); |
| |
| assert!(exec.run_until_stalled(&mut another_fut).is_pending()); |
| } |
| |
| // Discovery tests |
| |
| #[test] |
| fn discover_invalid_length() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel 4, Discover, which doesn't have a payload, but we're including one. |
| assert!(remote.write(&[0x40, 0x01, 0x40]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received anything on the future |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with BAD_LENGTH |
| expect_remote_recv(&[0x43, 0x01, 0x11], &remote); |
| } |
| |
| #[test] |
| fn discover_event_responder_send_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_DISCOVER).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::Discover { responder } => { |
| let s = StreamInformation::new( |
| StreamEndpointId(0x0A), |
| false, |
| MediaType::Video, |
| EndpointType::Source, |
| ); |
| responder.send(&[s]) |
| } |
| _ => panic!("should have received a Discover"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| expect_remote_recv(&[0x42, 0x01, 0x0A << 2, 0x01 << 4], &remote); |
| } |
| |
| #[test] |
| fn discover_event_responder_reject_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_DISCOVER).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::Discover { responder } => responder.reject(ErrorCode::BadState), |
| _ => panic!("should have received a Discover"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| let discover_rsp = &[ |
| 0x43, // TxLabel (4), RemoteRejected (3) |
| 0x01, // Discover |
| 0x31, // BAD_STATE |
| ]; |
| expect_remote_recv(discover_rsp, &remote); |
| } |
| |
| #[test] |
| fn discover_command_works() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let mut response_fut = Box::pin(peer.discover()); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x01, received[1]); // 0x01 = Discover |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x01, // Discover |
| 0x3E << 2 | 0x0 << 1, // SEID (3E), Not In Use (0b0) |
| 0x00 << 4 | 0x1 << 3, // Audio (0x00), Sink (0x01) |
| 0x01 << 2 | 0x1 << 1, // SEID (1), In Use (0b1) |
| 0x01 << 4 | 0x0 << 3, // Video (0x01), Source (0x01) |
| 0x17 << 2 | 0x0 << 1, // SEID (17), Not In Use (0b0) |
| 0x02 << 4 | 0x1 << 3, // Multimedia (0x02), Sink (0x01) |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let endpoints = match complete { |
| Poll::Ready(Ok(response)) => response, |
| x => panic!("Should have a ready Ok response: {:?}", x), |
| }; |
| |
| assert_eq!(3, endpoints.len()); |
| let e1 = StreamInformation::new( |
| StreamEndpointId(0x3E), |
| false, |
| MediaType::Audio, |
| EndpointType::Sink, |
| ); |
| assert_eq!(e1, endpoints[0]); |
| let e2 = StreamInformation::new( |
| StreamEndpointId(0x01), |
| true, |
| MediaType::Video, |
| EndpointType::Source, |
| ); |
| assert_eq!(e2, endpoints[1]); |
| let e3 = StreamInformation::new( |
| StreamEndpointId(0x17), |
| false, |
| MediaType::Multimedia, |
| EndpointType::Sink, |
| ); |
| assert_eq!(e3, endpoints[2]); |
| } |
| |
| #[test] |
| fn discover_command_rejected() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let mut response_fut = Box::pin(peer.discover()); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x01, received[1]); // 0x01 = Discover |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| txlabel_raw | 0x0 << 2 | 0x3, // txlabel (same), Single (0b00), Response Reject (0b11) |
| 0x01, // Discover |
| 0x31, // BAD_STATE |
| ]; |
| |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let error = match complete { |
| Poll::Ready(Err(response)) => response, |
| x => panic!("Should have a ready Error response: {:?}", x), |
| }; |
| |
| assert_eq!(Error::RemoteRejected(0x31), error); |
| } |
| |
| // Get(All)Capabilities tests |
| |
| const CMD_GET_CAPABILITIES: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x02, // RFA (0b00), Get Capabilities (0x02) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| #[test] |
| fn get_capabilities_invalid_length() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel 4, GetCapabilities, which needs a SEID. |
| assert!(remote.write(&[0x40, 0x02]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received any request. |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with BAD_LENGTH |
| expect_remote_recv(&[0x43, 0x02, 0x11], &remote); |
| |
| // TxLabel 4, GetCapabilities, with more than we expected. |
| assert!(remote.write(&[0x40, 0x02, 0x01, 0x10]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received any request. |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with BAD_LENGTH |
| expect_remote_recv(&[0x43, 0x02, 0x11], &remote); |
| } |
| |
| #[test] |
| fn get_capabilities_event_responder_send_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_GET_CAPABILITIES).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::GetCapabilities { |
| responder, |
| stream_id, |
| } => { |
| assert_eq!(StreamEndpointId::try_from(12).unwrap(), stream_id); |
| let capabilities = &[ |
| ServiceCapability::MediaTransport, |
| ServiceCapability::Reporting, |
| ServiceCapability::Recovery { |
| recovery_type: 0x01, |
| max_recovery_window_size: 10, |
| max_number_media_packets: 255, |
| }, |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }, |
| ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::DigitalTransmissionContentProtection, |
| extra: vec![0xF0, 0x0D], |
| }, |
| ServiceCapability::DelayReporting, |
| ]; |
| responder.send(capabilities) |
| } |
| _ => panic!("should have received a GetCapabilities"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Should receive the encoded version of the MediaTransport |
| #[rustfmt::skip] |
| let get_capabilities_rsp = &[ |
| 0x42, // TxLabel (4) + ResponseAccept (0x02) |
| 0x02, // GetCapabilities Signal |
| // MediaTransport (Length of Service Capability = 0) |
| 0x01, 0x00, |
| // Reporting (LOSC = 0) |
| 0x02, 0x00, |
| // Recovery (LOSC = 3), Type (0x01), Window size (10), Number Media Packets (255) |
| 0x03, 0x03, 0x01, 0x0A, 0xFF, |
| // Media Codec (LOSC = 2 + 4), Media Type Audio (0x00), Codec type (0x40), Codec speciic |
| // as above |
| 0x07, 0x06, 0x00, 0x40, 0xDE, 0xAD, 0xBE, 0xEF, |
| // Content Protection (LOSC = 2 + 2), DTCP (0x01, 0x00), CP Specific as above |
| 0x04, 0x04, 0x01, 0x00, 0xF0, 0x0D, |
| /* NOTE: DelayReporting should NOT be included here |
| * (GetCapabilities response cannot return DelayReporting) */ |
| ]; |
| expect_remote_recv(get_capabilities_rsp, &remote); |
| } |
| |
| #[test] |
| fn get_capabilities_responder_reject_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_GET_CAPABILITIES).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::GetCapabilities { |
| responder, |
| stream_id, |
| } => { |
| assert_eq!(StreamEndpointId::try_from(12).unwrap(), stream_id); |
| responder.reject(ErrorCode::BadAcpSeid) |
| } |
| _ => panic!("should have received a GetAllCapabilities"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| let get_capabilities_rsp = &[ |
| 0x43, // TxLabel (4), RemoteRejected (3) |
| 0x02, // Get Capabilities |
| 0x12, // BAD_ACP_SEID |
| ]; |
| expect_remote_recv(get_capabilities_rsp, &remote); |
| } |
| |
| #[test] |
| fn get_capabilities_command_works() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = &StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.get_capabilities(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x02, received[1]); // 0x02 = GetCapabilities |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01) , RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| #[rustfmt::skip] |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x02, // Get Capabilities |
| // MediaTransport (Length of Service Capability = 0) |
| 0x01, 0x00, |
| // Recovery (LOSC = 3), Type (0x01), Window size (2), Number Media Packets (5) |
| 0x03, 0x03, 0x01, 0x02, 0x05, |
| // Media Codec (LOSC = 2 + 2), Video (0x1), Codec type (0x20), Codec speific (0xB0DE) |
| 0x07, 0x04, 0x10, 0x20, 0xB0, 0xDE, |
| // Content Protection (LOSC = 2 + 1), SCMS (0x02, 0x00), CP Specific (0x7A) |
| 0x04, 0x03, 0x02, 0x00, 0x7A, |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let capabilities = match complete { |
| Poll::Ready(Ok(response)) => response, |
| x => panic!("Should have a ready Ok response: {:?}", x), |
| }; |
| |
| assert_eq!(4, capabilities.len()); |
| let c1 = ServiceCapability::MediaTransport; |
| assert_eq!(c1, capabilities[0]); |
| let c2 = ServiceCapability::Recovery { |
| recovery_type: 1, |
| max_recovery_window_size: 2, |
| max_number_media_packets: 5, |
| }; |
| assert_eq!(c2, capabilities[1]); |
| let c3 = ServiceCapability::MediaCodec { |
| media_type: MediaType::Video, |
| codec_type: MediaCodecType::new(0x20), |
| codec_extra: vec![0xb0, 0xde], |
| }; |
| assert_eq!(c3, capabilities[2]); |
| let c4 = ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::SerialCopyManagementSystem, |
| extra: vec![0x7a], |
| }; |
| assert_eq!(c4, capabilities[3]); |
| } |
| |
| #[test] |
| fn get_capabilities_reject_command() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = &StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.get_capabilities(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x02, received[1]); // 0x02 = GetCapabilities |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01), RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| txlabel_raw | 0x0 << 2 | 0x3, // txlabel (same), Single (0b00), Response Reject (0b11) |
| 0x02, // Get Capabiliities |
| 0x12, // BAD_ACP_SEID |
| ]; |
| |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let error = match complete { |
| Poll::Ready(Err(response)) => response, |
| x => panic!("Should have a ready Error response: {:?}", x), |
| }; |
| |
| assert_eq!(Error::RemoteRejected(0x12), error); |
| } |
| |
| // Get All Capabilities |
| |
| const CMD_GET_ALL_CAPABILITIES: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x0C, // RFA (0b00), Get All Capabilities (0x0C) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| #[test] |
| fn get_all_capabilities_invalid_length() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel 4, GetAllCapabilities, which needs a SEID. |
| assert!(remote.write(&[0x40, 0x0C]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received any request. |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with BAD_LENGTH |
| expect_remote_recv(&[0x43, 0x0C, 0x11], &remote); |
| |
| // TxLabel 4, GetCapabilities, with more than we expected. |
| assert!(remote.write(&[0x40, 0x0C, 0x01, 0x10]).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received any request. |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with BAD_LENGTH |
| expect_remote_recv(&[0x43, 0x0C, 0x11], &remote); |
| } |
| |
| #[test] |
| fn get_all_capabilities_event_responder_send_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_GET_ALL_CAPABILITIES).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::GetAllCapabilities { |
| responder, |
| stream_id, |
| } => { |
| assert_eq!(StreamEndpointId::try_from(12).unwrap(), stream_id); |
| let capabilities = &[ |
| ServiceCapability::MediaTransport, |
| ServiceCapability::Reporting, |
| ServiceCapability::Recovery { |
| recovery_type: 0x01, |
| max_recovery_window_size: 10, |
| max_number_media_packets: 255, |
| }, |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }, |
| ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::DigitalTransmissionContentProtection, |
| extra: vec![0xF0, 0x0D], |
| }, |
| ServiceCapability::DelayReporting, |
| ]; |
| responder.send(capabilities) |
| } |
| _ => panic!("should have received a GetAllCapabilities"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Should receive the encoded version of the MediaTransport |
| #[rustfmt::skip] |
| let get_capabilities_rsp = &[ |
| 0x42, // TxLabel (4) + ResponseAccept (0x02) |
| 0x0C, // GetAllCapabilities Signal |
| // MediaTransport (Length of Service Capability = 0) |
| 0x01, 0x00, |
| // Reporting (LOSC = 0) |
| 0x02, 0x00, |
| // Recovery (LOSC = 3), Type (0x01), Window size (10), Number Media Packets (255) |
| 0x03, 0x03, 0x01, 0x0A, 0xFF, |
| // Media Codec (LOSC = 2 + 4), Media Type Audio (0x00), Codec type (0x40), Codec speciic |
| // as above |
| 0x07, 0x06, 0x00, 0x40, 0xDE, 0xAD, 0xBE, 0xEF, |
| // Content Protection (LOSC = 2 + 2), DTCP (0x01, 0x00), CP Specific as above |
| 0x04, 0x04, 0x01, 0x00, 0xF0, 0x0D, |
| // DelayReporting (LOSC = 0) |
| 0x08, 0x00, |
| ]; |
| expect_remote_recv(get_capabilities_rsp, &remote); |
| } |
| |
| #[test] |
| fn get_all_capabilities_responder_reject_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&CMD_GET_ALL_CAPABILITIES).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::GetAllCapabilities { |
| responder, |
| stream_id, |
| } => { |
| assert_eq!(StreamEndpointId::try_from(12).unwrap(), stream_id); |
| responder.reject(ErrorCode::BadAcpSeid) |
| } |
| _ => panic!("should have received a GetAllCapabilities"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| let get_all_capabilities_rsp = &[ |
| 0x43, // TxLabel (4), RemoteRejected (3) |
| 0x0C, // Get All Capabilities |
| 0x12, // BAD_ACP_SEID |
| ]; |
| expect_remote_recv(get_all_capabilities_rsp, &remote); |
| } |
| |
| #[test] |
| fn get_all_capabilities_command_works() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.get_all_capabilities(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x0C, received[1]); // 0x0C = GetAllCapabilities |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01) , RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| #[rustfmt::skip] |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x0C, // Get All Capabilities |
| // MediaTransport (Length of Service Capability = 0) |
| 0x01, 0x00, |
| // Reporting (LOSC = 0) |
| 0x02, 0x00, |
| // Media Codec (LOSC = 2 + 2), Video (0x1), Codec type (0x20), Codec speific (0xB0DE) |
| 0x07, 0x04, 0x10, 0x20, 0xB0, 0xDE, |
| // Content Protection (LOSC = 2 + 1), SCMS (0x02, 0x00), CP Specific (0x7A) |
| 0x04, 0x03, 0x02, 0x00, 0x7A, |
| // Delay Reporting |
| 0x08, 0x00, |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let capabilities = match complete { |
| Poll::Ready(Ok(response)) => response, |
| x => panic!("Should have a ready Ok response: {:?}", x), |
| }; |
| |
| assert_eq!(5, capabilities.len()); |
| let c1 = ServiceCapability::MediaTransport; |
| assert_eq!(c1, capabilities[0]); |
| let c2 = ServiceCapability::Reporting; |
| assert_eq!(c2, capabilities[1]); |
| let c3 = ServiceCapability::MediaCodec { |
| media_type: MediaType::Video, |
| codec_type: MediaCodecType::new(0x20), |
| codec_extra: vec![0xb0, 0xde], |
| }; |
| assert_eq!(c3, capabilities[2]); |
| let c4 = ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::SerialCopyManagementSystem, |
| extra: vec![0x7a], |
| }; |
| assert_eq!(c4, capabilities[3]); |
| let c5 = ServiceCapability::DelayReporting; |
| assert_eq!(c5, capabilities[4]); |
| } |
| |
| #[test] |
| fn get_all_capabilities_reject_command() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.get_all_capabilities(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x0C, received[1]); // 0x02 = GetAllCapabilities |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01), RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| txlabel_raw | 0x0 << 2 | 0x3, // txlabel (same), Single (0b00), Response Reject (0b11) |
| 0x0C, // Get Capabiliities |
| 0x12, // BAD_ACP_SEID |
| ]; |
| |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let error = match complete { |
| Poll::Ready(Err(response)) => response, |
| x => panic!("Should have a ready Error response: {:?}", x), |
| }; |
| |
| assert_eq!(Error::RemoteRejected(0x12), error); |
| } |
| |
| macro_rules! incoming_cmd_length_fail_test { |
| ($test_name:ident, $signal_value:expr, $length:expr) => { |
| #[test] |
| fn $test_name() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel 4, signal value, no params |
| let mut incoming_cmd = vec![0x40, $signal_value]; |
| |
| // Add $length bytes |
| incoming_cmd.extend_from_slice(&[0; $length]); |
| |
| // Send the command |
| assert!(remote.write(&incoming_cmd).is_ok()); |
| |
| let mut fut = stream.next(); |
| let complete = exec.run_until_stalled(&mut fut); |
| |
| // We shouldn't have received any request. |
| assert!(complete.is_pending()); |
| |
| // The peer should have responded with a Response Reject message with the |
| // same TxLabel with BAD_LENGTH |
| expect_remote_recv(&[0x43, $signal_value, 0x11], &remote); |
| } |
| }; |
| } |
| |
| macro_rules! seid_command_test { |
| ($test_name:ident, $signal_value:expr, $peer_function:ident) => { |
| #[test] |
| fn $test_name() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.$peer_function(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!($signal_value, received[1]); // $signal_value = $request_variant |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01) , RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| // txlabel (same), Single (0b00), Response Accept (0b10) |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, |
| $signal_value, // $request_variant |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| assert_eq!(Poll::Ready(Ok(())), complete); |
| } |
| }; |
| } |
| |
| macro_rules! seids_command_test { |
| ($test_name:ident, $signal_value:expr, $peer_function:ident) => { |
| #[test] |
| fn $test_name() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid1 = StreamEndpointId::try_from(1).unwrap(); |
| let seid2 = StreamEndpointId::try_from(16).unwrap(); |
| let seids = [seid1, seid2]; |
| let mut response_fut = Box::pin(peer.$peer_function(&seids)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!($signal_value, received[1]); // $signal_value = $request_variant |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01) , RFA |
| assert_eq!(0x10 << 2, received[3]); // SEID (0x10) , RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| // txlabel (same), Single (0b00), Response Accept (0b10) |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, |
| $signal_value, // Signal value |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| assert_eq!(Poll::Ready(Ok(())), complete); |
| } |
| }; |
| } |
| |
| macro_rules! seid_command_reject_test { |
| ($test_name:ident, $signal_value: expr, $peer_function:ident) => { |
| #[test] |
| fn $test_name() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.$peer_function(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!($signal_value, received[1]); // 0x02 = GetAllCapabilities |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01), RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| // txlabel (same), Single (0b00), Response Reject (0b11) |
| txlabel_raw | 0x0 << 2 | 0x3, |
| $signal_value, // $request_variant |
| 0x12, // BAD_ACP_SEID |
| ]; |
| |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let error = match complete { |
| Poll::Ready(Err(response)) => response, |
| x => panic!("Should have a ready Error response: {:?}", x), |
| }; |
| |
| assert_eq!(Error::RemoteRejected(0x12), error); |
| } |
| }; |
| } |
| |
| macro_rules! seids_command_reject_test { |
| ($test_name:ident, $signal_value: expr, $peer_function:ident) => { |
| #[test] |
| fn $test_name() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid1 = StreamEndpointId::try_from(1).unwrap(); |
| let seid2 = StreamEndpointId::try_from(16).unwrap(); |
| let seids = [seid1, seid2]; |
| let mut response_fut = Box::pin(peer.$peer_function(&seids)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!($signal_value, received[1]); // 0x02 = GetAllCapabilities |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01), RFA |
| assert_eq!(0x10 << 2, received[3]); // SEID (0x10), RFA |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let response: &[u8] = &[ |
| // txlabel (same), Single (0b00), Response Reject (0b11) |
| txlabel_raw | 0x0 << 2 | 0x3, |
| $signal_value, // $request_variant |
| 0x10 << 2, // SEID 16, RFA |
| 0x12, // BAD_ACP_SEID |
| ]; |
| |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| |
| let error = match complete { |
| Poll::Ready(Err(response)) => response, |
| x => panic!("Should have a ready Error response: {:?}", x), |
| }; |
| |
| assert_eq!(Error::RemoteStreamRejected(0x10, 0x12), error); |
| } |
| }; |
| } |
| macro_rules! seid_event_responder_send_test { |
| ($test_name:ident, $variant:ident, $signal_value:expr, $command_var:ident) => { |
| #[test] |
| fn $test_name() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&$command_var).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::$variant { |
| responder, |
| stream_id, |
| } => { |
| assert_eq!(StreamEndpointId::try_from(12).unwrap(), stream_id); |
| responder.send() |
| } |
| _ => panic!("received the wrong request"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Should receive the response. |
| let ok_rsp = &[ |
| 0x42, // TxLabel (4) + ResponseAccept (0x02) |
| $signal_value, // $variant Signal |
| ]; |
| expect_remote_recv(ok_rsp, &remote); |
| } |
| }; |
| } |
| |
| macro_rules! seids_event_responder_send_test { |
| ($test_name:ident, $variant:ident, $signal_value:expr, $command_var:ident) => { |
| #[test] |
| fn $test_name() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&$command_var).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::$variant { |
| responder, |
| stream_ids, |
| } => { |
| assert_eq!( |
| &StreamEndpointId::try_from(12).unwrap(), |
| stream_ids.get(0).unwrap() |
| ); |
| responder.send() |
| } |
| _ => panic!("received the wrong request"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Should receive the response. |
| let ok_rsp = &[ |
| 0x42, // TxLabel (4) + ResponseAccept (0x02) |
| $signal_value, // $variant Signal |
| ]; |
| expect_remote_recv(ok_rsp, &remote); |
| } |
| }; |
| } |
| |
| macro_rules! seid_event_responder_reject_test { |
| ($test_name:ident, $variant:ident, $signal_value:expr, $command_var:ident) => { |
| #[test] |
| fn $test_name() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&$command_var).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::$variant { |
| responder, |
| stream_id, |
| } => { |
| assert_eq!(StreamEndpointId::try_from(12).unwrap(), stream_id); |
| responder.reject(ErrorCode::BadAcpSeid) |
| } |
| _ => panic!("received the wrong request"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| let rejected_rsp = &[ |
| 0x43, // TxLabel (4), RemoteRejected (3) |
| $signal_value, // $variant |
| 0x12, // BAD_ACP_SEID |
| ]; |
| expect_remote_recv(rejected_rsp, &remote); |
| } |
| }; |
| } |
| |
| macro_rules! stream_event_responder_reject_test { |
| ($test_name:ident, $variant:ident, $signal_value:expr, $command_var:ident) => { |
| #[test] |
| fn $test_name() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| assert!(remote.write(&$command_var).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::$variant { |
| responder, |
| stream_ids, |
| } => { |
| let stream_id = stream_ids.get(0).unwrap(); |
| assert_eq!(&StreamEndpointId::try_from(12).unwrap(), stream_id); |
| responder.reject(stream_id, ErrorCode::BadAcpSeid) |
| } |
| _ => panic!("received the wrong request"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| let rejected_rsp = &[ |
| 0x43, // TxLabel (4), RemoteRejected (3) |
| $signal_value, // $variant |
| 12 << 2, // Stream ID (12), RFA |
| 0x12, // BAD_ACP_SEID |
| ]; |
| expect_remote_recv(rejected_rsp, &remote); |
| } |
| }; |
| } |
| |
| // Open |
| |
| const CMD_OPEN: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x06, // RFA (0b00), Open (0x06) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| const CMD_OPEN_VALUE: &'static u8 = &0x06; |
| |
| incoming_cmd_length_fail_test!(open_length_too_short, *CMD_OPEN_VALUE, 0); |
| incoming_cmd_length_fail_test!(open_length_too_long, *CMD_OPEN_VALUE, 2); |
| seid_command_test!(open_command_works, *CMD_OPEN_VALUE, open); |
| seid_command_reject_test!(open_command_reject_works, *CMD_OPEN_VALUE, open); |
| seid_event_responder_send_test!(open_responder_send, Open, *CMD_OPEN_VALUE, CMD_OPEN); |
| seid_event_responder_reject_test!(open_responder_reject, Open, *CMD_OPEN_VALUE, CMD_OPEN); |
| |
| // Start |
| |
| const CMD_START: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x07, // RFA (0b00), Start (0x07) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| const CMD_START_VALUE: &'static u8 = &0x07; |
| |
| incoming_cmd_length_fail_test!(start_length, *CMD_START_VALUE, 0); |
| seids_command_test!(start_command_works, *CMD_START_VALUE, start); |
| seids_command_reject_test!(start_command_reject_works, *CMD_START_VALUE, start); |
| seids_event_responder_send_test!(start_responder_send, Start, *CMD_START_VALUE, CMD_START); |
| stream_event_responder_reject_test!(start_responder_reject, Start, *CMD_START_VALUE, CMD_START); |
| |
| // Close |
| |
| const CMD_CLOSE: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x08, // RFA (0b00), Close (0x08) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| const CMD_CLOSE_VALUE: &'static u8 = &0x08; |
| |
| incoming_cmd_length_fail_test!(close_length_too_short, *CMD_CLOSE_VALUE, 0); |
| incoming_cmd_length_fail_test!(close_length_too_long, *CMD_CLOSE_VALUE, 2); |
| seid_command_test!(close_command_works, *CMD_CLOSE_VALUE, close); |
| seid_command_reject_test!(close_command_reject_works, *CMD_CLOSE_VALUE, close); |
| seid_event_responder_send_test!(close_responder_send, Close, *CMD_CLOSE_VALUE, CMD_CLOSE); |
| seid_event_responder_reject_test!(close_responder_reject, Close, *CMD_CLOSE_VALUE, CMD_CLOSE); |
| |
| // Suspend |
| |
| const CMD_SUSPEND: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x09, // RFA (0b00), Suspend (0x09) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| const CMD_SUSPEND_VALUE: &'static u8 = &0x09; |
| |
| incoming_cmd_length_fail_test!(suspend_length, *CMD_SUSPEND_VALUE, 0); |
| seids_command_test!(suspend_command_works, *CMD_SUSPEND_VALUE, suspend); |
| seids_command_reject_test!(suspend_command_reject_works, *CMD_SUSPEND_VALUE, suspend); |
| seids_event_responder_send_test!( |
| suspend_responder_send, |
| Suspend, |
| *CMD_SUSPEND_VALUE, |
| CMD_SUSPEND |
| ); |
| stream_event_responder_reject_test!( |
| suspend_responder_reject, |
| Suspend, |
| *CMD_SUSPEND_VALUE, |
| CMD_SUSPEND |
| ); |
| |
| // Abort |
| |
| const CMD_ABORT: &'static [u8] = &[ |
| 0x40, // TxLabel (4), Single Packet (0b00), Command (0b00) |
| 0x0A, // RFA (0b00), Suspend (0x0A) |
| 0x30, // SEID 12 (0b001100) |
| ]; |
| |
| const CMD_ABORT_VALUE: &'static u8 = &0x0A; |
| |
| incoming_cmd_length_fail_test!(abort_length_too_short, *CMD_ABORT_VALUE, 0); |
| incoming_cmd_length_fail_test!(abort_length_too_long, *CMD_ABORT_VALUE, 2); |
| seid_command_test!(abort_command_works, *CMD_ABORT_VALUE, abort); |
| seid_command_reject_test!(abort_command_reject_works, *CMD_ABORT_VALUE, abort); |
| seid_event_responder_send_test!(abort_responder_send, Abort, *CMD_ABORT_VALUE, CMD_ABORT); |
| seid_event_responder_reject_test!(abort_responder_reject, Abort, *CMD_ABORT_VALUE, CMD_ABORT); |
| |
| /// Abort requests with an invalid SEID are allowed to be dropped. |
| /// We timeout after some amount of time. |
| #[test] |
| fn abort_sent_no_response() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let seid = StreamEndpointId::try_from(1).unwrap(); |
| let mut response_fut = Box::pin(peer.abort(&seid)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(0x0A, received[1]); // $signal_value = $request_variant |
| assert_eq!(0x01 << 2, received[2]); // SEID (0x01) , RFA |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert!(complete.is_pending()); |
| |
| exec.wake_next_timer(); |
| assert_eq!( |
| Poll::Ready(Err(Error::Timeout)), |
| exec.run_until_stalled(&mut response_fut) |
| ); |
| } |
| |
| // Set Configuration |
| |
| fn expect_config_recv_cap_okay( |
| cmd: &[u8], local_seid: StreamEndpointId, remote_seid: StreamEndpointId, |
| capability: ServiceCapability, |
| ) { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let txlabel_mask = cmd[0] & 0xF0; |
| |
| assert!(remote.write(cmd).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::SetConfiguration { |
| responder, |
| local_stream_id, |
| remote_stream_id, |
| capabilities, |
| } => { |
| assert_eq!(local_seid, local_stream_id); |
| assert_eq!(remote_seid, remote_stream_id); |
| assert_eq!(capability, capabilities[0]); |
| responder.send() |
| } |
| _ => panic!("should have received a SetConfiguration"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Expected response: Same TxLabel & ResponseAccept (0x2) , Signal. |
| expect_remote_recv(&[txlabel_mask | 0x02, 0x03], &remote); |
| } |
| |
| // Set config must be at least 2 SEIDs and one configuration long. |
| incoming_cmd_length_fail_test!(set_config_length_too_short, 0x03, 2); |
| |
| #[test] |
| fn set_configration_invalid_media_transport_format() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel (4), ResponseReject, Signal, Relevant Cap (Media Transport), |
| // BadMediaTransportFormat |
| let rsp = &[0x43, 0x03, 0x01, 0x23]; |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x01, 0x01, 0x01, // Media Transport, Length 1 (too long), dummy |
| ]; |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| // These also test that the MediaTransport is decoded and received correctly. |
| #[test] |
| fn set_config_event_responder_send_works() { |
| let set_config_cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x01, 0x00, // Media Transport, Length 0 |
| ]; |
| |
| expect_config_recv_cap_okay( |
| set_config_cmd, |
| StreamEndpointId(1), |
| StreamEndpointId(2), |
| ServiceCapability::MediaTransport, |
| ); |
| } |
| |
| #[test] |
| fn set_config_event_responder_reject_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let set_config_cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x01, 0x00, // Media Transport, Length 0 |
| ]; |
| |
| assert!(remote.write(set_config_cmd).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::SetConfiguration { |
| responder, |
| local_stream_id, |
| remote_stream_id, |
| capabilities, |
| } => { |
| assert_eq!(StreamEndpointId(1), local_stream_id); |
| assert_eq!(StreamEndpointId(2), remote_stream_id); |
| assert_eq!(ServiceCapability::MediaTransport, capabilities[0]); |
| responder.reject(Some(&capabilities[0]), ErrorCode::UnsupportedConfiguration) |
| } |
| _ => panic!("should have received a SetConfiguration"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Expected response: Same TxLabel (4) & ResponseReject, Signal, |
| // Relevant capabilitiy, Error Code (Unsupported Configure) |
| expect_remote_recv(&[0x43, 0x03, 0x01, 0x29], &remote); |
| } |
| |
| #[test] |
| fn set_config_command_works() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| // This should cover all the implemented capabilities to confirm they are |
| // encoded correctly. |
| let caps = vec![ |
| ServiceCapability::MediaTransport, |
| ServiceCapability::Reporting, |
| ServiceCapability::Recovery { |
| recovery_type: 0x01, |
| max_recovery_window_size: 10, |
| max_number_media_packets: 255, |
| }, |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }, |
| ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::DigitalTransmissionContentProtection, |
| extra: vec![0xF0, 0x0D], |
| }, |
| ServiceCapability::DelayReporting, |
| ]; |
| let mut response_fut = |
| Box::pin(peer.set_configuration(&StreamEndpointId(1), &StreamEndpointId(2), &caps)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| #[rustfmt::skip] |
| let set_capabilities_req = &[ |
| 0x03, // Set Configuration Signal |
| 0x01 << 2, // ACP SEID, RFA |
| 0x02 << 2, // INT SEID, RFA |
| // MediaTransport (Length of Service Capability = 0) |
| 0x01, 0x00, |
| // Reporting (LOSC = 0) |
| 0x02, 0x00, |
| // Recovery (LOSC = 3), Type (0x01), Window size (10), Number Media Packets (255) |
| 0x03, 0x03, 0x01, 0x0A, 0xFF, |
| // Media Codec (LOSC = 2 + 4), Media Type Audio (0x00), Codec type (0x40), Codec speciic |
| // as above |
| 0x07, 0x06, 0x00, 0x40, 0xDE, 0xAD, 0xBE, 0xEF, |
| // Content Protection (LOSC = 2 + 2), DTCP (0x01, 0x00), CP Specific as above |
| 0x04, 0x04, 0x01, 0x00, 0xF0, 0x0D, |
| // DelayReporting (LOSC = 0) |
| 0x08, 0x00, |
| ]; |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(set_capabilities_req, &received[1..]); |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert!(complete.is_pending()); |
| |
| // Positive response |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x03, // Set Configuration |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert_eq!(Poll::Ready(Ok(())), complete); |
| } |
| |
| #[test] |
| fn set_config_error_response() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let caps = vec![ |
| ServiceCapability::MediaTransport, |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }, |
| ]; |
| let mut response_fut = |
| Box::pin(peer.set_configuration(&StreamEndpointId(1), &StreamEndpointId(2), &caps)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| #[rustfmt::skip] |
| let set_capabilities_req = &[ |
| 0x03, // Set Configuration Signal |
| 0x01 << 2, // ACP SEID, RFA |
| 0x02 << 2, // INT SEID, RFA |
| // MediaTransport (Length of Service Capability = 0) |
| 0x01, 0x00, |
| // Media Codec (LOSC = 2 + 4), Media Type Audio (0x00), Codec type (0x40), Codec speciic |
| // as above |
| 0x07, 0x06, 0x00, 0x40, 0xDE, 0xAD, 0xBE, 0xEF, |
| ]; |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(set_capabilities_req, &received[1..]); |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert!(complete.is_pending()); |
| |
| // Error response |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x3, // txlabel (same), Single (0b00), Response Reject |
| 0x03, // Set Configuration |
| 0x07, // Relevant Capability (Media Codec) |
| 0x29, // Unsupported configuration |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert_eq!( |
| Poll::Ready(Err(Error::RemoteConfigRejected(0x07, 0x29))), |
| complete |
| ); |
| } |
| |
| // Set Config: Reporting |
| |
| #[test] |
| fn set_config_bad_reporting_format() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel (4), ResponseReject, Signal, Relevant Cap (Reporting), BadPayloadFormat |
| let rsp = &[0x43, 0x03, 0x02, 0x18]; |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x02, 0x01, 0x01, // Reporting, Length 1 (too long), dummy |
| ]; |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| #[test] |
| fn set_config_reporting_ok() { |
| let set_config_cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x02, 0x00, // Reporting, Length 0 |
| ]; |
| |
| expect_config_recv_cap_okay( |
| set_config_cmd, |
| StreamEndpointId(1), |
| StreamEndpointId(2), |
| ServiceCapability::Reporting, |
| ); |
| } |
| |
| // Set Config: Content Protection |
| |
| #[test] |
| fn set_config_bad_content_protection_format() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel (4), ResponseReject, Signal, Relevant Cap (CP), BadCpFormat |
| let rsp = &[0x43, 0x03, 0x04, 0x27]; |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x04, 0x01, 0x01, // Content Protection (4), Length 1 (too short), dummy |
| ]; |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x04, 0x02, // Content Protection (4), Length 2 |
| 0xF0, 0x0F, // CP Type 0x0FF0 (invalid) |
| ]; |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| #[test] |
| fn set_config_content_protection_ok() { |
| let set_config_cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x04, 0x02, // Content Protection (4), Length 2 |
| 0x01, 0x00, // LSB, MSB of DTCP |
| ]; |
| |
| expect_config_recv_cap_okay( |
| set_config_cmd, |
| StreamEndpointId(1), |
| StreamEndpointId(2), |
| ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::DigitalTransmissionContentProtection, |
| extra: vec![], |
| }, |
| ); |
| } |
| |
| // Set Config: Recovery |
| |
| #[rustfmt::skip] |
| fn build_recovery_cmd(recovery_type: u8, mrws: u8, mnmp: u8) -> [u8; 9] { |
| [ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x03, 0x03, // Recovery (3), length 3 |
| recovery_type, // Recovery Type |
| mrws, // Maximum Recovery Window Size |
| mnmp, // Maximum Number of Media Packets |
| ] |
| } |
| |
| #[test] |
| fn set_config_bad_recovery_type() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| // TxLabel (4), Signal, Relevant Cap (Recovery), BadRecoveryType |
| let rsp = &[0x43, 0x03, 0x03, 0x22]; |
| |
| // Forbidden Recovery Type |
| let cmd = &build_recovery_cmd(0x00, 0x01, 0x01); |
| |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| |
| // RFD recovery type |
| let cmd = &build_recovery_cmd(0x02, 0x01, 0x01); |
| |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| #[test] |
| fn set_config_bad_recovery_format() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x03, 0x01, 0x01, // Recovery (3), length 1, 0x01 |
| ]; |
| // TxLabel 4, ResponseReject, Relevant Cap (Recovery), BadRecoveryFormat |
| let rsp = &[0x43, 0x03, 0x03, 0x25]; |
| |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| |
| // Bad Maximum Recovery Window Size (zero is not allowed) |
| let cmd = &build_recovery_cmd(0x01, 0x00, 0x01); |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| // Bad Maximum Recovery Window Size (too large) |
| let cmd = &build_recovery_cmd(0x01, 0x20, 0x01); |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| // Bad Maximum Number of Media Packets (zero is not allowed) |
| let cmd = &build_recovery_cmd(0x01, 0x01, 0x00); |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| // Bad Maximum Number of Media Packets (too large) |
| let cmd = &build_recovery_cmd(0x01, 0x01, 0x21); |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| #[test] |
| fn set_config_recovery_ok() { |
| let set_config_cmd = &build_recovery_cmd(0x01, 0x01, 0x01); |
| expect_config_recv_cap_okay( |
| set_config_cmd, |
| StreamEndpointId(1), |
| StreamEndpointId(2), |
| ServiceCapability::Recovery { |
| recovery_type: 0x01, |
| max_recovery_window_size: 0x01, |
| max_number_media_packets: 0x01, |
| }, |
| ); |
| } |
| |
| // Media Codec |
| |
| #[test] |
| fn set_config_bad_media_codec_format() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x07, 0x01, 0x01, // Media Codec (7), length 1, dummy byte |
| ]; |
| // TxLabel 4, ResponseReject, Relevant Cap (Media Codec), BadPayloadFormat |
| let rsp = &[0x43, 0x03, 0x07, 0x18]; |
| |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| |
| let cmd = &[ |
| 0x40, 0x03, 0x04, 0x08, // TxLabel 4, Signal, ACP (1) and INT (2) SEID |
| 0x07, 0x02, // Media Codec (7), length 2 |
| 0x40, // Unknown Media Type, RFA |
| 0x01, // Media Codec Type |
| ]; |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| #[test] |
| fn set_config_media_type_ok() { |
| // Sample set config command sent from a phone |
| let set_config_cmd = &[ |
| 0x40, 0x03, 0x18, 0x04, // TxLabel 4, Signal, ACP (6) and INT (1) SEID |
| 0x07, 0x06, // Media Codec (7), length 6 |
| 0x00, // Media Type: Audio |
| 0x00, // Media Codec Type: SBC (0x00) |
| 0x21, // Sampling Freq: 44.1khz (0x20), Channel Mode Joint Stereo (0x01) |
| 0x15, // Block length 16 (0x10), 8 Subbands (0x04), Loudness Allocation (0x01) |
| 0x02, // Minimum bitpool: 2 |
| 0x35, // Maximum bitpool: 53 (0x35) |
| ]; |
| expect_config_recv_cap_okay( |
| set_config_cmd, |
| StreamEndpointId(6), |
| StreamEndpointId(1), |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0), |
| codec_extra: vec![0x21, 0x15, 0x02, 0x35], |
| }, |
| ); |
| } |
| |
| // Reconfigure |
| |
| // Set config must be at least 1 SEID and one configuration long. |
| incoming_cmd_length_fail_test!(reconfigure_length_too_short, 0x05, 1); |
| |
| #[test] |
| fn reconfigure_invalid_capabilities() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let cmd = &[ |
| 0x40, 0x05, 0x04, // TxLabel 4, Signal, ACP (1) SEID |
| 0x03, 0x03, // Recovery (3), length 3, |
| 0x01, 0x01, 0x01, // RFC2733, MRWS, MNMP |
| ]; |
| // TxLabel (4), ResponseRreject, Relevant cap (Recovery), Invalid Capabilities |
| let rsp = &[0x43, 0x05, 0x03, 0x1A]; |
| |
| stream_request_response(&mut exec, &mut stream, &remote, cmd, rsp); |
| } |
| |
| #[test] |
| fn reconfig_event_responder_send_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let reconfigure_cmd = &[ |
| 0x40, 0x05, 0x04, // TxLabel 4, Signal, ACP (1) SEID |
| 0x07, 0x02, // Media Codec, Length 2 |
| 0x00, // Media Type: Audio |
| 0x00, // Media Codec Type: SBC |
| ]; |
| |
| assert!(remote.write(reconfigure_cmd).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::Reconfigure { |
| responder, |
| local_stream_id, |
| capabilities, |
| } => { |
| assert_eq!(StreamEndpointId(1), local_stream_id); |
| assert_eq!( |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0), |
| codec_extra: vec![], |
| }, |
| capabilities[0] |
| ); |
| responder.send() |
| } |
| _ => panic!("should have received a Reconfigure"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Expected response: Same TxLabel (4) & ResponseAccept, Signal, |
| expect_remote_recv(&[0x42, 0x05], &remote); |
| } |
| |
| #[test] |
| fn reconfig_event_responder_reject_works() { |
| let (mut stream, _, remote, mut exec) = setup_stream_test(); |
| |
| let reconfigure_cmd = &[ |
| 0x40, 0x05, 0x04, // TxLabel 4, Signal, ACP (1) SEID |
| 0x07, 0x02, // Media Codec, Length 2 |
| 0x00, // Media Type: Audio |
| 0x00, // Media Codec Type: SBC |
| ]; |
| |
| assert!(remote.write(reconfigure_cmd).is_ok()); |
| |
| let respond_res = match next_request(&mut stream, &mut exec) { |
| Request::Reconfigure { |
| responder, |
| local_stream_id, |
| capabilities, |
| } => { |
| assert_eq!(StreamEndpointId(1), local_stream_id); |
| assert_eq!( |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0), |
| codec_extra: vec![], |
| }, |
| capabilities[0] |
| ); |
| responder.reject(Some(&capabilities[0]), ErrorCode::UnsupportedConfiguration) |
| } |
| _ => panic!("should have received a Reconfigure"), |
| }; |
| |
| assert!(respond_res.is_ok()); |
| |
| // Expected response: Same TxLabel (4) & ResponseReject, Signal, |
| // Relevant capability, Error Code (Unsupported Config) |
| expect_remote_recv(&[0x43, 0x05, 0x07, 0x29], &remote); |
| } |
| |
| #[test] |
| fn reconfigure_command_works() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| // This should cover all the implemented capabilities to confirm they are |
| // encoded correctly. |
| let caps = vec![ |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }, |
| ServiceCapability::ContentProtection { |
| protection_type: ContentProtectionType::DigitalTransmissionContentProtection, |
| extra: vec![0xF0, 0x0D], |
| }, |
| ]; |
| let mut response_fut = Box::pin(peer.reconfigure(&StreamEndpointId(1), &caps)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| #[rustfmt::skip] |
| let reconfigure_req = &[ |
| 0x05, // Reconfigure Signal |
| 0x01 << 2, // ACP SEID, RFA |
| // Media Codec (LOSC = 2 + 4), Media Type Audio (0x00), Codec type (0x40), Codec speciic |
| // as above |
| 0x07, 0x06, 0x00, 0x40, 0xDE, 0xAD, 0xBE, 0xEF, |
| // Content Protection (LOSC = 2 + 2), DTCP (0x01, 0x00), CP Specific as above |
| 0x04, 0x04, 0x01, 0x00, 0xF0, 0x0D, |
| ]; |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(reconfigure_req, &received[1..]); |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert!(complete.is_pending()); |
| |
| // Positive response |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x2, // txlabel (same), Single (0b00), Response Accept (0b10) |
| 0x05, // Reconfigure |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert_eq!(Poll::Ready(Ok(())), complete); |
| } |
| |
| #[test] |
| fn reconfigure_error_response() { |
| let mut exec = fasync::Executor::new().expect("failed to create an executor"); |
| let (peer, remote) = setup_peer(); |
| let caps = vec![ |
| ServiceCapability::MediaTransport, |
| ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }, |
| ]; |
| let mut response_fut = Box::pin(peer.reconfigure(&StreamEndpointId(1), &caps)); |
| // This isn't valid, you can't reconfigure Media Transport |
| assert_eq!( |
| Poll::Ready(Err(Error::Encoding)), |
| exec.run_until_stalled(&mut response_fut) |
| ); |
| |
| let caps = vec![ServiceCapability::MediaCodec { |
| media_type: MediaType::Audio, |
| codec_type: MediaCodecType::new(0x40), |
| codec_extra: vec![0xDE, 0xAD, 0xBE, 0xEF], |
| }]; |
| let mut response_fut = Box::pin(peer.reconfigure(&StreamEndpointId(1), &caps)); |
| assert!(exec.run_until_stalled(&mut response_fut).is_pending()); |
| |
| let received = recv_remote(&remote).unwrap(); |
| #[rustfmt::skip] |
| let reconfigure_req = &[ |
| 0x05, // Reconfigure Signal |
| 0x01 << 2, // ACP SEID, RFA |
| // Media Codec (LOSC = 2 + 4), Media Type Audio (0x00), Codec type (0x40), Codec speciic |
| // as above |
| 0x07, 0x06, 0x00, 0x40, 0xDE, 0xAD, 0xBE, 0xEF, |
| ]; |
| // Last half of header must be Single (0b00) and Command (0b00) |
| assert_eq!(0x00, received[0] & 0xF); |
| assert_eq!(reconfigure_req, &received[1..]); |
| |
| let txlabel_raw = received[0] & 0xF0; |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert!(complete.is_pending()); |
| |
| // Error response |
| let response: &[u8] = &[ |
| txlabel_raw << 4 | 0x0 << 2 | 0x3, // txlabel (same), Single (0b00), Response Reject |
| 0x05, // Set Configuration |
| 0x00, // Relevant Capability (none) |
| 0x13, // SEP In Use |
| ]; |
| assert!(remote.write(response).is_ok()); |
| |
| let complete = exec.run_until_stalled(&mut response_fut); |
| assert_eq!( |
| Poll::Ready(Err(Error::RemoteConfigRejected(0x00, 0x13))), |
| complete |
| ); |
| } |