test: Add golden tests infrastructure (#4395)

This commit is contained in:
Hocuri
2023-05-22 00:10:33 +02:00
committed by GitHub
parent c68a2e3820
commit b9b0d20e8d
3 changed files with 96 additions and 15 deletions

44
Cargo.lock generated
View File

@@ -977,6 +977,16 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "3.2.0" version = "3.2.0"
@@ -1183,6 +1193,7 @@ dependencies = [
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
"pgp", "pgp",
"pretty_assertions",
"pretty_env_logger", "pretty_env_logger",
"proptest", "proptest",
"qrcodegen", "qrcodegen",
@@ -1400,6 +1411,12 @@ dependencies = [
"cipher", "cipher",
] ]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@@ -3201,6 +3218,15 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@@ -3545,6 +3571,18 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "pretty_assertions"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
dependencies = [
"ctor",
"diff",
"output_vt100",
"yansi",
]
[[package]] [[package]]
name = "pretty_env_logger" name = "pretty_env_logger"
version = "0.4.0" version = "0.4.0"
@@ -5744,6 +5782,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]] [[package]]
name = "yasna" name = "yasna"
version = "0.5.1" version = "0.5.1"

View File

@@ -104,6 +104,7 @@ proptest = { version = "1", default-features = false, features = ["std"] }
tempfile = "3" tempfile = "3"
testdir = "0.7.3" testdir = "0.7.3"
tokio = { version = "1", features = ["parking_lot", "rt-multi-thread", "macros"] } tokio = { version = "1", features = ["parking_lot", "rt-multi-thread", "macros"] }
pretty_assertions = "1.3.0"
[workspace] [workspace]
members = [ members = [

View File

@@ -3,8 +3,10 @@
//! This private module is only compiled for test runs. //! This private module is only compiled for test runs.
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Write;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::panic; use std::panic;
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@@ -12,11 +14,12 @@ use ansi_term::Color;
use async_channel::{self as channel, Receiver, Sender}; use async_channel::{self as channel, Receiver, Sender};
use chat::ChatItem; use chat::ChatItem;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pretty_assertions::assert_eq;
use rand::Rng; use rand::Rng;
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
use tokio::runtime::Handle; use tokio::runtime::Handle;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tokio::task; use tokio::{fs, task};
use crate::chat::{ use crate::chat::{
self, add_to_chat_contacts_table, create_group_chat, Chat, ChatId, MessageListOptions, self, add_to_chat_contacts_table, create_group_chat, Chat, ChatId, MessageListOptions,
@@ -285,7 +288,7 @@ impl TestContext {
println!("\n========== Chats of {}: ==========", self.name()); println!("\n========== Chats of {}: ==========", self.name());
if let Ok(chats) = Chatlist::try_load(self, 0, None, None).await { if let Ok(chats) = Chatlist::try_load(self, 0, None, None).await {
for (chat, _) in chats.iter() { for (chat, _) in chats.iter() {
self.print_chat(*chat).await; print!("{}", self.display_chat(*chat).await);
} }
} }
println!(); println!();
@@ -624,6 +627,28 @@ impl TestContext {
res res
} }
#[allow(unused)]
pub async fn golden_test_chat(&self, chat_id: ChatId, filename: &str) {
let filename = Path::new("test-data/golden/").join(filename);
let actual = self.display_chat(chat_id).await;
// We're using `unwrap_or_default()` here so that if the file doesn't exist,
// it can be created using `write` below.
let expected = fs::read(&filename).await.unwrap_or_default();
let expected = String::from_utf8(expected).unwrap();
if (std::env::var("UPDATE_GOLDEN_TESTS") == Ok("1".to_string())) && actual != expected {
fs::write(&filename, &actual)
.await
.unwrap_or_else(|e| panic!("Error writing {filename:?}: {e}"));
} else {
assert_eq!(
actual, expected,
"To update the expected value, run `UPDATE_GOLDEN_TESTS=1 cargo test`"
);
}
}
/// Prints out the entire chat to stdout. /// Prints out the entire chat to stdout.
/// ///
/// You can use this to debug your test by printing the entire chat conversation. /// You can use this to debug your test by printing the entire chat conversation.
@@ -631,7 +656,9 @@ impl TestContext {
// merge them to a public function in the `deltachat` crate. // merge them to a public function in the `deltachat` crate.
#[allow(dead_code)] #[allow(dead_code)]
#[allow(clippy::indexing_slicing)] #[allow(clippy::indexing_slicing)]
pub async fn print_chat(&self, chat_id: ChatId) { async fn display_chat(&self, chat_id: ChatId) -> String {
let mut res = String::new();
let msglist = chat::get_chat_msgs_ex( let msglist = chat::get_chat_msgs_ex(
self, self,
chat_id, chat_id,
@@ -662,7 +689,8 @@ impl TestContext {
} else { } else {
format!("{} member(s)", members.len()) format!("{} member(s)", members.len())
}; };
println!( writeln!(
res,
"{}#{}: {} [{}]{}{}{} {}", "{}#{}: {} [{}]{}{}{} {}",
sel_chat.typ, sel_chat.typ,
sel_chat.get_id(), sel_chat.get_id(),
@@ -686,32 +714,38 @@ impl TestContext {
} else { } else {
"" ""
}, },
); )
.unwrap();
let mut lines_out = 0; let mut lines_out = 0;
for msg_id in msglist { for msg_id in msglist {
if msg_id == MsgId::new(DC_MSG_ID_DAYMARKER) { if msg_id == MsgId::new(DC_MSG_ID_DAYMARKER) {
println!( writeln!(res,
"--------------------------------------------------------------------------------" "--------------------------------------------------------------------------------"
); )
.unwrap();
lines_out += 1 lines_out += 1
} else if !msg_id.is_special() { } else if !msg_id.is_special() {
if lines_out == 0 { if lines_out == 0 {
println!( writeln!(res,
"--------------------------------------------------------------------------------", "--------------------------------------------------------------------------------",
); ).unwrap();
lines_out += 1 lines_out += 1
} }
let msg = Message::load_from_db(self, msg_id).await.unwrap(); let msg = Message::load_from_db(self, msg_id).await.unwrap();
log_msg(self, "", &msg).await; write_msg(self, "", &msg, &mut res).await;
} }
} }
if lines_out > 0 { if lines_out > 0 {
println!( writeln!(
res,
"--------------------------------------------------------------------------------" "--------------------------------------------------------------------------------"
); )
.unwrap();
} }
res
} }
pub async fn create_group_with_members( pub async fn create_group_with_members(
@@ -1041,7 +1075,7 @@ fn print_event(event: &Event) {
/// Logs an individual message to stdout. /// Logs an individual message to stdout.
/// ///
/// This includes a bunch of the message meta-data as well. /// This includes a bunch of the message meta-data as well.
async fn log_msg(context: &Context, prefix: &str, msg: &Message) { async fn write_msg(context: &Context, prefix: &str, msg: &Message, buf: &mut String) {
let contact = match Contact::get_by_id(context, msg.get_from_id()).await { let contact = match Contact::get_by_id(context, msg.get_from_id()).await {
Ok(contact) => contact, Ok(contact) => contact,
Err(e) => { Err(e) => {
@@ -1061,7 +1095,8 @@ async fn log_msg(context: &Context, prefix: &str, msg: &Message) {
_ => "", _ => "",
}; };
let msgtext = msg.get_text(); let msgtext = msg.get_text();
println!( writeln!(
buf,
"{}{}{}{}: {} (Contact#{}): {} {}{}{}{}{}", "{}{}{}{}: {} (Contact#{}): {} {}{}{}{}{}",
prefix, prefix,
msg.get_id(), msg.get_id(),
@@ -1095,7 +1130,8 @@ async fn log_msg(context: &Context, prefix: &str, msg: &Message) {
"" ""
}, },
statestr, statestr,
); )
.unwrap();
} }
#[cfg(test)] #[cfg(test)]