use crate::error::PfcpError;
use crate::ie::additional_usage_reports_information::AdditionalUsageReportsInformation;
use crate::ie::application_detection_information::ApplicationDetectionInformation;
use crate::ie::duration_measurement::DurationMeasurement;
use crate::ie::end_time::EndTime;
use crate::ie::ethernet_traffic_information::EthernetTrafficInformation;
use crate::ie::query_urr_reference::QueryURRReference;
use crate::ie::quota_holding_time::QuotaHoldingTime;
use crate::ie::sequence_number::SequenceNumber;
use crate::ie::start_time::StartTime;
use crate::ie::time_of_first_packet::TimeOfFirstPacket;
use crate::ie::time_of_last_packet::TimeOfLastPacket;
use crate::ie::time_quota::TimeQuota;
use crate::ie::ue_ip_address_usage_information::UEIPAddressUsageInformation;
use crate::ie::urr_id::UrrId;
use crate::ie::usage_information::UsageInformation;
use crate::ie::usage_report_trigger::UsageReportTrigger;
use crate::ie::volume_measurement::VolumeMeasurement;
use crate::ie::volume_quota::VolumeQuota;
use crate::ie::{Ie, IeType};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UsageReport {
pub urr_id: UrrId,
pub ur_seqn: SequenceNumber,
pub usage_report_trigger: UsageReportTrigger,
pub volume_measurement: Option<VolumeMeasurement>,
pub duration_measurement: Option<DurationMeasurement>,
pub time_of_first_packet: Option<TimeOfFirstPacket>,
pub time_of_last_packet: Option<TimeOfLastPacket>,
pub usage_information: Option<UsageInformation>,
pub volume_quota: Option<VolumeQuota>,
pub time_quota: Option<TimeQuota>,
pub quota_holding_time: Option<QuotaHoldingTime>,
pub start_time: Option<StartTime>,
pub end_time: Option<EndTime>,
pub query_urr_reference: Option<QueryURRReference>,
pub application_detection_information: Option<ApplicationDetectionInformation>,
pub ue_ip_address_usage_information: Option<UEIPAddressUsageInformation>,
pub additional_usage_reports_information: Option<AdditionalUsageReportsInformation>,
pub ethernet_traffic_information: Option<EthernetTrafficInformation>,
}
impl UsageReport {
pub fn new(
urr_id: UrrId,
ur_seqn: SequenceNumber,
usage_report_trigger: UsageReportTrigger,
) -> Self {
UsageReport {
urr_id,
ur_seqn,
usage_report_trigger,
volume_measurement: None,
duration_measurement: None,
time_of_first_packet: None,
time_of_last_packet: None,
usage_information: None,
volume_quota: None,
time_quota: None,
quota_holding_time: None,
start_time: None,
end_time: None,
query_urr_reference: None,
application_detection_information: None,
ue_ip_address_usage_information: None,
additional_usage_reports_information: None,
ethernet_traffic_information: None,
}
}
pub fn builder(urr_id: UrrId) -> UsageReportBuilder {
UsageReportBuilder::new(urr_id)
}
pub fn marshal(&self) -> Vec<u8> {
let mut buffer = vec![];
buffer.extend_from_slice(&self.urr_id.to_ie().marshal());
buffer.extend_from_slice(&self.ur_seqn.to_ie().marshal());
buffer.extend_from_slice(&self.usage_report_trigger.to_ie().marshal());
if let Some(ref vm) = self.volume_measurement {
if let Ok(ie) = vm.to_ie() {
buffer.extend_from_slice(&ie.marshal());
}
}
if let Some(ref dm) = self.duration_measurement {
let ie = dm.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref tofp) = self.time_of_first_packet {
let ie = tofp.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref tolp) = self.time_of_last_packet {
let ie = tolp.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref ui) = self.usage_information {
let ie = ui.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref vq) = self.volume_quota {
if let Ok(ie) = vq.to_ie() {
buffer.extend_from_slice(&ie.marshal());
}
}
if let Some(ref tq) = self.time_quota {
let ie = tq.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref qht) = self.quota_holding_time {
let ie = qht.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref st) = self.start_time {
let ie = st.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref et) = self.end_time {
let ie = et.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref qur) = self.query_urr_reference {
let ie = qur.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref adi) = self.application_detection_information {
if let Ok(ie) = adi.to_ie() {
buffer.extend_from_slice(&ie.marshal());
}
}
if let Some(ref ueip) = self.ue_ip_address_usage_information {
if let Ok(ie) = ueip.to_ie() {
buffer.extend_from_slice(&ie.marshal());
}
}
if let Some(ref auri) = self.additional_usage_reports_information {
let ie = auri.to_ie();
buffer.extend_from_slice(&ie.marshal());
}
if let Some(ref eti) = self.ethernet_traffic_information {
buffer.extend_from_slice(&eti.to_ie().marshal());
}
buffer
}
pub fn unmarshal(data: &[u8]) -> Result<Self, PfcpError> {
let mut cursor = 0;
let mut urr_id = None;
let mut ur_seqn = None;
let mut usage_report_trigger = None;
let mut volume_measurement = None;
let mut duration_measurement = None;
let mut time_of_first_packet = None;
let mut time_of_last_packet = None;
let mut usage_information = None;
let mut volume_quota = None;
let mut time_quota = None;
let mut quota_holding_time = None;
let mut start_time = None;
let mut end_time = None;
let mut query_urr_reference = None;
let mut application_detection_information = None;
let mut ue_ip_address_usage_information = None;
let mut additional_usage_reports_information = None;
let mut ethernet_traffic_information = None;
while cursor < data.len() {
let ie = Ie::unmarshal(&data[cursor..])?;
match ie.ie_type {
IeType::UrrId => urr_id = Some(UrrId::unmarshal(&ie.payload)?),
IeType::SequenceNumber => ur_seqn = Some(SequenceNumber::unmarshal(&ie.payload)?),
IeType::UsageReportTrigger => {
usage_report_trigger = Some(UsageReportTrigger::unmarshal(&ie.payload)?)
}
IeType::VolumeMeasurement => {
volume_measurement = Some(VolumeMeasurement::unmarshal(&ie.payload)?)
}
IeType::DurationMeasurement => {
duration_measurement = Some(DurationMeasurement::unmarshal(&ie.payload)?)
}
IeType::TimeOfFirstPacket => {
time_of_first_packet = Some(TimeOfFirstPacket::unmarshal(&ie.payload)?)
}
IeType::TimeOfLastPacket => {
time_of_last_packet = Some(TimeOfLastPacket::unmarshal(&ie.payload)?)
}
IeType::UsageInformation => {
usage_information = Some(UsageInformation::unmarshal(&ie.payload)?)
}
IeType::VolumeQuota => volume_quota = Some(VolumeQuota::unmarshal(&ie.payload)?),
IeType::TimeQuota => time_quota = Some(TimeQuota::unmarshal(&ie.payload)?),
IeType::QuotaHoldingTime => {
quota_holding_time = Some(QuotaHoldingTime::unmarshal(&ie.payload)?)
}
IeType::StartTime => start_time = Some(StartTime::unmarshal(&ie.payload)?),
IeType::EndTime => end_time = Some(EndTime::unmarshal(&ie.payload)?),
IeType::QueryUrrReference => {
query_urr_reference = Some(QueryURRReference::unmarshal(&ie.payload)?)
}
IeType::ApplicationDetectionInformation => {
application_detection_information =
Some(ApplicationDetectionInformation::unmarshal(&ie.payload)?)
}
IeType::UeIpAddressUsageInformation => {
ue_ip_address_usage_information =
Some(UEIPAddressUsageInformation::unmarshal(&ie.payload)?)
}
IeType::AdditionalUsageReportsInformation => {
additional_usage_reports_information =
Some(AdditionalUsageReportsInformation::unmarshal(&ie.payload)?)
}
IeType::EthernetTrafficInformation => {
ethernet_traffic_information =
Some(EthernetTrafficInformation::unmarshal(&ie.payload)?)
}
_ => (),
}
cursor += ie.len() as usize;
}
Ok(UsageReport {
urr_id: urr_id.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::UrrId,
message_type: None,
parent_ie: Some(IeType::UsageReportWithinSessionReportRequest),
})?,
ur_seqn: ur_seqn.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::UrSeqn,
message_type: None,
parent_ie: Some(IeType::UsageReportWithinSessionReportRequest),
})?,
usage_report_trigger: usage_report_trigger.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::UsageReportTrigger,
message_type: None,
parent_ie: Some(IeType::UsageReportWithinSessionReportRequest),
})?,
volume_measurement,
duration_measurement,
time_of_first_packet,
time_of_last_packet,
usage_information,
volume_quota,
time_quota,
quota_holding_time,
start_time,
end_time,
query_urr_reference,
application_detection_information,
ue_ip_address_usage_information,
additional_usage_reports_information,
ethernet_traffic_information,
})
}
pub fn to_ie(&self) -> Ie {
Ie::new(
IeType::UsageReportWithinSessionReportRequest,
self.marshal(),
)
}
}
#[derive(Debug, Default)]
pub struct UsageReportBuilder {
urr_id: Option<UrrId>,
ur_seqn: Option<SequenceNumber>,
usage_report_trigger: Option<UsageReportTrigger>,
volume_measurement: Option<VolumeMeasurement>,
duration_measurement: Option<DurationMeasurement>,
time_of_first_packet: Option<TimeOfFirstPacket>,
time_of_last_packet: Option<TimeOfLastPacket>,
usage_information: Option<UsageInformation>,
volume_quota: Option<VolumeQuota>,
time_quota: Option<TimeQuota>,
quota_holding_time: Option<QuotaHoldingTime>,
start_time: Option<StartTime>,
end_time: Option<EndTime>,
query_urr_reference: Option<QueryURRReference>,
application_detection_information: Option<ApplicationDetectionInformation>,
ue_ip_address_usage_information: Option<UEIPAddressUsageInformation>,
additional_usage_reports_information: Option<AdditionalUsageReportsInformation>,
ethernet_traffic_information: Option<EthernetTrafficInformation>,
}
impl UsageReportBuilder {
pub fn new(urr_id: UrrId) -> Self {
UsageReportBuilder {
urr_id: Some(urr_id),
ur_seqn: None,
usage_report_trigger: None,
volume_measurement: None,
duration_measurement: None,
time_of_first_packet: None,
time_of_last_packet: None,
usage_information: None,
volume_quota: None,
time_quota: None,
quota_holding_time: None,
start_time: None,
end_time: None,
query_urr_reference: None,
application_detection_information: None,
ue_ip_address_usage_information: None,
additional_usage_reports_information: None,
ethernet_traffic_information: None,
}
}
pub fn sequence_number(mut self, ur_seqn: SequenceNumber) -> Self {
self.ur_seqn = Some(ur_seqn);
self
}
pub fn trigger(mut self, trigger: UsageReportTrigger) -> Self {
self.usage_report_trigger = Some(trigger);
self
}
pub fn quota_exhausted(mut self) -> Self {
self.usage_report_trigger = Some(UsageReportTrigger::VOLTH | UsageReportTrigger::TIMTH);
self
}
pub fn periodic_report(mut self) -> Self {
self.usage_report_trigger = Some(UsageReportTrigger::PERIO);
self
}
pub fn volume_threshold_triggered(mut self) -> Self {
self.usage_report_trigger = Some(UsageReportTrigger::VOLTH);
self
}
pub fn time_threshold_triggered(mut self) -> Self {
self.usage_report_trigger = Some(UsageReportTrigger::TIMTH);
self
}
pub fn start_of_traffic(mut self) -> Self {
self.usage_report_trigger = Some(UsageReportTrigger::START);
self
}
pub fn stop_of_traffic(mut self) -> Self {
self.usage_report_trigger = Some(UsageReportTrigger::STOPT);
self
}
pub fn build(self) -> Result<UsageReport, PfcpError> {
let urr_id = self.urr_id.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::UrrId,
message_type: None,
parent_ie: Some(IeType::UsageReportWithinSessionReportRequest),
})?;
let ur_seqn = self.ur_seqn.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::UrSeqn,
message_type: None,
parent_ie: Some(IeType::UsageReportWithinSessionReportRequest),
})?;
let usage_report_trigger =
self.usage_report_trigger
.ok_or(PfcpError::MissingMandatoryIe {
ie_type: IeType::UsageReportTrigger,
message_type: None,
parent_ie: Some(IeType::UsageReportWithinSessionReportRequest),
})?;
Ok(UsageReport {
urr_id,
ur_seqn,
usage_report_trigger,
volume_measurement: self.volume_measurement,
duration_measurement: self.duration_measurement,
time_of_first_packet: self.time_of_first_packet,
time_of_last_packet: self.time_of_last_packet,
usage_information: self.usage_information,
volume_quota: self.volume_quota,
time_quota: self.time_quota,
quota_holding_time: self.quota_holding_time,
start_time: self.start_time,
end_time: self.end_time,
query_urr_reference: self.query_urr_reference,
application_detection_information: self.application_detection_information,
ue_ip_address_usage_information: self.ue_ip_address_usage_information,
additional_usage_reports_information: self.additional_usage_reports_information,
ethernet_traffic_information: self.ethernet_traffic_information,
})
}
pub fn quota_exhausted_report(urr_id: UrrId, ur_seqn: SequenceNumber) -> Self {
UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.quota_exhausted()
}
pub fn periodic_usage_report(urr_id: UrrId, ur_seqn: SequenceNumber) -> Self {
UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.periodic_report()
}
pub fn volume_threshold_report(urr_id: UrrId, ur_seqn: SequenceNumber) -> Self {
UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.volume_threshold_triggered()
}
pub fn time_threshold_report(urr_id: UrrId, ur_seqn: SequenceNumber) -> Self {
UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.time_threshold_triggered()
}
pub fn start_of_traffic_report(urr_id: UrrId, ur_seqn: SequenceNumber) -> Self {
UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.start_of_traffic()
}
pub fn stop_of_traffic_report(urr_id: UrrId, ur_seqn: SequenceNumber) -> Self {
UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.stop_of_traffic()
}
pub fn volume_measurement(mut self, volume_measurement: VolumeMeasurement) -> Self {
self.volume_measurement = Some(volume_measurement);
self
}
pub fn duration_measurement(mut self, duration_measurement: DurationMeasurement) -> Self {
self.duration_measurement = Some(duration_measurement);
self
}
pub fn time_of_first_packet(mut self, time_of_first_packet: TimeOfFirstPacket) -> Self {
self.time_of_first_packet = Some(time_of_first_packet);
self
}
pub fn time_of_last_packet(mut self, time_of_last_packet: TimeOfLastPacket) -> Self {
self.time_of_last_packet = Some(time_of_last_packet);
self
}
pub fn usage_information(mut self, usage_information: UsageInformation) -> Self {
self.usage_information = Some(usage_information);
self
}
pub fn with_volume_data(mut self, total: u64, uplink: u64, downlink: u64) -> Self {
let volume_measurement = VolumeMeasurement::new(
0x07, Some(total),
Some(uplink),
Some(downlink),
None,
None,
None,
);
self.volume_measurement = Some(volume_measurement);
self
}
pub fn with_packet_data(mut self, total: u64, uplink: u64, downlink: u64) -> Self {
let volume_measurement = VolumeMeasurement::new(
0x38, None,
None,
None,
Some(total),
Some(uplink),
Some(downlink),
);
self.volume_measurement = Some(volume_measurement);
self
}
pub fn with_duration(mut self, seconds: u32) -> Self {
self.duration_measurement = Some(DurationMeasurement::new(seconds));
self
}
pub fn with_packet_times(mut self, first_timestamp: u32, last_timestamp: u32) -> Self {
self.time_of_first_packet = Some(TimeOfFirstPacket::new(first_timestamp));
self.time_of_last_packet = Some(TimeOfLastPacket::new(last_timestamp));
self
}
pub fn with_usage_flags(mut self, bef: bool, aft: bool, uae: bool, ube: bool) -> Self {
self.usage_information = Some(UsageInformation::new_with_flags(bef, aft, uae, ube));
self
}
pub fn volume_quota(mut self, volume_quota: VolumeQuota) -> Self {
self.volume_quota = Some(volume_quota);
self
}
pub fn time_quota(mut self, time_quota: TimeQuota) -> Self {
self.time_quota = Some(time_quota);
self
}
pub fn quota_holding_time(mut self, quota_holding_time: QuotaHoldingTime) -> Self {
self.quota_holding_time = Some(quota_holding_time);
self
}
pub fn start_time(mut self, start_time: StartTime) -> Self {
self.start_time = Some(start_time);
self
}
pub fn end_time(mut self, end_time: EndTime) -> Self {
self.end_time = Some(end_time);
self
}
pub fn with_volume_quota(mut self, total: u64, uplink: u64, downlink: u64) -> Self {
let volume_quota = VolumeQuota::new(
0x07, Some(total),
Some(uplink),
Some(downlink),
);
self.volume_quota = Some(volume_quota);
self
}
pub fn with_time_quota(mut self, quota_seconds: u32) -> Self {
self.time_quota = Some(TimeQuota::new(quota_seconds));
self
}
pub fn with_quota_holding_time(mut self, holding_seconds: u32) -> Self {
self.quota_holding_time = Some(QuotaHoldingTime::new(holding_seconds));
self
}
pub fn with_monitoring_window(mut self, start_timestamp: u32, end_timestamp: u32) -> Self {
self.start_time = Some(StartTime::new(start_timestamp));
self.end_time = Some(EndTime::new(end_timestamp));
self
}
pub fn query_urr_reference(mut self, query_urr_reference: QueryURRReference) -> Self {
self.query_urr_reference = Some(query_urr_reference);
self
}
pub fn application_detection_information(
mut self,
application_detection_information: ApplicationDetectionInformation,
) -> Self {
self.application_detection_information = Some(application_detection_information);
self
}
pub fn ue_ip_address_usage_information(
mut self,
ue_ip_address_usage_information: UEIPAddressUsageInformation,
) -> Self {
self.ue_ip_address_usage_information = Some(ue_ip_address_usage_information);
self
}
pub fn additional_usage_reports_information(
mut self,
additional_usage_reports_information: AdditionalUsageReportsInformation,
) -> Self {
self.additional_usage_reports_information = Some(additional_usage_reports_information);
self
}
pub fn ethernet_traffic_information(
mut self,
ethernet_traffic_information: EthernetTrafficInformation,
) -> Self {
self.ethernet_traffic_information = Some(ethernet_traffic_information);
self
}
pub fn with_query_reference(mut self, reference: u32) -> Self {
self.query_urr_reference = Some(QueryURRReference::new(reference));
self
}
pub fn with_detected_application(mut self, app_id: &str) -> Self {
self.application_detection_information =
Some(ApplicationDetectionInformation::simple_app(app_id));
self
}
pub fn with_application_instance(mut self, app_id: &str, instance_id: &str) -> Self {
self.application_detection_information = Some(
ApplicationDetectionInformation::app_with_instance(app_id, instance_id),
);
self
}
pub fn with_ue_ipv4_usage(mut self, ipv4: std::net::Ipv4Addr, count: u32) -> Self {
self.ue_ip_address_usage_information =
Some(UEIPAddressUsageInformation::with_ipv4(ipv4, count));
self
}
pub fn with_additional_flags(mut self, auri: bool, nouri: bool) -> Self {
let flags = if auri { 0x01 } else { 0x00 } | if nouri { 0x02 } else { 0x00 };
self.additional_usage_reports_information =
Some(AdditionalUsageReportsInformation::new(flags));
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_usage_report_marshal_unmarshal() {
let urr_id = UrrId::new(1);
let ur_seqn = SequenceNumber::new(1);
let usage_report_trigger = UsageReportTrigger::new(1);
let usage_report = UsageReport::new(urr_id, ur_seqn, usage_report_trigger);
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_builder_basic() {
let urr_id = UrrId::new(42);
let ur_seqn = SequenceNumber::new(123);
let trigger = UsageReportTrigger::PERIO;
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.trigger(trigger)
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.usage_report_trigger, trigger);
}
#[test]
fn test_usage_report_builder_quota_exhausted() {
let urr_id = UrrId::new(1);
let ur_seqn = SequenceNumber::new(42);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.quota_exhausted()
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(
usage_report.usage_report_trigger,
UsageReportTrigger::VOLTH | UsageReportTrigger::TIMTH
);
}
#[test]
fn test_usage_report_builder_periodic_report() {
let urr_id = UrrId::new(2);
let ur_seqn = SequenceNumber::new(43);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.periodic_report()
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.usage_report_trigger, UsageReportTrigger::PERIO);
}
#[test]
fn test_usage_report_builder_volume_threshold() {
let urr_id = UrrId::new(3);
let ur_seqn = SequenceNumber::new(44);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.volume_threshold_triggered()
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.usage_report_trigger, UsageReportTrigger::VOLTH);
}
#[test]
fn test_usage_report_builder_time_threshold() {
let urr_id = UrrId::new(4);
let ur_seqn = SequenceNumber::new(45);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.time_threshold_triggered()
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.usage_report_trigger, UsageReportTrigger::TIMTH);
}
#[test]
fn test_usage_report_builder_start_of_traffic() {
let urr_id = UrrId::new(5);
let ur_seqn = SequenceNumber::new(46);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.start_of_traffic()
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.usage_report_trigger, UsageReportTrigger::START);
}
#[test]
fn test_usage_report_builder_stop_of_traffic() {
let urr_id = UrrId::new(6);
let ur_seqn = SequenceNumber::new(47);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.stop_of_traffic()
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.usage_report_trigger, UsageReportTrigger::STOPT);
}
#[test]
fn test_usage_report_builder_convenience_methods() {
let quota_report =
UsageReportBuilder::quota_exhausted_report(UrrId::new(1), SequenceNumber::new(42))
.build()
.unwrap();
assert_eq!(
quota_report.usage_report_trigger,
UsageReportTrigger::VOLTH | UsageReportTrigger::TIMTH
);
let periodic_report =
UsageReportBuilder::periodic_usage_report(UrrId::new(2), SequenceNumber::new(43))
.build()
.unwrap();
assert_eq!(
periodic_report.usage_report_trigger,
UsageReportTrigger::PERIO
);
let volume_report =
UsageReportBuilder::volume_threshold_report(UrrId::new(3), SequenceNumber::new(44))
.build()
.unwrap();
assert_eq!(
volume_report.usage_report_trigger,
UsageReportTrigger::VOLTH
);
let time_report =
UsageReportBuilder::time_threshold_report(UrrId::new(4), SequenceNumber::new(45))
.build()
.unwrap();
assert_eq!(time_report.usage_report_trigger, UsageReportTrigger::TIMTH);
let start_report =
UsageReportBuilder::start_of_traffic_report(UrrId::new(5), SequenceNumber::new(46))
.build()
.unwrap();
assert_eq!(start_report.usage_report_trigger, UsageReportTrigger::START);
let stop_report =
UsageReportBuilder::stop_of_traffic_report(UrrId::new(6), SequenceNumber::new(47))
.build()
.unwrap();
assert_eq!(stop_report.usage_report_trigger, UsageReportTrigger::STOPT);
}
#[test]
fn test_usage_report_builder_validation_errors() {
let builder = UsageReportBuilder::default();
let result = builder.build();
assert!(result.is_err());
match result.unwrap_err() {
PfcpError::MissingMandatoryIe { ie_type, .. } => {
assert_eq!(ie_type, IeType::UrrId);
}
_ => panic!("Expected MissingMandatoryIe error"),
}
let urr_id = UrrId::new(1);
let builder = UsageReportBuilder::new(urr_id);
let result = builder.build();
assert!(result.is_err());
match result.unwrap_err() {
PfcpError::MissingMandatoryIe { ie_type, .. } => {
assert_eq!(ie_type, IeType::UrSeqn);
}
_ => panic!("Expected MissingMandatoryIe error"),
}
let urr_id = UrrId::new(1);
let ur_seqn = SequenceNumber::new(42);
let builder = UsageReportBuilder::new(urr_id).sequence_number(ur_seqn);
let result = builder.build();
assert!(result.is_err());
match result.unwrap_err() {
PfcpError::MissingMandatoryIe { ie_type, .. } => {
assert_eq!(ie_type, IeType::UsageReportTrigger);
}
_ => panic!("Expected MissingMandatoryIe error"),
}
}
#[test]
fn test_usage_report_builder_round_trip_marshal() {
let urr_id = UrrId::new(99);
let ur_seqn = SequenceNumber::new(255);
let original = UsageReportBuilder::quota_exhausted_report(urr_id, ur_seqn)
.build()
.unwrap();
let marshaled = original.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(original, unmarshaled);
}
#[test]
fn test_usage_report_builder_comprehensive() {
let triggers = [
UsageReportTrigger::PERIO,
UsageReportTrigger::VOLTH,
UsageReportTrigger::TIMTH,
UsageReportTrigger::QUHTI,
UsageReportTrigger::START,
UsageReportTrigger::STOPT,
UsageReportTrigger::DROTH,
UsageReportTrigger::LIUSA,
UsageReportTrigger::VOLTH | UsageReportTrigger::TIMTH, ];
for (i, trigger) in triggers.iter().enumerate() {
let urr_id = UrrId::new(100 + i as u32);
let ur_seqn = SequenceNumber::new(200 + i as u32);
let usage_report = UsageReportBuilder::new(urr_id)
.sequence_number(ur_seqn)
.trigger(*trigger)
.build()
.unwrap();
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
let ie = usage_report.to_ie();
assert_eq!(ie.ie_type, IeType::UsageReportWithinSessionReportRequest);
}
}
#[test]
fn test_usage_report_with_volume_measurement() {
let urr_id = UrrId::new(1);
let ur_seqn = SequenceNumber::new(42);
let volume_measurement = VolumeMeasurement::new(
0x07, Some(1000000),
Some(600000),
Some(400000),
None,
None,
None,
);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.volume_threshold_triggered()
.volume_measurement(volume_measurement.clone())
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.volume_measurement, Some(volume_measurement));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_duration_measurement() {
let urr_id = UrrId::new(2);
let ur_seqn = SequenceNumber::new(43);
let duration_measurement = DurationMeasurement::new(3600);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.time_threshold_triggered()
.duration_measurement(duration_measurement.clone())
.build()
.unwrap();
assert_eq!(
usage_report.duration_measurement,
Some(duration_measurement)
);
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_time_of_first_packet() {
let urr_id = UrrId::new(3);
let ur_seqn = SequenceNumber::new(44);
let time_of_first_packet = TimeOfFirstPacket::new(0x12345678);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.start_of_traffic()
.time_of_first_packet(time_of_first_packet.clone())
.build()
.unwrap();
assert_eq!(
usage_report.time_of_first_packet,
Some(time_of_first_packet)
);
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_time_of_last_packet() {
let urr_id = UrrId::new(4);
let ur_seqn = SequenceNumber::new(45);
let time_of_last_packet = TimeOfLastPacket::new(0x87654321);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.stop_of_traffic()
.time_of_last_packet(time_of_last_packet.clone())
.build()
.unwrap();
assert_eq!(usage_report.time_of_last_packet, Some(time_of_last_packet));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_usage_information() {
let urr_id = UrrId::new(5);
let ur_seqn = SequenceNumber::new(46);
let usage_information = UsageInformation::new_with_flags(true, false, true, false);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.periodic_report()
.usage_information(usage_information.clone())
.build()
.unwrap();
assert_eq!(usage_report.usage_information, Some(usage_information));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_all_measurements() {
let urr_id = UrrId::new(6);
let ur_seqn = SequenceNumber::new(47);
let volume_measurement = VolumeMeasurement::new(
0x3F, Some(2000000),
Some(1200000),
Some(800000),
Some(2000),
Some(1200),
Some(800),
);
let duration_measurement = DurationMeasurement::new(7200); let time_of_first_packet = TimeOfFirstPacket::new(0x11111111);
let time_of_last_packet = TimeOfLastPacket::new(0x22222222);
let usage_information = UsageInformation::new_with_flags(true, true, false, false);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.quota_exhausted()
.volume_measurement(volume_measurement.clone())
.duration_measurement(duration_measurement.clone())
.time_of_first_packet(time_of_first_packet.clone())
.time_of_last_packet(time_of_last_packet.clone())
.usage_information(usage_information.clone())
.build()
.unwrap();
assert_eq!(usage_report.volume_measurement, Some(volume_measurement));
assert_eq!(
usage_report.duration_measurement,
Some(duration_measurement)
);
assert_eq!(
usage_report.time_of_first_packet,
Some(time_of_first_packet)
);
assert_eq!(usage_report.time_of_last_packet, Some(time_of_last_packet));
assert_eq!(usage_report.usage_information, Some(usage_information));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_builder_convenience_methods_phase1() {
let volume_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(100))
.volume_threshold_triggered()
.with_volume_data(5000000, 3000000, 2000000)
.build()
.unwrap();
let vm = volume_report.volume_measurement.unwrap();
assert!(vm.has_total_volume());
assert!(vm.has_uplink_volume());
assert!(vm.has_downlink_volume());
assert_eq!(vm.total_volume, Some(5000000));
assert_eq!(vm.uplink_volume, Some(3000000));
assert_eq!(vm.downlink_volume, Some(2000000));
let packet_report = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(101))
.volume_threshold_triggered()
.with_packet_data(10000, 6000, 4000)
.build()
.unwrap();
let vm = packet_report.volume_measurement.unwrap();
assert!(vm.has_total_packets());
assert!(vm.has_uplink_packets());
assert!(vm.has_downlink_packets());
assert_eq!(vm.total_packets, Some(10000));
assert_eq!(vm.uplink_packets, Some(6000));
assert_eq!(vm.downlink_packets, Some(4000));
let duration_report = UsageReportBuilder::new(UrrId::new(3))
.sequence_number(SequenceNumber::new(102))
.time_threshold_triggered()
.with_duration(1800)
.build()
.unwrap();
let dm = duration_report.duration_measurement.unwrap();
assert_eq!(dm.duration_seconds, 1800);
let timing_report = UsageReportBuilder::new(UrrId::new(4))
.sequence_number(SequenceNumber::new(103))
.start_of_traffic()
.with_packet_times(0x12345678, 0x87654321)
.build()
.unwrap();
let tofp = timing_report.time_of_first_packet.unwrap();
let tolp = timing_report.time_of_last_packet.unwrap();
assert_eq!(tofp.timestamp, 0x12345678);
assert_eq!(tolp.timestamp, 0x87654321);
let usage_report = UsageReportBuilder::new(UrrId::new(5))
.sequence_number(SequenceNumber::new(104))
.periodic_report()
.with_usage_flags(true, false, true, false)
.build()
.unwrap();
let ui = usage_report.usage_information.unwrap();
assert!(ui.has_bef());
assert!(!ui.has_aft());
assert!(ui.has_uae());
assert!(!ui.has_ube());
}
#[test]
fn test_usage_report_comprehensive_measurement_scenario() {
let usage_report =
UsageReportBuilder::quota_exhausted_report(UrrId::new(99), SequenceNumber::new(255))
.with_volume_data(5000000000, 3000000000, 2000000000) .with_packet_data(5000000, 3000000, 2000000) .with_duration(3600) .with_packet_times(0x60000000, 0x60000E10) .with_usage_flags(false, true, false, true) .build()
.unwrap();
assert_eq!(
usage_report.usage_report_trigger,
UsageReportTrigger::VOLTH | UsageReportTrigger::TIMTH
);
assert!(usage_report.volume_measurement.is_some());
assert!(usage_report.duration_measurement.is_some());
assert!(usage_report.time_of_first_packet.is_some());
assert!(usage_report.time_of_last_packet.is_some());
assert!(usage_report.usage_information.is_some());
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
let ie = usage_report.to_ie();
assert_eq!(ie.ie_type, IeType::UsageReportWithinSessionReportRequest);
}
#[test]
fn test_usage_report_phase1_marshal_unmarshal_edge_cases() {
let zero_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(1))
.periodic_report()
.with_volume_data(0, 0, 0)
.with_duration(0)
.with_packet_times(0, 0)
.with_usage_flags(false, false, false, false)
.build()
.unwrap();
let marshaled = zero_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(zero_report, unmarshaled);
let max_report = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(2))
.periodic_report()
.with_volume_data(u64::MAX, u64::MAX, u64::MAX)
.with_duration(u32::MAX)
.with_packet_times(u32::MAX, u32::MAX)
.with_usage_flags(true, true, true, true)
.build()
.unwrap();
let marshaled = max_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(max_report, unmarshaled);
}
#[test]
fn test_usage_report_with_volume_quota() {
let urr_id = UrrId::new(1);
let ur_seqn = SequenceNumber::new(42);
let volume_quota = VolumeQuota::new(
0x07, Some(5000000000),
Some(3000000000),
Some(2000000000),
);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.quota_exhausted()
.volume_quota(volume_quota.clone())
.build()
.unwrap();
assert_eq!(usage_report.urr_id, urr_id);
assert_eq!(usage_report.ur_seqn, ur_seqn);
assert_eq!(usage_report.volume_quota, Some(volume_quota));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_time_quota() {
let urr_id = UrrId::new(2);
let ur_seqn = SequenceNumber::new(43);
let time_quota = TimeQuota::new(3600);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.time_threshold_triggered()
.time_quota(time_quota.clone())
.build()
.unwrap();
assert_eq!(usage_report.time_quota, Some(time_quota));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_quota_holding_time() {
let urr_id = UrrId::new(3);
let ur_seqn = SequenceNumber::new(44);
let quota_holding_time = QuotaHoldingTime::new(300);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.quota_exhausted()
.quota_holding_time(quota_holding_time.clone())
.build()
.unwrap();
assert_eq!(usage_report.quota_holding_time, Some(quota_holding_time));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_start_time() {
let urr_id = UrrId::new(4);
let ur_seqn = SequenceNumber::new(45);
let start_time = StartTime::new(0x60000000);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.start_of_traffic()
.start_time(start_time.clone())
.build()
.unwrap();
assert_eq!(usage_report.start_time, Some(start_time));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_end_time() {
let urr_id = UrrId::new(5);
let ur_seqn = SequenceNumber::new(46);
let end_time = EndTime::new(0x60000E10);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.stop_of_traffic()
.end_time(end_time.clone())
.build()
.unwrap();
assert_eq!(usage_report.end_time, Some(end_time));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_with_all_phase2_fields() {
let urr_id = UrrId::new(6);
let ur_seqn = SequenceNumber::new(47);
let volume_quota = VolumeQuota::new(
0x07,
Some(10000000000), Some(6000000000), Some(4000000000), );
let time_quota = TimeQuota::new(7200); let quota_holding_time = QuotaHoldingTime::new(600); let start_time = StartTime::new(0x60000000);
let end_time = EndTime::new(0x60001C20);
let usage_report = UsageReportBuilder::new(urr_id.clone())
.sequence_number(ur_seqn)
.quota_exhausted()
.volume_quota(volume_quota.clone())
.time_quota(time_quota.clone())
.quota_holding_time(quota_holding_time.clone())
.start_time(start_time.clone())
.end_time(end_time.clone())
.build()
.unwrap();
assert_eq!(usage_report.volume_quota, Some(volume_quota));
assert_eq!(usage_report.time_quota, Some(time_quota));
assert_eq!(usage_report.quota_holding_time, Some(quota_holding_time));
assert_eq!(usage_report.start_time, Some(start_time));
assert_eq!(usage_report.end_time, Some(end_time));
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
}
#[test]
fn test_usage_report_builder_convenience_methods_phase2() {
let volume_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(100))
.quota_exhausted()
.with_volume_quota(5000000000, 3000000000, 2000000000)
.build()
.unwrap();
let vq = volume_report.volume_quota.unwrap();
assert!(vq.has_total_volume());
assert!(vq.has_uplink_volume());
assert!(vq.has_downlink_volume());
assert_eq!(vq.total_volume, Some(5000000000));
assert_eq!(vq.uplink_volume, Some(3000000000));
assert_eq!(vq.downlink_volume, Some(2000000000));
let time_report = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(101))
.time_threshold_triggered()
.with_time_quota(3600)
.build()
.unwrap();
let tq = time_report.time_quota.unwrap();
assert_eq!(tq.quota_seconds, 3600);
let holding_report = UsageReportBuilder::new(UrrId::new(3))
.sequence_number(SequenceNumber::new(102))
.quota_exhausted()
.with_quota_holding_time(300)
.build()
.unwrap();
let qht = holding_report.quota_holding_time.unwrap();
assert_eq!(qht.holding_time_seconds, 300);
let window_report = UsageReportBuilder::new(UrrId::new(4))
.sequence_number(SequenceNumber::new(103))
.periodic_report()
.with_monitoring_window(0x60000000, 0x60000E10)
.build()
.unwrap();
let st = window_report.start_time.unwrap();
let et = window_report.end_time.unwrap();
assert_eq!(st.timestamp, 0x60000000);
assert_eq!(et.timestamp, 0x60000E10);
}
#[test]
fn test_usage_report_comprehensive_phase1_and_phase2_scenario() {
let usage_report =
UsageReportBuilder::quota_exhausted_report(UrrId::new(99), SequenceNumber::new(255))
.with_volume_data(5000000000, 3000000000, 2000000000) .with_duration(3600) .with_packet_times(0x60000000, 0x60000E10) .with_usage_flags(false, true, false, true) .with_volume_quota(5000000000, 3000000000, 2000000000) .with_time_quota(3600) .with_quota_holding_time(300) .with_monitoring_window(0x60000000, 0x60000E10) .build()
.unwrap();
assert!(usage_report.volume_measurement.is_some());
assert!(usage_report.duration_measurement.is_some());
assert!(usage_report.time_of_first_packet.is_some());
assert!(usage_report.time_of_last_packet.is_some());
assert!(usage_report.usage_information.is_some());
assert!(usage_report.volume_quota.is_some());
assert!(usage_report.time_quota.is_some());
assert!(usage_report.quota_holding_time.is_some());
assert!(usage_report.start_time.is_some());
assert!(usage_report.end_time.is_some());
let vm = usage_report.volume_measurement.as_ref().unwrap();
let vq = usage_report.volume_quota.as_ref().unwrap();
assert_eq!(vm.total_volume, vq.total_volume);
let dm = usage_report.duration_measurement.as_ref().unwrap();
let tq = usage_report.time_quota.as_ref().unwrap();
assert_eq!(dm.duration_seconds, tq.quota_seconds);
let marshaled = usage_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(usage_report, unmarshaled);
let ie = usage_report.to_ie();
assert_eq!(ie.ie_type, IeType::UsageReportWithinSessionReportRequest);
}
#[test]
fn test_usage_report_phase2_marshal_unmarshal_edge_cases() {
let zero_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(1))
.periodic_report()
.with_volume_quota(0, 0, 0)
.with_time_quota(0)
.with_quota_holding_time(0)
.with_monitoring_window(0, 0)
.build()
.unwrap();
let marshaled = zero_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(zero_report, unmarshaled);
let max_report = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(2))
.periodic_report()
.with_volume_quota(u64::MAX, u64::MAX, u64::MAX)
.with_time_quota(u32::MAX)
.with_quota_holding_time(u32::MAX)
.with_monitoring_window(u32::MAX, u32::MAX)
.build()
.unwrap();
let marshaled = max_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(max_report, unmarshaled);
}
#[test]
fn test_usage_report_phase2_individual_ies() {
let vq_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(1))
.quota_exhausted()
.volume_quota(VolumeQuota::new(0x01, Some(1000000), None, None))
.build()
.unwrap();
let marshaled = vq_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(vq_report, unmarshaled);
let tq_report = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(2))
.time_threshold_triggered()
.time_quota(TimeQuota::new(1800))
.build()
.unwrap();
let marshaled = tq_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(tq_report, unmarshaled);
let qht_report = UsageReportBuilder::new(UrrId::new(3))
.sequence_number(SequenceNumber::new(3))
.quota_exhausted()
.quota_holding_time(QuotaHoldingTime::new(600))
.build()
.unwrap();
let marshaled = qht_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(qht_report, unmarshaled);
let st_report = UsageReportBuilder::new(UrrId::new(4))
.sequence_number(SequenceNumber::new(4))
.start_of_traffic()
.start_time(StartTime::new(0x60000000))
.build()
.unwrap();
let marshaled = st_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(st_report, unmarshaled);
let et_report = UsageReportBuilder::new(UrrId::new(5))
.sequence_number(SequenceNumber::new(5))
.stop_of_traffic()
.end_time(EndTime::new(0x60000E10))
.build()
.unwrap();
let marshaled = et_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(et_report, unmarshaled);
}
#[test]
fn test_usage_report_phase3_individual_ies() {
use std::net::Ipv4Addr;
let qur_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(1))
.periodic_report()
.query_urr_reference(QueryURRReference::new(0x12345678))
.build()
.unwrap();
let marshaled = qur_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(qur_report, unmarshaled);
let adi_report = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(2))
.stop_of_traffic()
.application_detection_information(ApplicationDetectionInformation::simple_app(
"YouTube",
))
.build()
.unwrap();
let marshaled = adi_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(adi_report, unmarshaled);
let ueip_report = UsageReportBuilder::new(UrrId::new(3))
.sequence_number(SequenceNumber::new(3))
.quota_exhausted()
.ue_ip_address_usage_information(UEIPAddressUsageInformation::with_ipv4(
Ipv4Addr::new(192, 168, 1, 100),
5,
))
.build()
.unwrap();
let marshaled = ueip_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(ueip_report, unmarshaled);
let auri_report = UsageReportBuilder::new(UrrId::new(4))
.sequence_number(SequenceNumber::new(4))
.time_threshold_triggered()
.additional_usage_reports_information(AdditionalUsageReportsInformation::with_auri())
.build()
.unwrap();
let marshaled = auri_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(auri_report, unmarshaled);
}
#[test]
fn test_usage_report_with_all_phase3_fields() {
use std::net::Ipv4Addr;
let comprehensive_report = UsageReportBuilder::new(UrrId::new(1))
.sequence_number(SequenceNumber::new(100))
.periodic_report()
.query_urr_reference(QueryURRReference::new(0xABCDEF01))
.application_detection_information(ApplicationDetectionInformation::full_app_detection(
"Netflix",
"video_session_123",
"tcp:443,udp:443",
))
.ue_ip_address_usage_information(UEIPAddressUsageInformation::with_ipv4(
Ipv4Addr::new(10, 0, 0, 1),
10,
))
.additional_usage_reports_information(
AdditionalUsageReportsInformation::with_both_flags(),
)
.build()
.unwrap();
assert!(comprehensive_report.query_urr_reference.is_some());
assert!(comprehensive_report
.application_detection_information
.is_some());
assert!(comprehensive_report
.ue_ip_address_usage_information
.is_some());
assert!(comprehensive_report
.additional_usage_reports_information
.is_some());
let marshaled = comprehensive_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(comprehensive_report, unmarshaled);
let qur = comprehensive_report.query_urr_reference.unwrap();
assert_eq!(qur.reference, 0xABCDEF01);
let adi = comprehensive_report
.application_detection_information
.unwrap();
assert_eq!(adi.application_id, "Netflix");
assert_eq!(
adi.application_instance_id,
Some("video_session_123".to_string())
);
assert_eq!(adi.flow_information, Some("tcp:443,udp:443".to_string()));
let ueip = comprehensive_report
.ue_ip_address_usage_information
.unwrap();
assert_eq!(ueip.ipv4_address, Some(Ipv4Addr::new(10, 0, 0, 1)));
assert_eq!(ueip.number_of_ue_ip_addresses, Some(10));
let auri = comprehensive_report
.additional_usage_reports_information
.unwrap();
assert!(auri.has_auri());
assert!(auri.has_nouri());
}
#[test]
fn test_usage_report_phase3_convenience_methods() {
use std::net::Ipv4Addr;
let convenience_report = UsageReportBuilder::new(UrrId::new(42))
.sequence_number(SequenceNumber::new(123))
.quota_exhausted()
.with_query_reference(0x87654321)
.with_detected_application("WhatsApp")
.with_application_instance("Instagram", "mobile_session_456")
.with_ue_ipv4_usage(Ipv4Addr::new(203, 0, 113, 1), 3)
.with_additional_flags(true, false) .build()
.unwrap();
let marshaled = convenience_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(convenience_report, unmarshaled);
let qur = convenience_report.query_urr_reference.unwrap();
assert_eq!(qur.reference, 0x87654321);
let adi = convenience_report
.application_detection_information
.unwrap();
assert_eq!(adi.application_id, "Instagram");
assert_eq!(
adi.application_instance_id,
Some("mobile_session_456".to_string())
);
let ueip = convenience_report.ue_ip_address_usage_information.unwrap();
assert_eq!(ueip.ipv4_address, Some(Ipv4Addr::new(203, 0, 113, 1)));
assert_eq!(ueip.number_of_ue_ip_addresses, Some(3));
let auri = convenience_report
.additional_usage_reports_information
.unwrap();
assert!(auri.has_auri());
assert!(!auri.has_nouri());
}
#[test]
fn test_usage_report_comprehensive_phase1_phase2_phase3_scenario() {
use std::net::Ipv4Addr;
let complete_report = UsageReportBuilder::new(UrrId::new(999))
.sequence_number(SequenceNumber::new(555))
.quota_exhausted()
.with_volume_data(5000000, 3000000, 2000000)
.with_packet_data(10000, 6000, 4000)
.with_duration(3600)
.with_packet_times(0x60000000, 0x60000E10)
.with_usage_flags(true, true, false, false)
.with_volume_quota(5000000, 3000000, 2000000)
.with_time_quota(3600)
.with_quota_holding_time(300)
.with_monitoring_window(0x60000000, 0x60000E10)
.with_query_reference(0xCAFEBABE)
.with_detected_application("TikTok")
.with_ue_ipv4_usage(Ipv4Addr::new(172, 16, 0, 1), 1)
.with_additional_flags(true, true)
.build()
.unwrap();
assert!(complete_report.volume_measurement.is_some());
assert!(complete_report.duration_measurement.is_some());
assert!(complete_report.time_of_first_packet.is_some());
assert!(complete_report.time_of_last_packet.is_some());
assert!(complete_report.usage_information.is_some());
assert!(complete_report.volume_quota.is_some());
assert!(complete_report.time_quota.is_some());
assert!(complete_report.quota_holding_time.is_some());
assert!(complete_report.start_time.is_some());
assert!(complete_report.end_time.is_some());
assert!(complete_report.query_urr_reference.is_some());
assert!(complete_report.application_detection_information.is_some());
assert!(complete_report.ue_ip_address_usage_information.is_some());
assert!(complete_report
.additional_usage_reports_information
.is_some());
let marshaled = complete_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(complete_report, unmarshaled);
assert_eq!(
complete_report.query_urr_reference.unwrap().reference,
0xCAFEBABE
);
assert_eq!(
complete_report
.application_detection_information
.unwrap()
.application_id,
"TikTok"
);
assert_eq!(
complete_report
.ue_ip_address_usage_information
.unwrap()
.ipv4_address,
Some(Ipv4Addr::new(172, 16, 0, 1))
);
}
#[test]
fn test_usage_report_phase3_marshal_unmarshal_edge_cases() {
use std::net::Ipv4Addr;
let edge_case_report = UsageReportBuilder::new(UrrId::new(777))
.sequence_number(SequenceNumber::new(888))
.volume_threshold_triggered()
.query_urr_reference(QueryURRReference::new(u32::MAX))
.application_detection_information(ApplicationDetectionInformation::full_app_detection(
"🎵Music", "session_with_unicode_🎧",
"complex:flow:info:with:colons",
))
.ue_ip_address_usage_information(UEIPAddressUsageInformation::with_ipv4(
Ipv4Addr::new(0, 0, 0, 0), u32::MAX, ))
.additional_usage_reports_information(AdditionalUsageReportsInformation::new(0xFF)) .build()
.unwrap();
let marshaled = edge_case_report.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(edge_case_report, unmarshaled);
assert_eq!(
edge_case_report.query_urr_reference.unwrap().reference,
u32::MAX
);
let adi = edge_case_report.application_detection_information.unwrap();
assert_eq!(adi.application_id, "🎵Music");
assert!(adi.application_instance_id.unwrap().contains("unicode_🎧"));
let ueip = edge_case_report.ue_ip_address_usage_information.unwrap();
assert_eq!(ueip.ipv4_address, Some(Ipv4Addr::new(0, 0, 0, 0)));
assert_eq!(ueip.number_of_ue_ip_addresses, Some(u32::MAX));
let auri = edge_case_report
.additional_usage_reports_information
.unwrap();
assert_eq!(auri.flags, 0xFF);
}
#[test]
fn test_usage_report_phase3_real_world_scenarios() {
use std::net::Ipv4Addr;
let video_streaming =
UsageReportBuilder::quota_exhausted_report(UrrId::new(1), SequenceNumber::new(1))
.with_volume_data(500000000, 50000000, 450000000) .with_detected_application("Netflix")
.with_ue_ipv4_usage(Ipv4Addr::new(192, 168, 1, 100), 1)
.with_query_reference(0x10000001)
.build()
.unwrap();
let social_media = UsageReportBuilder::new(UrrId::new(2))
.sequence_number(SequenceNumber::new(2))
.stop_of_traffic()
.with_application_instance("Instagram", "stories_session")
.with_ue_ipv4_usage(Ipv4Addr::new(10, 0, 0, 50), 1)
.with_additional_flags(false, true) .build()
.unwrap();
let enterprise_app =
UsageReportBuilder::time_threshold_report(UrrId::new(3), SequenceNumber::new(3))
.application_detection_information(
ApplicationDetectionInformation::full_app_detection(
"Slack",
"enterprise_workspace_ABC123",
"tcp:443,websocket:443",
),
)
.ue_ip_address_usage_information(UEIPAddressUsageInformation::with_ipv4(
Ipv4Addr::new(172, 16, 100, 200),
25, ))
.with_query_reference(0x30000003)
.with_additional_flags(true, false) .build()
.unwrap();
for scenario in [video_streaming, social_media, enterprise_app] {
let marshaled = scenario.marshal();
let unmarshaled = UsageReport::unmarshal(&marshaled).unwrap();
assert_eq!(scenario, unmarshaled);
}
}
}