use broadcast_common::{Parse, Serialize};
use scte104::operations::provisioning_request::{
DpiPidEntry, InjectorComponentList, ProvisioningRequest, ProvisioningService,
};
use scte104::operations::splice_request::{SpliceInsertType, SpliceRequest};
use scte104::operations::{AnyOperation, AnySingleOperation, ConfigRequest, FaultRequest};
use scte104::time::Timestamp;
use scte104::{MultipleOperationMessage, SingleOperationMessage};
#[rustfmt::skip]
const SPLICE_REQUEST_VECTOR: [u8; 15] = [
0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x01, 0x13, 0x88, 0x01, 0x2C, 0x00, 0x00, 0x01, 0x00, ];
#[test]
fn splice_request_vector_parses_and_round_trips() {
let sr = SpliceRequest::parse(&SPLICE_REQUEST_VECTOR).expect("parse spec-derived vector");
assert_eq!(sr.splice_insert_type, SpliceInsertType::SpliceStartNormal);
assert_eq!(sr.splice_event_id, 0x0000_0042);
assert_eq!(sr.unique_program_id, 1);
assert_eq!(sr.pre_roll_time, 5000);
assert_eq!(sr.break_duration, 300);
assert_eq!(sr.avail_num, 0);
assert_eq!(sr.avails_expected, 0);
assert_eq!(sr.auto_return_flag, 1);
assert_eq!(sr.not_an_entry_flag, 0);
let mut out = vec![0u8; sr.serialized_len()];
sr.serialize_into(&mut out).unwrap();
assert_eq!(
out, SPLICE_REQUEST_VECTOR,
"byte-identical to the Table 9-5 spec-derived vector"
);
}
#[rustfmt::skip]
const GENERAL_RESPONSE_MESSAGE_VECTOR: [u8; 13] = [
0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x01, 0x2A, 0x00, 0x00, ];
#[test]
fn single_operation_message_vector_parses_and_round_trips() {
let msg = SingleOperationMessage::parse(&GENERAL_RESPONSE_MESSAGE_VECTOR)
.expect("parse spec-derived Table 8-1 vector");
assert_eq!(msg.op_id, 0x0000);
assert_eq!(msg.message_size, 13);
assert_eq!(msg.result, 0x0000);
assert_eq!(msg.result_extension, 0xFFFF);
assert_eq!(msg.protocol_version, 0);
assert_eq!(msg.as_index, 1);
assert_eq!(msg.message_number, 42);
assert_eq!(msg.dpi_pid_index, 0);
assert!(matches!(msg.data, AnySingleOperation::GeneralResponse(_)));
let mut out = vec![0u8; msg.serialized_len()];
msg.serialize_into(&mut out).unwrap();
assert_eq!(
out, GENERAL_RESPONSE_MESSAGE_VECTOR,
"byte-identical to the Table 8-1 spec-derived vector"
);
}
#[rustfmt::skip]
const MULTIPLE_OPERATION_MESSAGE_VECTOR: [u8; 31] = [
0xFF, 0xFF, 0x00, 0x1F, 0x00, 0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x01, 0x13, 0x88, 0x01, 0x2C, 0x00, 0x00, 0x01, 0x00, ];
#[test]
fn multiple_operation_message_vector_parses_and_round_trips() {
let msg = MultipleOperationMessage::parse(&MULTIPLE_OPERATION_MESSAGE_VECTOR)
.expect("parse spec-derived Table 8-2 vector");
assert_eq!(msg.message_size, 31);
assert_eq!(msg.protocol_version, 0);
assert_eq!(msg.as_index, 1);
assert_eq!(msg.message_number, 42);
assert_eq!(msg.dpi_pid_index, 0);
assert_eq!(msg.scte35_protocol_version, 0);
assert_eq!(msg.timestamp, Timestamp::None);
assert_eq!(msg.operations.len(), 1);
assert_eq!(msg.operations[0].op_id, 0x0101);
match &msg.operations[0].data {
AnyOperation::SpliceRequest(sr) => {
assert_eq!(sr.splice_event_id, 0x42);
assert_eq!(sr.pre_roll_time, 5000);
}
other => panic!("expected SpliceRequest, got {other:?}"),
}
let mut out = vec![0u8; msg.serialized_len()];
msg.serialize_into(&mut out).unwrap();
assert_eq!(
out, MULTIPLE_OPERATION_MESSAGE_VECTOR,
"byte-identical to the Table 8-2 spec-derived vector"
);
}
#[rustfmt::skip]
const CONFIG_REQUEST_VECTOR: [u8; 12] = [
0xC0, 0xA8, 0x01, 0x01, 0x14, 0x2F, 0x01, 0x00, 0x07, 0x00, 0x03, 0x01, ];
#[test]
fn config_request_vector_parses_and_round_trips() {
let cr = ConfigRequest::parse(&CONFIG_REQUEST_VECTOR).expect("parse spec-derived vector");
assert_eq!(cr.as_ip_address, 0xC0A8_0101);
assert_eq!(cr.as_socket_number, 5167);
assert_eq!(cr.activeflag, 1);
assert_eq!(cr.protocol_version, 0);
assert_eq!(cr.last_as_index, 7);
assert_eq!(cr.last_injectorcount, 3);
assert_eq!(cr.permanent_connection_requested, 1);
let mut out = vec![0u8; cr.serialized_len()];
cr.serialize_into(&mut out).unwrap();
assert_eq!(
out, CONFIG_REQUEST_VECTOR,
"byte-identical to the Table 10-1 spec-derived vector"
);
}
#[rustfmt::skip]
const FAULT_REQUEST_VECTOR: [u8; 40] = [
0x0A, 0x00, 0x00, 0x02, 0x1F, 0x40, 0x73, 0x76, 0x63, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ];
#[test]
fn fault_request_vector_parses_and_round_trips() {
let fr = FaultRequest::parse(&FAULT_REQUEST_VECTOR).expect("parse spec-derived vector");
assert_eq!(fr.injector_ip_address, 0x0A00_0002);
assert_eq!(fr.injector_socket_number, 8000);
assert_eq!(&fr.injector_service_name[..4], b"svc1");
assert!(fr.injector_service_name[4..].iter().all(|&b| b == 0));
assert_eq!(fr.dpi_pid_index, 5);
let mut out = vec![0u8; fr.serialized_len()];
fr.serialize_into(&mut out).unwrap();
assert_eq!(
out, FAULT_REQUEST_VECTOR,
"byte-identical to the Table 10-5 spec-derived vector"
);
}
#[rustfmt::skip]
const PROVISIONING_REQUEST_VECTOR: [u8; 51] = [
0x01, 0x0A, 0x00, 0x00, 0x01, 0x1F, 0x40, 0x73, 0x76, 0x63, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x01, 0x10, 0x02, 0x20, 0x21, 0x01, 0x30, ];
#[test]
fn provisioning_request_vector_parses_and_round_trips() {
let pr = ProvisioningRequest::parse(&PROVISIONING_REQUEST_VECTOR)
.expect("parse spec-derived Table 10-3 vector");
assert_eq!(pr.services.len(), 1);
let svc = &pr.services[0];
assert_eq!(svc.injector_ip_address, 0x0A00_0001);
assert_eq!(svc.injector_socket_number, 8000);
assert_eq!(&svc.service_name[..4], b"svc1");
assert_eq!(
svc.dpi_pids,
vec![DpiPidEntry {
dpi_pid_index: 5,
shared_pid: 0,
event_id_compliance_flag: 1,
}]
);
assert_eq!(svc.component_mode, 1);
assert_eq!(
svc.injector_component_list,
Some(InjectorComponentList {
video_component_tag: 0x10,
audio_component_tags: vec![0x20, 0x21],
data_component_tags: vec![0x30],
})
);
let mut out = vec![0u8; pr.serialized_len()];
pr.serialize_into(&mut out).unwrap();
assert_eq!(
out, PROVISIONING_REQUEST_VECTOR,
"byte-identical to the Table 10-3 spec-derived vector"
);
}
#[test]
fn provisioning_request_typed_construction_matches_vector() {
let mut service_name = [0u8; 32];
service_name[..4].copy_from_slice(b"svc1");
let pr = ProvisioningRequest {
services: vec![ProvisioningService {
injector_ip_address: 0x0A00_0001,
injector_socket_number: 8000,
service_name,
dpi_pids: vec![DpiPidEntry {
dpi_pid_index: 5,
shared_pid: 0,
event_id_compliance_flag: 1,
}],
component_mode: 1,
injector_component_list: Some(InjectorComponentList {
video_component_tag: 0x10,
audio_component_tags: vec![0x20, 0x21],
data_component_tags: vec![0x30],
}),
}],
};
assert_eq!(pr.to_bytes(), PROVISIONING_REQUEST_VECTOR);
}
#[test]
fn new_op_ids_match_table_8_3() {
use scte104::traits::OperationDef;
assert_eq!(ConfigRequest::OP_ID, 0x0009);
assert_eq!(scte104::operations::ConfigResponse::OP_ID, 0x000A);
assert_eq!(ProvisioningRequest::OP_ID, 0x000B);
assert_eq!(scte104::operations::ProvisioningResponse::OP_ID, 0x000C);
assert_eq!(FaultRequest::OP_ID, 0x000F);
assert_eq!(scte104::operations::FaultResponse::OP_ID, 0x0010);
assert_eq!(scte104::operations::AsAliveRequest::OP_ID, 0x0011);
assert_eq!(scte104::operations::AsAliveResponse::OP_ID, 0x0012);
}