1use alloc::collections::BTreeMap;
10use alloc::vec::Vec;
11
12use super::types::{AirtimeProfile, InterfaceId, PacketBytes, TransportAction};
13use crate::constants;
14
15#[derive(Debug, Clone)]
17pub struct AnnounceQueueEntry {
18 pub destination_hash: [u8; 16],
20 pub time: f64,
22 pub hops: u8,
24 pub emitted: f64,
26 pub raw: PacketBytes,
28}
29
30#[derive(Debug, Clone)]
32pub struct InterfaceAnnounceQueue {
33 pub entries: Vec<AnnounceQueueEntry>,
35 pub announce_allowed_at: f64,
37}
38
39impl InterfaceAnnounceQueue {
40 pub fn new() -> Self {
41 InterfaceAnnounceQueue {
42 entries: Vec::new(),
43 announce_allowed_at: 0.0,
44 }
45 }
46
47 pub fn insert(&mut self, entry: AnnounceQueueEntry) {
51 if let Some(pos) = self
53 .entries
54 .iter()
55 .position(|e| e.destination_hash == entry.destination_hash)
56 {
57 let existing = &self.entries[pos];
58 if entry.hops < existing.hops
60 || (entry.hops == existing.hops && entry.emitted > existing.emitted)
61 {
62 self.entries[pos] = entry;
63 }
64 } else {
66 if self.entries.len() >= constants::MAX_QUEUED_ANNOUNCES {
68 self.entries.remove(0);
70 }
71 self.entries.push(entry);
72 }
73 }
74
75 pub fn remove_stale(&mut self, now: f64) {
77 self.entries
78 .retain(|e| now - e.time < constants::QUEUED_ANNOUNCE_LIFE);
79 }
80
81 pub fn select_next(&self) -> Option<usize> {
84 if self.entries.is_empty() {
85 return None;
86 }
87 let mut best_idx = 0;
88 let mut best_hops = self.entries[0].hops;
89 let mut best_time = self.entries[0].time;
90
91 for (i, entry) in self.entries.iter().enumerate().skip(1) {
92 if entry.hops < best_hops || (entry.hops == best_hops && entry.time < best_time) {
93 best_idx = i;
94 best_hops = entry.hops;
95 best_time = entry.time;
96 }
97 }
98 Some(best_idx)
99 }
100
101 pub fn is_allowed(&self, now: f64) -> bool {
103 now >= self.announce_allowed_at
104 }
105
106 pub fn calculate_next_allowed(
111 now: f64,
112 raw_len: usize,
113 bitrate: u64,
114 airtime_profile: Option<AirtimeProfile>,
115 announce_cap: f64,
116 ) -> f64 {
117 if announce_cap <= 0.0 {
118 return now; }
120
121 let time_to_send = airtime_profile
122 .map(|profile| profile.transmit_time_secs(raw_len))
123 .unwrap_or_else(|| {
124 if bitrate == 0 {
125 0.0
126 } else {
127 let bits = (raw_len * 8) as f64;
128 bits / (bitrate as f64)
129 }
130 });
131 if time_to_send <= 0.0 {
132 return now;
133 }
134 let delay = time_to_send / announce_cap;
135 now + delay
136 }
137}
138
139impl Default for InterfaceAnnounceQueue {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145#[derive(Debug, Clone)]
147pub struct AnnounceQueues {
148 queues: BTreeMap<InterfaceId, InterfaceAnnounceQueue>,
149 max_interfaces: usize,
150 interface_cap_drops: u64,
151}
152
153impl AnnounceQueues {
154 pub fn new(max_interfaces: usize) -> Self {
155 AnnounceQueues {
156 queues: BTreeMap::new(),
157 max_interfaces,
158 interface_cap_drops: 0,
159 }
160 }
161
162 #[allow(clippy::too_many_arguments)]
167 pub fn gate_announce(
168 &mut self,
169 interface: InterfaceId,
170 raw: PacketBytes,
171 dest_hash: [u8; 16],
172 hops: u8,
173 emitted: f64,
174 now: f64,
175 bitrate: Option<u64>,
176 airtime_profile: Option<AirtimeProfile>,
177 announce_cap: f64,
178 ) -> Option<TransportAction> {
179 let bitrate = match bitrate {
181 Some(br) if br > 0 => br,
182 _ if airtime_profile.is_none() => {
183 return Some(TransportAction::SendOnInterface { interface, raw });
184 }
185 _ => 0,
186 };
187
188 if !self.queues.contains_key(&interface) && self.queues.len() >= self.max_interfaces {
189 self.interface_cap_drops = self.interface_cap_drops.saturating_add(1);
190 return None;
191 }
192
193 let queue = self.queues.entry(interface).or_default();
194
195 if queue.is_allowed(now) {
196 queue.announce_allowed_at = InterfaceAnnounceQueue::calculate_next_allowed(
198 now,
199 raw.len(),
200 bitrate,
201 airtime_profile,
202 announce_cap,
203 );
204 Some(TransportAction::SendOnInterface { interface, raw })
205 } else {
206 queue.insert(AnnounceQueueEntry {
208 destination_hash: dest_hash,
209 time: now,
210 hops,
211 emitted,
212 raw,
213 });
214 None
215 }
216 }
217
218 pub fn process_queues(
221 &mut self,
222 now: f64,
223 interfaces: &BTreeMap<InterfaceId, super::types::InterfaceInfo>,
224 ) -> Vec<TransportAction> {
225 let mut actions = Vec::new();
226 let mut empty_queues = Vec::new();
227
228 for (iface_id, queue) in self.queues.iter_mut() {
229 queue.remove_stale(now);
231
232 while queue.is_allowed(now) {
234 if let Some(idx) = queue.select_next() {
235 let entry = queue.entries.remove(idx);
236
237 let (bitrate, airtime_profile, announce_cap) =
239 if let Some(info) = interfaces.get(iface_id) {
240 (
241 info.bitrate.unwrap_or(0),
242 info.airtime_profile,
243 info.announce_cap,
244 )
245 } else {
246 (0, None, constants::ANNOUNCE_CAP)
247 };
248
249 if bitrate > 0 || airtime_profile.is_some() {
250 queue.announce_allowed_at = InterfaceAnnounceQueue::calculate_next_allowed(
251 now,
252 entry.raw.len(),
253 bitrate,
254 airtime_profile,
255 announce_cap,
256 );
257 } else {
258 queue.announce_allowed_at = now;
259 }
260
261 actions.push(TransportAction::SendOnInterface {
262 interface: *iface_id,
263 raw: entry.raw,
264 });
265 } else {
266 break;
267 }
268 }
269
270 if queue.entries.is_empty() {
271 empty_queues.push(*iface_id);
272 }
273 }
274
275 for iface_id in empty_queues {
276 self.queues.remove(&iface_id);
277 }
278
279 actions
280 }
281
282 pub fn blocks_recursive_path_request(&self, interface: InterfaceId, now: f64) -> bool {
284 self.queues
285 .get(&interface)
286 .is_some_and(|queue| !queue.entries.is_empty() || !queue.is_allowed(now))
287 }
288
289 pub fn reserve_recursive_path_request(
291 &mut self,
292 interface: InterfaceId,
293 raw_len: usize,
294 now: f64,
295 bitrate: Option<u64>,
296 airtime_profile: Option<AirtimeProfile>,
297 announce_cap: f64,
298 ) {
299 let bitrate = match bitrate {
300 Some(br) if br > 0 => br,
301 _ if airtime_profile.is_none() => return,
302 _ => 0,
303 };
304
305 if !self.queues.contains_key(&interface) && self.queues.len() >= self.max_interfaces {
306 self.interface_cap_drops = self.interface_cap_drops.saturating_add(1);
307 return;
308 }
309
310 let queue = self.queues.entry(interface).or_default();
311 queue.announce_allowed_at = InterfaceAnnounceQueue::calculate_next_allowed(
312 now,
313 raw_len,
314 bitrate,
315 airtime_profile,
316 announce_cap,
317 );
318 }
319
320 pub fn remove_interface(&mut self, interface: InterfaceId) -> bool {
322 self.queues.remove(&interface).is_some()
323 }
324
325 pub fn queue_count(&self) -> usize {
327 self.queues.len()
328 }
329
330 pub fn nonempty_queue_count(&self) -> usize {
332 self.queues
333 .values()
334 .filter(|queue| !queue.entries.is_empty())
335 .count()
336 }
337
338 pub fn total_queued_announces(&self) -> usize {
340 self.queues.values().map(|queue| queue.entries.len()).sum()
341 }
342
343 pub fn total_queued_bytes(&self) -> usize {
345 self.queues
346 .values()
347 .flat_map(|queue| queue.entries.iter())
348 .map(|entry| entry.raw.len())
349 .sum()
350 }
351
352 pub fn interface_cap_drop_count(&self) -> u64 {
354 self.interface_cap_drops
355 }
356
357 #[cfg(test)]
359 pub fn queue_for(&self, id: &InterfaceId) -> Option<&InterfaceAnnounceQueue> {
360 self.queues.get(id)
361 }
362}
363
364impl Default for AnnounceQueues {
365 fn default() -> Self {
366 Self::new(1024)
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373 use alloc::string::String;
374
375 fn make_entry(dest: u8, hops: u8, time: f64) -> AnnounceQueueEntry {
376 AnnounceQueueEntry {
377 destination_hash: [dest; 16],
378 time,
379 hops,
380 emitted: time,
381 raw: vec![0x01, 0x02, 0x03].into(),
382 }
383 }
384
385 fn make_interface_info(id: u64, bitrate: Option<u64>) -> super::super::types::InterfaceInfo {
386 super::super::types::InterfaceInfo {
387 id: InterfaceId(id),
388 name: String::from("test"),
389 mode: crate::constants::MODE_FULL,
390 gravity: 0,
391 recursive_prs: false,
392 announces_from_internal: true,
393 announces_to_internal: None,
394 out_capable: true,
395 in_capable: true,
396 bitrate,
397 airtime_profile: None,
398 announce_rate_target: None,
399 announce_rate_grace: 0,
400 announce_rate_penalty: 0.0,
401 announce_cap: constants::ANNOUNCE_CAP,
402 is_local_client: false,
403 wants_tunnel: false,
404 tunnel_id: None,
405 mtu: constants::MTU as u32,
406 ingress_control: crate::transport::types::IngressControlConfig::disabled(),
407 ia_freq: 0.0,
408 ip_freq: 0.0,
409 op_freq: 0.0,
410 op_samples: 0,
411 started: 0.0,
412 }
413 }
414
415 #[test]
418 fn test_queue_entry_creation() {
419 let entry = make_entry(0xAA, 3, 1000.0);
420 assert_eq!(entry.hops, 3);
421 assert_eq!(entry.destination_hash, [0xAA; 16]);
422 }
423
424 #[test]
425 fn test_queue_insert_and_select() {
426 let mut queue = InterfaceAnnounceQueue::new();
427 queue.insert(make_entry(0x01, 3, 100.0));
428 queue.insert(make_entry(0x02, 1, 200.0));
429 queue.insert(make_entry(0x03, 2, 150.0));
430
431 let idx = queue.select_next().unwrap();
433 assert_eq!(queue.entries[idx].destination_hash, [0x02; 16]);
434 }
435
436 #[test]
437 fn test_queue_select_fifo_on_same_hops() {
438 let mut queue = InterfaceAnnounceQueue::new();
439 queue.insert(make_entry(0x01, 2, 200.0)); queue.insert(make_entry(0x02, 2, 100.0)); let idx = queue.select_next().unwrap();
444 assert_eq!(queue.entries[idx].destination_hash, [0x02; 16]);
445 }
446
447 #[test]
448 fn test_queue_dedup_update() {
449 let mut queue = InterfaceAnnounceQueue::new();
450 queue.insert(make_entry(0x01, 3, 100.0));
451 assert_eq!(queue.entries.len(), 1);
452
453 queue.insert(make_entry(0x01, 1, 200.0));
455 assert_eq!(queue.entries.len(), 1);
456 assert_eq!(queue.entries[0].hops, 1);
457
458 queue.insert(make_entry(0x01, 5, 300.0));
460 assert_eq!(queue.entries.len(), 1);
461 assert_eq!(queue.entries[0].hops, 1);
462 }
463
464 #[test]
465 fn newer_duplicate_updates_matched_entry_not_trailing_entry() {
466 let mut queue = InterfaceAnnounceQueue::new();
467 queue.insert(make_entry(0x01, 3, 100.0));
468 queue.insert(make_entry(0x02, 4, 150.0));
469
470 let mut replacement = make_entry(0x01, 3, 200.0);
471 replacement.raw = vec![0x09, 0x08].into();
472 queue.insert(replacement);
473
474 assert_eq!(queue.entries.len(), 2);
475 assert_eq!(queue.entries[0].destination_hash, [0x01; 16]);
476 assert_eq!(queue.entries[0].time, 200.0);
477 assert_eq!(queue.entries[0].hops, 3);
478 assert_eq!(queue.entries[0].emitted, 200.0);
479 assert_eq!(&*queue.entries[0].raw, &[0x09, 0x08]);
480
481 assert_eq!(queue.entries[1].destination_hash, [0x02; 16]);
482 assert_eq!(queue.entries[1].time, 150.0);
483 assert_eq!(queue.entries[1].hops, 4);
484 assert_eq!(queue.entries[1].emitted, 150.0);
485 assert_eq!(&*queue.entries[1].raw, &[0x01, 0x02, 0x03]);
486 }
487
488 #[test]
489 fn duplicate_lookup_updates_only_first_preexisting_match() {
490 let mut queue = InterfaceAnnounceQueue::new();
491 queue.entries.push(make_entry(0x01, 3, 100.0));
492 queue.entries.push(make_entry(0x01, 3, 150.0));
493
494 let mut replacement = make_entry(0x01, 3, 200.0);
495 replacement.raw = vec![0x09].into();
496 queue.insert(replacement);
497
498 assert_eq!(queue.entries.len(), 2);
499 assert_eq!(queue.entries[0].time, 200.0);
500 assert_eq!(queue.entries[0].emitted, 200.0);
501 assert_eq!(&*queue.entries[0].raw, &[0x09]);
502 assert_eq!(queue.entries[1].time, 150.0);
503 assert_eq!(queue.entries[1].emitted, 150.0);
504 assert_eq!(&*queue.entries[1].raw, &[0x01, 0x02, 0x03]);
505 }
506
507 #[test]
508 fn test_queue_stale_removal() {
509 let mut queue = InterfaceAnnounceQueue::new();
510 queue.insert(make_entry(0x01, 1, 100.0));
511 queue.insert(make_entry(0x02, 2, 200.0));
512
513 queue.remove_stale(100.0 + constants::QUEUED_ANNOUNCE_LIFE + 1.0);
515 assert_eq!(queue.entries.len(), 1);
516 assert_eq!(queue.entries[0].destination_hash, [0x02; 16]);
517 }
518
519 #[test]
520 fn test_queue_max_size() {
521 let mut queue = InterfaceAnnounceQueue::new();
522 for i in 0..constants::MAX_QUEUED_ANNOUNCES {
523 queue.insert(AnnounceQueueEntry {
524 destination_hash: {
525 let mut d = [0u8; 16];
526 d[0] = (i >> 8) as u8;
527 d[1] = i as u8;
528 d
529 },
530 time: i as f64,
531 hops: 1,
532 emitted: i as f64,
533 raw: vec![0x01].into(),
534 });
535 }
536 assert_eq!(queue.entries.len(), constants::MAX_QUEUED_ANNOUNCES);
537
538 queue.insert(make_entry(0xFF, 1, 99999.0));
540 assert_eq!(queue.entries.len(), constants::MAX_QUEUED_ANNOUNCES);
541 }
542
543 #[test]
544 fn test_queue_empty_select() {
545 let queue = InterfaceAnnounceQueue::new();
546 assert!(queue.select_next().is_none());
547 }
548
549 #[test]
550 fn test_bandwidth_allowed() {
551 let mut queue = InterfaceAnnounceQueue::new();
552 assert!(queue.is_allowed(0.0));
553 assert!(queue.is_allowed(100.0));
554
555 queue.announce_allowed_at = 200.0;
556 assert!(!queue.is_allowed(100.0));
557 assert!(!queue.is_allowed(199.9));
558 assert!(queue.is_allowed(200.0));
559 assert!(queue.is_allowed(300.0));
560 }
561
562 #[test]
563 fn test_calculate_next_allowed() {
564 let next = InterfaceAnnounceQueue::calculate_next_allowed(1000.0, 100, 1000, None, 0.02);
568 assert!((next - 1040.0).abs() < 0.001);
569 }
570
571 #[test]
572 fn test_calculate_next_allowed_zero_bitrate() {
573 let next = InterfaceAnnounceQueue::calculate_next_allowed(1000.0, 100, 0, None, 0.02);
574 assert_eq!(next, 1000.0); }
576
577 #[test]
578 fn test_calculate_next_allowed_uses_lora_airtime() {
579 let profile = AirtimeProfile::Lora {
580 bandwidth: 125_000,
581 spreading_factor: 8,
582 coding_rate: 5,
583 preamble_symbols: 8,
584 explicit_header: true,
585 crc: true,
586 };
587
588 let next =
589 InterfaceAnnounceQueue::calculate_next_allowed(1000.0, 100, 0, Some(profile), 0.02);
590
591 assert!((next - 1015.3856).abs() < 0.0001);
595 }
596
597 #[test]
600 fn test_gate_announce_no_bitrate_immediate() {
601 let mut queues = AnnounceQueues::new(1024);
602 let result = queues.gate_announce(
603 InterfaceId(1),
604 vec![0x01, 0x02, 0x03].into(),
605 [0xAA; 16],
606 2,
607 1000.0,
608 1000.0,
609 None, None,
611 0.02,
612 );
613 assert!(result.is_some());
614 assert!(matches!(
615 result.unwrap(),
616 TransportAction::SendOnInterface { .. }
617 ));
618 }
619
620 #[test]
621 fn test_gate_announce_uses_airtime_profile_without_bitrate() {
622 let mut queues = AnnounceQueues::new(1024);
623 let profile = AirtimeProfile::Lora {
624 bandwidth: 125_000,
625 spreading_factor: 8,
626 coding_rate: 5,
627 preamble_symbols: 8,
628 explicit_header: true,
629 crc: true,
630 };
631
632 let first = queues.gate_announce(
633 InterfaceId(1),
634 vec![0x01; 100].into(),
635 [0xAA; 16],
636 2,
637 1000.0,
638 1000.0,
639 None,
640 Some(profile),
641 0.02,
642 );
643 assert!(first.is_some());
644
645 let queue = queues.queue_for(&InterfaceId(1)).unwrap();
646 assert!((queue.announce_allowed_at - 1015.3856).abs() < 0.0001);
647
648 let second = queues.gate_announce(
649 InterfaceId(1),
650 vec![0x02; 100].into(),
651 [0xBB; 16],
652 2,
653 1000.0,
654 1000.0,
655 None,
656 Some(profile),
657 0.02,
658 );
659 assert!(second.is_none());
660 assert_eq!(queues.queue_for(&InterfaceId(1)).unwrap().entries.len(), 1);
661 }
662
663 #[test]
664 fn test_gate_announce_bandwidth_available() {
665 let mut queues = AnnounceQueues::new(1024);
666 let result = queues.gate_announce(
667 InterfaceId(1),
668 vec![0x01; 100].into(),
669 [0xBB; 16],
670 2,
671 1000.0,
672 1000.0,
673 Some(10000), None,
675 0.02,
676 );
677 assert!(result.is_some());
679
680 let queue = queues.queue_for(&InterfaceId(1)).unwrap();
682 assert!(queue.announce_allowed_at > 1000.0);
683 }
684
685 #[test]
686 fn test_gate_announce_bandwidth_exhausted_queues() {
687 let mut queues = AnnounceQueues::new(1024);
688
689 let r1 = queues.gate_announce(
691 InterfaceId(1),
692 vec![0x01; 100].into(),
693 [0xAA; 16],
694 2,
695 1000.0,
696 1000.0,
697 Some(1000), None,
699 0.02,
700 );
701 assert!(r1.is_some());
702
703 let r2 = queues.gate_announce(
705 InterfaceId(1),
706 vec![0x02; 100].into(),
707 [0xBB; 16],
708 3,
709 1000.0,
710 1000.0,
711 Some(1000),
712 None,
713 0.02,
714 );
715 assert!(r2.is_none()); let queue = queues.queue_for(&InterfaceId(1)).unwrap();
718 assert_eq!(queue.entries.len(), 1);
719 }
720
721 #[test]
722 fn test_process_queues_dequeues_when_allowed() {
723 let mut queues = AnnounceQueues::new(1024);
724
725 let _ = queues.gate_announce(
727 InterfaceId(1),
728 vec![0x01; 10].into(),
729 [0xAA; 16],
730 2,
731 0.0,
732 0.0,
733 Some(1000),
734 None,
735 0.02,
736 );
737 let _ = queues.gate_announce(
738 InterfaceId(1),
739 vec![0x02; 10].into(),
740 [0xBB; 16],
741 3,
742 0.0,
743 0.0,
744 Some(1000),
745 None,
746 0.02,
747 );
748
749 assert_eq!(queues.queue_for(&InterfaceId(1)).unwrap().entries.len(), 1);
751
752 let mut interfaces = BTreeMap::new();
753 interfaces.insert(InterfaceId(1), make_interface_info(1, Some(1000)));
754
755 let allowed_at = queues
757 .queue_for(&InterfaceId(1))
758 .unwrap()
759 .announce_allowed_at;
760 let actions = queues.process_queues(allowed_at + 1.0, &interfaces);
761
762 assert_eq!(actions.len(), 1);
763 assert!(matches!(
764 &actions[0],
765 TransportAction::SendOnInterface { interface, .. } if *interface == InterfaceId(1)
766 ));
767
768 assert!(queues.queue_for(&InterfaceId(1)).is_none());
770 }
771
772 #[test]
773 fn test_local_announce_bypasses_cap() {
774 let mut queues = AnnounceQueues::new(1024);
778
779 let _ = queues.gate_announce(
781 InterfaceId(1),
782 vec![0x01; 100].into(),
783 [0xAA; 16],
784 2,
785 0.0,
786 0.0,
787 Some(1000),
788 None,
789 0.02,
790 );
791
792 let r = queues.gate_announce(
795 InterfaceId(1),
796 vec![0x02; 100].into(),
797 [0xBB; 16],
798 0,
799 0.0,
800 0.0,
801 Some(1000),
802 None,
803 0.02,
804 );
805 assert!(r.is_none()); }
807
808 #[test]
809 fn test_remove_interface_queue() {
810 let mut queues = AnnounceQueues::new(1024);
811 let _ = queues.gate_announce(
812 InterfaceId(1),
813 vec![0x01; 100].into(),
814 [0xAA; 16],
815 2,
816 0.0,
817 0.0,
818 Some(1000),
819 None,
820 0.02,
821 );
822 let _ = queues.gate_announce(
823 InterfaceId(1),
824 vec![0x02; 100].into(),
825 [0xBB; 16],
826 3,
827 0.0,
828 0.0,
829 Some(1000),
830 None,
831 0.02,
832 );
833
834 assert!(queues.queue_for(&InterfaceId(1)).is_some());
835 assert!(queues.remove_interface(InterfaceId(1)));
836 assert!(queues.queue_for(&InterfaceId(1)).is_none());
837 assert!(!queues.remove_interface(InterfaceId(1)));
838 }
839
840 #[test]
841 fn test_process_queues_prunes_empty_queue() {
842 let mut queues = AnnounceQueues::new(1024);
843
844 let _ = queues.gate_announce(
845 InterfaceId(1),
846 vec![0x01; 10].into(),
847 [0xAA; 16],
848 2,
849 0.0,
850 0.0,
851 Some(1000),
852 None,
853 0.02,
854 );
855 let _ = queues.gate_announce(
856 InterfaceId(1),
857 vec![0x02; 10].into(),
858 [0xBB; 16],
859 3,
860 0.0,
861 0.0,
862 Some(1000),
863 None,
864 0.02,
865 );
866
867 let mut interfaces = BTreeMap::new();
868 interfaces.insert(InterfaceId(1), make_interface_info(1, Some(1000)));
869 let allowed_at = queues
870 .queue_for(&InterfaceId(1))
871 .unwrap()
872 .announce_allowed_at;
873
874 let actions = queues.process_queues(allowed_at + 1.0, &interfaces);
875 assert_eq!(actions.len(), 1);
876 assert!(queues.queue_for(&InterfaceId(1)).is_none());
877 assert_eq!(queues.queue_count(), 0);
878 }
879
880 #[test]
881 fn test_process_queues_keeps_nonempty_queue() {
882 let mut queues = AnnounceQueues::new(1024);
883 let _ = queues.gate_announce(
884 InterfaceId(1),
885 vec![0x01; 100].into(),
886 [0xAA; 16],
887 2,
888 0.0,
889 0.0,
890 Some(1000),
891 None,
892 0.02,
893 );
894 let _ = queues.gate_announce(
895 InterfaceId(1),
896 vec![0x02; 100].into(),
897 [0xBB; 16],
898 3,
899 0.0,
900 0.0,
901 Some(1000),
902 None,
903 0.02,
904 );
905 let _ = queues.gate_announce(
906 InterfaceId(1),
907 vec![0x03; 100].into(),
908 [0xCC; 16],
909 4,
910 0.0,
911 0.0,
912 Some(1000),
913 None,
914 0.02,
915 );
916
917 let mut interfaces = BTreeMap::new();
918 interfaces.insert(InterfaceId(1), make_interface_info(1, Some(1000)));
919 let allowed_at = queues
920 .queue_for(&InterfaceId(1))
921 .unwrap()
922 .announce_allowed_at;
923
924 let actions = queues.process_queues(allowed_at + 1.0, &interfaces);
925 assert_eq!(actions.len(), 1);
926 assert!(queues.queue_for(&InterfaceId(1)).is_some());
927 assert_eq!(queues.queue_for(&InterfaceId(1)).unwrap().entries.len(), 1);
928 }
929
930 #[test]
931 fn test_gate_announce_refuses_new_interface_when_at_capacity() {
932 let mut queues = AnnounceQueues::new(1);
933
934 let _ = queues.gate_announce(
935 InterfaceId(1),
936 vec![0x01; 100].into(),
937 [0xAA; 16],
938 2,
939 0.0,
940 0.0,
941 Some(1000),
942 None,
943 0.02,
944 );
945 let second = queues.gate_announce(
946 InterfaceId(1),
947 vec![0x02; 100].into(),
948 [0xBB; 16],
949 3,
950 0.0,
951 0.0,
952 Some(1000),
953 None,
954 0.02,
955 );
956 assert!(second.is_none());
957 assert_eq!(queues.queue_count(), 1);
958
959 let rejected = queues.gate_announce(
960 InterfaceId(2),
961 vec![0x03; 100].into(),
962 [0xCC; 16],
963 4,
964 0.0,
965 0.0,
966 Some(1000),
967 None,
968 0.02,
969 );
970 assert!(rejected.is_none());
971 assert_eq!(queues.queue_count(), 1);
972 assert!(queues.queue_for(&InterfaceId(2)).is_none());
973 assert_eq!(queues.interface_cap_drop_count(), 1);
974 }
975
976 #[test]
977 fn test_gate_announce_allows_existing_queue_when_at_capacity() {
978 let mut queues = AnnounceQueues::new(1);
979
980 let _ = queues.gate_announce(
981 InterfaceId(1),
982 vec![0x01; 100].into(),
983 [0xAA; 16],
984 2,
985 0.0,
986 0.0,
987 Some(1000),
988 None,
989 0.02,
990 );
991 let queued = queues.gate_announce(
992 InterfaceId(1),
993 vec![0x02; 100].into(),
994 [0xBB; 16],
995 3,
996 0.0,
997 0.0,
998 Some(1000),
999 None,
1000 0.02,
1001 );
1002 assert!(queued.is_none());
1003 assert_eq!(queues.queue_count(), 1);
1004 assert_eq!(queues.queue_for(&InterfaceId(1)).unwrap().entries.len(), 1);
1005 assert_eq!(queues.interface_cap_drop_count(), 0);
1006 }
1007}