TestComponent: assert closer to callsite

Attempts to assert closer to callsite to make it easier to debug. This
requires that we also pay attention to remaining items in the
expect_buffer. This check is done on Drop.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2022-09-23 17:22:32 +02:00
parent b61766d462
commit c438402b15
4 changed files with 48 additions and 41 deletions

View file

@ -12,6 +12,7 @@ use std::marker::Send;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::task::Context;
use std::thread;
use async_trait::async_trait;
use futures::{task::Poll, Stream};
@ -162,7 +163,6 @@ impl From<Message> for TestElement {
#[derive(Debug)]
pub struct TestComponent {
in_buffer: VecDeque<TestElement>,
out_buffer: VecDeque<TestElement>,
expect_buffer: VecDeque<Expect>,
}
@ -175,7 +175,6 @@ impl TestComponent {
.map(|el| TestElement(el))
.collect::<Vec<_>>(),
),
out_buffer: VecDeque::new(),
expect_buffer: VecDeque::new(),
}
}
@ -212,33 +211,39 @@ impl TestComponent {
.push_back(Expect::Presence(Box::new(callback), desc.into()))
}
/// Asserts expected output and actual output are the same
pub fn assert(&mut self) {
loop {
let out = self.out_buffer.pop_front();
let expected = self.expect_buffer.pop_front();
match (out, expected) {
(None, None) => break,
(Some(out), Some(expected)) => match expected {
Expect::Element(el) => assert_eq!(String::from(&el), String::from(&out)),
Expect::Iq(cb, _) => cb(Iq::try_from(out.0).unwrap()),
Expect::Message(cb, _) => cb(Message::try_from(out.0).unwrap()),
Expect::Presence(cb, _) => cb(Presence::try_from(out.0).unwrap()),
},
(Some(out), None) => panic!("Missing matching expected element: {:?}", out),
(None, Some(expected)) => match expected {
Expect::Element(el) => panic!("Missing matching sent element: {:?}", el),
Expect::Iq(_, desc) => panic!("Missing iq: {}", desc),
Expect::Message(_, desc) => panic!("Missing message: {}", desc),
Expect::Presence(_, desc) => panic!("Missing presence: {}", desc),
},
}
}
}
fn _send_stanza<E: Into<TestElement> + Send>(&mut self, el: E) -> Result<(), Error> {
Ok(self.out_buffer.push_back(el.into()))
let out: TestElement = el.into();
let expected = self.expect_buffer.pop_front();
match expected {
Some(expected) => match expected {
Expect::Element(el) => assert_eq!(String::from(&el), String::from(&out)),
Expect::Iq(cb, _) => match Iq::try_from(out.0.clone()) {
Ok(iq) => cb(iq),
_ => panic!(
"Mismatch stanza type. Expected iq, got: {}",
String::from(&out)
),
},
Expect::Message(cb, _) => match Message::try_from(out.0.clone()) {
Ok(message) => cb(message),
_ => panic!(
"Mismatch stanza type. Expected message, got: {}",
String::from(&out)
),
},
Expect::Presence(cb, _) => match Presence::try_from(out.0.clone()) {
Ok(presence) => cb(presence),
_ => panic!(
"Mismatch stanza type. Expected presence, got: {}",
String::from(&out)
),
},
},
None => panic!("Missing matching expected element: {:?}", out),
}
Ok(())
}
}
@ -254,6 +259,21 @@ impl Stream for TestComponent {
}
}
impl Drop for TestComponent {
fn drop(&mut self) {
// 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
// appear if there isn't any other error.
if !thread::panicking() {
assert_eq!(
self.expect_buffer.len(),
0,
"Remaining expected elements in the buffer"
);
}
}
}
#[async_trait]
impl ComponentTrait for TestComponent {
async fn send_stanza<E: Into<Element> + Send>(&mut self, el: E) -> Result<(), Error> {

View file

@ -473,7 +473,6 @@ mod tests {
)
.await
.unwrap();
component.assert();
}
#[tokio::test]
@ -534,7 +533,6 @@ mod tests {
)
.await
.unwrap();
component.assert();
}
#[tokio::test]
@ -634,7 +632,6 @@ mod tests {
)
.await
.unwrap();
component.assert();
}
#[tokio::test]
@ -706,6 +703,5 @@ mod tests {
)
.await
.unwrap();
component.assert();
}
}

View file

@ -61,5 +61,4 @@ async fn test_iq_unimplemented() {
component.expect(reply);
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
}

View file

@ -106,7 +106,6 @@ async fn test_join_presence_empty_room() {
}, "Room subject to participant1");
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
assert_eq!(rooms.len(), 1);
match rooms.get(&roomjid) {
@ -160,7 +159,6 @@ async fn test_join_presence_nick_already_assigned() {
);
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
match rooms.get(&roomjid) {
Some(room) => assert_eq!(room.occupants.len(), 1),
@ -263,7 +261,6 @@ async fn test_join_presence_existing_room() {
}, "Subject for participant2");
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
match rooms.get(&roomjid) {
Some(room) => assert_eq!(room.occupants.len(), 2),
@ -330,7 +327,6 @@ async fn test_presence_resync() {
);
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
match rooms.get(&roomjid) {
Some(room) => assert_eq!(room.occupants.len(), 2),
@ -354,7 +350,6 @@ async fn test_leave_non_existing_room() {
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
handle_stanza(&mut component, &mut rooms).await.unwrap();
// The leave should be ignored, there should be no output at all.
component.assert();
}
#[tokio::test]
@ -394,7 +389,6 @@ async fn test_leave_last_participant() {
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
assert_eq!(rooms.len(), 0);
}
@ -457,7 +451,6 @@ async fn test_leave_room_not_last() {
);
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
assert_eq!(rooms.len(), 1);
match rooms.get(&roomjid) {
Some(room) => assert_eq!(room.occupants.len(), 1),
@ -547,7 +540,6 @@ async fn test_join_msn() {
component.expect_message(|_| (), "Subject message for participant2");
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
assert_eq!(rooms.len(), 1);
match rooms.get(&roomjid) {