Some checks failed
Native code generation / deterministic (push) Failing after 2m19s
Imaging and meshing gate / native (push) Failing after 4m24s
JPEG 2000 feature / linux (push) Successful in 2m50s
Native Rust workspace compile / compile (push) Failing after 15m31s
Skia feature / linux (push) Successful in 31m51s
118 lines
4.9 KiB
Rust
118 lines
4.9 KiB
Rust
use libremetaverse_voice_vivox::VivoxControlClient;
|
|
use std::net::Ipv4Addr;
|
|
use std::time::Duration;
|
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
|
use tokio::net::TcpListener;
|
|
|
|
const EXPECTED_ACTIONS: [&str; 10] = [
|
|
"Aux.GetCaptureDevices.1",
|
|
"Aux.GetRenderDevices.1",
|
|
"Connector.Create.1",
|
|
"Account.Login.1",
|
|
"Session.Create.1",
|
|
"Session.Connect.1",
|
|
"Session.SetParticipantVolumeForMe.1",
|
|
"Session.Terminate.1",
|
|
"Account.Logout.1",
|
|
"Connector.InitiateShutdown.1",
|
|
];
|
|
|
|
fn response(id: &str, action: &str, result: &str) -> String {
|
|
format!(
|
|
"<Response requestId=\"{id}\" action=\"{action}\"><ReturnCode>0</ReturnCode><Results><StatusCode>0</StatusCode><StatusString>OK</StatusString>{result}</Results></Response>\n\n\n"
|
|
)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn native_vivox_protocol_preserves_control_order_events_and_teardown() {
|
|
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
|
|
let endpoint = listener.local_addr().unwrap();
|
|
let server = tokio::spawn(async move {
|
|
let (stream, _) = listener.accept().await.unwrap();
|
|
let (reader, mut writer) = stream.into_split();
|
|
let mut reader = BufReader::new(reader);
|
|
let mut actions = Vec::new();
|
|
loop {
|
|
let mut line = String::new();
|
|
assert_ne!(reader.read_line(&mut line).await.unwrap(), 0);
|
|
let line = line.trim();
|
|
if line.is_empty() {
|
|
continue;
|
|
}
|
|
let id = line
|
|
.split("requestId=\"")
|
|
.nth(1)
|
|
.and_then(|value| value.split('"').next())
|
|
.unwrap();
|
|
let action = line
|
|
.split("action=\"")
|
|
.nth(1)
|
|
.and_then(|value| value.split('"').next())
|
|
.unwrap();
|
|
actions.push(action.to_owned());
|
|
let result = match action {
|
|
"Aux.GetCaptureDevices.1" => {
|
|
"<CaptureDevices><CaptureDevice><Device>Mic</Device></CaptureDevice></CaptureDevices><CurrentCaptureDevice><Device>Mic</Device></CurrentCaptureDevice>"
|
|
}
|
|
"Aux.GetRenderDevices.1" => {
|
|
"<RenderDevices><RenderDevice><Device>Speakers</Device></RenderDevice></RenderDevices><CurrentRenderDevice><Device>Speakers</Device></CurrentRenderDevice>"
|
|
}
|
|
"Connector.Create.1" => "<ConnectorHandle>connector-secret</ConnectorHandle>",
|
|
"Account.Login.1" => "<AccountHandle>account-secret</AccountHandle>",
|
|
"Session.Create.1" => "<SessionHandle>session-secret</SessionHandle>",
|
|
"Session.Connect.1"
|
|
| "Session.SetParticipantVolumeForMe.1"
|
|
| "Session.Terminate.1"
|
|
| "Account.Logout.1"
|
|
| "Connector.InitiateShutdown.1" => "",
|
|
_ => panic!("unexpected Vivox action {action}"),
|
|
};
|
|
writer
|
|
.write_all(response(id, action, result).as_bytes())
|
|
.await
|
|
.unwrap();
|
|
if action == "Session.Connect.1" {
|
|
writer.write_all(b"<Event type=\"ParticipantPropertiesEvent\"><SessionHandle>session-secret</SessionHandle><ParticipantURI>sip:participant-token@example.test</ParticipantURI><DisplayName>Alice Resident</DisplayName><IsSpeaking>true</IsSpeaking><Volume>0</Volume><Energy>0.25</Energy></Event>\n\n\n").await.unwrap();
|
|
}
|
|
if action == "Connector.InitiateShutdown.1" {
|
|
writer.shutdown().await.unwrap();
|
|
return actions;
|
|
}
|
|
}
|
|
});
|
|
|
|
let mut client = VivoxControlClient::connect(endpoint, Duration::from_secs(2))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(client.capture_devices().await.unwrap().available, ["Mic"]);
|
|
assert_eq!(
|
|
client.render_devices().await.unwrap().available,
|
|
["Speakers"]
|
|
);
|
|
client
|
|
.create_connector("https://www.voice.example.test/api2/")
|
|
.await
|
|
.unwrap();
|
|
client.login("voice-user", "voice-password").await.unwrap();
|
|
let session = client
|
|
.create_session("sip:channel-token@example.test", "Parity Region", None)
|
|
.await
|
|
.unwrap();
|
|
client.connect_session(&session).await.unwrap();
|
|
let event = client.next_event().await.unwrap();
|
|
assert_eq!(event.kind, "ParticipantPropertiesEvent");
|
|
assert_eq!(event.display_name.as_deref(), Some("Alice Resident"));
|
|
assert_eq!(event.is_speaking, Some(true));
|
|
let participant = event.participant_uri.unwrap();
|
|
client
|
|
.set_participant_volume(&session, &participant, 0)
|
|
.await
|
|
.unwrap();
|
|
client.terminate_session(&session).await.unwrap();
|
|
client.logout().await.unwrap();
|
|
client.shutdown().await.unwrap();
|
|
client.shutdown().await.unwrap();
|
|
let actions = server.await.unwrap();
|
|
assert_eq!(actions, EXPECTED_ACTIONS);
|
|
}
|