1
0
Fork 0
mirror of https://codeberg.org/demostf/sync.git synced 2026-06-03 16:44:07 +02:00

add tests

This commit is contained in:
Robin Appelman 2019-06-14 15:50:05 +02:00
commit 474c2beab8
4 changed files with 460 additions and 32 deletions

94
src/client.rs Normal file
View file

@ -0,0 +1,94 @@
use crate::SyncCommand;
use enum_dispatch::enum_dispatch;
use mio::Token;
use std::cell::RefCell;
use std::rc::Rc;
use ws::{Result, Sender};
#[enum_dispatch(Client)]
pub(crate) trait ClientTrait {
fn send(&self, msg: &str) -> Result<()>;
fn token(&self) -> Token;
}
#[derive(PartialEq, Clone, Debug)]
pub(crate) struct SenderClient(Sender);
impl ClientTrait for SenderClient {
fn send(&self, msg: &str) -> Result<()> {
self.0.send(msg)
}
fn token(&self) -> Token {
self.0.token()
}
}
impl From<Sender> for SenderClient {
fn from(sender: Sender) -> Self {
SenderClient(sender)
}
}
#[derive(Clone, Debug)]
pub(crate) struct MockClient {
received: Rc<RefCell<Vec<String>>>,
token: Token,
}
impl PartialEq for MockClient {
fn eq(&self, other: &MockClient) -> bool {
self.token == other.token
}
}
impl ClientTrait for MockClient {
fn send(&self, msg: &str) -> Result<()> {
self.received.borrow_mut().push(msg.into());
Ok(())
}
fn token(&self) -> Token {
self.token
}
}
impl MockClient {
pub fn new(token: usize) -> Self {
MockClient {
received: Rc::new(RefCell::new(Vec::new())),
token: Token(token),
}
}
pub fn received(&self) -> Vec<SyncCommand> {
RefCell::borrow(&self.received)
.iter()
.map(|msg| serde_json::from_str::<SyncCommand>(msg).expect("invalid message"))
.collect()
}
pub fn clear(&self) {
self.received.borrow_mut().clear()
}
}
#[enum_dispatch]
#[derive(PartialEq, Clone, Debug)]
pub(crate) enum Client {
Sender(SenderClient),
Mock(MockClient),
}
impl Client {
pub fn mock(token: usize) -> Self {
Client::Mock(MockClient::new(token))
}
}
impl From<Sender> for Client {
fn from(sender: Sender) -> Self {
Client::Sender(sender.into())
}
}

View file

