Compare commits

...

1 commit

Author SHA1 Message Date
c9a0745c81
WIP Scenario
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2022-09-26 14:09:57 +02:00
5 changed files with 168 additions and 18 deletions

View file

@ -8,6 +8,7 @@ description = "MUC implementation allowing participants to play the Hanabi game.
[dependencies]
async-trait = "^0.1"
chrono = "0.4.22"
dyn-clone = "1.0.9"
env_logger = "^0.9"
futures = "^0.3"
lazy_static = "^1.4"

View file

@ -19,7 +19,10 @@ use crate::error::Error;
use std::collections::VecDeque;
#[cfg(test)]
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::pin::Pin;
use std::task::Context;
@ -27,6 +30,8 @@ use std::task::Context;
use std::thread;
use async_trait::async_trait;
#[cfg(test)]
use dyn_clone::{clone_trait_object, DynClone};
use futures::{task::Poll, Stream};
use log::debug;
use tokio_xmpp::Component as TokioXMPPComponent;
@ -92,16 +97,47 @@ impl Component {
}
}
/*
#[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 {
/// Simple Element
Element(TestElement),
/// Callback taking an Iq, with a description alongside
Iq(Box<dyn FnOnce(Iq) + Send + 'static>, String),
/// Callback taking a Presence, with a description alongside
Presence(Box<dyn FnOnce(Presence) + Send + 'static>, String),
Iq(Box<dyn ExpectIq>, String),
/// 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)]
@ -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)]
#[derive(Clone, Eq, PartialEq)]
pub struct TestElement(pub Element);
@ -197,12 +290,7 @@ pub struct TestComponent {
impl TestComponent {
pub fn new(in_buffer: Vec<Element>) -> Self {
TestComponent {
in_buffer: VecDeque::from(
in_buffer
.into_iter()
.map(|el| TestElement(el))
.collect::<Vec<_>>(),
),
in_buffer: VecDeque::from(in_buffer.into_iter().map(TestElement).collect::<Vec<_>>()),
expect_buffer: VecDeque::new(),
}
}
@ -212,16 +300,12 @@ impl TestComponent {
self.expect_buffer.push_back(Expect::Element(el.into()))
}
pub fn expect_iq<F: FnOnce(Iq) + Send + 'static, S: Into<String>>(
&mut self,
callback: F,
desc: S,
) {
pub fn expect_iq<F: ExpectIq, S: Into<String>>(&mut self, callback: F, desc: S) {
self.expect_buffer
.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,
callback: F,
desc: S,
@ -230,7 +314,7 @@ impl TestComponent {
.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,
callback: F,
desc: S,
@ -239,6 +323,12 @@ impl TestComponent {
.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> {
let out: TestElement = el.into();
let expected = self.expect_buffer.pop_front();

View file

@ -14,6 +14,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#![feature(let_chains)]
#![feature(once_cell)]
// #![feature(trait_alias)]
// Maybe change that someday?
#![allow(clippy::result_large_err)]
@ -22,6 +24,8 @@ mod error;
mod handlers;
mod room;
#[cfg(test)]
mod scenarios;
#[cfg(test)]
mod tests;

50
src/scenarios.rs Normal file
View 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();
}

View file

@ -16,9 +16,11 @@
use crate::component::TestComponent;
use crate::handlers::handle_stanza;
use crate::room::Room;
use crate::scenarios::CREATE_ROOM_1;
use std::collections::{BTreeMap, HashMap};
use std::str::FromStr;
use std::sync::LazyLock;
use lazy_static::lazy_static;
use xmpp_parsers::{
@ -56,6 +58,9 @@ async fn test_join_presence_empty_room() {
let mut component = TestComponent::new(vec![join]);
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:
// - No presence sent except for self-presence
// - No message history