WIP scansion/test codegen

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2023-10-27 17:57:40 +02:00
parent 4366ae116a
commit e432c8b17e
Signed by: pep
GPG key ID: DEDA74AEECA9D0F2
10 changed files with 380 additions and 17 deletions

View file

@ -22,3 +22,19 @@ jid = { version = "*" }
[dev-dependencies] [dev-dependencies]
syntect = "5.0" syntect = "5.0"
diff = "0.1" diff = "0.1"
scansion = { version = "*" }
[build-dependencies]
jid = { version = "*", features = ["minidom"] }
minidom = { version = "*" }
quote = "1.0"
rand = "0.8"
scansion = { version = "*" }
[patch.crates-io]
sasl = { git = "https://code.bouah.net/pep/xmpp-rs.git" }
jid = { git = "https://code.bouah.net/pep/xmpp-rs.git", branch = "jid-quote" }
minidom = { git = "https://code.bouah.net/pep/xmpp-rs.git" }
xmpp-parsers = { git = "https://code.bouah.net/pep/xmpp-rs.git" }
tokio-xmpp = { git = "https://code.bouah.net/pep/xmpp-rs.git" }
scansion = { git = "https://code.bouah.net/pep/scansion-rs", branch = "quote" }

201
build.rs Normal file
View file

