You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
5.3 KiB
164 lines
5.3 KiB
// Copyright (c) 2022 Laurent Pelecq
|
|
//
|
|
// Licensed under the Apache License, Version 2.0
|
|
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
|
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. All files in the project carrying such notice may not be copied,
|
|
// modified, or distributed except according to those terms.
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
use mio::{Events, Poll, Token};
|
|
#[cfg(feature = "async-mio")]
|
|
use std::{
|
|
io::{Read, Write},
|
|
slice::Iter,
|
|
time::Duration,
|
|
};
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
use ssip_client::{client::Source, *};
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
mod server;
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
struct State<'a, 'b> {
|
|
pub done: bool,
|
|
pub countdown: usize,
|
|
pub writable: bool,
|
|
pub start_get: bool,
|
|
pub iter_requests: Iter<'a, Request>,
|
|
pub iter_answers: Iter<'b, &'static str>,
|
|
}
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
impl<'a, 'b> State<'a, 'b> {
|
|
fn new(iter_requests: Iter<'a, Request>, iter_answers: Iter<'b, &'static str>) -> Self {
|
|
State {
|
|
done: false,
|
|
countdown: 50,
|
|
writable: false,
|
|
start_get: false,
|
|
iter_requests,
|
|
iter_answers,
|
|
}
|
|
}
|
|
|
|
fn terminated(&self) -> bool {
|
|
self.done || self.countdown == 0
|
|
}
|
|
|
|
fn must_send(&self) -> bool {
|
|
self.writable && self.countdown > 0
|
|
}
|
|
|
|
fn next_request(&mut self) -> Option<&Request> {
|
|
if self.start_get {
|
|
self.iter_requests.next()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn assert_answer(&mut self, val: &str) {
|
|
match self.iter_answers.next() {
|
|
Some(expected_val) => assert_eq!(expected_val, &val),
|
|
None => panic!("no more answers"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
fn basic_async_client_communication<S: Read + Write + Source>(
|
|
client: &mut QueuedClient<S>,
|
|
) -> ClientResult<usize> {
|
|
let get_requests = vec![Request::GetOutputModule, Request::GetRate];
|
|
let get_answers = vec!["espeak", "10"];
|
|
let mut state = State::new(get_requests.iter(), get_answers.iter());
|
|
|
|
let mut poll = Poll::new()?;
|
|
let mut events = Events::with_capacity(128);
|
|
let input_token = Token(0);
|
|
let output_token = Token(1);
|
|
let timeout = Duration::new(0, 500 * 1000 * 1000 /* 500 ms */);
|
|
client.register(&poll, input_token, output_token).unwrap();
|
|
client.push(Request::SetName(ClientName::new("test", "test")));
|
|
while !state.terminated() {
|
|
if !state.writable || !client.has_next() {
|
|
poll.poll(&mut events, Some(timeout))?;
|
|
}
|
|
state.countdown -= 1;
|
|
for event in &events {
|
|
let token = event.token();
|
|
if token == input_token {
|
|
match dbg!(client.receive_next()?) {
|
|
Response::ClientNameSet => {
|
|
client.push(Request::SetLanguage(ClientScope::Current, "en".to_string()))
|
|
}
|
|
Response::LanguageSet => client.push(Request::Stop(MessageScope::Last)),
|
|
Response::Stopped => state.start_get = true,
|
|
Response::Get(val) => state.assert_answer(&val),
|
|
result => panic!("Unexpected response: {:?}", result),
|
|
}
|
|
if let Some(request) = state.next_request() {
|
|
client.push(request.clone());
|
|
} else if state.start_get {
|
|
state.done = true; // No more get request
|
|
}
|
|
} else if token == output_token {
|
|
state.writable = true;
|
|
}
|
|
}
|
|
if state.must_send() {
|
|
match client.send_next() {
|
|
Ok(_) => (),
|
|
Err(ClientError::NotReady) => state.writable = false,
|
|
Err(err) => return Err(err),
|
|
}
|
|
}
|
|
}
|
|
Ok(state.countdown)
|
|
}
|
|
|
|
#[cfg(feature = "async-mio")]
|
|
const BASIC_COMMUNICATION: [(&str, &str); 5] = [
|
|
(
|
|
"SET self CLIENT_NAME test:test:main\r\n",
|
|
"208 OK CLIENT NAME SET\r\n",
|
|
),
|
|
("SET self LANGUAGE en\r\n", "201 OK LANGUAGE SET\r\n"),
|
|
("STOP self\r\n", "210 OK STOPPED\r\n"),
|
|
(
|
|
"GET OUTPUT_MODULE\r\n",
|
|
"251-espeak\r\n251 OK GET RETURNED\r\n",
|
|
),
|
|
("GET RATE\r\n", "251-10\r\n251 OK GET RETURNED\r\n"),
|
|
];
|
|
|
|
#[test]
|
|
#[cfg(feature = "async-mio")]
|
|
fn basic_async_unix_communication() -> ClientResult<()> {
|
|
let socket_dir = tempfile::tempdir()?;
|
|
let socket_path = socket_dir.path().join("basic_async_communication.socket");
|
|
assert!(!socket_path.exists());
|
|
let handle = server::run_unix(&socket_path, &BASIC_COMMUNICATION)?;
|
|
let mut client = QueuedClient::new(fifo::Builder::new().path(&socket_path).build()?);
|
|
let countdown = basic_async_client_communication(&mut client)?;
|
|
handle.join().unwrap().unwrap();
|
|
socket_dir.close()?;
|
|
assert!(countdown > 0);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(feature = "async-mio")]
|
|
fn basic_async_tcp_communication() -> ClientResult<()> {
|
|
let addr = "127.0.0.1:9999";
|
|
let handle = server::run_tcp(addr, &BASIC_COMMUNICATION)?;
|
|
let mut client = QueuedClient::new(tcp::Builder::new(addr.parse().unwrap()).build()?);
|
|
let countdown = basic_async_client_communication(&mut client)?;
|
|
handle.join().unwrap().unwrap();
|
|
assert!(countdown > 0);
|
|
Ok(())
|
|
}
|