Add missing tests changes.

main
Laurent Pelecq 2 years ago
parent 883e491a7f
commit ea75e00edc

@ -8,6 +8,9 @@
#[cfg(feature = "async-mio")] #[cfg(feature = "async-mio")]
use mio::{Events, Poll, Token}; use mio::{Events, Poll, Token};
#[cfg(feature = "async-mio")]
use std::{slice::Iter, time::Duration};
#[cfg(feature = "async-mio")] #[cfg(feature = "async-mio")]
use ssip_client::*; use ssip_client::*;
@ -18,86 +21,143 @@ mod server;
use server::Server; use server::Server;
#[cfg(feature = "async-mio")] #[cfg(feature = "async-mio")]
mod utils { enum Answer {
Str(&'static str),
Int(i8),
}
#[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, Answer>,
}
use ssip_client::*; #[cfg(feature = "async-mio")]
impl<'a, 'b> State<'a, 'b> {
fn new(iter_requests: Iter<'a, Request>, iter_answers: Iter<'b, Answer>) -> Self {
State {
done: false,
countdown: 50,
writable: false,
start_get: false,
iter_requests,
iter_answers,
}
}
const MAX_RETRIES: u16 = 10; fn terminated(&self) -> bool {
self.done || self.countdown == 0
}
pub struct Controler { fn must_send(&self) -> bool {
step: u16, self.writable && self.countdown > 0
retry: u16,
} }
impl Controler { fn next_request(&mut self) -> Option<&Request> {
pub fn new() -> Controler { if self.start_get {
Controler { self.iter_requests.next()
step: 0, } else {
retry: MAX_RETRIES, None
}
} }
}
pub fn step(&self) -> u16 { fn assert_string(&mut self, val: &str) {
self.step match self.iter_answers.next() {
Some(Answer::Str(expected_val)) => assert_eq!(expected_val, &val),
Some(Answer::Int(expected_val)) => panic!(
"expecting integer {} instead of string '{}'",
expected_val, val
),
None => panic!("no more answers"),
} }
}
pub fn check_result<V>(&mut self, result: ClientResult<V>) -> Option<V> { fn assert_integer(&mut self, val: i8) {
match result { match self.iter_answers.next() {
Ok(value) => { Some(Answer::Int(expected_val)) => assert_eq!(expected_val, &val),
self.step += 1; Some(Answer::Str(expected_val)) => panic!(
self.retry = MAX_RETRIES; "expecting string '{}' instead of integer {}",
Some(value) expected_val, val
} ),
Err(ClientError::NotReady) if self.retry > 0 => { None => panic!("no more answers"),
self.retry -= 1;
None
}
Err(err) => panic!("{:?}", err),
}
} }
} }
} }
#[cfg(feature = "async-mio")]
use utils::Controler;
#[test] #[test]
#[cfg(feature = "async-mio")] #[cfg(feature = "async-mio")]
fn basic_async_communication() -> std::io::Result<()> { fn basic_async_communication() -> ClientResult<()> {
const COMMUNICATION: [(&str, &str); 1] = [( const COMMUNICATION: [(&str, &str); 5] = [
"SET self CLIENT_NAME test:test:main\r\n", (
"208 OK CLIENT NAME SET\r\n", "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"),
];
let socket_path = Server::temporary_path(); let get_requests = vec![Request::GetOutputModule, Request::GetRate];
let get_answers = vec![Answer::Str("espeak"), Answer::Int(10)];
let mut state = State::new(get_requests.iter(), get_answers.iter());
let socket_dir = tempfile::tempdir()?;
let socket_path = socket_dir.path().join("basic_async_communication.socket");
assert!(!socket_path.exists()); assert!(!socket_path.exists());
let server_path = socket_path.clone(); let handle = Server::run(&socket_path, &COMMUNICATION);
let result = std::panic::catch_unwind(move || -> std::io::Result<u16> { let mut poll = Poll::new()?;
let handle = Server::run(&server_path, &COMMUNICATION); let mut events = Events::with_capacity(128);
let mut poll = Poll::new()?; let mut client = AsyncClient::new(fifo::Builder::new().path(&socket_path).build()?);
let mut events = Events::with_capacity(128); let input_token = Token(0);
let mut client = fifo::Builder::new().path(&server_path).build().unwrap(); let output_token = Token(1);
let input_token = Token(0); let timeout = Duration::new(0, 500 * 1000 * 1000 /* 500 ms */);
let output_token = Token(1); client.register(&poll, input_token, output_token).unwrap();
client.register(&poll, input_token, output_token).unwrap(); client.push(Request::SetName(ClientName::new("test", "test")));
let mut controler = Controler::new(); while !state.terminated() {
while controler.step() < 2 { if !state.writable || !client.has_next() {
poll.poll(&mut events, None)?; poll.poll(&mut events, Some(timeout))?;
for event in &events { }
if event.token() == output_token && event.is_writable() && controler.step() == 0 { state.countdown -= 1;
controler.check_result(client.set_client_name(ClientName::new("test", "test"))); for event in &events {
} else if event.token() == input_token let token = event.token();
&& event.is_readable() if token == input_token {
&& controler.step() == 1 match dbg!(client.receive_next()?) {
{ Response::ClientNameSet => {
controler.check_result(client.check_client_name_set()); client.push(Request::SetLanguage(ClientScope::Current, "en".to_string()))
}
Response::LanguageSet => client.push(Request::Stop(MessageScope::Last)),
Response::Stopped => state.start_get = true,
Response::GetString(val) => state.assert_string(&val),
Response::GetInteger(val) => state.assert_integer(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;
} }
} }
handle.join().unwrap().unwrap(); if state.must_send() {
Ok(controler.step()) match client.send_next() {
}); Ok(()) => (),
std::fs::remove_file(socket_path)?; Err(ClientError::NotReady) => state.writable = false,
assert_eq!(2, result.unwrap().unwrap()); err => return err,
}
}
}
handle.join().unwrap().unwrap();
assert!(state.countdown > 0);
socket_dir.close()?;
Ok(()) Ok(())
} }

@ -24,30 +24,25 @@ use server::Server;
fn test_client<F>( fn test_client<F>(
communication: &'static [(&'static str, &'static str)], communication: &'static [(&'static str, &'static str)],
process: F, process: F,
) -> io::Result<()> ) -> ClientResult<()>
where where
F: FnMut(&mut Client<UnixStream>) -> io::Result<()>, F: FnMut(&mut Client<UnixStream>) -> io::Result<()>,
{ {
let socket_path = Server::temporary_path(); let socket_dir = tempfile::tempdir()?;
let socket_path = socket_dir.path().join("test_client.socket");
assert!(!socket_path.exists()); assert!(!socket_path.exists());
let server_path = socket_path.clone(); let server_path = socket_path.clone();
let mut process_wrapper = std::panic::AssertUnwindSafe(process); let mut process_wrapper = std::panic::AssertUnwindSafe(process);
let result = std::panic::catch_unwind(move || { let handle = Server::run(&server_path, communication);
let handle = Server::run(&server_path, communication); let mut client = ssip_client::fifo::Builder::new()
let mut client = ssip_client::fifo::Builder::new() .path(&server_path)
.path(&server_path) .build()?;
.build() client
.unwrap(); .set_client_name(ClientName::new("test", "test"))?
client .check_client_name_set()?;
.set_client_name(ClientName::new("test", "test")) process_wrapper(&mut client)?;
.unwrap() handle.join().unwrap().unwrap();
.check_client_name_set() socket_dir.close()?;
.unwrap();
process_wrapper(&mut client).unwrap();
handle.join().unwrap()
});
std::fs::remove_file(socket_path)?;
result.unwrap().unwrap();
Ok(()) Ok(())
} }
@ -59,7 +54,7 @@ const SET_CLIENT_COMMUNICATION: (&str, &str) = (
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn connect_and_quit() -> io::Result<()> { fn connect_and_quit() -> ClientResult<()> {
test_client( test_client(
&[ &[
SET_CLIENT_COMMUNICATION, SET_CLIENT_COMMUNICATION,
@ -74,7 +69,7 @@ fn connect_and_quit() -> io::Result<()> {
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn say_one_line() -> io::Result<()> { fn say_one_line() -> ClientResult<()> {
test_client( test_client(
&[ &[
SET_CLIENT_COMMUNICATION, SET_CLIENT_COMMUNICATION,
@ -106,7 +101,7 @@ macro_rules! test_setter {
($setter:ident, $question:expr, $answer:expr, $code:expr, $($arg:tt)*) => { ($setter:ident, $question:expr, $answer:expr, $code:expr, $($arg:tt)*) => {
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn $setter() -> io::Result<()> { fn $setter() -> ClientResult<()> {
test_client( test_client(
&[SET_CLIENT_COMMUNICATION, ($question, $answer)], &[SET_CLIENT_COMMUNICATION, ($question, $answer)],
|client| { |client| {
@ -122,7 +117,7 @@ macro_rules! test_getter {
($getter:ident, $receive:ident, $arg:tt, $question:expr, $answer:expr, $value:expr) => { ($getter:ident, $receive:ident, $arg:tt, $question:expr, $answer:expr, $value:expr) => {
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn $getter() -> io::Result<()> { fn $getter() -> ClientResult<()> {
test_client( test_client(
&[SET_CLIENT_COMMUNICATION, ($question, $answer)], &[SET_CLIENT_COMMUNICATION, ($question, $answer)],
|client| { |client| {
@ -142,7 +137,7 @@ macro_rules! test_list {
($getter:ident, $question:expr, $answer:expr, $code:expr, $values:expr) => { ($getter:ident, $question:expr, $answer:expr, $code:expr, $values:expr) => {
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn $getter() -> io::Result<()> { fn $getter() -> ClientResult<()> {
test_client( test_client(
&[SET_CLIENT_COMMUNICATION, ($question, $answer)], &[SET_CLIENT_COMMUNICATION, ($question, $answer)],
|client| { |client| {
@ -165,7 +160,7 @@ test_setter!(
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn set_debug() -> io::Result<()> { fn set_debug() -> ClientResult<()> {
test_client( test_client(
&[ &[
SET_CLIENT_COMMUNICATION, SET_CLIENT_COMMUNICATION,
@ -341,7 +336,7 @@ test_list!(
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn list_synthesis_voices() -> io::Result<()> { fn list_synthesis_voices() -> ClientResult<()> {
test_client( test_client(
&[ &[
SET_CLIENT_COMMUNICATION, SET_CLIENT_COMMUNICATION,
@ -367,7 +362,7 @@ fn list_synthesis_voices() -> io::Result<()> {
#[test] #[test]
#[cfg(not(feature = "async-mio"))] #[cfg(not(feature = "async-mio"))]
fn receive_notification() -> io::Result<()> { fn receive_notification() -> ClientResult<()> {
test_client( test_client(
&[ &[
SET_CLIENT_COMMUNICATION, SET_CLIENT_COMMUNICATION,

@ -7,7 +7,7 @@
// modified, or distributed except according to those terms. // modified, or distributed except according to those terms.
use std::io::{self, BufRead, BufReader, BufWriter, Write}; use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::{Path, PathBuf}; use std::path::Path;
use std::thread; use std::thread;
use std::os::unix::net::UnixListener; use std::os::unix::net::UnixListener;
@ -24,13 +24,13 @@ impl Server {
/// Argument `communication` is an array of pairs. The first item is a list of strings /// Argument `communication` is an array of pairs. The first item is a list of strings
/// the server will receive and the second item is the answer. /// the server will receive and the second item is the answer.
pub fn new<P>( pub fn new<P>(
socket_path: &P, socket_path: P,
communication: &[(&'static str, &'static str)], communication: &[(&'static str, &'static str)],
) -> io::Result<Self> ) -> io::Result<Self>
where where
P: AsRef<Path>, P: AsRef<Path>,
{ {
let listener = UnixListener::bind(socket_path)?; let listener = UnixListener::bind(socket_path.as_ref())?;
Ok(Server { Ok(Server {
listener, listener,
communication: communication.to_vec(), communication: communication.to_vec(),
@ -56,7 +56,7 @@ impl Server {
if line != *question { if line != *question {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,
format!("read <{}> instead of <{}>", dbg!(line), *question), format!("read <{}> instead of <{}>", line, *question),
)); ));
} }
} }
@ -66,11 +66,6 @@ impl Server {
Ok(()) Ok(())
} }
pub fn temporary_path() -> PathBuf {
let tid = unsafe { libc::pthread_self() } as u64;
std::env::temp_dir().join(format!("ssip-client-test-{}-{}", std::process::id(), tid))
}
pub fn run<P>( pub fn run<P>(
socket_path: P, socket_path: P,
communication: &'static [(&'static str, &'static str)], communication: &'static [(&'static str, &'static str)],

Loading…
Cancel
Save