@ -0,0 +1,201 @@
// Copyright (C) 2023-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 std::ffi::OsString;
use std::fmt::Display;
use std::fs::File;
use std::io::{self, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use minidom::Error as MinidomError;
use quote::{format_ident, quote, TokenStreamExt};
use scansion::{read_actions_component, read_spec, Spec};
fn generate_spec_tokens(spec: Spec) -> Result<impl TokenStreamExt + Display, MinidomError> {
println!("FOO0");
let docstr = {
let mut tmp = String::new();
if let Some(ref meta) = spec.metadata {
tmp.push_str(meta.title.as_str());
tmp.push('\n');
if let Some(ref desc) = meta.description {
tmp.push_str(desc.as_str());
tmp.push('\n');
}
for tag in &meta.tags {
tmp.push_str("tag: ");
tmp.push_str(tag.as_str());
tmp.push('\n');
}
} else {
tmp.push_str("No metadata");
}
tmp
};
let context = spec.context.clone();
let actions = read_actions_component(spec, &context).unwrap();
let stanzas_in = actions
.inbound
.into_iter()
.map(|elem| {
// TODO: Prevent having to parse elements again.
// We do need some kind of structure already though to be able to verify the presence
// of attrs and all before the generation.
let elem_str = String::from(&elem);
quote! { ScanElement::new(#elem_str.parse::<Element>().unwrap()).apply_context(&context) }
})
.collect::<Vec<_>>();
let stanzas_out = actions
.outbound
.into_iter()
.map(|elem| {
let elem_str = String::from(&elem);
quote! {
component.expect(
#elem_str.parse::<ScanElement>().unwrap().apply_context(&context)
);
}
})
.collect::<Vec<_>>();
println!("FOO1");
let (context_keys, context_values) = context
.into_iter()
.fold((vec![], vec![]), |(mut keys, mut vals), (k, v)| {
println!("BAR0");
keys.push(quote! { #k });
println!("BAR1");
vals.push(quote! { #v });
println!("BAR2: {:?}; {:?}", keys, vals);
(keys, vals)
});
println!("FOO2");
Ok(quote! {
#![doc = #docstr]
use crate::component::TestComponent;
use crate::handlers::handle_stanza;
use crate::room::Room;
use ::std::collections::HashMap;
use ::xmpp_parsers::{Jid, BareJid, FullJid, Element};
use ::scansion::{ScanElement, Entity, Client};
#[tokio::test]
async fn spec() {
let context: ::scansion::Context = {
let tmp = ::std::collections::HashMap::new();
let (keys, values) = (vec![#(#context_keys),*], vec![#(#context_values),*]);
for (k, v) in keys.iter().zip(values.iter()) {
tmp.insert(String::from(*k), v.clone());
}
tmp
};
let stanzas_in = vec![#(#stanzas_in),*];
let mut component = TestComponent::new_scan(stanzas_in);
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
#(#stanzas_out)*
println!("FOO: {component:?}");
handle_stanza(&mut component, &mut rooms).await.unwrap();
}
})
}
fn generate_mod_tokens(modfiles: Vec<OsString>) -> impl TokenStreamExt + Display {
let modfiles: Vec<_> = modfiles
.into_iter()
.map(|s| format_ident!("{}", s.into_string().unwrap().strip_suffix(".rs").unwrap()))
.collect();
quote! {
/// Scansion tests module.
/// These tests are generated by the build script, DO NOT EDIT.
#(#[cfg(test)] mod #modfiles;)*
}
}
fn read_input_dir(indir: PathBuf, outdir: PathBuf) -> io::Result<Vec<OsString>> {
// Will be used to generate mod.rs
let mut modfiles: Vec<OsString> = Vec::new();
for entry in indir.read_dir()? {
let mut p = entry?.path();
match p.extension() {
Some(ext) if ext == "scs" => (),
_ => continue,
}
let mut infile = File::open(p.clone())?;
let mut contents = String::new();
infile.read_to_string(&mut contents)?;
let spec = read_spec(&contents);
match spec {
Ok(_) => println!("Path: {p:?}: \x1b[32m OK\x1b[0m"),
Err(err) => {
println!("Path: {p:?}: \x1b[31mERR\x1b[0m\n{err:?}");
continue;
}
}
// Path is now only going to be used as .rs
p.set_extension("rs");
let filename = OsString::from(p.file_name().unwrap());
let outpath = outdir.join(filename.clone());
println!("Outpath: {outpath:?}");
let tokens = generate_spec_tokens(spec.unwrap()).unwrap();
let mut output = BufWriter::new(File::create(&outpath)?);
write!(output, "{}", tokens)?;
// Add to the set of files for which generation succeeded to then generate mod.rs
modfiles.push(filename);
}
Ok(modfiles)
}
fn main() -> io::Result<()> {
let indir = Path::new("./specs");
let outdir = Path::new("./src/tests/scansion");
if !indir.is_dir() || !outdir.is_dir() {
return Err(io::Error::new(
io::ErrorKind::Other,
"Input and output paths must be directories.",
));
}
let modfiles = read_input_dir(indir.to_path_buf(), outdir.to_path_buf())?;
let mut modout = BufWriter::new(File::create(outdir.join("mod.rs"))?);
let tokens = generate_mod_tokens(modfiles);
write!(modout, "{}", tokens)?;
Ok(())
}

View file

@ -28,6 +28,7 @@ use async_trait::async_trait;
use diff; use diff;
use futures::{task::Poll, Stream}; use futures::{task::Poll, Stream};
use log::debug; use log::debug;
use scansion::ScanElement;
use syntect::{ use syntect::{
easy::HighlightLines, easy::HighlightLines,
highlighting::{Style, ThemeSet}, highlighting::{Style, ThemeSet},
@ -59,17 +60,23 @@ impl fmt::Debug for Expect {
} }
} }
#[derive(Clone, Eq, PartialEq)] #[derive(Clone)]
pub struct TestElement(pub Element); pub struct TestElement(pub ScanElement);
impl Deref for TestElement { impl Deref for TestElement {
type Target = Element; type Target = ScanElement;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl PartialEq<Element> for TestElement {
fn eq(&self, other: &Element) -> bool {
self.0 == *other
}
}
impl fmt::Debug for TestElement { impl fmt::Debug for TestElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from(&self.0)) write!(f, "{}", String::from(&self.0))
@ -90,31 +97,43 @@ impl From<&TestElement> for String {
impl From<Element> for TestElement { impl From<Element> for TestElement {
fn from(elem: Element) -> Self { fn from(elem: Element) -> Self {
Self(elem) Self(ScanElement::new(elem))
} }
} }
impl From<TestElement> for Element { impl From<TestElement> for Element {
fn from(elem: TestElement) -> Self { fn from(elem: TestElement) -> Self {
elem.0 elem.0.elem
} }
} }
impl From<Iq> for TestElement { impl From<Iq> for TestElement {
fn from(elem: Iq) -> Self { fn from(elem: Iq) -> Self {
Self(Element::from(elem)) Self(Element::from(elem).into())
} }
} }
impl From<Presence> for TestElement { impl From<Presence> for TestElement {
fn from(elem: Presence) -> Self { fn from(elem: Presence) -> Self {
Self(Element::from(elem)) Self(Element::from(elem).into())
} }
} }
impl From<Message> for TestElement { impl From<Message> for TestElement {
fn from(elem: Message) -> Self { fn from(elem: Message) -> Self {
Self(Element::from(elem)) Self(Element::from(elem).into())
}
}
impl From<ScanElement> for TestElement {
fn from(scan: ScanElement) -> Self {
Self(scan)
}
}
impl From<TestElement> for ScanElement {
fn from(elem: TestElement) -> ScanElement {
ScanElement::new(elem.into())
} }
} }
@ -132,7 +151,21 @@ impl Component {
in_buffer: VecDeque::from( in_buffer: VecDeque::from(
in_buffer in_buffer
.into_iter() .into_iter()
.map(|el| TestElement(el)) .map(|elem| TestElement::from(elem))
.collect::<Vec<_>>(),
),
expect_buffer: HashMap::new(),
}
}
pub fn new_scan(in_buffer: Vec<ScanElement>) -> Self {
let _ = env_logger::builder().is_test(true).try_init();
Component {
in_buffer: VecDeque::from(
in_buffer
.into_iter()
.map(|elem| TestElement::from(elem))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
), ),
expect_buffer: HashMap::new(), expect_buffer: HashMap::new(),
@ -358,7 +391,7 @@ impl Stream for Component {
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Option<Self::Item>> {
while self.in_buffer.len() > 0 { while self.in_buffer.len() > 0 {
return Poll::Ready(self.in_buffer.pop_front().map(|el| el.0)); return Poll::Ready(self.in_buffer.pop_front().map(|el| el.0.elem));
} }
Poll::Ready(None) Poll::Ready(None)
@ -370,11 +403,25 @@ impl Drop for Component {
// Don't assert if we're already panicking. Rustc displays a huge backtrace when "panicked // Don't assert if we're already panicking. Rustc displays a huge backtrace when "panicked
// while panicking" even when nobody asks for it (RUST_BACKTRACE unset). Let the error // while panicking" even when nobody asks for it (RUST_BACKTRACE unset). Let the error
// appear if there isn't any other error. // appear if there isn't any other error.
if !thread::panicking() { if !thread::panicking() && !self.expect_buffer.is_empty() {
assert!( let mut buffer = String::new();
self.expect_buffer.is_empty(), for (_stream, buf) in &self.expect_buffer {
"Remaining expected elements in the buffer" for expect in buf {
); match expect {
Expect::Element(el) => {
let elem = String::from(el);
buffer
.push_str(format!("expected: `{}`", Component::hl(&elem)).as_str());
}
expect_cb => {
buffer.push_str(format!("expected: {:?}", expect_cb).as_str());
}
}
}
}
debug!("\n{}", buffer);
panic!("Remaining expected elements in the buffer");
} }
} }
} }

View file

@ -462,14 +462,14 @@ impl Room {
let presence_join = Presence::new(PresenceType::None) let presence_join = Presence::new(PresenceType::None)
.with_from(Jid::Full(new_session.participant().clone())); .with_from(Jid::Full(new_session.participant().clone()));
let presence_join_to_others = presence_join.clone().with_payloads(vec![MucUser { let _presence_join_to_others = presence_join.clone().with_payloads(vec![MucUser {
status: vec![], status: vec![],
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)], items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
} }
.into()]); .into()]);
let occupant = self.get_occupant(&new_session)?; let occupant = self.get_occupant(&new_session)?;
let mucuser = MucUser { let _mucuser = MucUser {
status: vec![], status: vec![],
items: { items: {
occupant occupant
@ -482,6 +482,16 @@ impl Room {
}, },
}; };
self.broadcast_presence(
component,
self.get_occupant(&new_session)?,
new_session.presence.clone(),
BroadcastPresence::Update,
None,
)
.await?;
/*
for (nick, occupant) in self.occupants.iter() { for (nick, occupant) in self.occupants.iter() {
for session in occupant.iter() { for session in occupant.iter() {
// Self occupant // Self occupant
@ -522,6 +532,7 @@ impl Room {
} }
} }
} }
*/
Ok(()) Ok(())
} }

View file

@ -16,5 +16,6 @@
mod iq; mod iq;
mod presence; mod presence;
mod presence_msn; mod presence_msn;
mod scansion;
#[allow(dead_code)] #[allow(dead_code)]
pub mod templates; pub mod templates;

0
src/tests/scansion/.keep Normal file
View file

View file

@ -0,0 +1,6 @@
#[doc = r" Scansion tests module."]
#[doc = r" These tests are generated by the build script, DO NOT EDIT."]
#[cfg(test)]
mod muc_create_destroy;
#[cfg(test)]
mod muc_creation;

View file

@ -0,0 +1,51 @@
#![doc = "MUC creation, basic messages and destruction\n"]
use crate::component::TestComponent;
use crate::handlers::handle_stanza;
use crate::room::Room;
use scansion::ScanElement;
use std::collections::HashMap;
use xmpp_parsers::{BareJid, Element};
#[tokio::test]
async fn spec() {
println!("FOO0: {:?}", "Admin\nJuliet\nRomeo\n");
println!(
"FOO0: {:?}",
"admin@localhost/DfNgg9VE\njuliet@localhost/lVwkim_k\nromeo@localhost/mK0dD6Ha\n"
);
let stanzas_in = vec ! [ScanElement :: new ("<presence xmlns='jabber:client' to=\"garden@conference.localhost/romeo\">\n\t\t<x xmlns='http://jabber.org/protocol/muc'/>\n\t</presence>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<iq xmlns='jabber:client' id=\"lx3\" to=\"garden@conference.localhost\" type=\"set\">\n\t\t<query xmlns='http://jabber.org/protocol/muc#owner'>\n\t\t\t<x xmlns='jabber:x:data' type=\"submit\"/>\n\t\t</query>\n\t</iq>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"rm1\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>Where are thou my Juliet?</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<presence xmlns='jabber:client' to=\"garden@conference.localhost/juliet\">\n\t\t<x xmlns='http://jabber.org/protocol/muc'/>\n\t</presence>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"jm1\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>/me jumps out from behind a tree</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"jm2\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>Here I am!</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"rm2\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>What is this place?</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"jm3\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>I think we're in a script!</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"rm3\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>Oh no! Does that mean our love is not real?!</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"jm4\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>I refuse to accept this! Let's burn this place to the ground!</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<message xmlns='jabber:client' id=\"rm4\" to=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<body>Yes!</body>\n\t</message>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<iq xmlns='jabber:client' id=\"lx4\" to=\"garden@conference.localhost\" type=\"set\">\n\t\t<query xmlns='http://jabber.org/protocol/muc#owner'>\n\t\t\t<destroy>\n\t\t\t\t<reason>We refuse to live in this fantasy!</reason>\n\t\t\t</destroy>\n\t\t</query>\n\t</iq>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<presence xmlns='jabber:client' to=\"elsewhere@conference.localhost/romeo\">\n\t\t<x xmlns='http://jabber.org/protocol/muc'/>\n\t</presence>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<iq xmlns='jabber:client' id=\"lx5\" to=\"elsewhere@conference.localhost\" type=\"set\">\n\t\t<query xmlns='http://jabber.org/protocol/muc#owner'>\n\t\t\t<x xmlns='jabber:x:data' type=\"submit\"/>\n\t\t</query>\n\t</iq>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context)) , ScanElement :: new ("<iq xmlns='jabber:client' id=\"destroy\" to=\"conference.localhost\" type=\"set\">\n\t\t<command xmlns='http://jabber.org/protocol/commands' node=\"http://prosody.im/protocol/muc#destroy\">\n\t\t\t<x xmlns='jabber:x:data'>\n\t\t\t\t<field var=\"rooms\">\n\t\t\t\t\t<value>elsewhere@conference.localhost</value>\n\t\t\t\t</field>\n\t\t\t</x>\n\t\t</command>\n\t</iq>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context))] ;
let mut component = TestComponent::new_scan(stanzas_in);
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
component . expect ("<presence xmlns='jabber:client' from=\"garden@conference.localhost/romeo\">\n\t\t<x xmlns='vcard-temp:x:update'>\n\t\t\t<photo/>\n\t\t</x>\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<status code=\"201\"/>\n\t\t\t<item affiliation=\"owner\" jid=\"${Romeo&#39;s full JID}\" role=\"moderator\"/>\n\t\t\t<status code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<subject/>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<iq xmlns='jabber:client' from=\"garden@conference.localhost\" id=\"lx3\" type=\"result\"/>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm1\" type=\"groupchat\">\n\t\t<body>Where are thou my Juliet?</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"garden@conference.localhost/romeo\">\n\t\t<x xmlns='vcard-temp:x:update'>\n\t\t\t<photo/>\n\t\t</x>\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<item affiliation=\"owner\" role=\"moderator\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"garden@conference.localhost/juliet\">\n\t\t<x xmlns='vcard-temp:x:update'>\n\t\t\t<photo/>\n\t\t</x>\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<item affiliation=\"none\" jid=\"${Juliet&#39;s full JID}\" role=\"participant\"/>\n\t\t\t<status code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm1\" type=\"groupchat\">\n\t\t<body>Where are thou my Juliet?</body>\n\t\t<delay xmlns='urn:xmpp:delay' from=\"garden@conference.localhost\" stamp=\"{scansion:any}\"/>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost\" type=\"groupchat\">\n\t\t<subject/>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"garden@conference.localhost/juliet\">\n\t\t<x xmlns='vcard-temp:x:update'>\n\t\t\t<photo/>\n\t\t</x>\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<item affiliation=\"none\" jid=\"${Juliet&#39;s full JID}\" role=\"participant\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm1\" type=\"groupchat\">\n\t\t<body>/me jumps out from behind a tree</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm1\" type=\"groupchat\">\n\t\t<body>/me jumps out from behind a tree</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm2\" type=\"groupchat\">\n\t\t<body>Here I am!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm2\" type=\"groupchat\">\n\t\t<body>Here I am!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm2\" type=\"groupchat\">\n\t\t<body>What is this place?</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm2\" type=\"groupchat\">\n\t\t<body>What is this place?</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm3\" type=\"groupchat\">\n\t\t<body>I think we're in a script!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm3\" type=\"groupchat\">\n\t\t<body>I think we're in a script!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm3\" type=\"groupchat\">\n\t\t<body>Oh no! Does that mean our love is not real?!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm3\" type=\"groupchat\">\n\t\t<body>Oh no! Does that mean our love is not real?!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm4\" type=\"groupchat\">\n\t\t<body>I refuse to accept this! Let's burn this place to the ground!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" id=\"jm4\" type=\"groupchat\">\n\t\t<body>I refuse to accept this! Let's burn this place to the ground!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm4\" type=\"groupchat\">\n\t\t<body>Yes!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" id=\"rm4\" type=\"groupchat\">\n\t\t<body>Yes!</body>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"garden@conference.localhost/juliet\" type=\"unavailable\">\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<destroy>\n\t\t\t\t<reason>We refuse to live in this fantasy!</reason>\n\t\t\t</destroy>\n\t\t\t<item affiliation=\"none\" jid=\"${Juliet&#39;s full JID}\" role=\"none\"/>\n\t\t\t<status code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"garden@conference.localhost/romeo\" type=\"unavailable\">\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<destroy>\n\t\t\t\t<reason>We refuse to live in this fantasy!</reason>\n\t\t\t</destroy>\n\t\t\t<item affiliation=\"owner\" jid=\"${Romeo&#39;s full JID}\" role=\"none\"/>\n\t\t\t<status code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<iq xmlns='jabber:client' from=\"garden@conference.localhost\" id=\"lx4\" type=\"result\"/>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"elsewhere@conference.localhost/romeo\">\n\t\t<x xmlns='vcard-temp:x:update'>\n\t\t\t<photo/>\n\t\t</x>\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<status code=\"201\"/>\n\t\t\t<item affiliation=\"owner\" jid=\"${Romeo&#39;s full JID}\" role=\"moderator\"/>\n\t\t\t<status code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<message xmlns='jabber:client' from=\"elsewhere@conference.localhost\" type=\"groupchat\">\n\t\t<subject/>\n\t</message>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<iq xmlns='jabber:client' from=\"elsewhere@conference.localhost\" id=\"lx5\" type=\"result\"/>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<presence xmlns='jabber:client' from=\"elsewhere@conference.localhost/romeo\" type=\"unavailable\">\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<destroy/>\n\t\t\t<item affiliation=\"owner\" jid=\"${Romeo&#39;s full JID}\" role=\"none\"/>\n\t\t\t<status code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
component . expect ("<iq xmlns='jabber:client' from=\"conference.localhost\" id=\"destroy\" type=\"result\">\n\t\t<command xmlns='http://jabber.org/protocol/commands' node=\"http://prosody.im/protocol/muc#destroy\" sessionid=\"{scansion:any}\" status=\"completed\">\n\t\t\t<note type=\"info\">The following rooms were destroyed:\nelsewhere@conference.localhost</note>\n\t\t</command>\n\t</iq>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
println!("FOO: {component:?}");
handle_stanza(&mut component, &mut rooms).await.unwrap();
}

View file

@ -0,0 +1,18 @@
#![doc = "MUC Creation\nSingle user MUC creation\ntag: muc\n"]
use crate::component::TestComponent;
use crate::handlers::handle_stanza;
use crate::room::Room;
use scansion::ScanElement;
use std::collections::HashMap;
use xmpp_parsers::{BareJid, Element};
#[tokio::test]
async fn spec() {
println!("FOO0: {:?}", "louise\n");
println!("FOO0: {:?}", "louise@localhost/194\n");
let stanzas_in = vec ! [ScanElement :: new ("<presence xmlns='jabber:client' from=\"${louise&#39;s full JID}\" to=\"room@muc.localhost/louise\">\n\t\t<x xmlns='http://jabber.org/protocol/muc'/>\n\t</presence>" . parse :: < Element > () . unwrap ()) . with_context (Some (& context))] ;
let mut component = TestComponent::new_scan(stanzas_in);
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
component . expect ("<presence xmlns='jabber:client' from=\"muc.localhost\" id=\"{scansion:any}\" to=\"${louise&#39;s full JID}\">\n\t\t<x xmlns='http://jabber.org/protocol/muc#user'>\n\t\t\t<status code=\"201\"/>\n\t\t\t<item affiliation=\"owner\" jid=\"${louise&#39;s full JID}\" role=\"moderator\"/>\n\t\t\t<statuc code=\"110\"/>\n\t\t</x>\n\t</presence>" . parse :: < ScanElement > () . unwrap () . with_context (Some (& context))) ;
println!("FOO: {component:?}");
handle_stanza(&mut component, &mut rooms).await.unwrap();
}

View file

@ -0,0 +1,12 @@
use crate::component::TestComponent;
use crate::handlers::handle_stanza;
use crate::room::Room;
use std::collections::HashMap;
use xmpp_parsers::BareJid;
#[doc = "MUC: Mediated invites\n"]
#[tokio::test]
async fn spec() {
let mut component = TestComponent::new(vec![]);
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
handle_stanza(&mut component, &mut rooms).await.unwrap();
}