Fixed unit tests for async changes.

This commit is contained in:
Aaron Weiss 2017-06-21 21:50:38 -04:00
parent 77d44a5055
commit 86e224b8aa
No known key found for this signature in database
GPG key ID: 0237035D9BF03AE2
6 changed files with 404 additions and 175 deletions

View file

@ -29,5 +29,5 @@ serde_derive = "1.0"
serde_json = "1.0"
tokio-core = "0.1"
tokio-io = "0.1"
tokio-mockstream = "1.0"
tokio-mockstream = "1.1"
tokio-tls = "0.1"

View file

@ -1,14 +1,17 @@
//! A module providing IRC connections for use by `IrcServer`s.
use std::fmt;
use std::{fmt, io};
use error;
use client::data::Config;
use client::transport::IrcTransport;
use client::transport::{IrcTransport, LogView, Logged};
use proto::{IrcCodec, Message};
use encoding::{EncoderTrap};
use encoding::label::encoding_from_whatwg_label;
use futures::{Async, Poll, Future, Sink, StartSend, Stream};
use native_tls::TlsConnector;
use tokio_core::reactor::Handle;
use tokio_core::net::{TcpStream, TcpStreamNew};
use tokio_io::AsyncRead;
use tokio_mockstream::MockStream;
use tokio_tls::{TlsConnectorExt, TlsStream};
/// An IRC connection used internally by `IrcServer`.
@ -17,6 +20,8 @@ pub enum Connection {
Unsecured(IrcTransport<TcpStream>),
#[doc(hidden)]
Secured(IrcTransport<TlsStream<TcpStream>>),
#[doc(hidden)]
Mock(Logged<MockStream>),
}
impl fmt::Debug for Connection {
@ -27,6 +32,7 @@ impl fmt::Debug for Connection {
match *self {
Connection::Unsecured(_) => "Connection::Unsecured(...)",
Connection::Secured(_) => "Connection::Secured(...)",
Connection::Mock(_) => "Connection::Mock(...)",
}
)
}
@ -41,6 +47,8 @@ pub enum ConnectionFuture<'a> {
Unsecured(&'a Config, TcpStreamNew),
#[doc(hidden)]
Secured(&'a Config, TlsFuture),
#[doc(hidden)]
Mock(&'a Config),
}
impl<'a> Future for ConnectionFuture<'a> {
@ -61,6 +69,29 @@ impl<'a> Future for ConnectionFuture<'a> {
Ok(Async::Ready(Connection::Secured(transport)))
}
&mut ConnectionFuture::Mock(ref config) => {
let enc: error::Result<_> = encoding_from_whatwg_label(config.encoding()).ok_or(
io::Error::new(
io::ErrorKind::InvalidInput,
&format!("Attempted to use unknown codec {}.", config.encoding())[..],
).into(),
);
let encoding = enc?;
let init_str = config.mock_initial_value();
let initial: error::Result<_> = {
encoding.encode(&init_str, EncoderTrap::Replace).map_err(|data| {
io::Error::new(
io::ErrorKind::InvalidInput,
&format!("Failed to encode {} as {}.", data, encoding.name())[..],
).into()
})
};
let framed = MockStream::new(&initial?).framed(IrcCodec::new(config.encoding())?);
let transport = IrcTransport::new(config, framed);
Ok(Async::Ready(Connection::Mock(Logged::wrap(transport))))
}
}
}
}
@ -68,7 +99,9 @@ impl<'a> Future for ConnectionFuture<'a> {
impl Connection {
/// Creates a new `Connection` using the specified `Config` and `Handle`.
pub fn new<'a>(config: &'a Config, handle: &Handle) -> error::Result<ConnectionFuture<'a>> {
if config.use_ssl() {
if config.use_mock_connection() {
Ok(ConnectionFuture::Mock(config))
} else if config.use_ssl() {
let domain = format!("{}:{}", config.server(), config.port());
let connector = TlsConnector::builder()?.build()?;
let stream = TcpStream::connect(&config.socket_addr(), handle)
@ -90,6 +123,15 @@ impl Connection {
))
}
}
/// Gets a view of the internal logging if and only if this connection is using a mock stream.
/// Otherwise, this will always return `None`. This is used for unit testing.
pub fn log_view(&self) -> Option<LogView> {
match self {
&Connection::Mock(ref inner) => Some(inner.view()),
_ => None
}
}
}
impl Stream for Connection {
@ -100,6 +142,7 @@ impl Stream for Connection {
match self {
&mut Connection::Unsecured(ref mut inner) => inner.poll(),
&mut Connection::Secured(ref mut inner) => inner.poll(),
&mut Connection::Mock(ref mut inner) => inner.poll(),
}
}
}
@ -112,6 +155,7 @@ impl Sink for Connection {
match self {
&mut Connection::Unsecured(ref mut inner) => inner.start_send(item),
&mut Connection::Secured(ref mut inner) => inner.start_send(item),
&mut Connection::Mock(ref mut inner) => inner.start_send(item),
}
}
@ -119,6 +163,7 @@ impl Sink for Connection {
match self {
&mut Connection::Unsecured(ref mut inner) => inner.poll_complete(),
&mut Connection::Secured(ref mut inner) => inner.poll_complete(),
&mut Connection::Mock(ref mut inner) => inner.poll_complete(),
}
}
}