@ -1,11 +1,16 @@
use mio::Token;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use ws::{listen, CloseCode, Error, Handler, Message, Result, Sender};
use ws::{listen, CloseCode, Error, Handler, Message, Result};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
mod client;
use client::{Client, ClientTrait};
use std::cell::RefCell;
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(tag = "type")]
#[serde(rename_all = "lowercase")]
enum SyncCommand {
@ -15,6 +20,7 @@ enum SyncCommand {
Play { session: String, play: bool },
}
#[derive(PartialEq, Debug)]
struct Session {
owner: Token,
clients: HashMap<Token, Client>,
@ -46,31 +52,6 @@ impl Session {
}
}
trait ClientTrait {
fn send(&self, msg: &str) -> Result<()>;
fn token(&self) -> Token;
}
#[derive(Clone, Debug)]
struct Client(Sender);
impl ClientTrait for Client {
fn send(&self, msg: &str) -> Result<()> {
self.0.send(msg)
}
fn token(&self) -> Token {
self.0.token()
}
}
impl From<Sender> for Client {
fn from(sender: Sender) -> Self {
Client(sender)
}
}
struct Server {
out: Client,
sessions: Rc<RefCell<HashMap<String, Session>>>,
@ -190,6 +171,7 @@ fn main() {
#[cfg(test)]
mod tests {
use super::*;
use maplit::hashmap;
#[test]
fn test_deserialize() {
@ -201,4 +183,326 @@ mod tests {
serde_json::from_str(input).unwrap()
);
}
#[test]
fn test_create() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(HashMap::new());
let sender = Client::mock(1);
let command = SyncCommand::Create {
session: "test".into(),
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
},
sessions.into_inner()
);
}
#[test]
fn test_play_owner() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
});
let sender = Client::mock(1);
let command = SyncCommand::Play {
session: "test".into(),
play: true,
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: true
}
},
sessions.into_inner()
);
}
#[test]
fn test_play_not_owner() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
});
let sender = Client::mock(2);
let command = SyncCommand::Play {
session: "test".into(),
play: true,
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
},
sessions.into_inner()
);
}
#[test]
fn test_tick_owner() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
});
let sender = Client::mock(1);
let command = SyncCommand::Tick {
session: "test".into(),
tick: 99,
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 99,
playing: false
}
},
sessions.into_inner()
);
}
#[test]
fn test_tick_not_owner() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
});
let sender = Client::mock(2);
let command = SyncCommand::Tick {
session: "test".into(),
tick: 99,
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
},
sessions.into_inner()
);
}
#[test]
fn test_join() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 99,
playing: true
}
});
let sender = Client::mock(2);
let command = SyncCommand::Join {
session: "test".into(),
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![Token(2) => sender.clone()],
tick: 99,
playing: true
}
},
sessions.into_inner()
);
if let Client::Mock(mock) = sender {
assert_eq!(
vec![
SyncCommand::Tick {
session: "test".into(),
tick: 99
},
SyncCommand::Play {
session: "test".into(),
play: true
}
],
mock.received()
);
};
}
#[test]
fn test_join_non_existing() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
});
let sender = Client::mock(2);
let command = SyncCommand::Join {
session: "test2".into(),
};
handle_command(command, &sender, &sessions);
assert_eq!(
hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 0,
playing: false
}
},
sessions.into_inner()
);
}
#[test]
fn test_forward() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 99,
playing: true
},
"test2".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 99,
playing: true
}
});
let owner = Client::mock(1);
let sender1 = Client::mock(2);
let sender2 = Client::mock(3);
let command = SyncCommand::Join {
session: "test".into(),
};
handle_command(command, &sender1, &sessions);
let command = SyncCommand::Join {
session: "test2".into(),
};
handle_command(command, &sender2, &sessions);
if let Client::Mock(mock) = &sender1 {
mock.clear();
}
if let Client::Mock(mock) = &sender2 {
mock.clear();
}
let command = SyncCommand::Tick {
session: "test".into(),
tick: 999,
};
handle_command(command, &owner, &sessions);
if let Client::Mock(mock) = sender1 {
assert_eq!(
vec![SyncCommand::Tick {
session: "test".into(),
tick: 999
},],
mock.received()
);
};
if let Client::Mock(mock) = sender2 {
assert_eq!(0, mock.received().len());
};
}
#[test]
fn test_forward_non_owner() {
let sessions: RefCell<HashMap<String, Session>> = RefCell::new(hashmap! {
"test".into() => Session {
owner: Token(1),
clients: hashmap![],
tick: 99,
playing: true
}
});
let sender1 = Client::mock(2);
let sender2 = Client::mock(3);
let command = SyncCommand::Join {
session: "test".into(),
};
handle_command(command.clone(), &sender1, &sessions);
handle_command(command.clone(), &sender2, &sessions);
if let Client::Mock(mock) = &sender1 {
mock.clear();
}
if let Client::Mock(mock) = &sender2 {
mock.clear();
}
let command = SyncCommand::Tick {
session: "test".into(),
tick: 999,
};
handle_command(command, &sender1, &sessions);
if let Client::Mock(mock) = sender1 {
assert_eq!(0, mock.received().len());
};
if let Client::Mock(mock) = sender2 {
assert_eq!(0, mock.received().len());
};
}
}