use std::net::Ipv4Addr;
use std::time::Duration;
use str0m::media::{Direction, MediaKind};
use str0m::{Event, RtcError};
mod common;
use common::{Peer, TestRtc, init_crypto_default, init_log, negotiate, progress};
#[test]
fn api_disconnect_and_is_alive() -> Result<(), RtcError> {
init_log();
init_crypto_default();
let mut l = TestRtc::new(Peer::Left);
let mut r = TestRtc::new(Peer::Right);
l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
let (offer, pending) = l.span.in_scope(|| {
let mut change = l.rtc.sdp_api();
let _ = change.add_channel("test".into());
change.apply().unwrap()
});
let answer = r.span.in_scope(|| r.rtc.sdp_api().accept_offer(offer))?;
l.span
.in_scope(|| l.rtc.sdp_api().accept_answer(pending, answer))?;
loop {
if l.is_connected() && r.is_connected() {
break;
}
if l.duration() > Duration::from_secs(5) {
panic!("Failed to connect");
}
progress(&mut l, &mut r)?;
}
assert!(l.rtc.is_alive(), "Should be alive before disconnect");
l.rtc.disconnect();
assert!(!l.rtc.is_alive(), "Should not be alive after disconnect");
Ok(())
}
#[test]
fn api_error_not_sending_direction() -> Result<(), RtcError> {
init_log();
init_crypto_default();
let mut l = TestRtc::new(Peer::Left);
let mut r = TestRtc::new(Peer::Right);
l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
let mid = negotiate(&mut l, &mut r, |change| {
change.add_media(MediaKind::Audio, Direction::RecvOnly, None, None, None)
});
loop {
if l.is_connected() && r.is_connected() {
break;
}
progress(&mut l, &mut r)?;
}
let media = l.media(mid);
assert!(media.is_some(), "Media should exist");
assert_eq!(
media.unwrap().direction(),
Direction::RecvOnly,
"L should be RecvOnly"
);
let r_media = r.media(mid);
assert!(r_media.is_some(), "R media should exist");
assert_eq!(
r_media.unwrap().direction(),
Direction::SendOnly,
"R should be SendOnly (inverse of L's RecvOnly)"
);
Ok(())
}
#[test]
fn api_error_not_receiving_direction() -> Result<(), RtcError> {
init_log();
init_crypto_default();
let mut l = TestRtc::new(Peer::Left);
let mut r = TestRtc::new(Peer::Right);
l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
let mid = negotiate(&mut l, &mut r, |change| {
change.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None)
});
loop {
if l.is_connected() && r.is_connected() {
break;
}
progress(&mut l, &mut r)?;
}
let media = l.media(mid);
assert!(media.is_some(), "Media should exist");
assert_eq!(
media.unwrap().direction(),
Direction::SendOnly,
"L should be SendOnly"
);
Ok(())
}
#[test]
fn api_operations_after_disconnect() -> Result<(), RtcError> {
init_log();
init_crypto_default();
let mut l = TestRtc::new(Peer::Left);
let mut r = TestRtc::new(Peer::Right);
l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
let mid = negotiate(&mut l, &mut r, |change| {
change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None)
});
loop {
if l.is_connected() && r.is_connected() {
break;
}
progress(&mut l, &mut r)?;
}
l.rtc.disconnect();
let params = l.params_opus();
let pt = params.pt();
let wallclock = l.start + l.duration();
let time = l.duration().into();
if let Some(w) = l.writer(mid) {
let _ = w.write(pt, wallclock, time, vec![0u8; 80]);
}
Ok(())
}
#[test]
fn api_ice_state_change_events() -> Result<(), RtcError> {
init_log();
init_crypto_default();
let mut l = TestRtc::new(Peer::Left);
let mut r = TestRtc::new(Peer::Right);
l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
let (offer, pending) = l.span.in_scope(|| {
let mut change = l.rtc.sdp_api();
let _ = change.add_channel("test".into());
change.apply().unwrap()
});
let answer = r.span.in_scope(|| r.rtc.sdp_api().accept_offer(offer))?;
l.span
.in_scope(|| l.rtc.sdp_api().accept_answer(pending, answer))?;
loop {
if l.is_connected() && r.is_connected() {
break;
}
if l.duration() > Duration::from_secs(5) {
panic!("Failed to connect");
}
progress(&mut l, &mut r)?;
}
let ice_events: Vec<_> = l
.events
.iter()
.filter(|(_, e)| matches!(e, Event::IceConnectionStateChange(_)))
.collect();
assert!(
!ice_events.is_empty(),
"Should have received IceConnectionStateChange events"
);
Ok(())
}
#[test]
fn api_connected_event() -> Result<(), RtcError> {
init_log();
init_crypto_default();
let mut l = TestRtc::new(Peer::Left);
let mut r = TestRtc::new(Peer::Right);
l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
let (offer, pending) = l.span.in_scope(|| {
let mut change = l.rtc.sdp_api();
let _ = change.add_channel("test".into());
change.apply().unwrap()
});
let answer = r.span.in_scope(|| r.rtc.sdp_api().accept_offer(offer))?;
l.span
.in_scope(|| l.rtc.sdp_api().accept_answer(pending, answer))?;
loop {
if l.is_connected() && r.is_connected() {
break;
}
if l.duration() > Duration::from_secs(5) {
panic!("Failed to connect");
}
progress(&mut l, &mut r)?;
}
let connected = l.events.iter().any(|(_, e)| matches!(e, Event::Connected));
assert!(connected, "Should have received Connected event");
Ok(())
}