View file

@ -8,8 +8,8 @@ use client::conn::Connection;
use client::data::{Command, Config, Message, Response, User};
use client::data::Command::{JOIN, NICK, NICKSERV, PART, PRIVMSG, MODE, QUIT};
use client::server::utils::ServerExt;
use client::transport::LogView;
use futures::{Async, Poll, Future, Sink, Stream};
use futures::future;
use futures::stream::SplitStream;
use futures::sync::mpsc;
use futures::sync::oneshot;
@ -427,6 +427,8 @@ impl ServerState {
pub struct IrcServer {
/// The internal, thread-safe server state.
state: Arc<ServerState>,
/// A view of the logs for a mock connection.
view: Option<LogView>,
}
impl Server for IrcServer {
@ -492,9 +494,10 @@ impl IrcServer {
/// Creates a new IRC server connection from the specified configuration, connecting
/// immediately.
pub fn from_config(config: Config) -> error::Result<IrcServer> {
// Setting up a remote reactor running forever.
// Setting up a remote reactor running for the length of the connection.
let (tx_outgoing, rx_outgoing) = mpsc::unbounded();
let (tx_incoming, rx_incoming) = oneshot::channel();
let (tx_view, rx_view) = oneshot::channel();
let cfg = config.clone();
let _ = thread::spawn(move || {
@ -502,39 +505,49 @@ impl IrcServer {
// Setting up internal processing stuffs.
let handle = reactor.handle();
let (sink, stream) = reactor
let conn = reactor
.run(Connection::new(&cfg, &handle).unwrap())
.unwrap()
.split();
.unwrap();
tx_view.send(conn.log_view()).unwrap();
let (sink, stream) = conn.split();
let outgoing_future = sink.send_all(rx_outgoing.map_err(|_| {
let res: error::Error = error::ErrorKind::ChannelError.into();
res
}));
handle.spawn(outgoing_future.map(|_| ()).map_err(|_| ()));
})).map(|_| ()).map_err(|_| ());
// Send the stream half back to the original thread.
tx_incoming.send(stream).unwrap();
reactor.run(future::empty::<(), ()>()).unwrap();
reactor.run(outgoing_future).unwrap();
});
Ok(IrcServer {
state: Arc::new(ServerState::new(rx_incoming.wait()?, tx_outgoing, config)),
view: rx_view.wait()?,
})
}
/// Gets the log view from the internal transport. Only used for unit testing.
#[cfg(test)]
fn log_view(&self) -> &LogView {
self.view.as_ref().unwrap()
}
}
#[cfg(test)]
mod test {
use super::{IrcServer, Server};
use std::thread;
use std::time::Duration;
use std::collections::HashMap;
use std::default::Default;
use client::conn::MockConnection;
use client::data::Config;
#[cfg(not(feature = "nochanlists"))]
use client::data::User;
use proto::command::Command::{PART, PRIVMSG};
use futures::{Future, Stream};
pub fn test_config() -> Config {
Config {
@ -544,53 +557,67 @@ mod test {
server: Some(format!("irc.test.net")),
channels: Some(vec![format!("#test"), format!("#test2")]),
user_info: Some(format!("Testing.")),
use_mock_connection: Some(true),
..Default::default()
}
}
pub fn get_server_value(server: IrcServer) -> String {
server.conn().written(server.config().encoding()).unwrap()
// We sleep here because of synchronization issues.
// We can't guarantee that everything will have been sent by the time of this call.
thread::sleep(Duration::from_millis(100));
server.log_view().sent().unwrap().iter().fold(String::new(), |mut acc, msg| {
acc.push_str(&msg.to_string());
acc
})
}
#[test]
fn iterator() {
fn stream() {
let exp = "PRIVMSG test :Hi!\r\nPRIVMSG test :This is a test!\r\n\
:test!test@test JOIN #test\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(exp));
let server = IrcServer::from_config(Config {
mock_initial_value: Some(exp.to_owned()),
..test_config()
}).unwrap();
let mut messages = String::new();
for message in server.iter() {
messages.push_str(&message.unwrap().to_string());
}
server.stream().for_each(|message| {
messages.push_str(&message.to_string());
Ok(())
}).wait().unwrap();
assert_eq!(&messages[..], exp);
}
#[test]
fn handle_message() {
let value = "PING :irc.test.net\r\n:irc.test.net 376 test :End of /MOTD command.\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"PONG :irc.test.net\r\nJOIN #test\r\nJOIN #test2\r\n"
"JOIN #test\r\nJOIN #test2\r\n"
);
}
#[test]
fn handle_end_motd_with_nick_password() {
let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
let server = IrcServer::from_connection(
Config {
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
..Default::default()
},
MockConnection::new(value),
);
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NICKSERV IDENTIFY password\r\nJOIN #test\r\n\
@ -601,22 +628,21 @@ mod test {
#[test]
fn handle_end_motd_with_chan_keys() {
let value = ":irc.test.net 376 test :End of /MOTD command\r\n";
let server = IrcServer::from_connection(
Config {
nickname: Some(format!("test")),
channels: Some(vec![format!("#test"), format!("#test2")]),
channel_keys: {
let mut map = HashMap::new();
map.insert(format!("#test2"), format!("password"));
Some(map)
},
..Default::default()
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
nickname: Some(format!("test")),
channels: Some(vec![format!("#test"), format!("#test2")]),
channel_keys: {
let mut map = HashMap::new();
map.insert(format!("#test2"), format!("password"));
Some(map)
},
MockConnection::new(value),
);
for message in server.iter() {
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"JOIN #test\r\nJOIN #test2 password\r\n"
@ -627,20 +653,19 @@ mod test {
fn handle_end_motd_with_ghost() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.\r\n\
:irc.test.net 376 test2 :End of /MOTD command.\r\n";
let server = IrcServer::from_connection(
Config {
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
should_ghost: Some(true),
..Default::default()
},
MockConnection::new(value),
);
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
should_ghost: Some(true),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NICK :test2\r\nNICKSERV GHOST test password\r\n\
@ -652,21 +677,20 @@ mod test {
fn handle_end_motd_with_ghost_seq() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.\r\n\
:irc.test.net 376 test2 :End of /MOTD command.\r\n";
let server = IrcServer::from_connection(
Config {
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
should_ghost: Some(true),
ghost_sequence: Some(vec![format!("RECOVER"), format!("RELEASE")]),
..Default::default()
},
MockConnection::new(value),
);
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
nick_password: Some(format!("password")),
channels: Some(vec![format!("#test"), format!("#test2")]),
should_ghost: Some(true),
ghost_sequence: Some(vec![format!("RECOVER"), format!("RELEASE")]),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NICK :test2\r\nNICKSERV RECOVER test password\
@ -678,18 +702,17 @@ mod test {
#[test]
fn handle_end_motd_with_umodes() {
let value = ":irc.test.net 376 test :End of /MOTD command.\r\n";
let server = IrcServer::from_connection(
Config {
nickname: Some(format!("test")),
umodes: Some(format!("+B")),
channels: Some(vec![format!("#test"), format!("#test2")]),
..Default::default()
},
MockConnection::new(value),
);
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
nickname: Some(format!("test")),
umodes: Some(format!("+B")),
channels: Some(vec![format!("#test"), format!("#test2")]),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"MODE test +B\r\nJOIN #test\r\nJOIN #test2\r\n"
@ -698,11 +721,15 @@ mod test {
#[test]
fn nickname_in_use() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.\r\n";
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(&get_server_value(server)[..], "NICK :test2\r\n");
}
@ -711,15 +738,19 @@ mod test {
fn ran_out_of_nicknames() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.\r\n\
:irc.pdgn.co 433 * test2 :Nickname is already in use.\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
}
#[test]
fn send() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
assert!(
server
.send(PRIVMSG(format!("#test"), format!("Hi there!")))
@ -733,23 +764,27 @@ mod test {
#[test]
fn send_no_newline_injection() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
assert!(
server
.send(PRIVMSG(format!("#test"), format!("Hi there!\nJOIN #bad")))
.send(PRIVMSG(format!("#test"), format!("Hi there!\r\nJOIN #bad")))
.is_ok()
);
assert_eq!(&get_server_value(server)[..], "PRIVMSG #test :Hi there!\n");
assert_eq!(&get_server_value(server)[..], "PRIVMSG #test :Hi there!\r\n");
}
#[test]
#[cfg(not(feature = "nochanlists"))]
fn channel_tracking_names() {
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(server.list_channels().unwrap(), vec!["#test".to_owned()])
}
@ -757,10 +792,14 @@ mod test {
#[cfg(not(feature = "nochanlists"))]
fn channel_tracking_names_part() {
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert!(server.send(PART(format!("#test"), None)).is_ok());
assert!(server.list_channels().unwrap().is_empty())
}
@ -769,10 +808,14 @@ mod test {
#[cfg(not(feature = "nochanlists"))]
fn user_tracking_names() {
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
server.list_users("#test").unwrap(),
vec![User::new("test"), User::new("~owner"), User::new("&admin")]
@ -784,10 +827,14 @@ mod test {
fn user_tracking_names_join() {
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
:test2!test@test JOIN #test\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
server.list_users("#test").unwrap(),
vec![
@ -804,10 +851,14 @@ mod test {
fn user_tracking_names_part() {
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n\
:owner!test@test PART #test\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
server.list_users("#test").unwrap(),
vec![User::new("test"), User::new("&admin")]
@ -819,10 +870,14 @@ mod test {
fn user_tracking_names_mode() {
let value = ":irc.test.net 353 test = #test :+test ~owner &admin\r\n\
:test!test@test MODE #test +o test\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
server.list_users("#test").unwrap(),
vec![User::new("@test"), User::new("~owner"), User::new("&admin")]
@ -844,10 +899,14 @@ mod test {
#[cfg(feature = "nochanlists")]
fn no_user_tracking() {
let value = ":irc.test.net 353 test = #test :test ~owner &admin";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert!(server.list_users("#test").is_none())
}
@ -855,10 +914,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn finger_response() {
let value = ":test!test@test PRIVMSG test :\u{001}FINGER\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NOTICE test :\u{001}FINGER :test (test)\u{001}\
@ -870,10 +933,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn version_response() {
let value = ":test!test@test PRIVMSG test :\u{001}VERSION\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NOTICE test :\u{001}VERSION irc:git:Rust\u{001}\
@ -885,10 +952,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn source_response() {
let value = ":test!test@test PRIVMSG test :\u{001}SOURCE\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NOTICE test :\u{001}SOURCE https://github.com/aatxe/irc\u{001}\r\n\
@ -900,10 +971,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn ctcp_ping_response() {
let value = ":test!test@test PRIVMSG test :\u{001}PING test\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NOTICE test :\u{001}PING test\u{001}\r\n"
@ -914,10 +989,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn time_response() {
let value = ":test!test@test PRIVMSG test :\u{001}TIME\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
let val = get_server_value(server);
assert!(val.starts_with("NOTICE test :\u{001}TIME :"));
assert!(val.ends_with("\u{001}\r\n"));
@ -927,10 +1006,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn user_info_response() {
let value = ":test!test@test PRIVMSG test :\u{001}USERINFO\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(
&get_server_value(server)[..],
"NOTICE test :\u{001}USERINFO :Testing.\u{001}\
@ -942,10 +1025,14 @@ mod test {
#[cfg(feature = "ctcp")]
fn ctcp_ping_no_timestamp() {
let value = ":test!test@test PRIVMSG test :\u{001}PING\u{001}\r\n";
let server = IrcServer::from_connection(test_config(), MockConnection::new(value));
for message in server.iter() {
let server = IrcServer::from_config(Config {
mock_initial_value: Some(value.to_owned()),
..test_config()
}).unwrap();
server.stream().for_each(|message| {
println!("{:?}", message);
}
Ok(())
}).wait().unwrap();
assert_eq!(&get_server_value(server)[..], "");
}
}

View file

@ -354,15 +354,13 @@ where
#[cfg(test)]
mod test {
use super::ServerExt;
use std::default::Default;
use client::conn::MockConnection;
use client::data::Config;
use client::server::IrcServer;
use client::server::test::{get_server_value, test_config};
#[test]
fn identify() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.identify().unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -373,14 +371,11 @@ mod test {
#[test]
fn identify_with_password() {
let server = IrcServer::from_connection(
Config {
nickname: Some(format!("test")),
password: Some(format!("password")),
..Default::default()
},
MockConnection::empty(),
);
let server = IrcServer::from_config(Config {
nickname: Some(format!("test")),
password: Some(format!("password")),
..test_config()
}).unwrap();
server.identify().unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -391,14 +386,14 @@ mod test {
#[test]
fn send_pong() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_pong("irc.test.net").unwrap();
assert_eq!(&get_server_value(server)[..], "PONG :irc.test.net\r\n");
}
#[test]
fn send_join() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_join("#test,#test2,#test3").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -408,21 +403,21 @@ mod test {
#[test]
fn send_part() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_part("#test").unwrap();
assert_eq!(&get_server_value(server)[..], "PART #test\r\n");
}
#[test]
fn send_oper() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_oper("test", "test").unwrap();
assert_eq!(&get_server_value(server)[..], "OPER test :test\r\n");
}
#[test]
fn send_privmsg() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_privmsg("#test", "Hi, everybody!").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -432,7 +427,7 @@ mod test {
#[test]
fn send_notice() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_notice("#test", "Hi, everybody!").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -442,14 +437,14 @@ mod test {
#[test]
fn send_topic_no_topic() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_topic("#test", "").unwrap();
assert_eq!(&get_server_value(server)[..], "TOPIC #test\r\n");
}
#[test]
fn send_topic() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_topic("#test", "Testing stuff.").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -459,7 +454,7 @@ mod test {
#[test]
fn send_kill() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_kill("test", "Testing kills.").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -469,14 +464,14 @@ mod test {
#[test]
fn send_kick_no_message() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_kick("#test", "test", "").unwrap();
assert_eq!(&get_server_value(server)[..], "KICK #test test\r\n");
}
#[test]
fn send_kick() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_kick("#test", "test", "Testing kicks.").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -486,42 +481,42 @@ mod test {
#[test]
fn send_mode_no_modeparams() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_mode("#test", "+i", "").unwrap();
assert_eq!(&get_server_value(server)[..], "MODE #test +i\r\n");
}
#[test]
fn send_mode() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_mode("#test", "+o", "test").unwrap();
assert_eq!(&get_server_value(server)[..], "MODE #test +o test\r\n");
}
#[test]
fn send_samode_no_modeparams() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_samode("#test", "+i", "").unwrap();
assert_eq!(&get_server_value(server)[..], "SAMODE #test +i\r\n");
}
#[test]
fn send_samode() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_samode("#test", "+o", "test").unwrap();
assert_eq!(&get_server_value(server)[..], "SAMODE #test +o test\r\n");
}
#[test]
fn send_sanick() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_sanick("test", "test2").unwrap();
assert_eq!(&get_server_value(server)[..], "SANICK test test2\r\n");
}
#[test]
fn send_invite() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_invite("test", "#test").unwrap();
assert_eq!(&get_server_value(server)[..], "INVITE test #test\r\n");
}
@ -529,7 +524,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_ctcp() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_ctcp("test", "MESSAGE").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -540,7 +535,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_action() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_action("test", "tests.").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -551,7 +546,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_finger() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_finger("test").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -562,7 +557,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_version() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_version("test").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -573,7 +568,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_source() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_source("test").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -584,7 +579,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_user_info() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_user_info("test").unwrap();
assert_eq!(
&get_server_value(server)[..],
@ -595,7 +590,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_ctcp_ping() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_ctcp_ping("test").unwrap();
let val = get_server_value(server);
println!("{}", val);
@ -606,7 +601,7 @@ mod test {
#[test]
#[cfg(feature = "ctcp")]
fn send_time() {
let server = IrcServer::from_connection(test_config(), MockConnection::empty());
let server = IrcServer::from_config(test_config()).unwrap();
server.send_time("test").unwrap();
assert_eq!(
&get_server_value(server)[..],

View file

@ -1,5 +1,6 @@
//! An IRC transport that wraps an IRC-framed stream to provide automatic PING replies.
use std::io;
use std::sync::{Arc, RwLock, RwLockReadGuard};
use std::time::Instant;
use error;
use client::data::Config;
@ -30,6 +31,11 @@ where
last_ping: Instant::now(),
}
}
/// Gets the inner stream underlying the `IrcTransport`.
pub fn into_inner(self) -> Framed<T, IrcCodec> {
self.inner
}
}
impl<T> Stream for IrcTransport<T>
@ -76,3 +82,93 @@ where
Ok(self.inner.poll_complete()?)
}
}
/// A view of the logs from a particular `Logged` transport.
#[derive(Clone, Debug)]
pub struct LogView {
sent: Arc<RwLock<Vec<Message>>>,
received: Arc<RwLock<Vec<Message>>>,
}
impl LogView {
/// Gets a read guard for all the messages sent on the transport.
pub fn sent(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
self.sent.read().map_err(|_|
error::ErrorKind::PoisonedLog.into()
)
}
/// Gets a read guard for all the messages received on the transport.
pub fn received(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
self.received.read().map_err(|_|
error::ErrorKind::PoisonedLog.into()
)
}
}
/// A logged version of the `IrcTransport` that records all sent and received messages.
/// Note: this will introduce some performance overhead by cloning all messages.
pub struct Logged<T> where T: AsyncRead + AsyncWrite {
inner: IrcTransport<T>,
view: LogView,
}
impl<T> Logged<T> where T: AsyncRead + AsyncWrite {
/// Wraps the given `IrcTransport` in logging.
pub fn wrap(inner: IrcTransport<T>) -> Logged<T> {
Logged {
inner: inner,
view: LogView {
sent: Arc::new(RwLock::new(vec![])),
received: Arc::new(RwLock::new(vec![])),
}
}
}
/// Gets a view of the logging for this transport.
pub fn view(&self) -> LogView {
self.view.clone()
}
}
impl<T> Stream for Logged<T>
where
T: AsyncRead + AsyncWrite,
{
type Item = Message;
type Error = error::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match try_ready!(self.inner.poll()) {
Some(msg) => {
let recv: error::Result<_> = self.view.received.write().map_err(|_|
error::ErrorKind::PoisonedLog.into()
);
recv?.push(msg.clone());
Ok(Async::Ready(Some(msg)))
},
None => Ok(Async::Ready(None))
}
}
}
impl<T> Sink for Logged<T>
where
T: AsyncRead + AsyncWrite,
{
type SinkItem = Message;
type SinkError = error::Error;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
let res = self.inner.start_send(item.clone())?;
let sent: error::Result<_> = self.view.sent.write().map_err(|_|
error::ErrorKind::PoisonedLog.into()
);
sent?.push(item);
Ok(res)
}
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
Ok(self.inner.poll_complete()?)
}
}

View file

@ -35,5 +35,11 @@ error_chain! {
description("An error occured on one of the IrcServer's internal channels.")
display("An error occured on one of the IrcServer's internal channels.")
}
/// An error occured causing a mutex for a logged transport to be poisoned.
PoisonedLog {
description("An error occured causing a mutex for a logged transport to be poisoned.")
display("An error occured causing a mutex for a logged transport to be poisoned.")
}
}
}