Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
c9a0745c81 |
5 changed files with 168 additions and 18 deletions
|
@ -8,6 +8,7 @@ description = "MUC implementation allowing participants to play the Hanabi game.
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "^0.1"
|
async-trait = "^0.1"
|
||||||
chrono = "0.4.22"
|
chrono = "0.4.22"
|
||||||
|
dyn-clone = "1.0.9"
|
||||||
env_logger = "^0.9"
|
env_logger = "^0.9"
|
||||||
futures = "^0.3"
|
futures = "^0.3"
|
||||||
lazy_static = "^1.4"
|
lazy_static = "^1.4"
|
||||||
|
|
126
src/component.rs
126
src/component.rs
|
@ -19,7 +19,10 @@ use crate::error::Error;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::Send;
|
// #[cfg(test)]
|
||||||
|
// use std::iter::IntoIterator;
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::marker::{Send, Sync};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::Context;
|
use std::task::Context;
|
||||||
|
@ -27,6 +30,8 @@ use std::task::Context;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
#[cfg(test)]
|
||||||
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
use futures::{task::Poll, Stream};
|
use futures::{task::Poll, Stream};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use tokio_xmpp::Component as TokioXMPPComponent;
|
use tokio_xmpp::Component as TokioXMPPComponent;
|
||||||
|
@ -92,16 +97,47 @@ impl Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
pub trait ExpectCb<T>: FnOnce(T) + Send + Sync + DynClone + 'static {}
|
||||||
|
#[cfg(test)]
|
||||||
|
clone_trait_object!(ExpectCb<Iq>);
|
||||||
|
#[cfg(test)]
|
||||||
|
clone_trait_object!(ExpectCb<Message>);
|
||||||
|
#[cfg(test)]
|
||||||
|
clone_trait_object!(ExpectCb<Presence>);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl ExpectCb<T> for dyn FnOnce(T) {}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub trait Foo: FnOnce(Iq) + DynClone {}
|
||||||
|
#[cfg(test)]
|
||||||
|
clone_trait_object!(Foo);
|
||||||
|
#[cfg(test)]
|
||||||
|
pub trait Bar: Foo + Send + Sync + 'static {}
|
||||||
|
#[cfg(test)]
|
||||||
|
clone_trait_object!(Bar);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub trait ExpectIq: FnOnce(Iq) + Send + Sync + DynClone + 'static {}
|
||||||
|
#[cfg(test)]
|
||||||
|
impl ExpectIq for dyn FnOnce(Iq) where Self: ExpectIq {}
|
||||||
|
#[cfg(test)]
|
||||||
|
clone_trait_object!(ExpectIq);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[derive(Clone)]
|
||||||
enum Expect {
|
enum Expect {
|
||||||
/// Simple Element
|
/// Simple Element
|
||||||
Element(TestElement),
|
Element(TestElement),
|
||||||
/// Callback taking an Iq, with a description alongside
|
/// Callback taking an Iq, with a description alongside
|
||||||
Iq(Box<dyn FnOnce(Iq) + Send + 'static>, String),
|
Iq(Box<dyn ExpectIq>, String),
|
||||||
/// Callback taking a Presence, with a description alongside
|
|
||||||
Presence(Box<dyn FnOnce(Presence) + Send + 'static>, String),
|
|
||||||
/// Callback taking a Message, with a description alongside
|
/// Callback taking a Message, with a description alongside
|
||||||
Message(Box<dyn FnOnce(Message) + Send + 'static>, String),
|
Message(Box<dyn FnOnce(Message) + Send + Sync + 'static>, String),
|
||||||
|
/// Callback taking a Presence, with a description alongside
|
||||||
|
Presence(Box<dyn FnOnce(Presence) + Send + Sync + 'static>, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -117,6 +153,63 @@ impl fmt::Debug for Expect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Scenario {
|
||||||
|
inbuf: VecDeque<TestElement>,
|
||||||
|
expectbuf: VecDeque<Expect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl Scenario {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Scenario {
|
||||||
|
inbuf: VecDeque::new(),
|
||||||
|
expectbuf: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> (VecDeque<TestElement>, VecDeque<Expect>) {
|
||||||
|
(self.inbuf, self.expectbuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_input<E: Into<TestElement>>(mut self, el: E) -> Self {
|
||||||
|
self.inbuf.push_back(el.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_expect<E: Into<TestElement>>(mut self, el: E) -> Self {
|
||||||
|
self.expectbuf.push_back(Expect::Element(el.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_expect_iq<F: ExpectIq, S: Into<String>>(mut self, callback: F, desc: S) -> Self {
|
||||||
|
self.expectbuf
|
||||||
|
.push_back(Expect::Iq(Box::new(callback), desc.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_expect_message<F: FnOnce(Message) + Send + Sync + 'static, S: Into<String>>(
|
||||||
|
mut self,
|
||||||
|
callback: F,
|
||||||
|
desc: S,
|
||||||
|
) -> Self {
|
||||||
|
self.expectbuf
|
||||||
|
.push_back(Expect::Message(Box::new(callback), desc.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_expect_presence<F: FnOnce(Presence) + Send + Sync + 'static, S: Into<String>>(
|
||||||
|
mut self,
|
||||||
|
callback: F,
|
||||||
|
desc: S,
|
||||||
|
) -> Self {
|
||||||
|
self.expectbuf
|
||||||
|
.push_back(Expect::Presence(Box::new(callback), desc.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub struct TestElement(pub Element);
|
pub struct TestElement(pub Element);
|
||||||
|
@ -197,12 +290,7 @@ pub struct TestComponent {
|
||||||
impl TestComponent {
|
impl TestComponent {
|
||||||
pub fn new(in_buffer: Vec<Element>) -> Self {
|
pub fn new(in_buffer: Vec<Element>) -> Self {
|
||||||
TestComponent {
|
TestComponent {
|
||||||
in_buffer: VecDeque::from(
|
in_buffer: VecDeque::from(in_buffer.into_iter().map(TestElement).collect::<Vec<_>>()),
|
||||||
in_buffer
|
|
||||||
.into_iter()
|
|
||||||
.map(|el| TestElement(el))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
),
|
|
||||||
expect_buffer: VecDeque::new(),
|
expect_buffer: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,16 +300,12 @@ impl TestComponent {
|
||||||
self.expect_buffer.push_back(Expect::Element(el.into()))
|
self.expect_buffer.push_back(Expect::Element(el.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_iq<F: FnOnce(Iq) + Send + 'static, S: Into<String>>(
|
pub fn expect_iq<F: ExpectIq, S: Into<String>>(&mut self, callback: F, desc: S) {
|
||||||
&mut self,
|
|
||||||
callback: F,
|
|
||||||
desc: S,
|
|
||||||
) {
|
|
||||||
self.expect_buffer
|
self.expect_buffer
|
||||||
.push_back(Expect::Iq(Box::new(callback), desc.into()))
|
.push_back(Expect::Iq(Box::new(callback), desc.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_message<F: FnOnce(Message) + Send + 'static, S: Into<String>>(
|
pub fn expect_message<F: FnOnce(Message) + Send + Sync + 'static, S: Into<String>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: F,
|
callback: F,
|
||||||
desc: S,
|
desc: S,
|
||||||
|
@ -230,7 +314,7 @@ impl TestComponent {
|
||||||
.push_back(Expect::Message(Box::new(callback), desc.into()))
|
.push_back(Expect::Message(Box::new(callback), desc.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_presence<F: FnOnce(Presence) + Send + 'static, S: Into<String>>(
|
pub fn expect_presence<F: FnOnce(Presence) + Send + Sync + 'static, S: Into<String>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: F,
|
callback: F,
|
||||||
desc: S,
|
desc: S,
|
||||||
|
@ -239,6 +323,12 @@ impl TestComponent {
|
||||||
.push_back(Expect::Presence(Box::new(callback), desc.into()))
|
.push_back(Expect::Presence(Box::new(callback), desc.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expect_scenario<S: Into<String>>(&mut self, scenario: Scenario, desc: S) {
|
||||||
|
let (input, expected) = scenario.into_inner();
|
||||||
|
self.in_buffer.extend(input);
|
||||||
|
self.expect_buffer.extend(expected);
|
||||||
|
}
|
||||||
|
|
||||||
fn send_stanza_inner<E: Into<TestElement> + Send>(&mut self, el: E) -> Result<(), Error> {
|
fn send_stanza_inner<E: Into<TestElement> + Send>(&mut self, el: E) -> Result<(), Error> {
|
||||||
let out: TestElement = el.into();
|
let out: TestElement = el.into();
|
||||||
let expected = self.expect_buffer.pop_front();
|
let expected = self.expect_buffer.pop_front();
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(once_cell)]
|
||||||
|
// #![feature(trait_alias)]
|
||||||
// Maybe change that someday?
|
// Maybe change that someday?
|
||||||
#![allow(clippy::result_large_err)]
|
#![allow(clippy::result_large_err)]
|
||||||
|
|
||||||
|
@ -22,6 +24,8 @@ mod error;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod room;
|
mod room;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod scenarios;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
|
50
src/scenarios.rs
Normal file
50
src/scenarios.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright (C) 2022-2099 The crate authors.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Affero General Public License as published by the
|
||||||
|
// Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
// option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
||||||
|
// for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::component::Scenario;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use xmpp_parsers::BareJid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
lazy_static! {
|
||||||
|
static ref COMPONENT_JID: BareJid = BareJid::from_str("commons.social").unwrap();
|
||||||
|
static ref ROOM1: BareJid = COMPONENT_JID.with_node("direct-action");
|
||||||
|
|
||||||
|
/// https://en.wikipedia.org/wiki/Louise_Michel
|
||||||
|
static ref LOUISE_BARE: BareJid = BareJid::from_str("louise@example.net").unwrap();
|
||||||
|
static ref LOUISE_NICK: String = String::from("louise");
|
||||||
|
|
||||||
|
/// https://en.wikipedia.org/wiki/Kanno_Sugako
|
||||||
|
static ref SUGAKO_BARE: BareJid = BareJid::from_str("すがこ@example.net").unwrap();
|
||||||
|
static ref SUGAKO_NICK: String = String::from("すがこ");
|
||||||
|
|
||||||
|
/// https://en.wikipedia.org/wiki/Rosa_Luxemburg
|
||||||
|
static ref ROSA_BARE: BareJid = BareJid::from_str("rosa@example.net").unwrap();
|
||||||
|
static ref ROSA_NICK: String = String::from("rosa");
|
||||||
|
|
||||||
|
/// https://en.wikipedia.org/wiki/Peter_Kropotkin
|
||||||
|
static ref PETER_BARE: BareJid = BareJid::from_str("peter@example.net").unwrap();
|
||||||
|
static ref PETER_NICK: String = String::from("peter");
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Creates a room of one participant
|
||||||
|
// pub static CREATE_ROOM_1: LazyLock<Scenario> = LazyLock::new(|| Scenario::new());
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref CREATE_ROOM_1: Scenario = Scenario::new();
|
||||||
|
}
|
|
@ -16,9 +16,11 @@
|
||||||
use crate::component::TestComponent;
|
use crate::component::TestComponent;
|
||||||
use crate::handlers::handle_stanza;
|
use crate::handlers::handle_stanza;
|
||||||
use crate::room::Room;
|
use crate::room::Room;
|
||||||
|
use crate::scenarios::CREATE_ROOM_1;
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use xmpp_parsers::{
|
use xmpp_parsers::{
|
||||||
|
@ -56,6 +58,9 @@ async fn test_join_presence_empty_room() {
|
||||||
let mut component = TestComponent::new(vec![join]);
|
let mut component = TestComponent::new(vec![join]);
|
||||||
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
|
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
|
||||||
|
|
||||||
|
// let create_room = LazyLock::force(&CREATE_ROOM_1);
|
||||||
|
component.expect_scenario(*CREATE_ROOM_1, "Create room of 1 participant");
|
||||||
|
|
||||||
// Room is empty so there should be:
|
// Room is empty so there should be:
|
||||||
// - No presence sent except for self-presence
|
// - No presence sent except for self-presence
|
||||||
// - No message history
|
// - No message history
|
||||||
|
|
Loading…
Reference in a new issue