1use std::collections::VecDeque;
13use std::net::{IpAddr, SocketAddr};
14
15use sipx_sdp::ice::{CandidateType, ComponentId};
16
17use super::candidate::{
18 LocalCandidate, LocalId, PairFoundation, RemoteCandidate, RemoteId, find_local, find_remote,
19 pair_priority,
20};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum Role {
25 Controlling,
27 Controlled,
29}
30
31impl Role {
32 #[must_use]
46 pub const fn determine(offerer: bool, remote_lite: bool) -> Self {
47 if remote_lite || offerer {
48 Self::Controlling
49 } else {
50 Self::Controlled
51 }
52 }
53
54 #[must_use]
56 pub const fn opposite(self) -> Self {
57 match self {
58 Self::Controlling => Self::Controlled,
59 Self::Controlled => Self::Controlling,
60 }
61 }
62
63 #[must_use]
65 pub const fn is_controlling(self) -> bool {
66 matches!(self, Self::Controlling)
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72pub enum PairState {
73 Frozen,
75 Waiting,
77 InProgress,
79 Succeeded,
81 Failed,
83}
84
85impl PairState {
86 #[must_use]
88 pub const fn is_final(self) -> bool {
89 matches!(self, Self::Succeeded | Self::Failed)
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98pub enum ChecklistState {
99 Running,
101 Completed,
103 Failed,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
113pub struct PairId(pub u32);
114
115#[derive(Debug, Default)]
117pub struct PairIds(u32);
118
119impl PairIds {
120 pub fn allocate(&mut self) -> PairId {
122 let id = PairId(self.0);
123 self.0 = self.0.saturating_add(1);
124 id
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct CandidatePair {
131 pub id: PairId,
133 pub local: LocalId,
135 pub remote: RemoteId,
137 pub component: ComponentId,
139 pub foundation: PairFoundation,
141 pub priority: u64,
143 pub state: PairState,
145 pub nominated: bool,
147}
148
149impl CandidatePair {
150 #[must_use]
156 pub const fn is_discardable(&self) -> bool {
157 matches!(
158 self.state,
159 PairState::Frozen | PairState::Waiting | PairState::Failed
160 ) && !self.nominated
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct ValidPair {
172 pub component: ComponentId,
174 pub local: LocalId,
176 pub remote: SocketAddr,
178 pub priority: u64,
180 pub nominated: bool,
182 pub generated_by: PairId,
184}
185
186#[derive(Debug, Default)]
188pub struct Checklist {
189 pairs: Vec<CandidatePair>,
190 state: Option<ChecklistState>,
191 triggered: VecDeque<PairId>,
192 valid: Vec<ValidPair>,
193}
194
195impl Checklist {
196 #[must_use]
198 pub fn new(pairs: Vec<CandidatePair>) -> Self {
199 Self {
200 pairs,
201 state: None,
202 triggered: VecDeque::new(),
203 valid: Vec::new(),
204 }
205 }
206
207 #[must_use]
209 pub fn pairs(&self) -> &[CandidatePair] {
210 &self.pairs
211 }
212
213 #[must_use]
215 pub fn pair(&self, id: PairId) -> Option<&CandidatePair> {
216 self.pairs.iter().find(|pair| pair.id == id)
217 }
218
219 pub fn pair_mut(&mut self, id: PairId) -> Option<&mut CandidatePair> {
221 self.pairs.iter_mut().find(|pair| pair.id == id)
222 }
223
224 #[must_use]
226 pub fn state(&self) -> ChecklistState {
227 self.state.unwrap_or(ChecklistState::Running)
228 }
229
230 pub fn set_state(&mut self, state: ChecklistState) {
232 self.state = Some(state);
233 }
234
235 #[must_use]
237 pub fn valid(&self) -> &[ValidPair] {
238 &self.valid
239 }
240
241 pub fn add_valid(&mut self, pair: ValidPair) -> bool {
249 if let Some(existing) = self
250 .valid
251 .iter_mut()
252 .find(|known| known.local == pair.local && known.remote == pair.remote)
253 {
254 existing.nominated |= pair.nominated;
255 return false;
256 }
257 self.valid.push(pair);
258 true
259 }
260
261 pub fn nominate_valid(&mut self, generated_by: PairId) {
263 for valid in &mut self.valid {
264 if valid.generated_by == generated_by {
265 valid.nominated = true;
266 }
267 }
268 }
269
270 pub fn remove_valid(&mut self, generated_by: PairId) {
272 self.valid
273 .retain(|valid| valid.generated_by != generated_by);
274 }
275
276 pub fn trigger(&mut self, id: PairId) {
278 if !self.triggered.contains(&id) {
279 self.triggered.push_back(id);
280 }
281 }
282
283 pub fn take_triggered(&mut self) -> Option<PairId> {
287 self.triggered.pop_front()
288 }
289
290 #[must_use]
292 pub fn has_triggered(&self) -> bool {
293 !self.triggered.is_empty()
294 }
295
296 #[must_use]
298 pub fn is_triggered(&self, id: PairId) -> bool {
299 self.triggered.contains(&id)
300 }
301
302 #[must_use]
304 pub fn find(&self, local: LocalId, remote: RemoteId) -> Option<PairId> {
305 self.pairs
306 .iter()
307 .find(|pair| pair.local == local && pair.remote == remote)
308 .map(|pair| pair.id)
309 }
310
311 pub fn insert(&mut self, pair: CandidatePair) {
313 let position = self
314 .pairs
315 .iter()
316 .position(|existing| existing.priority < pair.priority)
317 .unwrap_or(self.pairs.len());
318 self.pairs.insert(position, pair);
319 }
320
321 pub fn sort(&mut self) {
327 self.pairs
328 .sort_by_key(|pair| std::cmp::Reverse(pair.priority));
329 }
330
331 #[must_use]
333 pub fn components(&self) -> Vec<ComponentId> {
334 let mut components: Vec<ComponentId> =
335 self.pairs.iter().map(|pair| pair.component).collect();
336 components.sort_unstable();
337 components.dedup();
338 components
339 }
340
341 pub fn keep_only_nominated(&mut self, component: ComponentId, keep: PairId) {
343 self.pairs
344 .retain(|pair| pair.component != component || pair.id == keep);
345 let live: Vec<PairId> = self.pairs.iter().map(|pair| pair.id).collect();
346 self.triggered.retain(|id| live.contains(id));
347 }
348}
349
350#[derive(Debug, Default)]
352pub struct ChecklistSet {
353 checklists: Vec<Checklist>,
354 next: usize,
355}
356
357impl ChecklistSet {
358 #[must_use]
360 pub fn new() -> Self {
361 Self::default()
362 }
363
364 pub fn push(&mut self, checklist: Checklist) {
367 self.checklists.push(checklist);
368 }
369
370 #[must_use]
372 pub fn checklists(&self) -> &[Checklist] {
373 &self.checklists
374 }
375
376 pub fn checklists_mut(&mut self) -> &mut [Checklist] {
378 &mut self.checklists
379 }
380
381 #[must_use]
383 pub fn is_empty(&self) -> bool {
384 self.checklists.is_empty()
385 }
386
387 #[must_use]
389 pub fn pair(&self, id: PairId) -> Option<&CandidatePair> {
390 self.checklists.iter().find_map(|list| list.pair(id))
391 }
392
393 pub fn pair_mut(&mut self, id: PairId) -> Option<&mut CandidatePair> {
395 self.checklists
396 .iter_mut()
397 .find_map(|list| list.pair_mut(id))
398 }
399
400 #[must_use]
402 pub fn checklist_of(&self, id: PairId) -> Option<usize> {
403 self.checklists
404 .iter()
405 .position(|list| list.pair(id).is_some())
406 }
407
408 #[must_use]
410 pub fn total_pairs(&self) -> usize {
411 self.checklists.iter().map(|list| list.pairs.len()).sum()
412 }
413
414 #[must_use]
416 pub fn outstanding(&self) -> usize {
417 self.checklists
418 .iter()
419 .flat_map(|list| list.pairs.iter())
420 .filter(|pair| matches!(pair.state, PairState::Waiting | PairState::InProgress))
421 .count()
422 }
423
424 pub fn limit(&mut self, limit: usize) {
439 while self.total_pairs() > limit {
440 let longest = self
441 .checklists
442 .iter()
443 .enumerate()
444 .filter(|(_, list)| list.pairs.iter().any(CandidatePair::is_discardable))
445 .max_by_key(|(_, list)| list.pairs.len())
446 .map(|(index, _)| index);
447 let Some(index) = longest else { return };
448 let Some(list) = self.checklists.get_mut(index) else {
449 return;
450 };
451 let lowest = list
452 .pairs
453 .iter()
454 .enumerate()
455 .filter(|(_, pair)| pair.is_discardable())
456 .min_by_key(|(_, pair)| pair.priority)
457 .map(|(position, _)| position);
458 match lowest {
459 Some(position) => {
460 let dropped = list.pairs.remove(position);
461 list.triggered.retain(|id| *id != dropped.id);
462 }
463 None => return,
464 }
465 }
466 }
467
468 pub fn compute_initial_states(&mut self) {
478 for list in &mut self.checklists {
479 for pair in &mut list.pairs {
480 pair.state = PairState::Frozen;
481 }
482 list.set_state(ChecklistState::Running);
483 }
484 self.unfreeze_added();
485 }
486
487 pub fn unfreeze_added(&mut self) {
496 let mut unfrozen: Vec<PairFoundation> = self
497 .checklists
498 .iter()
499 .flat_map(|list| list.pairs.iter())
500 .filter(|pair| pair.state != PairState::Frozen)
501 .map(|pair| pair.foundation.clone())
502 .collect();
503
504 for list in &mut self.checklists {
505 let mut order: Vec<usize> = (0..list.pairs.len()).collect();
506 order.sort_by_key(|position| {
507 list.pairs
508 .get(*position)
509 .map(|pair| (pair.component, std::cmp::Reverse(pair.priority)))
510 });
511 for position in order {
512 let Some(pair) = list.pairs.get_mut(position) else {
513 continue;
514 };
515 if pair.state != PairState::Frozen || unfrozen.contains(&pair.foundation) {
516 continue;
517 }
518 pair.state = PairState::Waiting;
519 unfrozen.push(pair.foundation.clone());
520 }
521 }
522 }
523
524 pub fn unfreeze_foundation(&mut self, foundation: &PairFoundation) {
527 for list in &mut self.checklists {
528 for pair in &mut list.pairs {
529 if pair.state == PairState::Frozen && pair.foundation == *foundation {
530 pair.state = PairState::Waiting;
531 }
532 }
533 }
534 }
535
536 pub fn unfreeze_idle(&mut self, index: usize) {
545 let Some(list) = self.checklists.get(index) else {
546 return;
547 };
548 if list
549 .pairs
550 .iter()
551 .any(|pair| pair.state == PairState::Waiting)
552 {
553 return;
554 }
555 let frozen: Vec<PairFoundation> = list
556 .pairs
557 .iter()
558 .filter(|pair| pair.state == PairState::Frozen)
559 .map(|pair| pair.foundation.clone())
560 .collect();
561 let busy: Vec<PairFoundation> = self
562 .checklists
563 .iter()
564 .flat_map(|list| list.pairs.iter())
565 .filter(|pair| matches!(pair.state, PairState::Waiting | PairState::InProgress))
566 .map(|pair| pair.foundation.clone())
567 .collect();
568
569 let Some(list) = self.checklists.get_mut(index) else {
570 return;
571 };
572 let mut thawed: Vec<PairFoundation> = Vec::new();
573 for foundation in frozen {
574 if busy.contains(&foundation) || thawed.contains(&foundation) {
575 continue;
576 }
577 if let Some(pair) = list
578 .pairs
579 .iter_mut()
580 .find(|pair| pair.state == PairState::Frozen && pair.foundation == foundation)
581 {
582 pair.state = PairState::Waiting;
583 thawed.push(foundation);
584 }
585 }
586 }
587
588 pub fn next_active(&mut self) -> Option<usize> {
596 let count = self.checklists.len();
597 for offset in 0..count {
598 let index = (self.next.wrapping_add(offset)) % count.max(1);
599 if self
600 .checklists
601 .get(index)
602 .is_some_and(|list| list.state() == ChecklistState::Running || list.has_triggered())
603 {
604 self.next = index.wrapping_add(1) % count.max(1);
605 return Some(index);
606 }
607 }
608 None
609 }
610
611 pub fn recompute_priorities(
617 &mut self,
618 role: Role,
619 locals: &[LocalCandidate],
620 remotes: &[RemoteCandidate],
621 ) {
622 for list in &mut self.checklists {
623 for pair in &mut list.pairs {
624 let (Some(local), Some(remote)) = (
625 find_local(locals, pair.local),
626 find_remote(remotes, pair.remote),
627 ) else {
628 continue;
629 };
630 pair.priority = ordered_pair_priority(role, local.priority, remote.priority);
631 }
632 list.sort();
633 }
634 for list in &mut self.checklists {
635 for valid in &mut list.valid {
636 let Some(local) = find_local(locals, valid.local) else {
637 continue;
638 };
639 let remote = remotes
640 .iter()
641 .find(|candidate| candidate.address == valid.remote)
642 .map(|candidate| candidate.priority);
643 if let Some(remote) = remote {
644 valid.priority = ordered_pair_priority(role, local.priority, remote);
645 }
646 }
647 }
648 }
649}
650
651#[must_use]
656pub fn ordered_pair_priority(
657 role: Role,
658 local: sipx_sdp::ice::Priority,
659 remote: sipx_sdp::ice::Priority,
660) -> u64 {
661 if role.is_controlling() {
662 pair_priority(local, remote)
663 } else {
664 pair_priority(remote, local)
665 }
666}
667
668#[must_use]
674pub fn is_link_local(address: IpAddr) -> bool {
675 match address {
676 IpAddr::V6(v6) => v6
677 .segments()
678 .first()
679 .is_some_and(|first| *first & 0xffc0 == 0xfe80),
680 IpAddr::V4(_) => false,
681 }
682}
683
684pub fn form_pairs(
693 ids: &mut PairIds,
694 role: Role,
695 locals: &[LocalCandidate],
696 remotes: &[RemoteCandidate],
697) -> Vec<CandidatePair> {
698 let max_local = locals
699 .iter()
700 .map(|candidate| candidate.gathered.component)
701 .max();
702 let max_remote = remotes.iter().map(|candidate| candidate.component).max();
703 let (Some(max_local), Some(max_remote)) = (max_local, max_remote) else {
704 return Vec::new();
705 };
706 let ceiling = max_local.min(max_remote);
707
708 let mut pairs = Vec::new();
709 for local in locals {
710 if local.gathered.component > ceiling {
711 continue;
712 }
713 for remote in remotes {
714 if local.gathered.component != remote.component {
715 continue;
716 }
717 let local_ip = local.gathered.address.ip();
718 let remote_ip = remote.address.ip();
719 if local_ip.is_ipv4() != remote_ip.is_ipv4() {
720 continue;
721 }
722 if is_link_local(local_ip) != is_link_local(remote_ip) {
723 continue;
724 }
725 pairs.push(CandidatePair {
726 id: ids.allocate(),
727 local: local.id,
728 remote: remote.id,
729 component: local.gathered.component,
730 foundation: PairFoundation {
731 local: local.foundation,
732 remote: remote.foundation.clone(),
733 },
734 priority: ordered_pair_priority(role, local.priority, remote.priority),
735 state: PairState::Frozen,
736 nominated: false,
737 });
738 }
739 }
740 pairs.sort_by_key(|pair| std::cmp::Reverse(pair.priority));
741 prune(&mut pairs, locals, remotes);
742 pairs
743}
744
745fn prune(pairs: &mut Vec<CandidatePair>, locals: &[LocalCandidate], remotes: &[RemoteCandidate]) {
754 for pair in pairs.iter_mut() {
755 let Some(local) = find_local(locals, pair.local) else {
756 continue;
757 };
758 if !matches!(
759 local.gathered.kind,
760 CandidateType::ServerReflexive | CandidateType::PeerReflexive
761 ) {
762 continue;
763 }
764 let base = local.gathered.base_address;
765 let component = local.gathered.component;
766 if let Some(host) = locals.iter().find(|candidate| {
767 candidate.gathered.kind == CandidateType::Host
768 && candidate.gathered.address == base
769 && candidate.gathered.component == component
770 }) {
771 pair.local = host.id;
772 }
773 }
774
775 let mut kept: Vec<(SocketAddr, SocketAddr)> = Vec::new();
776 pairs.retain(|pair| {
777 let (Some(local), Some(remote)) = (
778 find_local(locals, pair.local),
779 find_remote(remotes, pair.remote),
780 ) else {
781 return false;
782 };
783 let key = (local.gathered.base_address, remote.address);
784 if kept.contains(&key) {
785 return false;
786 }
787 kept.push(key);
788 true
789 });
790}
791
792#[cfg(test)]
793#[allow(
794 clippy::unwrap_used,
795 clippy::expect_used,
796 clippy::panic,
797 clippy::indexing_slicing
798)]
799mod tests {
800 use sipx_sdp::ice::{Foundation, Priority};
801
802 use super::*;
803 use crate::ice::candidate::{
804 Gathered, LocalBase, LocalFoundation, RemoteFoundation, SINGLE_ADDRESS_PREFERENCE,
805 assign_local_preferences,
806 };
807 use crate::ice::candidate::{find_local, find_remote};
808
809 fn component(id: u16) -> ComponentId {
810 ComponentId::new(id).unwrap()
811 }
812
813 fn host(id: usize, ip: &str, port: u16, component_id: u16) -> LocalCandidate {
814 let address = SocketAddr::new(ip.parse().unwrap(), port);
815 LocalCandidate {
816 id: LocalId(id),
817 gathered: Gathered {
818 base: LocalBase(0),
819 base_address: address,
820 address,
821 kind: CandidateType::Host,
822 component: component(component_id),
823 server: None,
824 },
825 foundation: LocalFoundation(1),
826 local_preference: SINGLE_ADDRESS_PREFERENCE,
827 priority: crate::ice::candidate::priority(
828 crate::ice::candidate::HOST_PREFERENCE,
829 SINGLE_ADDRESS_PREFERENCE,
830 component(component_id),
831 ),
832 }
833 }
834
835 fn remote(ip: &str, port: u16, component_id: u16, foundation: u32) -> RemoteCandidate {
836 RemoteCandidate {
837 id: RemoteId(ip.bytes().map(usize::from).sum::<usize>() * 100_000 + usize::from(port)),
839 address: SocketAddr::new(ip.parse().unwrap(), port),
840 kind: CandidateType::Host,
841 component: component(component_id),
842 foundation: RemoteFoundation::Signalled(
843 Foundation::new(&foundation.to_string()).unwrap(),
844 ),
845 priority: crate::ice::candidate::priority(
847 crate::ice::candidate::SERVER_REFLEXIVE_PREFERENCE,
848 SINGLE_ADDRESS_PREFERENCE,
849 component(component_id),
850 ),
851 }
852 }
853
854 fn pair(id: u32, component_id: u16, foundation: u32, priority: u64) -> CandidatePair {
855 CandidatePair {
856 id: PairId(id),
857 local: LocalId(0),
858 remote: RemoteId(0),
859 component: component(component_id),
860 foundation: PairFoundation {
861 local: LocalFoundation(foundation),
862 remote: RemoteFoundation::Learned(foundation),
863 },
864 priority,
865 state: PairState::Frozen,
866 nominated: false,
867 }
868 }
869
870 #[test]
878 fn the_rfcs_three_checklist_five_foundation_example_unfreezes_five_pairs() {
879 let mut set = ChecklistSet::new();
880 set.push(Checklist::new(vec![
881 pair(1, 1, 1, 300),
882 pair(2, 1, 2, 200),
883 pair(3, 1, 3, 100),
884 ]));
885 set.push(Checklist::new(vec![
886 pair(4, 1, 1, 300),
887 pair(5, 1, 2, 200),
888 pair(6, 1, 3, 100),
889 pair(7, 1, 4, 50),
890 ]));
891 set.push(Checklist::new(vec![pair(8, 1, 1, 300), pair(9, 1, 5, 10)]));
892
893 set.compute_initial_states();
894
895 let state = |id: u32| set.pair(PairId(id)).unwrap().state;
896 assert_eq!(state(1), PairState::Waiting);
898 assert_eq!(state(2), PairState::Waiting);
899 assert_eq!(state(3), PairState::Waiting);
900 assert_eq!(state(4), PairState::Frozen);
902 assert_eq!(state(5), PairState::Frozen);
903 assert_eq!(state(6), PairState::Frozen);
904 assert_eq!(state(7), PairState::Waiting);
905 assert_eq!(state(8), PairState::Frozen);
907 assert_eq!(state(9), PairState::Waiting);
908
909 assert!(
910 set.checklists()
911 .iter()
912 .all(|list| list.state() == ChecklistState::Running)
913 );
914 }
915
916 #[test]
919 fn the_unfrozen_pair_for_a_foundation_is_the_lowest_component_then_the_highest_priority() {
920 let mut set = ChecklistSet::new();
921 set.push(Checklist::new(vec![
922 pair(1, 2, 1, 900),
923 pair(2, 1, 1, 100),
924 pair(3, 1, 1, 500),
925 ]));
926 set.compute_initial_states();
927 assert_eq!(set.pair(PairId(3)).unwrap().state, PairState::Waiting);
929 assert_eq!(set.pair(PairId(1)).unwrap().state, PairState::Frozen);
930 assert_eq!(set.pair(PairId(2)).unwrap().state, PairState::Frozen);
931 }
932
933 #[test]
936 fn a_foundation_whose_only_unfrozen_pair_failed_is_thawed_again() {
937 let mut set = ChecklistSet::new();
938 set.push(Checklist::new(vec![pair(1, 1, 1, 300), pair(2, 2, 1, 200)]));
939 set.compute_initial_states();
940 assert_eq!(set.pair(PairId(1)).unwrap().state, PairState::Waiting);
941 assert_eq!(set.pair(PairId(2)).unwrap().state, PairState::Frozen);
942
943 set.pair_mut(PairId(1)).unwrap().state = PairState::Failed;
944 set.unfreeze_idle(0);
945 assert_eq!(set.pair(PairId(2)).unwrap().state, PairState::Waiting);
946 }
947
948 #[test]
951 fn nothing_is_thawed_while_the_foundation_still_has_a_check_outstanding() {
952 let mut set = ChecklistSet::new();
953 set.push(Checklist::new(vec![pair(1, 1, 1, 300), pair(2, 2, 1, 200)]));
954 set.compute_initial_states();
955 set.pair_mut(PairId(1)).unwrap().state = PairState::InProgress;
956 set.unfreeze_idle(0);
957 assert_eq!(set.pair(PairId(2)).unwrap().state, PairState::Frozen);
958 }
959
960 #[test]
961 fn pairs_are_ordered_by_decreasing_priority_and_ties_keep_their_order() {
962 let mut list = Checklist::new(vec![
963 pair(1, 1, 1, 100),
964 pair(2, 1, 2, 900),
965 pair(3, 1, 3, 100),
966 ]);
967 list.sort();
968 let order: Vec<u32> = list.pairs().iter().map(|pair| pair.id.0).collect();
969 assert_eq!(order, vec![2, 1, 3]);
970 list.sort();
971 let again: Vec<u32> = list.pairs().iter().map(|pair| pair.id.0).collect();
972 assert_eq!(order, again);
973 }
974
975 #[test]
976 fn the_hundred_pair_limit_is_enforced_and_configurable() {
977 let mut set = ChecklistSet::new();
978 set.push(Checklist::new(
979 (0..150u32).map(|n| pair(n, 1, 1, u64::from(n))).collect(),
980 ));
981 set.limit(100);
982 assert_eq!(set.total_pairs(), 100);
983 assert!(set.pair(PairId(0)).is_none());
985 assert!(set.pair(PairId(149)).is_some());
986
987 set.limit(10);
988 assert_eq!(set.total_pairs(), 10);
989 }
990
991 #[test]
994 fn the_limit_takes_from_the_longest_checklist_first() {
995 let mut set = ChecklistSet::new();
996 set.push(Checklist::new(
997 (0..8u32).map(|n| pair(n, 1, 1, u64::from(n))).collect(),
998 ));
999 set.push(Checklist::new(
1000 (10..12u32).map(|n| pair(n, 1, 1, u64::from(n))).collect(),
1001 ));
1002 set.limit(6);
1003 assert_eq!(set.checklists()[0].pairs().len(), 4);
1004 assert_eq!(set.checklists()[1].pairs().len(), 2);
1005 }
1006
1007 #[test]
1008 fn candidates_pair_only_within_a_component_and_an_address_family() {
1009 let locals = vec![host(1, "192.0.2.1", 5000, 1), host(2, "192.0.2.1", 5001, 2)];
1010 let remotes = vec![
1011 remote("198.51.100.1", 6000, 1, 1),
1012 remote("198.51.100.1", 6001, 2, 1),
1013 remote("2001:db8::1", 6002, 1, 2),
1014 ];
1015 let mut ids = PairIds::default();
1016 let pairs = form_pairs(&mut ids, Role::Controlling, &locals, &remotes);
1017 assert_eq!(pairs.len(), 2);
1018 assert!(pairs.iter().all(|pair| {
1019 find_local(&locals, pair.local).unwrap().gathered.component
1020 == find_remote(&remotes, pair.remote).unwrap().component
1021 }));
1022 }
1023
1024 #[test]
1026 fn an_ipv6_link_local_candidate_pairs_only_with_link_local_addresses() {
1027 assert!(is_link_local("fe80::1".parse().unwrap()));
1028 assert!(!is_link_local("2001:db8::1".parse().unwrap()));
1029 assert!(!is_link_local("192.0.2.1".parse().unwrap()));
1030
1031 let locals = vec![host(1, "fe80::1", 5000, 1), host(2, "2001:db8::1", 5000, 1)];
1032 let remotes = vec![
1033 remote("fe80::2", 6000, 1, 1),
1034 remote("2001:db8::2", 6000, 1, 2),
1035 ];
1036 let mut ids = PairIds::default();
1037 let pairs = form_pairs(&mut ids, Role::Controlling, &locals, &remotes);
1038 assert_eq!(pairs.len(), 2);
1039 for pair in &pairs {
1040 assert_eq!(
1041 is_link_local(
1042 find_local(&locals, pair.local)
1043 .unwrap()
1044 .gathered
1045 .address
1046 .ip()
1047 ),
1048 is_link_local(find_remote(&remotes, pair.remote).unwrap().address.ip())
1049 );
1050 }
1051 }
1052
1053 #[test]
1056 fn a_peer_offering_rtcp_to_an_agent_without_one_gets_no_rtcp_pairs() {
1057 let locals = vec![host(1, "192.0.2.1", 5000, 1)];
1058 let remotes = vec![
1059 remote("198.51.100.1", 6000, 1, 1),
1060 remote("198.51.100.1", 6001, 2, 1),
1061 ];
1062 let mut ids = PairIds::default();
1063 let pairs = form_pairs(&mut ids, Role::Controlling, &locals, &remotes);
1064 assert_eq!(pairs.len(), 1);
1065 assert_eq!(pairs[0].component, component(1));
1066 }
1067
1068 #[test]
1071 fn a_reflexive_local_becomes_its_base_and_the_redundant_pair_goes() {
1072 let base = SocketAddr::new("192.0.2.1".parse().unwrap(), 5000);
1073 let mut locals = vec![
1074 host(1, "192.0.2.1", 5000, 1),
1075 LocalCandidate {
1076 id: LocalId(1),
1077 gathered: Gathered {
1078 base: LocalBase(0),
1079 base_address: base,
1080 address: SocketAddr::new("198.51.100.9".parse().unwrap(), 7000),
1081 kind: CandidateType::ServerReflexive,
1082 component: component(1),
1083 server: Some("198.51.100.1".parse().unwrap()),
1084 },
1085 foundation: LocalFoundation(2),
1086 local_preference: SINGLE_ADDRESS_PREFERENCE,
1087 priority: Priority::new(1).unwrap(),
1088 },
1089 ];
1090 assign_local_preferences(&mut locals);
1091 let remotes = vec![remote("198.51.100.2", 6000, 1, 1)];
1092
1093 let mut ids = PairIds::default();
1094 let pairs = form_pairs(&mut ids, Role::Controlling, &locals, &remotes);
1095 assert_eq!(pairs.len(), 1);
1096 assert_eq!(
1098 find_local(&locals, pairs[0].local).unwrap().gathered.kind,
1099 CandidateType::Host
1100 );
1101 }
1102
1103 #[test]
1106 fn both_ends_compute_the_same_pair_priority_for_the_same_pair() {
1107 let ours = Priority::new(2_130_706_431).unwrap();
1108 let theirs = Priority::new(1_694_498_815).unwrap();
1109 assert_eq!(
1110 ordered_pair_priority(Role::Controlling, ours, theirs),
1111 ordered_pair_priority(Role::Controlled, theirs, ours)
1112 );
1113 }
1114
1115 #[test]
1116 fn a_role_change_recomputes_every_pair_priority_and_re_sorts() {
1117 let locals = vec![host(1, "192.0.2.1", 5000, 1)];
1118 let remotes = vec![remote("198.51.100.1", 6000, 1, 1)];
1119 let mut ids = PairIds::default();
1120 let mut set = ChecklistSet::new();
1121 set.push(Checklist::new(form_pairs(
1122 &mut ids,
1123 Role::Controlling,
1124 &locals,
1125 &remotes,
1126 )));
1127 let before = set.checklists()[0].pairs()[0].priority;
1128
1129 set.recompute_priorities(Role::Controlled, &locals, &remotes);
1130 let after = set.checklists()[0].pairs()[0].priority;
1131 assert_ne!(before, after);
1133 assert_eq!(
1134 after,
1135 ordered_pair_priority(Role::Controlled, locals[0].priority, remotes[0].priority)
1136 );
1137 let pair = &set.checklists()[0].pairs()[0];
1139 assert!(find_local(&locals, pair.local).is_some());
1140 assert!(find_remote(&remotes, pair.remote).is_some());
1141 }
1142
1143 #[test]
1144 fn the_triggered_queue_is_fifo_and_holds_a_pair_once() {
1145 let mut list = Checklist::new(vec![pair(1, 1, 1, 100), pair(2, 1, 2, 200)]);
1146 list.trigger(PairId(2));
1147 list.trigger(PairId(1));
1148 list.trigger(PairId(2));
1149 assert_eq!(list.take_triggered(), Some(PairId(2)));
1150 assert_eq!(list.take_triggered(), Some(PairId(1)));
1151 assert_eq!(list.take_triggered(), None);
1152 }
1153
1154 #[test]
1155 fn determining_the_role_follows_section_6_1_1() {
1156 assert_eq!(Role::determine(true, false), Role::Controlling);
1157 assert_eq!(Role::determine(false, false), Role::Controlled);
1158 assert_eq!(Role::determine(false, true), Role::Controlling);
1160 assert_eq!(Role::determine(true, true), Role::Controlling);
1161 assert_eq!(Role::Controlling.opposite(), Role::Controlled);
1162 assert_eq!(Role::Controlled.opposite(), Role::Controlling);
1163 }
1164}