feat: migrate from async-std to tokio

This commit is contained in:
Friedel Ziegelmayer
2022-06-27 14:05:21 +02:00
committed by GitHub
parent 997fb4061a
commit 290ee20e63
69 changed files with 1781 additions and 2231 deletions

View File

@@ -10,7 +10,7 @@ on:
env: env:
RUSTFLAGS: -Dwarnings RUSTFLAGS: -Dwarnings
jobs: jobs:
fmt: fmt:
@@ -83,13 +83,13 @@ jobs:
rust: 1.61.0 rust: 1.61.0
python: false # Python bindings compilation on Windows is not supported. python: false # Python bindings compilation on Windows is not supported.
# Minimum Supported Rust Version = 1.56.0 # Minimum Supported Rust Version = 1.56.1
# #
# Minimum Supported Python Version = 3.7 # Minimum Supported Python Version = 3.7
# This is the minimum version for which manylinux Python wheels are # This is the minimum version for which manylinux Python wheels are
# built. # built.
- os: ubuntu-latest - os: ubuntu-latest
rust: 1.56.0 rust: 1.56.1
python: 3.7 python: 3.7
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View File

@@ -17,6 +17,7 @@
- node: remove `split2` dependency #3418 - node: remove `split2` dependency #3418
- node: add git installation info to readme #3418 - node: add git installation info to readme #3418
- limit the rate of webxdc update sending #3417 - limit the rate of webxdc update sending #3417
- switch from `async-std` to `tokio` as the async runtime #3449
### Fixes ### Fixes
- set a default error if NDN does not provide an error - set a default error if NDN does not provide an error

1425
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ rust-version = "1.56"
[profile.dev] [profile.dev]
debug = 0 debug = 0
panic = 'abort' panic = 'abort'
opt-level = 1
[profile.release] [profile.release]
lto = true lto = true
@@ -19,19 +20,19 @@ deltachat_derive = { path = "./deltachat_derive" }
ansi_term = { version = "0.12.1", optional = true } ansi_term = { version = "0.12.1", optional = true }
anyhow = "1" anyhow = "1"
async-imap = { git = "https://github.com/async-email/async-imap" } async-imap = { version = "0.6", default-features = false, features = ["runtime-tokio"] }
async-native-tls = { version = "0.3" } async-native-tls = { version = "0.4", default-features = false, features = ["runtime-tokio"] }
async-smtp = { git = "https://github.com/async-email/async-smtp", branch="master", default-features=false, features = ["smtp-transport", "socks5"] } async-smtp = { version = "0.5", default-features = false, features = ["smtp-transport", "socks5", "runtime-tokio"] }
async-std-resolver = "0.21" trust-dns-resolver = "0.21"
async-std = { version = "1" } tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
async-tar = { version = "0.4", default-features=false } tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
backtrace = "0.3" backtrace = "0.3"
base64 = "0.13" base64 = "0.13"
bitflags = "1.3" bitflags = "1.3"
chrono = { version = "0.4", default-features=false, features = ["clock", "std"] } chrono = { version = "0.4", default-features=false, features = ["clock", "std"] }
dirs = { version = "4", optional=true } dirs = { version = "4", optional=true }
email = { git = "https://github.com/deltachat/rust-email", branch = "master" } email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" } encoded-words = "0.2"
escaper = "0.1" escaper = "0.1"
futures = "0.3" futures = "0.3"
hex = "0.4.0" hex = "0.4.0"
@@ -65,22 +66,24 @@ sha2 = "0.10"
smallvec = "1" smallvec = "1"
strum = "0.24" strum = "0.24"
strum_macros = "0.24" strum_macros = "0.24"
surf = { version = "2.3", default-features = false, features = ["h1-client"] }
thiserror = "1" thiserror = "1"
toml = "0.5" toml = "0.5"
url = "2" url = "2"
uuid = { version = "1", features = ["serde", "v4"] } uuid = { version = "1", features = ["serde", "v4"] }
fast-socks5 = "0.4" fast-socks5 = "0.8"
humansize = "1" humansize = "1"
qrcodegen = "1.7.0" qrcodegen = "1.7.0"
tagger = "4.3.3" tagger = "4.3.3"
textwrap = "0.15.0" textwrap = "0.15.0"
zip = { version = "0.6.2", default-features = false, features = ["deflate"] } async-channel = "1.6.1"
futures-lite = "1.12.0"
tokio-stream = { version = "0.1.9", features = ["fs"] }
reqwest = { version = "0.11.11", features = ["json"] }
async_zip = { git = "https://github.com/dignifiedquire/rs-async-zip", branch = "main", default-features = false, features = ["deflate"] }
[dev-dependencies] [dev-dependencies]
ansi_term = "0.12.0" ansi_term = "0.12.0"
async-std = { version = "1", features = ["unstable", "attributes"] } criterion = { version = "0.3.4", features = ["async_tokio"] }
criterion = { version = "0.3.4", features = ["async_std"] }
futures-lite = "1.12" futures-lite = "1.12"
log = "0.4" log = "0.4"
pretty_env_logger = "0.4" pretty_env_logger = "0.4"
@@ -132,5 +135,10 @@ harness = false
default = ["vendored"] default = ["vendored"]
internals = [] internals = []
repl = ["internals", "rustyline", "log", "pretty_env_logger", "ansi_term", "dirs"] repl = ["internals", "rustyline", "log", "pretty_env_logger", "ansi_term", "dirs"]
vendored = ["async-native-tls/vendored", "async-smtp/native-tls-vendored", "rusqlite/bundled-sqlcipher-vendored-openssl"] vendored = [
"async-native-tls/vendored",
"async-smtp/native-tls-vendored",
"rusqlite/bundled-sqlcipher-vendored-openssl",
"reqwest/native-tls-vendored"
]
nightly = ["pgp/nightly"] nightly = ["pgp/nightly"]

View File

@@ -1,4 +1,3 @@
use async_std::task::block_on;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deltachat::contact::Contact; use deltachat::contact::Contact;
use deltachat::context::Context; use deltachat::context::Context;
@@ -9,9 +8,7 @@ async fn address_book_benchmark(n: u32, read_count: u32) {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
let id = 100; let id = 100;
let context = Context::new(dbfile.into(), id, Events::new()) let context = Context::new(&dbfile, id, Events::new()).await.unwrap();
.await
.unwrap();
let book = (0..n) let book = (0..n)
.map(|i| format!("Name {}\naddr{}@example.org\n", i, i)) .map(|i| format!("Name {}\naddr{}@example.org\n", i, i))
@@ -27,12 +24,16 @@ async fn address_book_benchmark(n: u32, read_count: u32) {
} }
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
c.bench_function("create 500 contacts", |b| { c.bench_function("create 500 contacts", |b| {
b.iter(|| block_on(async { address_book_benchmark(black_box(500), black_box(0)).await })) b.to_async(&rt)
.iter(|| async { address_book_benchmark(black_box(500), black_box(0)).await })
}); });
c.bench_function("create 100 contacts and read it 1000 times", |b| { c.bench_function("create 100 contacts and read it 1000 times", |b| {
b.iter(|| block_on(async { address_book_benchmark(black_box(100), black_box(1000)).await })) b.to_async(&rt)
.iter(|| async { address_book_benchmark(black_box(100), black_box(1000)).await })
}); });
} }

View File

@@ -1,12 +1,11 @@
use async_std::path::PathBuf;
use async_std::task::block_on;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deltachat::accounts::Accounts; use deltachat::accounts::Accounts;
use std::path::PathBuf;
use tempfile::tempdir; use tempfile::tempdir;
async fn create_accounts(n: u32) { async fn create_accounts(n: u32) {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let mut accounts = Accounts::new(p.clone()).await.unwrap(); let mut accounts = Accounts::new(p.clone()).await.unwrap();
@@ -18,7 +17,8 @@ async fn create_accounts(n: u32) {
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("create 1 account", |b| { c.bench_function("create 1 account", |b| {
b.iter(|| block_on(async { create_accounts(black_box(1)).await })) let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(&rt).iter(|| create_accounts(black_box(1)))
}); });
} }

View File

@@ -1,6 +1,5 @@
use async_std::path::Path; use std::path::Path;
use criterion::async_executor::AsyncStdExecutor;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deltachat::chat::{self, ChatId}; use deltachat::chat::{self, ChatId};
@@ -10,9 +9,7 @@ use deltachat::Events;
async fn get_chat_msgs_benchmark(dbfile: &Path, chats: &[ChatId]) { async fn get_chat_msgs_benchmark(dbfile: &Path, chats: &[ChatId]) {
let id = 100; let id = 100;
let context = Context::new(dbfile.into(), id, Events::new()) let context = Context::new(dbfile, id, Events::new()).await.unwrap();
.await
.unwrap();
for c in chats.iter().take(10) { for c in chats.iter().take(10) {
black_box(chat::get_chat_msgs(&context, *c, 0).await.ok()); black_box(chat::get_chat_msgs(&context, *c, 0).await.ok());
@@ -23,8 +20,10 @@ fn criterion_benchmark(c: &mut Criterion) {
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many // To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
// messages, such as your primary account. // messages, such as your primary account.
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") { if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
let chats: Vec<_> = async_std::task::block_on(async { let rt = tokio::runtime::Runtime::new().unwrap();
let context = Context::new((&path).into(), 100, Events::new())
let chats: Vec<_> = rt.block_on(async {
let context = Context::new(Path::new(&path), 100, Events::new())
.await .await
.unwrap(); .unwrap();
let chatlist = Chatlist::try_load(&context, 0, None, None).await.unwrap(); let chatlist = Chatlist::try_load(&context, 0, None, None).await.unwrap();
@@ -33,7 +32,7 @@ fn criterion_benchmark(c: &mut Criterion) {
}); });
c.bench_function("chat::get_chat_msgs (load messages from 10 chats)", |b| { c.bench_function("chat::get_chat_msgs (load messages from 10 chats)", |b| {
b.to_async(AsyncStdExecutor) b.to_async(&rt)
.iter(|| get_chat_msgs_benchmark(black_box(path.as_ref()), black_box(&chats))) .iter(|| get_chat_msgs_benchmark(black_box(path.as_ref()), black_box(&chats)))
}); });
} else { } else {

View File

@@ -1,4 +1,5 @@
use criterion::async_executor::AsyncStdExecutor; use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deltachat::chatlist::Chatlist; use deltachat::chatlist::Chatlist;
@@ -13,11 +14,14 @@ fn criterion_benchmark(c: &mut Criterion) {
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many // To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
// messages, such as your primary account. // messages, such as your primary account.
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") { if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
let context = async_std::task::block_on(async { let rt = tokio::runtime::Runtime::new().unwrap();
Context::new(path.into(), 100, Events::new()).await.unwrap() let context = rt.block_on(async {
Context::new(Path::new(&path), 100, Events::new())
.await
.unwrap()
}); });
c.bench_function("chatlist:try_load (Get Chatlist)", |b| { c.bench_function("chatlist:try_load (Get Chatlist)", |b| {
b.to_async(AsyncStdExecutor) b.to_async(&rt)
.iter(|| get_chat_list_benchmark(black_box(&context))) .iter(|| get_chat_list_benchmark(black_box(&context)))
}); });
} else { } else {

View File

@@ -1,8 +1,6 @@
use async_std::{path::PathBuf, task::block_on}; use std::path::PathBuf;
use criterion::{
async_executor::AsyncStdExecutor, black_box, criterion_group, criterion_main, BatchSize, use criterion::{black_box, criterion_group, criterion_main, Criterion};
Criterion,
};
use deltachat::{ use deltachat::{
config::Config, config::Config,
context::Context, context::Context,
@@ -43,15 +41,13 @@ async fn create_context() -> Context {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
let id = 100; let id = 100;
let context = Context::new(dbfile.into(), id, Events::new()) let context = Context::new(&dbfile, id, Events::new()).await.unwrap();
.await
.unwrap();
let backup: PathBuf = std::env::current_dir() let backup: PathBuf = std::env::current_dir()
.unwrap() .unwrap()
.join("delta-chat-backup.tar") .join("delta-chat-backup.tar");
.into();
if backup.exists().await { if backup.exists() {
println!("Importing backup"); println!("Importing backup");
imex(&context, ImexMode::ImportBackup, &backup, None) imex(&context, ImexMode::ImportBackup, &backup, None)
.await .await
@@ -74,11 +70,15 @@ async fn create_context() -> Context {
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("Receive messages"); let mut group = c.benchmark_group("Receive messages");
group.bench_function("Receive 100 simple text msgs", |b| { group.bench_function("Receive 100 simple text msgs", |b| {
b.to_async(AsyncStdExecutor).iter_batched( let rt = tokio::runtime::Runtime::new().unwrap();
|| block_on(create_context()), let context = rt.block_on(create_context());
|context| recv_all_emails(black_box(context)),
BatchSize::LargeInput, b.to_async(&rt).iter(|| {
); let ctx = context.clone();
async move {
recv_all_emails(black_box(ctx)).await;
}
});
}); });
group.finish(); group.finish();
} }

View File

@@ -1,13 +1,11 @@
use async_std::task::block_on;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deltachat::context::Context; use deltachat::context::Context;
use deltachat::Events; use deltachat::Events;
use std::path::Path; use std::path::Path;
async fn search_benchmark(path: impl AsRef<Path>) { async fn search_benchmark(dbfile: impl AsRef<Path>) {
let dbfile = path.as_ref();
let id = 100; let id = 100;
let context = Context::new(dbfile.into(), id, Events::new()) let context = Context::new(dbfile.as_ref(), id, Events::new())
.await .await
.unwrap(); .unwrap();
@@ -20,8 +18,10 @@ fn criterion_benchmark(c: &mut Criterion) {
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many // To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
// messages, such as your primary account. // messages, such as your primary account.
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") { if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
let rt = tokio::runtime::Runtime::new().unwrap();
c.bench_function("search hello", |b| { c.bench_function("search hello", |b| {
b.iter(|| block_on(async { search_benchmark(black_box(&path)).await })) b.to_async(&rt).iter(|| search_benchmark(black_box(&path)))
}); });
} else { } else {
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE"); println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");

View File

@@ -20,10 +20,11 @@ libc = "0.2"
human-panic = "1" human-panic = "1"
num-traits = "0.2" num-traits = "0.2"
serde_json = "1.0" serde_json = "1.0"
async-std = "1" tokio = { version = "1", features = ["rt-multi-thread"] }
anyhow = "1" anyhow = "1"
thiserror = "1" thiserror = "1"
rand = "0.7" rand = "0.7"
once_cell = "1.12.0"
[features] [features]
default = ["vendored"] default = ["vendored"]

View File

@@ -15,17 +15,19 @@ extern crate human_panic;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt::Write; use std::fmt::Write;
use std::future::Future;
use std::ops::Deref; use std::ops::Deref;
use std::ptr; use std::ptr;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use anyhow::Context as _; use anyhow::Context as _;
use async_std::sync::RwLock;
use async_std::task::{block_on, spawn};
use deltachat::qr_code_generator::get_securejoin_qr_svg; use deltachat::qr_code_generator::get_securejoin_qr_svg;
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
use once_cell::sync::Lazy;
use rand::Rng; use rand::Rng;
use tokio::runtime::Runtime;
use tokio::sync::RwLock;
use deltachat::chat::{ChatId, ChatVisibility, MuteDuration, ProtectionStatus}; use deltachat::chat::{ChatId, ChatVisibility, MuteDuration, ProtectionStatus};
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL; use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
@@ -38,6 +40,7 @@ use deltachat::stock_str::StockMessage;
use deltachat::webxdc::StatusUpdateSerial; use deltachat::webxdc::StatusUpdateSerial;
use deltachat::*; use deltachat::*;
use deltachat::{accounts::Accounts, log::LogExt}; use deltachat::{accounts::Accounts, log::LogExt};
use tokio::task::JoinHandle;
mod dc_array; mod dc_array;
mod lot; mod lot;
@@ -61,6 +64,23 @@ use deltachat::chatlist::Chatlist;
/// Struct representing the deltachat context. /// Struct representing the deltachat context.
pub type dc_context_t = Context; pub type dc_context_t = Context;
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("unable to create tokio runtime"));
fn block_on<T>(fut: T) -> T::Output
where
T: Future,
{
RT.block_on(fut)
}
fn spawn<T>(fut: T) -> JoinHandle<T::Output>
where
T: Future + Send + 'static,
T::Output: Send + 'static,
{
RT.spawn(fut)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn dc_context_new( pub unsafe extern "C" fn dc_context_new(
_os_name: *const libc::c_char, _os_name: *const libc::c_char,
@@ -77,11 +97,7 @@ pub unsafe extern "C" fn dc_context_new(
let ctx = if blobdir.is_null() || *blobdir == 0 { let ctx = if blobdir.is_null() || *blobdir == 0 {
// generate random ID as this functionality is not yet available on the C-api. // generate random ID as this functionality is not yet available on the C-api.
let id = rand::thread_rng().gen(); let id = rand::thread_rng().gen();
block_on(Context::new( block_on(Context::new(as_path(dbfile), id, Events::new()))
as_path(dbfile).to_path_buf().into(),
id,
Events::new(),
))
} else { } else {
eprintln!("blobdir can not be defined explicitly anymore"); eprintln!("blobdir can not be defined explicitly anymore");
return ptr::null_mut(); return ptr::null_mut();
@@ -105,11 +121,7 @@ pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> *
} }
let id = rand::thread_rng().gen(); let id = rand::thread_rng().gen();
match block_on(Context::new_closed( match block_on(Context::new_closed(as_path(dbfile), id, Events::new())) {
as_path(dbfile).to_path_buf().into(),
id,
Events::new(),
)) {
Ok(context) => Box::into_raw(Box::new(context)), Ok(context) => Box::into_raw(Box::new(context)),
Err(err) => { Err(err) => {
eprintln!("failed to create context: {:#}", err); eprintln!("failed to create context: {:#}", err);
@@ -681,10 +693,13 @@ pub unsafe extern "C" fn dc_get_next_event(events: *mut dc_event_emitter_t) -> *
} }
let events = &*events; let events = &*events;
events block_on(async move {
.recv_sync() events
.map(|ev| Box::into_raw(Box::new(ev))) .recv()
.unwrap_or_else(ptr::null_mut) .await
.map(|ev| Box::into_raw(Box::new(ev)))
.unwrap_or_else(ptr::null_mut)
})
} }
#[no_mangle] #[no_mangle]
@@ -2393,7 +2408,7 @@ pub unsafe extern "C" fn dc_get_last_error(context: *mut dc_context_t) -> *mut l
return "".strdup(); return "".strdup();
} }
let ctx = &*context; let ctx = &*context;
block_on(ctx.get_last_error()).strdup() ctx.get_last_error().strdup()
} }
// dc_array_t // dc_array_t
@@ -4126,7 +4141,7 @@ pub unsafe extern "C" fn dc_accounts_new(
return ptr::null_mut(); return ptr::null_mut();
} }
let accs = block_on(Accounts::new(as_path(dbfile).to_path_buf().into())); let accs = block_on(Accounts::new(as_path(dbfile).into()));
match accs { match accs {
Ok(accs) => Box::into_raw(Box::new(AccountsWrapper::new(accs))), Ok(accs) => Box::into_raw(Box::new(AccountsWrapper::new(accs))),
@@ -4298,7 +4313,7 @@ pub unsafe extern "C" fn dc_accounts_migrate_account(
block_on(async move { block_on(async move {
let mut accounts = accounts.write().await; let mut accounts = accounts.write().await;
match accounts match accounts
.migrate_account(async_std::path::PathBuf::from(dbfile)) .migrate_account(std::path::PathBuf::from(dbfile))
.await .await
{ {
Ok(id) => id, Ok(id) => id,
@@ -4418,9 +4433,7 @@ pub unsafe extern "C" fn dc_accounts_get_next_event(
return ptr::null_mut(); return ptr::null_mut();
} }
let emitter = &mut *emitter; let emitter = &mut *emitter;
block_on(emitter.recv())
emitter
.recv_sync()
.map(|ev| Box::into_raw(Box::new(ev))) .map(|ev| Box::into_raw(Box::new(ev)))
.unwrap_or_else(ptr::null_mut) .unwrap_or_else(ptr::null_mut)
} }

View File

@@ -1,9 +1,10 @@
extern crate dirs; extern crate dirs;
use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, SystemTime};
use anyhow::{bail, ensure, Result}; use anyhow::{bail, ensure, Result};
use async_std::path::Path;
use deltachat::chat::{ use deltachat::chat::{
self, Chat, ChatId, ChatItem, ChatVisibility, MuteDuration, ProtectionStatus, self, Chat, ChatId, ChatItem, ChatVisibility, MuteDuration, ProtectionStatus,
}; };
@@ -22,8 +23,7 @@ use deltachat::peerstate::*;
use deltachat::qr::*; use deltachat::qr::*;
use deltachat::sql; use deltachat::sql;
use deltachat::{config, provider}; use deltachat::{config, provider};
use std::fs; use tokio::fs;
use std::time::{Duration, SystemTime};
/// Reset database tables. /// Reset database tables.
/// Argument is a bitmask, executing single or multiple actions in one call. /// Argument is a bitmask, executing single or multiple actions in one call.
@@ -135,17 +135,13 @@ async fn poke_spec(context: &Context, spec: Option<&str>) -> bool {
} else { } else {
/* import a directory */ /* import a directory */
let dir_name = std::path::Path::new(&real_spec); let dir_name = std::path::Path::new(&real_spec);
let dir = std::fs::read_dir(dir_name); let dir = fs::read_dir(dir_name).await;
if dir.is_err() { if dir.is_err() {
error!(context, "Import: Cannot open directory \"{}\".", &real_spec,); error!(context, "Import: Cannot open directory \"{}\".", &real_spec,);
return false; return false;
} else { } else {
let dir = dir.unwrap(); let mut dir = dir.unwrap();
for entry in dir { while let Ok(Some(entry)) = dir.next_entry().await {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name(); let name_f = entry.file_name();
let name = name_f.to_string_lossy(); let name = name_f.to_string_lossy();
if name.ends_with(".eml") { if name.ends_with(".eml") {
@@ -492,7 +488,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
let setup_code = create_setup_code(&context); let setup_code = create_setup_code(&context);
let file_name = blobdir.join("autocrypt-setup-message.html"); let file_name = blobdir.join("autocrypt-setup-message.html");
let file_content = render_setup_file(&context, &setup_code).await?; let file_content = render_setup_file(&context, &setup_code).await?;
async_std::fs::write(&file_name, file_content).await?; fs::write(&file_name, file_content).await?;
println!( println!(
"Setup message written to: {}\nSetup code: {}", "Setup message written to: {}\nSetup code: {}",
file_name.display(), file_name.display(),
@@ -532,7 +528,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
.join("connectivity.html"); .join("connectivity.html");
match context.get_connectivity_html().await { match context.get_connectivity_html().await {
Ok(html) => { Ok(html) => {
fs::write(&file, html)?; fs::write(&file, html).await?;
println!("Report written to: {:#?}", file); println!("Report written to: {:#?}", file);
} }
Err(err) => { Err(err) => {
@@ -892,7 +888,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
ensure!(sel_chat.is_some(), "No chat selected."); ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "No html-file given."); ensure!(!arg1.is_empty(), "No html-file given.");
let path: &Path = arg1.as_ref(); let path: &Path = arg1.as_ref();
let html = &*fs::read(&path)?; let html = &*fs::read(&path).await?;
let html = String::from_utf8_lossy(html); let html = String::from_utf8_lossy(html);
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
@@ -1079,7 +1075,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
.unwrap_or_default() .unwrap_or_default()
.join(format!("msg-{}.html", id.to_u32())); .join(format!("msg-{}.html", id.to_u32()));
let html = id.get_html(&context).await?.unwrap_or_default(); let html = id.get_html(&context).await?.unwrap_or_default();
fs::write(&file, html)?; fs::write(&file, html).await?;
println!("HTML written to: {:#?}", file); println!("HTML written to: {:#?}", file);
} }
"listfresh" => { "listfresh" => {

View File

@@ -9,15 +9,16 @@ extern crate deltachat;
use std::borrow::Cow::{self, Borrowed, Owned}; use std::borrow::Cow::{self, Borrowed, Owned};
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::Path;
use std::process::Command; use std::process::Command;
use ansi_term::Color; use ansi_term::Color;
use anyhow::{bail, Error}; use anyhow::{bail, Error};
use async_std::path::Path;
use deltachat::chat::ChatId; use deltachat::chat::ChatId;
use deltachat::config; use deltachat::config;
use deltachat::context::*; use deltachat::context::*;
use deltachat::oauth2::*; use deltachat::oauth2::*;
use deltachat::qr_code_generator::get_securejoin_qr_svg;
use deltachat::securejoin::*; use deltachat::securejoin::*;
use deltachat::{EventType, Events}; use deltachat::{EventType, Events};
use log::{error, info, warn}; use log::{error, info, warn};
@@ -30,11 +31,11 @@ use rustyline::validate::Validator;
use rustyline::{ use rustyline::{
Cmd, CompletionType, Config, Context as RustyContext, EditMode, Editor, Helper, KeyEvent, Cmd, CompletionType, Config, Context as RustyContext, EditMode, Editor, Helper, KeyEvent,
}; };
use tokio::fs;
use tokio::runtime::Handle;
mod cmdline; mod cmdline;
use self::cmdline::*; use self::cmdline::*;
use deltachat::qr_code_generator::get_securejoin_qr_svg;
use std::fs;
/// Event Handler /// Event Handler
fn receive_event(event: EventType) { fn receive_event(event: EventType) {
@@ -298,10 +299,10 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
println!("Error: Bad arguments, expected [db-name]."); println!("Error: Bad arguments, expected [db-name].");
bail!("No db-name specified"); bail!("No db-name specified");
} }
let context = Context::new(Path::new(&args[1]).to_path_buf(), 0, Events::new()).await?; let context = Context::new(Path::new(&args[1]), 0, Events::new()).await?;
let events = context.get_event_emitter(); let events = context.get_event_emitter();
async_std::task::spawn(async move { tokio::task::spawn(async move {
while let Some(event) = events.recv().await { while let Some(event) = events.recv().await {
receive_event(event.typ); receive_event(event.typ);
} }
@@ -316,8 +317,9 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
.output_stream(OutputStreamType::Stdout) .output_stream(OutputStreamType::Stdout)
.build(); .build();
let mut selected_chat = ChatId::default(); let mut selected_chat = ChatId::default();
let (reader_s, reader_r) = async_std::channel::bounded(100);
let input_loop = async_std::task::spawn_blocking(move || { let ctx = context.clone();
let input_loop = tokio::task::spawn_blocking(move || {
let h = DcHelper { let h = DcHelper {
completer: FilenameCompleter::new(), completer: FilenameCompleter::new(),
highlighter: MatchingBracketHighlighter::new(), highlighter: MatchingBracketHighlighter::new(),
@@ -339,16 +341,30 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
Ok(line) => { Ok(line) => {
// TODO: ignore "set mail_pw" // TODO: ignore "set mail_pw"
rl.add_history_entry(line.as_str()); rl.add_history_entry(line.as_str());
async_std::task::block_on(reader_s.send(line)).unwrap(); let contine = Handle::current().block_on(async {
match handle_cmd(line.trim(), ctx.clone(), &mut selected_chat).await {
Ok(ExitResult::Continue) => true,
Ok(ExitResult::Exit) => {
println!("Exiting ...");
false
}
Err(err) => {
println!("Error: {}", err);
true
}
}
});
if !contine {
break;
}
} }
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => { Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
println!("Exiting..."); println!("Exiting...");
drop(reader_s);
break; break;
} }
Err(err) => { Err(err) => {
println!("Error: {}", err); println!("Error: {}", err);
drop(reader_s);
break; break;
} }
} }
@@ -359,15 +375,8 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
Ok::<_, Error>(()) Ok::<_, Error>(())
}); });
while let Ok(line) = reader_r.recv().await {
match handle_cmd(line.trim(), context.clone(), &mut selected_chat).await {
Ok(ExitResult::Continue) => {}
Ok(ExitResult::Exit) => break,
Err(err) => println!("Error: {}", err),
}
}
context.stop_io().await; context.stop_io().await;
input_loop.await?; input_loop.await??;
Ok(()) Ok(())
} }
@@ -437,7 +446,7 @@ async fn handle_cmd(
let file = dirs::home_dir().unwrap_or_default().join("qr.svg"); let file = dirs::home_dir().unwrap_or_default().join("qr.svg");
match get_securejoin_qr_svg(&ctx, group).await { match get_securejoin_qr_svg(&ctx, group).await {
Ok(svg) => { Ok(svg) => {
fs::write(&file, svg)?; fs::write(&file, svg).await?;
println!("QR code svg written to: {:#?}", file); println!("QR code svg written to: {:#?}", file);
} }
Err(err) => { Err(err) => {
@@ -458,11 +467,12 @@ async fn handle_cmd(
Ok(ExitResult::Continue) Ok(ExitResult::Continue)
} }
fn main() -> Result<(), Error> { #[tokio::main]
async fn main() -> Result<(), Error> {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let args = std::env::args().collect(); let args = std::env::args().collect();
async_std::task::block_on(async move { start(args).await })?; start(args).await?;
Ok(()) Ok(())
} }

View File

@@ -29,21 +29,21 @@ fn cb(event: EventType) {
} }
/// Run with `RUST_LOG=simple=info cargo run --release --example simple --features repl -- email pw`. /// Run with `RUST_LOG=simple=info cargo run --release --example simple --features repl -- email pw`.
#[async_std::main] #[tokio::main]
async fn main() { async fn main() {
pretty_env_logger::try_init_timed().ok(); pretty_env_logger::try_init_timed().ok();
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
log::info!("creating database {:?}", dbfile); log::info!("creating database {:?}", dbfile);
let ctx = Context::new(dbfile.into(), 0, Events::new()) let ctx = Context::new(&dbfile, 0, Events::new())
.await .await
.expect("Failed to create context"); .expect("Failed to create context");
let info = ctx.get_info().await; let info = ctx.get_info().await;
log::info!("info: {:#?}", info); log::info!("info: {:#?}", info);
let events = ctx.get_event_emitter(); let events = ctx.get_event_emitter();
let events_spawn = async_std::task::spawn(async move { let events_spawn = tokio::task::spawn(async move {
while let Some(event) = events.recv().await { while let Some(event) = events.recv().await {
cb(event.typ); cb(event.typ);
} }
@@ -80,7 +80,7 @@ async fn main() {
} }
// wait for the message to be sent out // wait for the message to be sent out
async_std::task::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
log::info!("fetching chats.."); log::info!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).await.unwrap(); let chats = Chatlist::try_load(&ctx, 0, None, None).await.unwrap();
@@ -96,5 +96,5 @@ async fn main() {
ctx.stop_io().await; ctx.stop_io().await;
log::info!("closing"); log::info!("closing");
drop(ctx); drop(ctx);
events_spawn.await; events_spawn.await.unwrap();
} }

View File

@@ -1,13 +1,12 @@
//! # Account manager module. //! # Account manager module.
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use async_std::fs;
use async_std::path::PathBuf;
use uuid::Uuid;
use anyhow::{ensure, Context as _, Result}; use anyhow::{ensure, Context as _, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::fs;
use uuid::Uuid;
use crate::context::Context; use crate::context::Context;
use crate::events::{Event, EventEmitter, EventType, Events}; use crate::events::{Event, EventEmitter, EventType, Events};
@@ -26,7 +25,7 @@ pub struct Accounts {
impl Accounts { impl Accounts {
/// Loads or creates an accounts folder at the given `dir`. /// Loads or creates an accounts folder at the given `dir`.
pub async fn new(dir: PathBuf) -> Result<Self> { pub async fn new(dir: PathBuf) -> Result<Self> {
if !dir.exists().await { if !dir.exists() {
Accounts::create(&dir).await?; Accounts::create(&dir).await?;
} }
@@ -47,14 +46,10 @@ impl Accounts {
/// Opens an existing accounts structure. Will error if the folder doesn't exist, /// Opens an existing accounts structure. Will error if the folder doesn't exist,
/// no account exists and no config exists. /// no account exists and no config exists.
pub async fn open(dir: PathBuf) -> Result<Self> { pub async fn open(dir: PathBuf) -> Result<Self> {
ensure!(dir.exists().await, "directory does not exist"); ensure!(dir.exists(), "directory does not exist");
let config_file = dir.join(CONFIG_NAME); let config_file = dir.join(CONFIG_NAME);
ensure!( ensure!(config_file.exists(), "{:?} does not exist", config_file);
config_file.exists().await,
"{:?} does not exist",
config_file
);
let config = Config::from_file(config_file) let config = Config::from_file(config_file)
.await .await
@@ -106,7 +101,7 @@ impl Accounts {
let account_config = self.config.new_account(&self.dir).await?; let account_config = self.config.new_account(&self.dir).await?;
let ctx = Context::new( let ctx = Context::new(
account_config.dbfile().into(), &account_config.dbfile(),
account_config.id, account_config.id,
self.events.clone(), self.events.clone(),
) )
@@ -121,7 +116,7 @@ impl Accounts {
let account_config = self.config.new_account(&self.dir).await?; let account_config = self.config.new_account(&self.dir).await?;
let ctx = Context::new_closed( let ctx = Context::new_closed(
account_config.dbfile().into(), &account_config.dbfile(),
account_config.id, account_config.id,
self.events.clone(), self.events.clone(),
) )
@@ -148,7 +143,7 @@ impl Accounts {
loop { loop {
counter += 1; counter += 1;
if let Err(err) = fs::remove_dir_all(async_std::path::PathBuf::from(&cfg.dir)) if let Err(err) = fs::remove_dir_all(&cfg.dir)
.await .await
.context("failed to remove account data") .context("failed to remove account data")
{ {
@@ -157,7 +152,7 @@ impl Accounts {
} }
// Wait 1 second and try again. // Wait 1 second and try again.
async_std::task::sleep(std::time::Duration::from_millis(1000)).await; tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
} else { } else {
break; break;
} }
@@ -173,16 +168,8 @@ impl Accounts {
let blobdir = Context::derive_blobdir(&dbfile); let blobdir = Context::derive_blobdir(&dbfile);
let walfile = Context::derive_walfile(&dbfile); let walfile = Context::derive_walfile(&dbfile);
ensure!( ensure!(dbfile.exists(), "no database found: {}", dbfile.display());
dbfile.exists().await, ensure!(blobdir.exists(), "no blobdir found: {}", blobdir.display());
"no database found: {}",
dbfile.display()
);
ensure!(
blobdir.exists().await,
"no blobdir found: {}",
blobdir.display()
);
let old_id = self.config.get_selected_account().await; let old_id = self.config.get_selected_account().await;
@@ -193,7 +180,7 @@ impl Accounts {
.await .await
.context("failed to create new account")?; .context("failed to create new account")?;
let new_dbfile = account_config.dbfile().into(); let new_dbfile = account_config.dbfile();
let new_blobdir = Context::derive_blobdir(&new_dbfile); let new_blobdir = Context::derive_blobdir(&new_dbfile);
let new_walfile = Context::derive_walfile(&new_dbfile); let new_walfile = Context::derive_walfile(&new_dbfile);
@@ -207,7 +194,7 @@ impl Accounts {
fs::rename(&blobdir, &new_blobdir) fs::rename(&blobdir, &new_blobdir)
.await .await
.context("failed to rename blobdir")?; .context("failed to rename blobdir")?;
if walfile.exists().await { if walfile.exists() {
fs::rename(&walfile, &new_walfile) fs::rename(&walfile, &new_walfile)
.await .await
.context("failed to rename walfile")?; .context("failed to rename walfile")?;
@@ -217,13 +204,13 @@ impl Accounts {
match res { match res {
Ok(_) => { Ok(_) => {
let ctx = Context::new(new_dbfile, account_config.id, self.events.clone()).await?; let ctx = Context::new(&new_dbfile, account_config.id, self.events.clone()).await?;
self.accounts.insert(account_config.id, ctx); self.accounts.insert(account_config.id, ctx);
Ok(account_config.id) Ok(account_config.id)
} }
Err(err) => { Err(err) => {
// remove temp account // remove temp account
fs::remove_dir_all(async_std::path::PathBuf::from(&account_config.dir)) fs::remove_dir_all(std::path::PathBuf::from(&account_config.dir))
.await .await
.context("failed to remove account data")?; .context("failed to remove account data")?;
@@ -321,7 +308,7 @@ struct InnerConfig {
} }
impl Config { impl Config {
pub async fn new(dir: &PathBuf) -> Result<Self> { pub async fn new(dir: &Path) -> Result<Self> {
let inner = InnerConfig { let inner = InnerConfig {
accounts: Vec::new(), accounts: Vec::new(),
selected_account: 0, selected_account: 0,
@@ -355,18 +342,14 @@ impl Config {
pub async fn load_accounts(&self, events: &Events) -> Result<BTreeMap<u32, Context>> { pub async fn load_accounts(&self, events: &Events) -> Result<BTreeMap<u32, Context>> {
let mut accounts = BTreeMap::new(); let mut accounts = BTreeMap::new();
for account_config in &self.inner.accounts { for account_config in &self.inner.accounts {
let ctx = Context::new( let ctx = Context::new(&account_config.dbfile(), account_config.id, events.clone())
account_config.dbfile().into(), .await
account_config.id, .with_context(|| {
events.clone(), format!(
) "failed to create context from file {:?}",
.await account_config.dbfile()
.with_context(|| { )
format!( })?;
"failed to create context from file {:?}",
account_config.dbfile()
)
})?;
accounts.insert(account_config.id, ctx); accounts.insert(account_config.id, ctx);
} }
@@ -375,7 +358,7 @@ impl Config {
} }
/// Create a new account in the given root directory. /// Create a new account in the given root directory.
async fn new_account(&mut self, dir: &PathBuf) -> Result<AccountConfig> { async fn new_account(&mut self, dir: &Path) -> Result<AccountConfig> {
let id = { let id = {
let id = self.inner.next_id; let id = self.inner.next_id;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
@@ -383,7 +366,7 @@ impl Config {
self.inner.accounts.push(AccountConfig { self.inner.accounts.push(AccountConfig {
id, id,
dir: target_dir.into(), dir: target_dir,
uuid, uuid,
}); });
self.inner.next_id += 1; self.inner.next_id += 1;
@@ -464,10 +447,10 @@ impl AccountConfig {
mod tests { mod tests {
use super::*; use super::*;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_account_new_open() { async fn test_account_new_open() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts1").into(); let p: PathBuf = dir.path().join("accounts1");
let mut accounts1 = Accounts::new(p.clone()).await.unwrap(); let mut accounts1 = Accounts::new(p.clone()).await.unwrap();
accounts1.add_account().await.unwrap(); accounts1.add_account().await.unwrap();
@@ -482,10 +465,10 @@ mod tests {
assert_eq!(accounts1.accounts.len(), accounts2.accounts.len()); assert_eq!(accounts1.accounts.len(), accounts2.accounts.len());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_account_new_add_remove() { async fn test_account_new_add_remove() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let mut accounts = Accounts::new(p.clone()).await.unwrap(); let mut accounts = Accounts::new(p.clone()).await.unwrap();
assert_eq!(accounts.accounts.len(), 0); assert_eq!(accounts.accounts.len(), 0);
@@ -509,10 +492,10 @@ mod tests {
assert_eq!(accounts.accounts.len(), 1); assert_eq!(accounts.accounts.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_accounts_remove_last() -> Result<()> { async fn test_accounts_remove_last() -> Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let mut accounts = Accounts::new(p.clone()).await?; let mut accounts = Accounts::new(p.clone()).await?;
assert!(accounts.get_selected_account().await.is_none()); assert!(accounts.get_selected_account().await.is_none());
@@ -530,17 +513,17 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_migrate_account() { async fn test_migrate_account() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let mut accounts = Accounts::new(p.clone()).await.unwrap(); let mut accounts = Accounts::new(p.clone()).await.unwrap();
assert_eq!(accounts.accounts.len(), 0); assert_eq!(accounts.accounts.len(), 0);
assert_eq!(accounts.config.get_selected_account().await, 0); assert_eq!(accounts.config.get_selected_account().await, 0);
let extern_dbfile: PathBuf = dir.path().join("other").into(); let extern_dbfile: PathBuf = dir.path().join("other");
let ctx = Context::new(extern_dbfile.clone(), 0, Events::new()) let ctx = Context::new(&extern_dbfile, 0, Events::new())
.await .await
.unwrap(); .unwrap();
ctx.set_config(crate::config::Config::Addr, Some("me@mail.com")) ctx.set_config(crate::config::Config::Addr, Some("me@mail.com"))
@@ -567,10 +550,10 @@ mod tests {
} }
/// Tests that accounts are sorted by ID. /// Tests that accounts are sorted by ID.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_accounts_sorted() { async fn test_accounts_sorted() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let mut accounts = Accounts::new(p.clone()).await.unwrap(); let mut accounts = Accounts::new(p.clone()).await.unwrap();
@@ -585,10 +568,10 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_accounts_ids_unique_increasing_and_persisted() -> Result<()> { async fn test_accounts_ids_unique_increasing_and_persisted() -> Result<()> {
let dir = tempfile::tempdir()?; let dir = tempfile::tempdir()?;
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let dummy_accounts = 10; let dummy_accounts = 10;
let (id0, id1, id2) = { let (id0, id1, id2) = {
@@ -667,10 +650,10 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_no_accounts_event_emitter() -> Result<()> { async fn test_no_accounts_event_emitter() -> Result<()> {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let accounts = Accounts::new(p.clone()).await?; let accounts = Accounts::new(p.clone()).await?;
@@ -682,7 +665,7 @@ mod tests {
// Test that event emitter does not return `None` immediately. // Test that event emitter does not return `None` immediately.
let duration = std::time::Duration::from_millis(1); let duration = std::time::Duration::from_millis(1);
assert!(async_std::future::timeout(duration, event_emitter.recv()) assert!(tokio::time::timeout(duration, event_emitter.recv())
.await .await
.is_err()); .is_err());
@@ -693,10 +676,10 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_encrypted_account() -> Result<()> { async fn test_encrypted_account() -> Result<()> {
let dir = tempfile::tempdir().context("failed to create tempdir")?; let dir = tempfile::tempdir().context("failed to create tempdir")?;
let p: PathBuf = dir.path().join("accounts").into(); let p: PathBuf = dir.path().join("accounts");
let mut accounts = Accounts::new(p.clone()) let mut accounts = Accounts::new(p.clone())
.await .await

View File

@@ -4,14 +4,13 @@ use core::cmp::max;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
use std::path::{Path, PathBuf};
use async_std::path::{Path, PathBuf};
use async_std::prelude::*;
use async_std::{fs, io};
use anyhow::{format_err, Context as _, Error, Result}; use anyhow::{format_err, Context as _, Error, Result};
use image::{DynamicImage, ImageFormat}; use image::{DynamicImage, ImageFormat};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use tokio::io::AsyncWriteExt;
use tokio::{fs, io};
use crate::config::Config; use crate::config::Config;
use crate::constants::{ use crate::constants::{
@@ -89,7 +88,7 @@ impl<'a> BlobObject<'a> {
Err(err) => { Err(err) => {
if attempt >= MAX_ATTEMPT { if attempt >= MAX_ATTEMPT {
return Err(err).context("failed to create file"); return Err(err).context("failed to create file");
} else if attempt == 1 && !dir.exists().await { } else if attempt == 1 && !dir.exists() {
fs::create_dir_all(dir).await.ok_or_log(context); fs::create_dir_all(dir).await.ok_or_log(context);
} else { } else {
name = format!("{}-{}{}", stem, rand::random::<u32>(), ext); name = format!("{}-{}{}", stem, rand::random::<u32>(), ext);
@@ -371,108 +370,81 @@ impl<'a> BlobObject<'a> {
mut img_wh: u32, mut img_wh: u32,
max_bytes: Option<usize>, max_bytes: Option<usize>,
) -> Result<Option<String>> { ) -> Result<Option<String>> {
let mut img = image::open(&blob_abs).context("image recode failure")?; tokio::task::block_in_place(move || {
let orientation = self.get_exif_orientation(context); let mut img = image::open(&blob_abs).context("image recode failure")?;
let mut encoded = Vec::new(); let orientation = self.get_exif_orientation(context);
let mut changed_name = None; let mut encoded = Vec::new();
let mut changed_name = None;
fn encode_img(img: &DynamicImage, encoded: &mut Vec<u8>) -> anyhow::Result<()> { let exceeds_width = img.width() > img_wh || img.height() > img_wh;
encoded.clear();
let mut buf = Cursor::new(encoded);
img.write_to(&mut buf, image::ImageFormat::Jpeg)?;
Ok(())
}
fn encoded_img_exceeds_bytes(
context: &Context,
img: &DynamicImage,
max_bytes: Option<usize>,
encoded: &mut Vec<u8>,
) -> anyhow::Result<bool> {
if let Some(max_bytes) = max_bytes {
encode_img(img, encoded)?;
if encoded.len() > max_bytes {
info!(
context,
"image size {}B ({}x{}px) exceeds {}B, need to scale down",
encoded.len(),
img.width(),
img.height(),
max_bytes,
);
return Ok(true);
}
}
Ok(false)
}
let exceeds_width = img.width() > img_wh || img.height() > img_wh;
let do_scale = let do_scale =
exceeds_width || encoded_img_exceeds_bytes(context, &img, max_bytes, &mut encoded)?; exceeds_width || encoded_img_exceeds_bytes(context, &img, max_bytes, &mut encoded)?;
let do_rotate = matches!(orientation, Ok(90) | Ok(180) | Ok(270)); let do_rotate = matches!(orientation, Ok(90) | Ok(180) | Ok(270));
if do_scale || do_rotate { if do_scale || do_rotate {
if do_rotate { if do_rotate {
img = match orientation { img = match orientation {
Ok(90) => img.rotate90(), Ok(90) => img.rotate90(),
Ok(180) => img.rotate180(), Ok(180) => img.rotate180(),
Ok(270) => img.rotate270(), Ok(270) => img.rotate270(),
_ => img, _ => img,
}
}
if do_scale {
if !exceeds_width {
// The image is already smaller than img_wh, but exceeds max_bytes
// We can directly start with trying to scale down to 2/3 of its current width
img_wh = max(img.width(), img.height()) * 2 / 3
}
loop {
let new_img = img.thumbnail(img_wh, img_wh);
if encoded_img_exceeds_bytes(context, &new_img, max_bytes, &mut encoded)? {
if img_wh < 20 {
return Err(format_err!(
"Failed to scale image to below {}B",
max_bytes.unwrap_or_default()
));
}
img_wh = img_wh * 2 / 3;
} else {
if encoded.is_empty() {
encode_img(&new_img, &mut encoded)?;
}
info!(
context,
"Final scaled-down image size: {}B ({}px)",
encoded.len(),
img_wh
);
break;
} }
} }
if do_scale {
if !exceeds_width {
// The image is already smaller than img_wh, but exceeds max_bytes
// We can directly start with trying to scale down to 2/3 of its current width
img_wh = max(img.width(), img.height()) * 2 / 3
}
loop {
let new_img = img.thumbnail(img_wh, img_wh);
if encoded_img_exceeds_bytes(context, &new_img, max_bytes, &mut encoded)? {
if img_wh < 20 {
return Err(format_err!(
"Failed to scale image to below {}B",
max_bytes.unwrap_or_default()
));
}
img_wh = img_wh * 2 / 3;
} else {
if encoded.is_empty() {
encode_img(&new_img, &mut encoded)?;
}
info!(
context,
"Final scaled-down image size: {}B ({}px)",
encoded.len(),
img_wh
);
break;
}
}
}
// The file format is JPEG now, we may have to change the file extension
if !matches!(ImageFormat::from_path(&blob_abs), Ok(ImageFormat::Jpeg)) {
blob_abs = blob_abs.with_extension("jpg");
let file_name = blob_abs.file_name().context("No avatar file name (???)")?;
let file_name = file_name.to_str().context("Filename is no UTF-8 (???)")?;
changed_name = Some(format!("$BLOBDIR/{}", file_name));
}
if encoded.is_empty() {
encode_img(&img, &mut encoded)?;
}
std::fs::write(&blob_abs, &encoded)
.context("failed to write recoded blob to file")?;
} }
// The file format is JPEG now, we may have to change the file extension Ok(changed_name)
if !matches!(ImageFormat::from_path(&blob_abs), Ok(ImageFormat::Jpeg)) { })
blob_abs = blob_abs.with_extension("jpg");
let file_name = blob_abs.file_name().context("No avatar file name (???)")?;
let file_name = file_name.to_str().context("Filename is no UTF-8 (???)")?;
changed_name = Some(format!("$BLOBDIR/{}", file_name));
}
if encoded.is_empty() {
encode_img(&img, &mut encoded)?;
}
fs::write(&blob_abs, &encoded)
.await
.context("failed to write recoded blob to file")?;
}
Ok(changed_name)
} }
pub fn get_exif_orientation(&self, context: &Context) -> Result<i32, Error> { pub fn get_exif_orientation(&self, context: &Context) -> Result<i32, Error> {
@@ -500,6 +472,35 @@ impl<'a> fmt::Display for BlobObject<'a> {
} }
} }
fn encode_img(img: &DynamicImage, encoded: &mut Vec<u8>) -> anyhow::Result<()> {
encoded.clear();
let mut buf = Cursor::new(encoded);
img.write_to(&mut buf, image::ImageFormat::Jpeg)?;
Ok(())
}
fn encoded_img_exceeds_bytes(
context: &Context,
img: &DynamicImage,
max_bytes: Option<usize>,
encoded: &mut Vec<u8>,
) -> anyhow::Result<bool> {
if let Some(max_bytes) = max_bytes {
encode_img(img, encoded)?;
if encoded.len() > max_bytes {
info!(
context,
"image size {}B ({}x{}px) exceeds {}B, need to scale down",
encoded.len(),
img.width(),
img.height(),
max_bytes,
);
return Ok(true);
}
}
Ok(false)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use fs::File; use fs::File;
@@ -513,7 +514,16 @@ mod tests {
use super::*; use super::*;
#[async_std::test] fn check_image_size(path: impl AsRef<Path>, width: u32, height: u32) -> image::DynamicImage {
tokio::task::block_in_place(move || {
let img = image::open(path).expect("failed to open image");
assert_eq!(img.width(), width, "invalid width");
assert_eq!(img.height(), height, "invalid height");
img
})
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create() { async fn test_create() {
let t = TestContext::new().await; let t = TestContext::new().await;
let blob = BlobObject::create(&t, "foo", b"hello").await.unwrap(); let blob = BlobObject::create(&t, "foo", b"hello").await.unwrap();
@@ -524,28 +534,28 @@ mod tests {
assert_eq!(blob.to_abs_path(), t.get_blobdir().join("foo")); assert_eq!(blob.to_abs_path(), t.get_blobdir().join("foo"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_lowercase_ext() { async fn test_lowercase_ext() {
let t = TestContext::new().await; let t = TestContext::new().await;
let blob = BlobObject::create(&t, "foo.TXT", b"hello").await.unwrap(); let blob = BlobObject::create(&t, "foo.TXT", b"hello").await.unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt"); assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_as_file_name() { async fn test_as_file_name() {
let t = TestContext::new().await; let t = TestContext::new().await;
let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap(); let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
assert_eq!(blob.as_file_name(), "foo.txt"); assert_eq!(blob.as_file_name(), "foo.txt");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_as_rel_path() { async fn test_as_rel_path() {
let t = TestContext::new().await; let t = TestContext::new().await;
let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap(); let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
assert_eq!(blob.as_rel_path(), Path::new("foo.txt")); assert_eq!(blob.as_rel_path(), Path::new("foo.txt"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_suffix() { async fn test_suffix() {
let t = TestContext::new().await; let t = TestContext::new().await;
let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap(); let blob = BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
@@ -554,16 +564,16 @@ mod tests {
assert_eq!(blob.suffix(), None); assert_eq!(blob.suffix(), None);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_dup() { async fn test_create_dup() {
let t = TestContext::new().await; let t = TestContext::new().await;
BlobObject::create(&t, "foo.txt", b"hello").await.unwrap(); BlobObject::create(&t, "foo.txt", b"hello").await.unwrap();
let foo_path = t.get_blobdir().join("foo.txt"); let foo_path = t.get_blobdir().join("foo.txt");
assert!(foo_path.exists().await); assert!(foo_path.exists());
BlobObject::create(&t, "foo.txt", b"world").await.unwrap(); BlobObject::create(&t, "foo.txt", b"world").await.unwrap();
let mut dir = fs::read_dir(t.get_blobdir()).await.unwrap(); let mut dir = fs::read_dir(t.get_blobdir()).await.unwrap();
while let Some(dirent) = dir.next().await { while let Ok(Some(dirent)) = dir.next_entry().await {
let fname = dirent.unwrap().file_name(); let fname = dirent.file_name();
if fname == foo_path.file_name().unwrap() { if fname == foo_path.file_name().unwrap() {
assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello"); assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello");
} else { } else {
@@ -574,20 +584,20 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_double_ext_preserved() { async fn test_double_ext_preserved() {
let t = TestContext::new().await; let t = TestContext::new().await;
BlobObject::create(&t, "foo.tar.gz", b"hello") BlobObject::create(&t, "foo.tar.gz", b"hello")
.await .await
.unwrap(); .unwrap();
let foo_path = t.get_blobdir().join("foo.tar.gz"); let foo_path = t.get_blobdir().join("foo.tar.gz");
assert!(foo_path.exists().await); assert!(foo_path.exists());
BlobObject::create(&t, "foo.tar.gz", b"world") BlobObject::create(&t, "foo.tar.gz", b"world")
.await .await
.unwrap(); .unwrap();
let mut dir = fs::read_dir(t.get_blobdir()).await.unwrap(); let mut dir = fs::read_dir(t.get_blobdir()).await.unwrap();
while let Some(dirent) = dir.next().await { while let Ok(Some(dirent)) = dir.next_entry().await {
let fname = dirent.unwrap().file_name(); let fname = dirent.file_name();
if fname == foo_path.file_name().unwrap() { if fname == foo_path.file_name().unwrap() {
assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello"); assert_eq!(fs::read(&foo_path).await.unwrap(), b"hello");
} else { } else {
@@ -599,7 +609,7 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_long_names() { async fn test_create_long_names() {
let t = TestContext::new().await; let t = TestContext::new().await;
let s = "1".repeat(150); let s = "1".repeat(150);
@@ -608,7 +618,7 @@ mod tests {
assert!(blobname.len() < 128); assert!(blobname.len() < 128);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_and_copy() { async fn test_create_and_copy() {
let t = TestContext::new().await; let t = TestContext::new().await;
let src = t.dir.path().join("src"); let src = t.dir.path().join("src");
@@ -623,10 +633,10 @@ mod tests {
.await .await
.is_err()); .is_err());
let whoops = t.get_blobdir().join("whoops"); let whoops = t.get_blobdir().join("whoops");
assert!(!whoops.exists().await); assert!(!whoops.exists());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_from_path() { async fn test_create_from_path() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -646,7 +656,7 @@ mod tests {
let data = fs::read(blob.to_abs_path()).await.unwrap(); let data = fs::read(blob.to_abs_path()).await.unwrap();
assert_eq!(data, b"boo"); assert_eq!(data, b"boo");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_from_name_long() { async fn test_create_from_name_long() {
let t = TestContext::new().await; let t = TestContext::new().await;
let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html"); let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html");
@@ -709,7 +719,7 @@ mod tests {
assert!(!stem.contains('?')); assert!(!stem.contains('?'));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_outside_blobdir() { async fn test_selfavatar_outside_blobdir() {
let t = TestContext::new().await; let t = TestContext::new().await;
let avatar_src = t.dir.path().join("avatar.jpg"); let avatar_src = t.dir.path().join("avatar.jpg");
@@ -721,22 +731,17 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let avatar_blob = t.get_blobdir().join("avatar.jpg"); let avatar_blob = t.get_blobdir().join("avatar.jpg");
assert!(!avatar_blob.exists().await); assert!(!avatar_blob.exists());
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap())) t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
.await .await
.unwrap(); .unwrap();
assert!(avatar_blob.exists().await); assert!(avatar_blob.exists());
assert!(std::fs::metadata(&avatar_blob).unwrap().len() < avatar_bytes.len() as u64); assert!(tokio::fs::metadata(&avatar_blob).await.unwrap().len() < avatar_bytes.len() as u64);
let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap(); let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap();
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string())); assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
let img = image::open(avatar_src).unwrap(); check_image_size(avatar_src, 1000, 1000);
assert_eq!(img.width(), 1000); check_image_size(&avatar_blob, BALANCED_AVATAR_SIZE, BALANCED_AVATAR_SIZE);
assert_eq!(img.height(), 1000);
let img = image::open(&avatar_blob).unwrap();
assert_eq!(img.width(), BALANCED_AVATAR_SIZE);
assert_eq!(img.height(), BALANCED_AVATAR_SIZE);
async fn file_size(path_buf: &PathBuf) -> u64 { async fn file_size(path_buf: &PathBuf) -> u64 {
let file = File::open(path_buf).await.unwrap(); let file = File::open(path_buf).await.unwrap();
@@ -750,12 +755,14 @@ mod tests {
.unwrap(); .unwrap();
assert!(file_size(&avatar_blob).await <= 3000); assert!(file_size(&avatar_blob).await <= 3000);
assert!(file_size(&avatar_blob).await > 2000); assert!(file_size(&avatar_blob).await > 2000);
let img = image::open(&avatar_blob).unwrap(); tokio::task::block_in_place(move || {
assert!(img.width() > 130); let img = image::open(&avatar_blob).unwrap();
assert_eq!(img.width(), img.height()); assert!(img.width() > 130);
assert_eq!(img.width(), img.height());
});
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_in_blobdir() { async fn test_selfavatar_in_blobdir() {
let t = TestContext::new().await; let t = TestContext::new().await;
let avatar_src = t.get_blobdir().join("avatar.png"); let avatar_src = t.get_blobdir().join("avatar.png");
@@ -766,9 +773,7 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let img = image::open(&avatar_src).unwrap(); check_image_size(&avatar_src, 900, 900);
assert_eq!(img.width(), 900);
assert_eq!(img.height(), 900);
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap())) t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
.await .await
@@ -779,12 +784,10 @@ mod tests {
avatar_src.with_extension("jpg").to_str().unwrap() avatar_src.with_extension("jpg").to_str().unwrap()
); );
let img = image::open(avatar_cfg).unwrap(); check_image_size(avatar_cfg, BALANCED_AVATAR_SIZE, BALANCED_AVATAR_SIZE);
assert_eq!(img.width(), BALANCED_AVATAR_SIZE);
assert_eq!(img.height(), BALANCED_AVATAR_SIZE);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_copy_without_recode() { async fn test_selfavatar_copy_without_recode() {
let t = TestContext::new().await; let t = TestContext::new().await;
let avatar_src = t.dir.path().join("avatar.png"); let avatar_src = t.dir.path().join("avatar.png");
@@ -796,20 +799,20 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let avatar_blob = t.get_blobdir().join("avatar.png"); let avatar_blob = t.get_blobdir().join("avatar.png");
assert!(!avatar_blob.exists().await); assert!(!avatar_blob.exists());
t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap())) t.set_config(Config::Selfavatar, Some(avatar_src.to_str().unwrap()))
.await .await
.unwrap(); .unwrap();
assert!(avatar_blob.exists().await); assert!(avatar_blob.exists());
assert_eq!( assert_eq!(
std::fs::metadata(&avatar_blob).unwrap().len(), tokio::fs::metadata(&avatar_blob).await.unwrap().len(),
avatar_bytes.len() as u64 avatar_bytes.len() as u64
); );
let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap(); let avatar_cfg = t.get_config(Config::Selfavatar).await.unwrap();
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string())); assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_recode_image_1() { async fn test_recode_image_1() {
let bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg"); let bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
// BALANCED_IMAGE_SIZE > 1000, the original image size, so the image is not scaled down: // BALANCED_IMAGE_SIZE > 1000, the original image size, so the image is not scaled down:
@@ -829,7 +832,7 @@ mod tests {
.unwrap(); .unwrap();
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_recode_image_2() { async fn test_recode_image_2() {
// The "-rotated" files are rotated by 270 degrees using the Exif metadata // The "-rotated" files are rotated by 270 degrees using the Exif metadata
let bytes = include_bytes!("../test-data/image/rectangle2000x1800-rotated.jpg"); let bytes = include_bytes!("../test-data/image/rectangle2000x1800-rotated.jpg");
@@ -855,7 +858,7 @@ mod tests {
// Do this in parallel to speed up the test a bit // Do this in parallel to speed up the test a bit
// (it still takes very long though) // (it still takes very long though)
let bytes2 = bytes.clone(); let bytes2 = bytes.clone();
let join_handle = async_std::task::spawn(async move { let join_handle = tokio::task::spawn(async move {
let img_rotated = send_image_check_mediaquality( let img_rotated = send_image_check_mediaquality(
Some("0"), Some("0"),
&bytes2, &bytes2,
@@ -883,10 +886,10 @@ mod tests {
.unwrap(); .unwrap();
assert_correct_rotation(&img_rotated); assert_correct_rotation(&img_rotated);
join_handle.await; join_handle.await.unwrap();
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_recode_image_3() { async fn test_recode_image_3() {
let bytes = include_bytes!("../test-data/image/rectangle200x180-rotated.jpg"); let bytes = include_bytes!("../test-data/image/rectangle200x180-rotated.jpg");
let img_rotated = send_image_check_mediaquality(Some("0"), bytes, 200, 180, 270, 180, 200) let img_rotated = send_image_check_mediaquality(Some("0"), bytes, 200, 180, 270, 180, 200)
@@ -934,10 +937,10 @@ mod tests {
.await?; .await?;
let file = alice.get_blobdir().join("file.jpg"); let file = alice.get_blobdir().join("file.jpg");
File::create(&file).await?.write_all(bytes).await?; fs::write(&file, &bytes)
let img = image::open(&file)?; .await
assert_eq!(img.width(), original_width); .context("failed to write file")?;
assert_eq!(img.height(), original_height); check_image_size(&file, original_width, original_height);
let blob = BlobObject::new_from_path(&alice, &file).await?; let blob = BlobObject::new_from_path(&alice, &file).await?;
assert_eq!(blob.get_exif_orientation(&alice).unwrap_or(0), orientation); assert_eq!(blob.get_exif_orientation(&alice).unwrap_or(0), orientation);
@@ -949,9 +952,11 @@ mod tests {
let alice_msg = alice.get_last_msg().await; let alice_msg = alice.get_last_msg().await;
assert_eq!(alice_msg.get_width() as u32, compressed_width); assert_eq!(alice_msg.get_width() as u32, compressed_width);
assert_eq!(alice_msg.get_height() as u32, compressed_height); assert_eq!(alice_msg.get_height() as u32, compressed_height);
let img = image::open(alice_msg.get_file(&alice).unwrap())?; check_image_size(
assert_eq!(img.width() as u32, compressed_width); alice_msg.get_file(&alice).unwrap(),
assert_eq!(img.height() as u32, compressed_height); compressed_width,
compressed_height,
);
let bob_msg = bob.recv_msg(&sent).await; let bob_msg = bob.recv_msg(&sent).await;
assert_eq!(bob_msg.get_width() as u32, compressed_width); assert_eq!(bob_msg.get_width() as u32, compressed_width);
@@ -961,13 +966,11 @@ mod tests {
let blob = BlobObject::new_from_path(&bob, &file).await?; let blob = BlobObject::new_from_path(&bob, &file).await?;
assert_eq!(blob.get_exif_orientation(&bob).unwrap_or(0), 0); assert_eq!(blob.get_exif_orientation(&bob).unwrap_or(0), 0);
let img = image::open(file)?; let img = check_image_size(file, compressed_width, compressed_height);
assert_eq!(img.width() as u32, compressed_width);
assert_eq!(img.height() as u32, compressed_height);
Ok(img) Ok(img)
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_increation_in_blobdir() -> Result<()> { async fn test_increation_in_blobdir() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?;
@@ -986,7 +989,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_increation_not_blobdir() -> Result<()> { async fn test_increation_not_blobdir() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?;

View File

@@ -2,11 +2,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use anyhow::{bail, ensure, Context as _, Result}; use anyhow::{bail, ensure, Context as _, Result};
use async_std::path::{Path, PathBuf};
use deltachat_derive::{FromSql, ToSql}; use deltachat_derive::{FromSql, ToSql};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -3464,10 +3464,10 @@ mod tests {
use crate::contact::Contact; use crate::contact::Contact;
use crate::dc_receive_imf::dc_receive_imf; use crate::dc_receive_imf::dc_receive_imf;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
use async_std::fs::File; use tokio::fs::File;
use async_std::prelude::*; use tokio::io::AsyncWriteExt;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_info() { async fn test_chat_info() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat = t.create_chat_with_contact("bob", "bob@example.com").await; let chat = t.create_chat_with_contact("bob", "bob@example.com").await;
@@ -3498,7 +3498,7 @@ mod tests {
assert_eq!(info, loaded); assert_eq!(info, loaded);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_draft_no_draft() { async fn test_get_draft_no_draft() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat = t.get_self_chat().await; let chat = t.get_self_chat().await;
@@ -3506,14 +3506,14 @@ mod tests {
assert!(draft.is_none()); assert!(draft.is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_draft_special_chat_id() { async fn test_get_draft_special_chat_id() {
let t = TestContext::new().await; let t = TestContext::new().await;
let draft = DC_CHAT_ID_LAST_SPECIAL.get_draft(&t).await.unwrap(); let draft = DC_CHAT_ID_LAST_SPECIAL.get_draft(&t).await.unwrap();
assert!(draft.is_none()); assert!(draft.is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_draft_no_chat() { async fn test_get_draft_no_chat() {
// This is a weird case, maybe this should be an error but we // This is a weird case, maybe this should be an error but we
// do not get this info from the database currently. // do not get this info from the database currently.
@@ -3522,7 +3522,7 @@ mod tests {
assert!(draft.is_none()); assert!(draft.is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_draft() { async fn test_get_draft() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = &t.get_self_chat().await.id; let chat_id = &t.get_self_chat().await.id;
@@ -3536,7 +3536,7 @@ mod tests {
assert_eq!(msg_text, draft_text); assert_eq!(msg_text, draft_text);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete_draft() -> Result<()> { async fn test_delete_draft() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "abc").await?;
@@ -3557,7 +3557,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forwarding_draft_failing() -> Result<()> { async fn test_forwarding_draft_failing() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = &t.get_self_chat().await.id; let chat_id = &t.get_self_chat().await.id;
@@ -3571,7 +3571,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_draft_stable_ids() -> Result<()> { async fn test_draft_stable_ids() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = &t.get_self_chat().await.id; let chat_id = &t.get_self_chat().await.id;
@@ -3616,7 +3616,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_change_quotes_on_reused_message_object() -> Result<()> { async fn test_change_quotes_on_reused_message_object() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
@@ -3668,7 +3668,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_contact_to_chat_ex_add_self() { async fn test_add_contact_to_chat_ex_add_self() {
// Adding self to a contact should succeed, even though it's pointless. // Adding self to a contact should succeed, even though it's pointless.
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -3681,7 +3681,7 @@ mod tests {
assert_eq!(added, false); assert_eq!(added, false);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_modify_chat_multi_device() -> Result<()> { async fn test_modify_chat_multi_device() -> Result<()> {
let a1 = TestContext::new_alice().await; let a1 = TestContext::new_alice().await;
let a2 = TestContext::new_alice().await; let a2 = TestContext::new_alice().await;
@@ -3756,7 +3756,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_modify_chat_disordered() -> Result<()> { async fn test_modify_chat_disordered() -> Result<()> {
// Alice creates a group with Bob, Claire and Daisy and then removes Claire and Daisy // Alice creates a group with Bob, Claire and Daisy and then removes Claire and Daisy
// (sleep() is needed as otherwise smeared time from Alice looks to Bob like messages from the future which are all set to "now" then) // (sleep() is needed as otherwise smeared time from Alice looks to Bob like messages from the future which are all set to "now" then)
@@ -3770,47 +3770,47 @@ mod tests {
send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?; send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?;
add_contact_to_chat(&alice, alice_chat_id, bob_id).await?; add_contact_to_chat(&alice, alice_chat_id, bob_id).await?;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
let add1 = alice.pop_sent_msg().await; let add1 = alice.pop_sent_msg().await;
add_contact_to_chat(&alice, alice_chat_id, claire_id).await?; add_contact_to_chat(&alice, alice_chat_id, claire_id).await?;
let add2 = alice.pop_sent_msg().await; let add2 = alice.pop_sent_msg().await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
add_contact_to_chat(&alice, alice_chat_id, daisy_id).await?; add_contact_to_chat(&alice, alice_chat_id, daisy_id).await?;
let add3 = alice.pop_sent_msg().await; let add3 = alice.pop_sent_msg().await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 4); assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 4);
remove_contact_from_chat(&alice, alice_chat_id, claire_id).await?; remove_contact_from_chat(&alice, alice_chat_id, claire_id).await?;
let remove1 = alice.pop_sent_msg().await; let remove1 = alice.pop_sent_msg().await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?; remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?;
let remove2 = alice.pop_sent_msg().await; let remove2 = alice.pop_sent_msg().await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2); assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2);
// Bob receives the add and deletion messages out of order // Bob receives the add and deletion messages out of order
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
bob.recv_msg(&add1).await; bob.recv_msg(&add1).await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
bob.recv_msg(&add3).await; bob.recv_msg(&add3).await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
let bob_chat_id = bob.recv_msg(&add2).await.chat_id; let bob_chat_id = bob.recv_msg(&add2).await.chat_id;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 4); assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 4);
bob.recv_msg(&remove2).await; bob.recv_msg(&remove2).await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
bob.recv_msg(&remove1).await; bob.recv_msg(&remove1).await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 2); assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 2);
@@ -3818,7 +3818,7 @@ mod tests {
} }
/// Test that group updates are robust to lost messages and eventual out of order arrival. /// Test that group updates are robust to lost messages and eventual out of order arrival.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_modify_chat_lost() -> Result<()> { async fn test_modify_chat_lost() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -3833,11 +3833,11 @@ mod tests {
send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?; send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?;
let add = alice.pop_sent_msg().await; let add = alice.pop_sent_msg().await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
remove_contact_from_chat(&alice, alice_chat_id, claire_id).await?; remove_contact_from_chat(&alice, alice_chat_id, claire_id).await?;
let remove1 = alice.pop_sent_msg().await; let remove1 = alice.pop_sent_msg().await;
async_std::task::sleep(std::time::Duration::from_millis(1100)).await; tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?; remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?;
let remove2 = alice.pop_sent_msg().await; let remove2 = alice.pop_sent_msg().await;
@@ -3860,7 +3860,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_leave_group() -> Result<()> { async fn test_leave_group() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -3889,7 +3889,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_remove_contact_for_single() { async fn test_add_remove_contact_for_single() {
let ctx = TestContext::new_alice().await; let ctx = TestContext::new_alice().await;
let bob = Contact::create(&ctx, "", "bob@f.br").await.unwrap(); let bob = Contact::create(&ctx, "", "bob@f.br").await.unwrap();
@@ -3913,7 +3913,7 @@ mod tests {
assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1); assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_self_talk() -> Result<()> { async fn test_self_talk() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat = &t.get_self_chat().await; let chat = &t.get_self_chat().await;
@@ -3944,7 +3944,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_device_msg_unlabelled() { async fn test_add_device_msg_unlabelled() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -3979,7 +3979,7 @@ mod tests {
assert_eq!(msg2.chat_id.get_msg_cnt(&t).await.unwrap(), 2); assert_eq!(msg2.chat_id.get_msg_cnt(&t).await.unwrap(), 2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_device_msg_labelled() -> Result<()> { async fn test_add_device_msg_labelled() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -4029,7 +4029,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_device_msg_label_only() { async fn test_add_device_msg_label_only() {
let t = TestContext::new().await; let t = TestContext::new().await;
let res = add_device_msg(&t, Some(""), None).await; let res = add_device_msg(&t, Some(""), None).await;
@@ -4049,7 +4049,7 @@ mod tests {
assert!(!msg_id.as_ref().unwrap().is_unset()); assert!(!msg_id.as_ref().unwrap().is_unset());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_was_device_msg_ever_added() { async fn test_was_device_msg_ever_added() {
let t = TestContext::new().await; let t = TestContext::new().await;
add_device_msg(&t, Some("some-label"), None).await.ok(); add_device_msg(&t, Some("some-label"), None).await.ok();
@@ -4069,7 +4069,7 @@ mod tests {
assert!(was_device_msg_ever_added(&t, "").await.is_err()); assert!(was_device_msg_ever_added(&t, "").await.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete_device_chat() { async fn test_delete_device_chat() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -4089,7 +4089,7 @@ mod tests {
assert_eq!(chatlist_len(&t, 0).await, 0) assert_eq!(chatlist_len(&t, 0).await, 0)
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_device_chat_cannot_sent() { async fn test_device_chat_cannot_sent() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.update_device_chats().await.unwrap(); t.update_device_chats().await.unwrap();
@@ -4106,7 +4106,7 @@ mod tests {
assert!(forward_msgs(&t, &[msg_id], device_chat_id).await.is_err()); assert!(forward_msgs(&t, &[msg_id], device_chat_id).await.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete_and_reset_all_device_msgs() { async fn test_delete_and_reset_all_device_msgs() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mut msg = Message::new(Viewtype::Text); let mut msg = Message::new(Viewtype::Text);
@@ -4138,7 +4138,7 @@ mod tests {
.len() .len()
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_archive() { async fn test_archive() {
// create two chats // create two chats
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -4241,7 +4241,7 @@ mod tests {
assert_eq!(chatlist_len(&t, DC_GCL_ARCHIVED_ONLY).await, 1); assert_eq!(chatlist_len(&t, DC_GCL_ARCHIVED_ONLY).await, 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_unarchive_if_muted() -> Result<()> { async fn test_unarchive_if_muted() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -4335,7 +4335,7 @@ mod tests {
result result
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pinned() { async fn test_pinned() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -4347,9 +4347,9 @@ mod tests {
.await .await
.unwrap() .unwrap()
.chat_id; .chat_id;
async_std::task::sleep(std::time::Duration::from_millis(1000)).await; tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
let chat_id2 = t.get_self_chat().await.id; let chat_id2 = t.get_self_chat().await.id;
async_std::task::sleep(std::time::Duration::from_millis(1000)).await; tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
let chat_id3 = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") let chat_id3 = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
.await .await
.unwrap(); .unwrap();
@@ -4392,7 +4392,7 @@ mod tests {
assert_eq!(chatlist, vec![chat_id3, chat_id2, chat_id1]); assert_eq!(chatlist, vec![chat_id3, chat_id2, chat_id1]);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_chat_name() { async fn test_set_chat_name() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
@@ -4410,7 +4410,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_same_chat_twice() { async fn test_create_same_chat_twice() {
let context = TestContext::new().await; let context = TestContext::new().await;
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de") let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de")
@@ -4433,7 +4433,7 @@ mod tests {
assert_eq!(chat2.name, chat.name); assert_eq!(chat2.name, chat.name);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_shall_attach_selfavatar() -> Result<()> { async fn test_shall_attach_selfavatar() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -4451,7 +4451,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_mute_duration() { async fn test_set_mute_duration() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
@@ -4502,7 +4502,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_info_msg() -> Result<()> { async fn test_add_info_msg() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -4519,7 +4519,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_info_msg_with_cmd() -> Result<()> { async fn test_add_info_msg_with_cmd() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -4549,7 +4549,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_protection() { async fn test_set_protection() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
@@ -4617,7 +4617,7 @@ mod tests {
assert_eq!(msg.get_state(), MessageState::OutDelivered); // as bcc-self is disabled and there is nobody else in the chat assert_eq!(msg.get_state(), MessageState::OutDelivered); // as bcc-self is disabled and there is nobody else in the chat
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_lookup_by_contact_id() { async fn test_lookup_by_contact_id() {
let ctx = TestContext::new_alice().await; let ctx = TestContext::new_alice().await;
@@ -4660,7 +4660,7 @@ mod tests {
assert!(found.is_none()); assert!(found.is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_lookup_self_by_contact_id() { async fn test_lookup_self_by_contact_id() {
let ctx = TestContext::new_alice().await; let ctx = TestContext::new_alice().await;
@@ -4679,7 +4679,7 @@ mod tests {
assert_eq!(chat.blocked, Blocked::Not); assert_eq!(chat.blocked, Blocked::Not);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_group_with_removed_message_id() -> Result<()> { async fn test_group_with_removed_message_id() -> Result<()> {
// Alice creates a group with Bob, sends a message to bob // Alice creates a group with Bob, sends a message to bob
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -4733,7 +4733,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_marknoticed_chat() -> Result<()> { async fn test_marknoticed_chat() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat = t.create_chat_with_contact("bob", "bob@example.org").await; let chat = t.create_chat_with_contact("bob", "bob@example.org").await;
@@ -4778,7 +4778,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_request_fresh_messages() -> Result<()> { async fn test_contact_request_fresh_messages() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -4828,7 +4828,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_request_archive() -> Result<()> { async fn test_contact_request_archive() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -4868,7 +4868,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_classic_email_chat() -> Result<()> { async fn test_classic_email_chat() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -4913,7 +4913,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_get_color() -> Result<()> { async fn test_chat_get_color() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
@@ -4956,7 +4956,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sticker_png() -> Result<()> { async fn test_sticker_png() -> Result<()> {
test_sticker( test_sticker(
"sticker.png", "sticker.png",
@@ -4967,7 +4967,7 @@ mod tests {
.await .await
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sticker_jpeg() -> Result<()> { async fn test_sticker_jpeg() -> Result<()> {
test_sticker( test_sticker(
"sticker.jpg", "sticker.jpg",
@@ -4978,7 +4978,7 @@ mod tests {
.await .await
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sticker_gif() -> Result<()> { async fn test_sticker_gif() -> Result<()> {
test_sticker( test_sticker(
"sticker.gif", "sticker.gif",
@@ -4989,7 +4989,7 @@ mod tests {
.await .await
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sticker_forward() -> Result<()> { async fn test_sticker_forward() -> Result<()> {
// create chats // create chats
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -5020,7 +5020,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forward() -> Result<()> { async fn test_forward() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -5041,7 +5041,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forward_info_msg() -> Result<()> { async fn test_forward_info_msg() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -5067,7 +5067,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forward_quote() -> Result<()> { async fn test_forward_quote() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -5102,7 +5102,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forward_group() -> Result<()> { async fn test_forward_group() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -5152,7 +5152,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_only_minimal_data_are_forwarded() -> Result<()> { async fn test_only_minimal_data_are_forwarded() -> Result<()> {
// send a message from Alice to a group with Bob // send a message from Alice to a group with Bob
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -5194,7 +5194,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resend_own_message() -> Result<()> { async fn test_resend_own_message() -> Result<()> {
// Alice creates group with Bob and sends an initial message // Alice creates group with Bob and sends an initial message
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -5247,7 +5247,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resend_foreign_message_fails() -> Result<()> { async fn test_resend_foreign_message_fails() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?; let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
@@ -5266,7 +5266,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resend_opportunistically_encryption() -> Result<()> { async fn test_resend_opportunistically_encryption() -> Result<()> {
// Alice creates group with Bob and sends an initial message // Alice creates group with Bob and sends an initial message
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -5303,7 +5303,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resend_info_message_fails() -> Result<()> { async fn test_resend_info_message_fails() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?; let alice_grp = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
@@ -5327,7 +5327,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_can_send_group() -> Result<()> { async fn test_can_send_group() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = Contact::create(&alice, "", "bob@f.br").await?; let bob = Contact::create(&alice, "", "bob@f.br").await?;
@@ -5353,7 +5353,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_broadcast() -> Result<()> { async fn test_broadcast() -> Result<()> {
// create two context, send two messages so both know the other // create two context, send two messages so both know the other
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -5396,7 +5396,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_for_contact_with_blocked() -> Result<()> { async fn test_create_for_contact_with_blocked() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let (contact_id, _) = let (contact_id, _) =
@@ -5430,7 +5430,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_get_encryption_info() -> Result<()> { async fn test_chat_get_encryption_info() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;

View File

@@ -376,7 +376,7 @@ mod tests {
use crate::stock_str::StockMessage; use crate::stock_str::StockMessage;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_try_load() { async fn test_try_load() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat") let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")
@@ -432,7 +432,7 @@ mod tests {
assert_eq!(chats.len(), 1); assert_eq!(chats.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sort_self_talk_up_on_forward() { async fn test_sort_self_talk_up_on_forward() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.update_device_chats().await.unwrap(); t.update_device_chats().await.unwrap();
@@ -457,7 +457,7 @@ mod tests {
.is_self_talk()); .is_self_talk());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_special_chat_names() { async fn test_search_special_chat_names() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.update_device_chats().await.unwrap(); t.update_device_chats().await.unwrap();
@@ -488,7 +488,7 @@ mod tests {
assert_eq!(chats.len(), 1); assert_eq!(chats.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_single_chat() -> anyhow::Result<()> { async fn test_search_single_chat() -> anyhow::Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -548,7 +548,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_single_chat_without_authname() -> anyhow::Result<()> { async fn test_search_single_chat_without_authname() -> anyhow::Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -610,7 +610,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_summary_unwrap() { async fn test_get_summary_unwrap() {
let t = TestContext::new().await; let t = TestContext::new().await;
let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat") let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat")

View File

@@ -454,7 +454,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_media_quality_config_option() { async fn test_media_quality_config_option() {
let t = TestContext::new().await; let t = TestContext::new().await;
let media_quality = t.get_config_int(Config::MediaQuality).await.unwrap(); let media_quality = t.get_config_int(Config::MediaQuality).await.unwrap();
@@ -471,7 +471,7 @@ mod tests {
assert_eq!(media_quality, constants::MediaQuality::Worse); assert_eq!(media_quality, constants::MediaQuality::Worse);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ui_config() -> Result<()> { async fn test_ui_config() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -493,7 +493,7 @@ mod tests {
} }
/// Regression test for https://github.com/deltachat/deltachat-core-rust/issues/3012 /// Regression test for https://github.com/deltachat/deltachat-core-rust/issues/3012
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_config_bool() -> Result<()> { async fn test_set_config_bool() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -505,7 +505,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_self_addrs() -> Result<()> { async fn test_self_addrs() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -558,7 +558,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_change_primary_self_addr() -> Result<()> { async fn test_change_primary_self_addr() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;

View File

@@ -6,9 +6,10 @@ mod read_url;
mod server_params; mod server_params;
use anyhow::{bail, ensure, Context as _, Result}; use anyhow::{bail, ensure, Context as _, Result};
use async_std::prelude::*; use futures::FutureExt;
use async_std::task; use futures_lite::FutureExt as _;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use tokio::task;
use crate::config::Config; use crate::config::Config;
use crate::context::Context; use crate::context::Context;
@@ -55,8 +56,6 @@ impl Context {
/// Configures this account with the currently set parameters. /// Configures this account with the currently set parameters.
pub async fn configure(&self) -> Result<()> { pub async fn configure(&self) -> Result<()> {
use futures::future::FutureExt;
ensure!( ensure!(
self.scheduler.read().await.is_none(), self.scheduler.read().await.is_none(),
"cannot configure, already running" "cannot configure, already running"
@@ -404,7 +403,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
progress!(ctx, 850); progress!(ctx, 850);
// Wait for SMTP configuration // Wait for SMTP configuration
match smtp_config_task.await { match smtp_config_task.await.unwrap() {
Ok(smtp_param) => { Ok(smtp_param) => {
param.smtp = smtp_param; param.smtp = smtp_param;
} }
@@ -447,7 +446,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
ctx.interrupt_inbox(InterruptInfo::new(false)).await; ctx.interrupt_inbox(InterruptInfo::new(false)).await;
progress!(ctx, 940); progress!(ctx, 940);
update_device_chats_handle.await?; update_device_chats_handle.await??;
ctx.sql.set_raw_config_bool("configured", true).await?; ctx.sql.set_raw_config_bool("configured", true).await?;
@@ -549,7 +548,7 @@ async fn try_imap_one_param(
); );
info!(context, "Trying: {}", inf); info!(context, "Trying: {}", inf);
let (_s, r) = async_std::channel::bounded(1); let (_s, r) = async_channel::bounded(1);
let mut imap = match Imap::new(param, socks5_config.clone(), addr, provider_strict_tls, r).await let mut imap = match Imap::new(param, socks5_config.clone(), addr, provider_strict_tls, r).await
{ {
@@ -634,10 +633,13 @@ async fn nicer_configuration_error(context: &Context, errors: Vec<ConfigurationE
return "no error".to_string(); return "no error".to_string();
}; };
if errors if errors.iter().all(|e| {
.iter() e.msg.to_lowercase().contains("could not resolve")
.all(|e| e.msg.to_lowercase().contains("could not resolve")) || e.msg
{ .to_lowercase()
.contains("temporary failure in name resolution")
|| e.msg.to_lowercase().contains("name or service not known")
}) {
return stock_str::error_no_network(context).await; return stock_str::error_no_network(context).await;
} }
@@ -678,7 +680,7 @@ mod tests {
use crate::config::Config; use crate::config::Config;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_no_panic_on_bad_credentials() { async fn test_no_panic_on_bad_credentials() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.set_config(Config::Addr, Some("probably@unexistant.addr")) t.set_config(Config::Addr, Some("probably@unexistant.addr"))

View File

@@ -1,7 +1,6 @@
use crate::context::Context; use anyhow::{anyhow, format_err};
use anyhow::format_err; use crate::context::Context;
use anyhow::Context as _;
pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> { pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
match read_url_inner(context, url).await { match read_url_inner(context, url).await {
@@ -16,24 +15,27 @@ pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
} }
} }
pub async fn read_url_inner(context: &Context, mut url: &str) -> anyhow::Result<String> { pub async fn read_url_inner(context: &Context, url: &str) -> anyhow::Result<String> {
let mut _temp; // For the borrow checker let client = reqwest::Client::new();
let mut url = url.to_string();
// Follow up to 10 http-redirects // Follow up to 10 http-redirects
for _i in 0..10 { for _i in 0..10 {
let mut response = surf::get(url).send().await.map_err(|e| e.into_inner())?; let response = client.get(&url).send().await?;
if response.status().is_redirection() { if response.status().is_redirection() {
_temp = response let headers = response.headers();
.header("location") let header = headers
.context("Redirection doesn't have a target location")? .get_all("location")
.iter()
.last() .last()
.to_string(); .ok_or_else(|| anyhow!("Redirection doesn't have a target location"))?
info!(context, "Following redirect to {}", _temp); .to_str()?;
url = &_temp; info!(context, "Following redirect to {}", header);
url = header.to_string();
continue; continue;
} }
return response.body_string().await.map_err(|e| e.into_inner()); return response.text().await.map_err(Into::into);
} }
Err(format_err!("Followed 10 redirections")) Err(format_err!("Followed 10 redirections"))

View File

@@ -2,9 +2,9 @@
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use std::path::PathBuf;
use anyhow::{bail, ensure, Context as _, Result}; use anyhow::{bail, ensure, Context as _, Result};
use async_std::path::PathBuf;
use deltachat_derive::{FromSql, ToSql}; use deltachat_derive::{FromSql, ToSql};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
@@ -1438,8 +1438,8 @@ fn split_address_book(book: &str) -> Vec<(&str, &str)> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use async_std::fs::File; use tokio::fs::File;
use async_std::io::WriteExt; use tokio::io::AsyncWriteExt;
use super::*; use super::*;
@@ -1508,7 +1508,7 @@ mod tests {
) )
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_contacts() -> Result<()> { async fn test_get_contacts() -> Result<()> {
let context = TestContext::new().await; let context = TestContext::new().await;
@@ -1572,7 +1572,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_is_self_addr() -> Result<()> { async fn test_is_self_addr() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!(t.is_self_addr("me@me.org").await?, false); assert_eq!(t.is_self_addr("me@me.org").await?, false);
@@ -1584,7 +1584,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_or_lookup() { async fn test_add_or_lookup() {
// add some contacts, this also tests add_address_book() // add some contacts, this also tests add_address_book()
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -1685,7 +1685,7 @@ mod tests {
assert!(!contact.is_blocked()); assert!(!contact.is_blocked());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_name_changes() -> Result<()> { async fn test_contact_name_changes() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -1797,7 +1797,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete() -> Result<()> { async fn test_delete() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -1825,7 +1825,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remote_authnames() { async fn test_remote_authnames() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -1876,7 +1876,7 @@ mod tests {
assert_eq!(contact.get_display_name(), "bob3"); assert_eq!(contact.get_display_name(), "bob3");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remote_authnames_create_empty() { async fn test_remote_authnames_create_empty() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -1925,7 +1925,7 @@ mod tests {
/// ///
/// In the past, "Not Bob" name was stuck until "Bob" changed the name to "Not Bob" and back in /// In the past, "Not Bob" name was stuck until "Bob" changed the name to "Not Bob" and back in
/// the "From:" field or user set the name to empty string manually. /// the "From:" field or user set the name to empty string manually.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remote_authnames_update_to() -> Result<()> { async fn test_remote_authnames_update_to() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -1958,7 +1958,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remote_authnames_edit_empty() { async fn test_remote_authnames_edit_empty() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -1995,7 +1995,7 @@ mod tests {
assert!(addr_cmp(" mailto:AA@AA.ORG", "Aa@Aa.orG")); assert!(addr_cmp(" mailto:AA@AA.ORG", "Aa@Aa.orG"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_name_in_address() { async fn test_name_in_address() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -2034,7 +2034,7 @@ mod tests {
.is_err()); .is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_lookup_id_by_addr() { async fn test_lookup_id_by_addr() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -2059,7 +2059,7 @@ mod tests {
assert_eq!(id, Some(ContactId::SELF)); assert_eq!(id, Some(ContactId::SELF));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_get_color() -> Result<()> { async fn test_contact_get_color() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let contact_id = Contact::create(&t, "name", "name@example.net").await?; let contact_id = Contact::create(&t, "name", "name@example.net").await?;
@@ -2078,7 +2078,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_get_encrinfo() -> Result<()> { async fn test_contact_get_encrinfo() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -2123,7 +2123,7 @@ CCCB 5AA9 F6E1 141C 9431
/// Tests that status is synchronized when sending encrypted BCC-self messages and not /// Tests that status is synchronized when sending encrypted BCC-self messages and not
/// synchronized when the message is not encrypted. /// synchronized when the message is not encrypted.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_synchronize_status() -> Result<()> { async fn test_synchronize_status() -> Result<()> {
// Alice has two devices. // Alice has two devices.
let alice1 = TestContext::new_alice().await; let alice1 = TestContext::new_alice().await;
@@ -2188,7 +2188,7 @@ CCCB 5AA9 F6E1 141C 9431
} }
/// Tests that DC_EVENT_SELFAVATAR_CHANGED is emitted on avatar changes. /// Tests that DC_EVENT_SELFAVATAR_CHANGED is emitted on avatar changes.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_changed_event() -> Result<()> { async fn test_selfavatar_changed_event() -> Result<()> {
// Alice has two devices. // Alice has two devices.
let alice1 = TestContext::new_alice().await; let alice1 = TestContext::new_alice().await;
@@ -2247,7 +2247,7 @@ CCCB 5AA9 F6E1 141C 9431
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_last_seen() -> Result<()> { async fn test_last_seen() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;

View File

@@ -3,14 +3,13 @@
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::ffi::OsString; use std::ffi::OsString;
use std::ops::Deref; use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime}; use std::time::{Duration, Instant, SystemTime};
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use async_std::{ use async_channel::{self as channel, Receiver, Sender};
channel::{self, Receiver, Sender}, use tokio::sync::{Mutex, RwLock};
path::{Path, PathBuf},
sync::{Arc, Mutex, RwLock},
};
use crate::chat::{get_chat_cnt, ChatId}; use crate::chat::{get_chat_cnt, ChatId};
use crate::config::Config; use crate::config::Config;
@@ -75,7 +74,7 @@ pub struct InnerContext {
/// The text of the last error logged and emitted as an event. /// The text of the last error logged and emitted as an event.
/// If the ui wants to display an error after a failure, /// If the ui wants to display an error after a failure,
/// `last_error` should be used to avoid races with the event thread. /// `last_error` should be used to avoid races with the event thread.
pub(crate) last_error: RwLock<String>, pub(crate) last_error: std::sync::RwLock<String>,
} }
/// The state of ongoing process. /// The state of ongoing process.
@@ -115,7 +114,7 @@ pub fn get_info() -> BTreeMap<&'static str, String> {
impl Context { impl Context {
/// Creates new context and opens the database. /// Creates new context and opens the database.
pub async fn new(dbfile: PathBuf, id: u32, events: Events) -> Result<Context> { pub async fn new(dbfile: &Path, id: u32, events: Events) -> Result<Context> {
let context = Self::new_closed(dbfile, id, events).await?; let context = Self::new_closed(dbfile, id, events).await?;
// Open the database if is not encrypted. // Open the database if is not encrypted.
@@ -126,15 +125,15 @@ impl Context {
} }
/// Creates new context without opening the database. /// Creates new context without opening the database.
pub async fn new_closed(dbfile: PathBuf, id: u32, events: Events) -> Result<Context> { pub async fn new_closed(dbfile: &Path, id: u32, events: Events) -> Result<Context> {
let mut blob_fname = OsString::new(); let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default()); blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs"); blob_fname.push("-blobs");
let blobdir = dbfile.with_file_name(blob_fname); let blobdir = dbfile.with_file_name(blob_fname);
if !blobdir.exists().await { if !blobdir.exists() {
async_std::fs::create_dir_all(&blobdir).await?; tokio::fs::create_dir_all(&blobdir).await?;
} }
let context = Context::with_blobdir(dbfile, blobdir, id, events).await?; let context = Context::with_blobdir(dbfile.into(), blobdir, id, events).await?;
Ok(context) Ok(context)
} }
@@ -172,7 +171,7 @@ impl Context {
events: Events, events: Events,
) -> Result<Context> { ) -> Result<Context> {
ensure!( ensure!(
blobdir.is_dir().await, blobdir.is_dir(),
"Blobdir does not exist: {}", "Blobdir does not exist: {}",
blobdir.display() blobdir.display()
); );
@@ -193,7 +192,7 @@ impl Context {
quota: RwLock::new(None), quota: RwLock::new(None),
creation_time: std::time::SystemTime::now(), creation_time: std::time::SystemTime::now(),
last_full_folder_scan: Mutex::new(None), last_full_folder_scan: Mutex::new(None),
last_error: RwLock::new("".to_string()), last_error: std::sync::RwLock::new("".to_string()),
}; };
let ctx = Context { let ctx = Context {
@@ -643,14 +642,14 @@ impl Context {
Ok(mvbox.as_deref() == Some(folder_name)) Ok(mvbox.as_deref() == Some(folder_name))
} }
pub(crate) fn derive_blobdir(dbfile: &PathBuf) -> PathBuf { pub(crate) fn derive_blobdir(dbfile: &Path) -> PathBuf {
let mut blob_fname = OsString::new(); let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default()); blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs"); blob_fname.push("-blobs");
dbfile.with_file_name(blob_fname) dbfile.with_file_name(blob_fname)
} }
pub(crate) fn derive_walfile(dbfile: &PathBuf) -> PathBuf { pub(crate) fn derive_walfile(dbfile: &Path) -> PathBuf {
let mut wal_fname = OsString::new(); let mut wal_fname = OsString::new();
wal_fname.push(dbfile.file_name().unwrap_or_default()); wal_fname.push(dbfile.file_name().unwrap_or_default());
wal_fname.push("-wal"); wal_fname.push("-wal");
@@ -679,19 +678,19 @@ mod tests {
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use tempfile::tempdir; use tempfile::tempdir;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_wrong_db() -> Result<()> { async fn test_wrong_db() -> Result<()> {
let tmp = tempfile::tempdir()?; let tmp = tempfile::tempdir()?;
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123")?; tokio::fs::write(&dbfile, b"123").await?;
let res = Context::new(dbfile.into(), 1, Events::new()).await?; let res = Context::new(&dbfile, 1, Events::new()).await?;
// Broken database is indistinguishable from encrypted one. // Broken database is indistinguishable from encrypted one.
assert_eq!(res.is_open().await, false); assert_eq!(res.is_open().await, false);
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_fresh_msgs() { async fn test_get_fresh_msgs() {
let t = TestContext::new().await; let t = TestContext::new().await;
let fresh = t.get_fresh_msgs().await.unwrap(); let fresh = t.get_fresh_msgs().await.unwrap();
@@ -718,7 +717,7 @@ mod tests {
dc_receive_imf(t, msg.as_bytes(), false).await.unwrap(); dc_receive_imf(t, msg.as_bytes(), false).await.unwrap();
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_fresh_msgs_and_muted_chats() { async fn test_get_fresh_msgs_and_muted_chats() {
// receive various mails in 3 chats // receive various mails in 3 chats
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -768,7 +767,7 @@ mod tests {
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 9); // claire is counted again assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 9); // claire is counted again
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_fresh_msgs_and_muted_until() { async fn test_get_fresh_msgs_and_muted_until() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let bob = t.create_chat_with_contact("", "bob@g.it").await; let bob = t.create_chat_with_contact("", "bob@g.it").await;
@@ -826,61 +825,61 @@ mod tests {
assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 1); assert_eq!(t.get_fresh_msgs().await.unwrap().len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_blobdir_exists() { async fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
Context::new(dbfile.into(), 1, Events::new()).await.unwrap(); Context::new(&dbfile, 1, Events::new()).await.unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir()); assert!(blobdir.is_dir());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_wrong_blogdir() { async fn test_wrong_blogdir() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs"); let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap(); tokio::fs::write(&blobdir, b"123").await.unwrap();
let res = Context::new(dbfile.into(), 1, Events::new()).await; let res = Context::new(&dbfile, 1, Events::new()).await;
assert!(res.is_err()); assert!(res.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sqlite_parent_not_exists() { async fn test_sqlite_parent_not_exists() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let subdir = tmp.path().join("subdir"); let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite"); let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone(); let dbfile2 = dbfile.clone();
Context::new(dbfile.into(), 1, Events::new()).await.unwrap(); Context::new(&dbfile, 1, Events::new()).await.unwrap();
assert!(subdir.is_dir()); assert!(subdir.is_dir());
assert!(dbfile2.is_file()); assert!(dbfile2.is_file());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_with_empty_blobdir() { async fn test_with_empty_blobdir() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new(); let blobdir = PathBuf::new();
let res = Context::with_blobdir(dbfile.into(), blobdir, 1, Events::new()).await; let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new()).await;
assert!(res.is_err()); assert!(res.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_with_blobdir_not_exists() { async fn test_with_blobdir_not_exists() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite"); let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs"); let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir(dbfile.into(), blobdir.into(), 1, Events::new()).await; let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new()).await;
assert!(res.is_err()); assert!(res.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn no_crashes_on_context_deref() { async fn no_crashes_on_context_deref() {
let t = TestContext::new().await; let t = TestContext::new().await;
std::mem::drop(t); std::mem::drop(t);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_info() { async fn test_get_info() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -896,7 +895,7 @@ mod tests {
assert_eq!(info.get("level").unwrap(), "awesome"); assert_eq!(info.get("level").unwrap(), "awesome");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_info_completeness() { async fn test_get_info_completeness() {
// For easier debugging, // For easier debugging,
// get_info() shall return all important information configurable by the Config-values. // get_info() shall return all important information configurable by the Config-values.
@@ -944,7 +943,7 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_search_msgs() -> Result<()> { async fn test_search_msgs() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let self_talk = ChatId::create_for_contact(&alice, ContactId::SELF).await?; let self_talk = ChatId::create_for_contact(&alice, ContactId::SELF).await?;
@@ -1000,7 +999,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_limit_search_msgs() -> Result<()> { async fn test_limit_search_msgs() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let chat = alice let chat = alice
@@ -1033,13 +1032,13 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_check_passphrase() -> Result<()> { async fn test_check_passphrase() -> Result<()> {
let dir = tempdir()?; let dir = tempdir()?;
let dbfile = dir.path().join("db.sqlite"); let dbfile = dir.path().join("db.sqlite");
let id = 1; let id = 1;
let context = Context::new_closed(dbfile.clone().into(), id, Events::new()) let context = Context::new_closed(&dbfile, id, Events::new())
.await .await
.context("failed to create context")?; .context("failed to create context")?;
assert_eq!(context.open("foo".to_string()).await?, true); assert_eq!(context.open("foo".to_string()).await?, true);
@@ -1047,7 +1046,7 @@ mod tests {
drop(context); drop(context);
let id = 2; let id = 2;
let context = Context::new(dbfile.into(), id, Events::new()) let context = Context::new(&dbfile, id, Events::new())
.await .await
.context("failed to create context")?; .context("failed to create context")?;
assert_eq!(context.is_open().await, false); assert_eq!(context.is_open().await, false);
@@ -1058,7 +1057,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ongoing() -> Result<()> { async fn test_ongoing() -> Result<()> {
let context = TestContext::new().await; let context = TestContext::new().await;

View File

@@ -2203,8 +2203,8 @@ async fn add_or_lookup_contact_by_addr(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use async_std::fs::{self, File}; use tokio::fs::{self, File};
use async_std::io::WriteExt; use tokio::io::AsyncWriteExt;
use super::*; use super::*;
@@ -2216,7 +2216,7 @@ mod tests {
use crate::message::Message; use crate::message::Message;
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager}; use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_grpid_simple() { async fn test_grpid_simple() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ let raw = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
@@ -2234,7 +2234,7 @@ mod tests {
assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid); assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_grpid_from_multiple() { async fn test_grpid_from_multiple() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\ let raw = b"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
@@ -2283,7 +2283,7 @@ mod tests {
\n\ \n\
hello\n"; hello\n";
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_chats_only() { async fn test_adhoc_group_show_chats_only() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
assert_eq!(t.get_config_int(Config::ShowEmails).await.unwrap(), 0); assert_eq!(t.get_config_int(Config::ShowEmails).await.unwrap(), 0);
@@ -2306,7 +2306,7 @@ mod tests {
assert_eq!(chats.len(), 1); assert_eq!(chats.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_accepted_contact_unknown() { async fn test_adhoc_group_show_accepted_contact_unknown() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("1")).await.unwrap(); t.set_config(Config::ShowEmails, Some("1")).await.unwrap();
@@ -2317,7 +2317,7 @@ mod tests {
assert_eq!(chats.len(), 0); assert_eq!(chats.len(), 0);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_accepted_contact_known() { async fn test_adhoc_group_show_accepted_contact_known() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("1")).await.unwrap(); t.set_config(Config::ShowEmails, Some("1")).await.unwrap();
@@ -2330,7 +2330,7 @@ mod tests {
assert_eq!(chats.len(), 0); assert_eq!(chats.len(), 0);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_accepted_contact_accepted() { async fn test_adhoc_group_show_accepted_contact_accepted() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("1")).await.unwrap(); t.set_config(Config::ShowEmails, Some("1")).await.unwrap();
@@ -2368,7 +2368,7 @@ mod tests {
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3); assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_show_all() { async fn test_adhoc_group_show_all() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -2387,7 +2387,7 @@ mod tests {
assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3); assert_eq!(chat::get_chat_contacts(&t, chat_id).await.unwrap().len(), 3);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_read_receipt_and_unarchive() -> Result<()> { async fn test_read_receipt_and_unarchive() -> Result<()> {
// create alice's account // create alice's account
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -2501,7 +2501,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_no_from() { async fn test_no_from() {
// if there is no from given, from_id stays 0 which is just fine. These messages // if there is no from given, from_id stays 0 which is just fine. These messages
// are very rare, however, we have to add them to the database // are very rare, however, we have to add them to the database
@@ -2533,7 +2533,7 @@ mod tests {
assert!(chats.get_msg_id(0).is_ok()); assert!(chats.get_msg_id(0).is_ok());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_escaped_from() { async fn test_escaped_from() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let contact_id = Contact::create(&t, "foobar", "foobar@example.com") let contact_id = Contact::create(&t, "foobar", "foobar@example.com")
@@ -2566,7 +2566,7 @@ mod tests {
assert_eq!(msg.param.get_int(Param::WantsMdn).unwrap(), 1); assert_eq!(msg.param.get_int(Param::WantsMdn).unwrap(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_escaped_recipients() { async fn test_escaped_recipients() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
Contact::create(&t, "foobar", "foobar@example.com") Contact::create(&t, "foobar", "foobar@example.com")
@@ -2608,7 +2608,7 @@ mod tests {
assert_eq!(msg.param.get_int(Param::WantsMdn).unwrap(), 1); assert_eq!(msg.param.get_int(Param::WantsMdn).unwrap(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cc_to_contact() { async fn test_cc_to_contact() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
Contact::create(&t, "foobar", "foobar@example.com") Contact::create(&t, "foobar", "foobar@example.com")
@@ -2643,7 +2643,7 @@ mod tests {
assert_eq!(contact.get_display_name(), "Carl"); assert_eq!(contact.get_display_name(), "Carl");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_tiscali() { async fn test_parse_ndn_tiscali() {
test_parse_ndn( test_parse_ndn(
"alice@tiscali.it", "alice@tiscali.it",
@@ -2655,7 +2655,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_testrun() { async fn test_parse_ndn_testrun() {
test_parse_ndn( test_parse_ndn(
"alice@testrun.org", "alice@testrun.org",
@@ -2667,7 +2667,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_yahoo() { async fn test_parse_ndn_yahoo() {
test_parse_ndn( test_parse_ndn(
"alice@yahoo.com", "alice@yahoo.com",
@@ -2679,7 +2679,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_gmail() { async fn test_parse_ndn_gmail() {
test_parse_ndn( test_parse_ndn(
"alice@gmail.com", "alice@gmail.com",
@@ -2691,7 +2691,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_gmx() { async fn test_parse_ndn_gmx() {
test_parse_ndn( test_parse_ndn(
"alice@gmx.com", "alice@gmx.com",
@@ -2703,7 +2703,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_posteo() { async fn test_parse_ndn_posteo() {
test_parse_ndn( test_parse_ndn(
"alice@posteo.org", "alice@posteo.org",
@@ -2715,7 +2715,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_testrun_2() { async fn test_parse_ndn_testrun_2() {
test_parse_ndn( test_parse_ndn(
"alice@example.org", "alice@example.org",
@@ -2781,7 +2781,7 @@ mod tests {
assert_eq!(msg.error(), error_msg.map(|error| error.to_string())); assert_eq!(msg.error(), error_msg.map(|error| error.to_string()));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_ndn_group_msg() -> Result<()> { async fn test_parse_ndn_group_msg() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
t.configure_addr("alice@gmail.com").await; t.configure_addr("alice@gmail.com").await;
@@ -2841,7 +2841,7 @@ mod tests {
Message::load_from_db(context, msg_id).await.unwrap() Message::load_from_db(context, msg_id).await.unwrap()
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_only_mail() { async fn test_html_only_mail() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let msg = load_imf_email(&t, include_bytes!("../test-data/message/wrong-html.eml")).await; let msg = load_imf_email(&t, include_bytes!("../test-data/message/wrong-html.eml")).await;
@@ -2874,7 +2874,7 @@ mod tests {
\n\ \n\
hello back\n"; hello back\n";
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_github_mailing_list() -> Result<()> { async fn test_github_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.ctx.set_config(Config::ShowEmails, Some("2")).await?; t.ctx.set_config(Config::ShowEmails, Some("2")).await?;
@@ -2941,7 +2941,7 @@ mod tests {
\n\ \n\
body 4\n"; body 4\n";
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_classic_mailing_list() -> Result<()> { async fn test_classic_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.ctx t.ctx
@@ -2987,7 +2987,7 @@ Hello mailinglist!\r\n"
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_other_device_writes_to_mailinglist() -> Result<()> { async fn test_other_device_writes_to_mailinglist() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -3037,7 +3037,7 @@ Hello mailinglist!\r\n"
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_block_mailing_list() { async fn test_block_mailing_list() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.ctx t.ctx
@@ -3071,7 +3071,7 @@ Hello mailinglist!\r\n"
assert_eq!(msgs.len(), 2); assert_eq!(msgs.len(), 2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_block_then_unblock() { async fn test_mailing_list_decide_block_then_unblock() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3104,7 +3104,7 @@ Hello mailinglist!\r\n"
assert_eq!(msgs.len(), 2); assert_eq!(msgs.len(), 2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_not_now() { async fn test_mailing_list_decide_not_now() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.ctx t.ctx
@@ -3137,7 +3137,7 @@ Hello mailinglist!\r\n"
assert!(chat.is_contact_request()); assert!(chat.is_contact_request());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_decide_accept() { async fn test_mailing_list_decide_accept() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.ctx t.ctx
@@ -3165,7 +3165,7 @@ Hello mailinglist!\r\n"
assert!(chat.can_send(&t.ctx).await.unwrap()); assert!(chat.can_send(&t.ctx).await.unwrap());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_majordomo_mailing_list() { async fn test_majordomo_mailing_list() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3214,7 +3214,7 @@ Hello mailinglist!\r\n"
assert_eq!(chat::get_chat_msgs(&t, chat.id, 0).await.unwrap().len(), 2); assert_eq!(chat::get_chat_msgs(&t, chat.id, 0).await.unwrap().len(), 2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailchimp_mailing_list() { async fn test_mailchimp_mailing_list() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3244,7 +3244,7 @@ Hello mailinglist!\r\n"
assert_eq!(chat.name, "Atlas Obscura"); assert_eq!(chat.name, "Atlas Obscura");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dhl_mailing_list() { async fn test_dhl_mailing_list() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3269,7 +3269,7 @@ Hello mailinglist!\r\n"
assert_eq!(chat.name, "DHL Paket"); assert_eq!(chat.name, "DHL Paket");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dpd_mailing_list() { async fn test_dpd_mailing_list() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3294,7 +3294,7 @@ Hello mailinglist!\r\n"
assert_eq!(chat.name, "DPD"); assert_eq!(chat.name, "DPD");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_xt_local_mailing_list() -> Result<()> { async fn test_xt_local_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -3324,7 +3324,7 @@ Hello mailinglist!\r\n"
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_xing_mailing_list() -> Result<()> { async fn test_xing_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -3345,7 +3345,7 @@ Hello mailinglist!\r\n"
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ttline_mailing_list() -> Result<()> { async fn test_ttline_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -3366,7 +3366,7 @@ Hello mailinglist!\r\n"
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_mimepart_footer() { async fn test_mailing_list_with_mimepart_footer() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3397,7 +3397,7 @@ Hello mailinglist!\r\n"
assert_eq!(chat.name, "Intern"); assert_eq!(chat.name, "Intern");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_mimepart_footer_signed() { async fn test_mailing_list_with_mimepart_footer_signed() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3422,7 +3422,7 @@ Hello mailinglist!\r\n"
/// Test that the changes from apply_mailinglist_changes() are also applied /// Test that the changes from apply_mailinglist_changes() are also applied
/// if the message is assigned to the chat by In-Reply-To /// if the message is assigned to the chat by In-Reply-To
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_apply_mailinglist_changes_assigned_by_reply() { async fn test_apply_mailinglist_changes_assigned_by_reply() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3464,7 +3464,7 @@ Hello mailinglist!\r\n"
) )
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dont_show_tokens_in_contacts_list() { async fn test_dont_show_tokens_in_contacts_list() {
check_dont_show_in_contacts_list( check_dont_show_in_contacts_list(
"reply+OGHVYCLVBEGATYBICAXBIRQATABUOTUCERABERAHNO@reply.github.com", "reply+OGHVYCLVBEGATYBICAXBIRQATABUOTUCERABERAHNO@reply.github.com",
@@ -3472,7 +3472,7 @@ Hello mailinglist!\r\n"
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dont_show_noreply_in_contacts_list() { async fn test_dont_show_noreply_in_contacts_list() {
check_dont_show_in_contacts_list("noreply@github.com").await; check_dont_show_in_contacts_list("noreply@github.com").await;
} }
@@ -3510,7 +3510,7 @@ YEAAAAAA!.
assert!(contacts.is_empty()); // The contact should not have been added to the db assert!(contacts.is_empty()); // The contact should not have been added to the db
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pdf_filename_simple() { async fn test_pdf_filename_simple() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let msg = load_imf_email( let msg = load_imf_email(
@@ -3523,7 +3523,7 @@ YEAAAAAA!.
assert_eq!(msg.param.get(Param::File).unwrap(), "$BLOBDIR/simple.pdf"); assert_eq!(msg.param.get(Param::File).unwrap(), "$BLOBDIR/simple.pdf");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pdf_filename_continuation() { async fn test_pdf_filename_continuation() {
// test filenames split across multiple header lines, see rfc 2231 // test filenames split across multiple header lines, see rfc 2231
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -3550,7 +3550,7 @@ YEAAAAAA!.
/// or mua may use multipart/related not correctly - /// or mua may use multipart/related not correctly -
/// so this test is in competition with parse_thunderbird_html_embedded_image() /// so this test is in competition with parse_thunderbird_html_embedded_image()
/// that wants the image to be kept in the chat. /// that wants the image to be kept in the chat.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_many_images() { async fn test_many_images() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -3571,7 +3571,7 @@ YEAAAAAA!.
/// Test that classical MUA messages are assigned to group chats based on the `In-Reply-To` /// Test that classical MUA messages are assigned to group chats based on the `In-Reply-To`
/// header. /// header.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_in_reply_to() { async fn test_in_reply_to() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.configure_addr("bob@example.com").await; t.configure_addr("bob@example.com").await;
@@ -3636,7 +3636,7 @@ YEAAAAAA!.
/// Test that classical MUA messages are assigned to group chats /// Test that classical MUA messages are assigned to group chats
/// based on the `In-Reply-To` header for two-member groups. /// based on the `In-Reply-To` header for two-member groups.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_in_reply_to_two_member_group() { async fn test_in_reply_to_two_member_group() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.configure_addr("bob@example.com").await; t.configure_addr("bob@example.com").await;
@@ -3738,7 +3738,7 @@ YEAAAAAA!.
assert_eq!(msg.get_text().unwrap(), "private reply"); assert_eq!(msg.get_text().unwrap(), "private reply");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_save_mime_headers_off() -> anyhow::Result<()> { async fn test_save_mime_headers_off() -> anyhow::Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -3753,7 +3753,7 @@ YEAAAAAA!.
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_save_mime_headers_on() -> anyhow::Result<()> { async fn test_save_mime_headers_on() -> anyhow::Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.set_config_bool(Config::SaveMimeHeaders, true).await?; alice.set_config_bool(Config::SaveMimeHeaders, true).await?;
@@ -3924,7 +3924,7 @@ YEAAAAAA!.
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_alias_support_answer_from_nondc() { async fn test_alias_support_answer_from_nondc() {
// Bob, the other supporter, answers with a classic MUA. // Bob, the other supporter, answers with a classic MUA.
let bob_answer = b"To: support@example.org, claire@example.org\n\ let bob_answer = b"To: support@example.org, claire@example.org\n\
@@ -3944,7 +3944,7 @@ YEAAAAAA!.
check_alias_reply(bob_answer, false, false).await; check_alias_reply(bob_answer, false, false).await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_alias_answer_from_dc() { async fn test_alias_answer_from_dc() {
// Bob, the other supporter, answers with Delta Chat. // Bob, the other supporter, answers with Delta Chat.
let bob_answer = b"To: support@example.org, claire@example.org\n\ let bob_answer = b"To: support@example.org, claire@example.org\n\
@@ -3968,7 +3968,7 @@ YEAAAAAA!.
check_alias_reply(bob_answer, false, false).await; check_alias_reply(bob_answer, false, false).await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dont_assign_to_trash_by_parent() { async fn test_dont_assign_to_trash_by_parent() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -4016,7 +4016,7 @@ YEAAAAAA!.
assert_eq!(msg.text.unwrap(), "Reply"); assert_eq!(msg.text.unwrap(), "Reply");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dont_show_all_outgoing_msgs_in_self_chat() { async fn test_dont_show_all_outgoing_msgs_in_self_chat() {
// Regression test for <https://github.com/deltachat/deltachat-android/issues/1940>: // Regression test for <https://github.com/deltachat/deltachat-android/issues/1940>:
// Some servers add a `Bcc: <Self>` header, which caused all outgoing messages to // Some servers add a `Bcc: <Self>` header, which caused all outgoing messages to
@@ -4043,7 +4043,7 @@ Message content",
assert_ne!(msg.chat_id, t.get_self_chat().await.id); assert_ne!(msg.chat_id, t.get_self_chat().await.id);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_classic_mail_creates_chat() { async fn test_outgoing_classic_mail_creates_chat() {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -4073,7 +4073,7 @@ Message content",
assert_eq!(msg.get_text().unwrap(), "Subj Message content"); assert_eq!(msg.get_text().unwrap(), "Subj Message content");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_duplicate_message() -> Result<()> { async fn test_duplicate_message() -> Result<()> {
// Test that duplicate messages are ignored based on the Message-ID // Test that duplicate messages are ignored based on the Message-ID
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -4132,7 +4132,7 @@ Second signature";
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_private_classical_reply() { async fn test_chat_assignment_private_classical_reply() {
for outgoing_is_classical in &[true, false] { for outgoing_is_classical in &[true, false] {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -4216,7 +4216,7 @@ Private reply"#,
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_private_chat_reply() { async fn test_chat_assignment_private_chat_reply() {
for (outgoing_is_classical, outgoing_has_multiple_recipients) in for (outgoing_is_classical, outgoing_has_multiple_recipients) in
&[(true, true), (false, true), (false, false)] &[(true, true), (false, true), (false, false)]
@@ -4312,7 +4312,7 @@ Sent with my Delta Chat Messenger: https://delta.chat
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_nonprivate_classical_reply() { async fn test_chat_assignment_nonprivate_classical_reply() {
for outgoing_is_classical in &[true, false] { for outgoing_is_classical in &[true, false] {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -4421,7 +4421,7 @@ Reply to all"#,
/// messages have the same recipient lists and only differ in the subject and message contents. /// messages have the same recipient lists and only differ in the subject and message contents.
/// The messages can be properly assigned to chats only using the In-Reply-To or References /// The messages can be properly assigned to chats only using the In-Reply-To or References
/// headers. /// headers.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_chat_assignment_adhoc() -> Result<()> { async fn test_chat_assignment_adhoc() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_alice().await; let bob = TestContext::new_alice().await;
@@ -4501,7 +4501,7 @@ Second thread."#;
} }
/// Test that read receipts don't create chats. /// Test that read receipts don't create chats.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_read_receipts_dont_create_chats() -> Result<()> { async fn test_read_receipts_dont_create_chats() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -4534,7 +4534,7 @@ Second thread."#;
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_gmx_forwarded_msg() -> Result<()> { async fn test_gmx_forwarded_msg() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -4554,7 +4554,7 @@ Second thread."#;
} }
/// Tests that user is notified about new incoming contact requests. /// Tests that user is notified about new incoming contact requests.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_incoming_contact_request() -> Result<()> { async fn test_incoming_contact_request() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -4579,7 +4579,7 @@ Second thread."#;
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_parent_message() -> Result<()> { async fn test_get_parent_message() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -4642,7 +4642,7 @@ Message with references."#;
} }
/// Test a message with RFC 1847 encapsulation as created by Thunderbird. /// Test a message with RFC 1847 encapsulation as created by Thunderbird.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_rfc1847_encapsulation() -> Result<()> { async fn test_rfc1847_encapsulation() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -4668,7 +4668,7 @@ Message with references."#;
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_invalid_to_address() -> Result<()> { async fn test_invalid_to_address() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -4680,7 +4680,7 @@ Message with references."#;
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_reply_from_different_addr() -> Result<()> { async fn test_reply_from_different_addr() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
@@ -4745,7 +4745,7 @@ Reply from different address
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_long_filenames() -> Result<()> { async fn test_long_filenames() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;
@@ -4800,7 +4800,7 @@ Reply from different address
} }
/// Tests that contact request is accepted automatically on outgoing message. /// Tests that contact request is accepted automatically on outgoing message.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_accept_outgoing() -> Result<()> { async fn test_accept_outgoing() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice1 = tcm.alice().await; let alice1 = tcm.alice().await;
@@ -4845,7 +4845,7 @@ Reply from different address
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_private_reply_multidevice() -> Result<()> { async fn test_outgoing_private_reply_multidevice() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice1 = tcm.alice().await; let alice1 = tcm.alice().await;
@@ -4922,7 +4922,7 @@ Reply from different address
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_no_private_reply_to_blocked_account() -> Result<()> { async fn test_no_private_reply_to_blocked_account() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;

View File

@@ -5,20 +5,19 @@ use core::cmp::{max, min};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::str::from_utf8; use std::str::from_utf8;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use async_std::path::{Path, PathBuf};
use async_std::prelude::*;
use async_std::{fs, io};
use anyhow::{bail, Error, Result}; use anyhow::{bail, Error, Result};
use chrono::{Local, TimeZone}; use chrono::{Local, TimeZone};
use futures::StreamExt;
use mailparse::dateparse; use mailparse::dateparse;
use mailparse::headers::Headers; use mailparse::headers::Headers;
use mailparse::MailHeaderMap; use mailparse::MailHeaderMap;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use tokio::{fs, io};
use crate::chat::{add_device_msg, add_device_msg_with_importance}; use crate::chat::{add_device_msg, add_device_msg_with_importance};
use crate::constants::{DC_ELLIPSIS, DC_OUTDATED_WARNING_DAYS}; use crate::constants::{DC_ELLIPSIS, DC_OUTDATED_WARNING_DAYS};
@@ -278,7 +277,7 @@ pub fn dc_get_filemeta(buf: &[u8]) -> Result<(u32, u32), Error> {
/// ///
/// If `path` starts with "$BLOBDIR", replaces it with the blobdir path. /// If `path` starts with "$BLOBDIR", replaces it with the blobdir path.
/// Otherwise, returns path as is. /// Otherwise, returns path as is.
pub(crate) fn dc_get_abs_path<P: AsRef<Path>>(context: &Context, path: P) -> PathBuf { pub(crate) fn dc_get_abs_path(context: &Context, path: impl AsRef<Path>) -> PathBuf {
let p: &Path = path.as_ref(); let p: &Path = path.as_ref();
if let Ok(p) = p.strip_prefix("$BLOBDIR") { if let Ok(p) = p.strip_prefix("$BLOBDIR") {
context.get_blobdir().join(p) context.get_blobdir().join(p)
@@ -297,10 +296,10 @@ pub(crate) async fn dc_get_filebytes(context: &Context, path: impl AsRef<Path>)
pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) -> bool { pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) -> bool {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
if !path_abs.exists().await { if !path_abs.exists() {
return false; return false;
} }
if !path_abs.is_file().await { if !path_abs.is_file() {
warn!( warn!(
context, context,
"refusing to delete non-file \"{}\".", "refusing to delete non-file \"{}\".",
@@ -323,8 +322,9 @@ pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef<Path>) ->
} }
pub async fn dc_delete_files_in_dir(context: &Context, path: impl AsRef<Path>) { pub async fn dc_delete_files_in_dir(context: &Context, path: impl AsRef<Path>) {
match async_std::fs::read_dir(path).await { match tokio::fs::read_dir(path).await {
Ok(mut read_dir) => { Ok(read_dir) => {
let mut read_dir = tokio_stream::wrappers::ReadDirStream::new(read_dir);
while let Some(entry) = read_dir.next().await { while let Some(entry) = read_dir.next().await {
match entry { match entry {
Ok(file) => { Ok(file) => {
@@ -344,7 +344,7 @@ pub(crate) async fn dc_create_folder(
path: impl AsRef<Path>, path: impl AsRef<Path>,
) -> Result<(), io::Error> { ) -> Result<(), io::Error> {
let path_abs = dc_get_abs_path(context, &path); let path_abs = dc_get_abs_path(context, &path);
if !path_abs.exists().await { if !path_abs.exists() {
match fs::create_dir_all(path_abs).await { match fs::create_dir_all(path_abs).await {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => { Err(err) => {
@@ -655,7 +655,7 @@ mod tests {
assert_eq!(hop_info, expected) assert_eq!(hop_info, expected)
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_receive_headers_integration() { async fn test_parse_receive_headers_integration() {
let raw = include_bytes!("../test-data/message/mail_with_cc.txt"); let raw = include_bytes!("../test-data/message/mail_with_cc.txt");
let expected = r"State: Fresh let expected = r"State: Fresh
@@ -873,7 +873,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_file_handling() { async fn test_file_handling() {
let t = TestContext::new().await; let t = TestContext::new().await;
let context = &t; let context = &t;
@@ -889,8 +889,8 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
assert!(dc_write_file(context, "$BLOBDIR/foobar", b"content") assert!(dc_write_file(context, "$BLOBDIR/foobar", b"content")
.await .await
.is_ok()); .is_ok());
assert!(dc_file_exist!(context, "$BLOBDIR/foobar").await); assert!(dc_file_exist!(context, "$BLOBDIR/foobar"));
assert!(!dc_file_exist!(context, "$BLOBDIR/foobarx").await); assert!(!dc_file_exist!(context, "$BLOBDIR/foobarx"));
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/foobar").await, 7); assert_eq!(dc_get_filebytes(context, "$BLOBDIR/foobar").await, 7);
let abs_path = context let abs_path = context
@@ -899,23 +899,23 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
.to_string_lossy() .to_string_lossy()
.to_string(); .to_string();
assert!(dc_file_exist!(context, &abs_path).await); assert!(dc_file_exist!(context, &abs_path));
assert!(dc_delete_file(context, "$BLOBDIR/foobar").await); assert!(dc_delete_file(context, "$BLOBDIR/foobar").await);
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder") assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder")
.await .await
.is_ok()); .is_ok());
assert!(dc_file_exist!(context, "$BLOBDIR/foobar-folder").await); assert!(dc_file_exist!(context, "$BLOBDIR/foobar-folder"));
assert!(!dc_delete_file(context, "$BLOBDIR/foobar-folder").await); assert!(!dc_delete_file(context, "$BLOBDIR/foobar-folder").await);
let fn0 = "$BLOBDIR/data.data"; let fn0 = "$BLOBDIR/data.data";
assert!(dc_write_file(context, &fn0, b"content").await.is_ok()); assert!(dc_write_file(context, &fn0, b"content").await.is_ok());
assert!(dc_delete_file(context, &fn0).await); assert!(dc_delete_file(context, &fn0).await);
assert!(!dc_file_exist!(context, &fn0).await); assert!(!dc_file_exist!(context, &fn0));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_smeared_timestamp() { async fn test_create_smeared_timestamp() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_ne!( assert_ne!(
@@ -931,7 +931,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_smeared_timestamps() { async fn test_create_smeared_timestamps() {
let t = TestContext::new().await; let t = TestContext::new().await;
let count = MAX_SECONDS_TO_LEND_FROM_FUTURE - 1; let count = MAX_SECONDS_TO_LEND_FROM_FUTURE - 1;
@@ -1001,7 +1001,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
assert_eq!(improve_single_line_input("\r\nahte\n\r"), "ahte"); assert_eq!(improve_single_line_input("\r\nahte\n\r"), "ahte");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_maybe_warn_on_bad_time() { async fn test_maybe_warn_on_bad_time() {
let t = TestContext::new().await; let t = TestContext::new().await;
let timestamp_now = time(); let timestamp_now = time();
@@ -1064,7 +1064,7 @@ Hop: From: hq5.example.org; By: hq5.example.org; Date: Mon, 27 Dec 2021 11:21:22
assert_eq!(msgs.len(), 2); assert_eq!(msgs.len(), 2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_maybe_warn_on_outdated() { async fn test_maybe_warn_on_outdated() {
let t = TestContext::new().await; let t = TestContext::new().await;
let timestamp_now: i64 = time(); let timestamp_now: i64 = time();

View File

@@ -382,7 +382,7 @@ mod tests {
assert_eq!(txt.trim(), "two\nlines"); assert_eq!(txt.trim(), "two\nlines");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_quote_div() { async fn test_quote_div() {
let input = include_str!("../test-data/message/gmx-quote-body.eml"); let input = include_str!("../test-data/message/gmx-quote-body.eml");
let dehtml = dehtml(input).unwrap(); let dehtml = dehtml(input).unwrap();

View File

@@ -280,7 +280,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_download_limit() -> Result<()> { async fn test_download_limit() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -303,7 +303,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_update_download_state() -> Result<()> { async fn test_update_download_state() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat = t.create_chat_with_contact("Bob", "bob@example.org").await; let chat = t.create_chat_with_contact("Bob", "bob@example.org").await;
@@ -328,7 +328,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_partial_receive_imf() -> Result<()> { async fn test_partial_receive_imf() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -376,7 +376,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_partial_download_and_ephemeral() -> Result<()> { async fn test_partial_download_and_ephemeral() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = t let chat_id = t

View File

@@ -422,7 +422,7 @@ mod tests {
mod ensure_secret_key_exists { mod ensure_secret_key_exists {
use super::*; use super::*;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_prexisting() { async fn test_prexisting() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
assert_eq!( assert_eq!(
@@ -431,7 +431,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_not_configured() { async fn test_not_configured() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!(ensure_secret_key_exists(&t).await.is_err()); assert!(ensure_secret_key_exists(&t).await.is_err());
@@ -480,7 +480,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
assert_eq!(has_decrypted_pgp_armor(data), false); assert_eq!(has_decrypted_pgp_armor(data), false);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_encrypted_no_autocrypt() -> anyhow::Result<()> { async fn test_encrypted_no_autocrypt() -> anyhow::Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -588,7 +588,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
vec![(Some(peerstate), addr)] vec![(Some(peerstate), addr)]
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_should_encrypt() { async fn test_should_encrypt() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let encrypt_helper = EncryptHelper::new(&t).await.unwrap(); let encrypt_helper = EncryptHelper::new(&t).await.unwrap();
@@ -615,7 +615,7 @@ Sent with my Delta Chat Messenger: https://delta.chat";
assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap()); assert!(!encrypt_helper.should_encrypt(&t, false, &ps).unwrap());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mixed_up_mime() -> Result<()> { async fn test_mixed_up_mime() -> Result<()> {
// "Mixed Up" mail as received when sending an encrypted // "Mixed Up" mail as received when sending an encrypted
// message using Delta Chat Desktop via ProtonMail IMAP/SMTP // message using Delta Chat Desktop via ProtonMail IMAP/SMTP

View File

@@ -62,9 +62,9 @@ use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use anyhow::{ensure, Context as _, Result}; use anyhow::{ensure, Context as _, Result};
use async_std::channel::Receiver; use async_channel::Receiver;
use async_std::future::timeout;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::time::timeout;
use crate::chat::{send_msg, ChatId}; use crate::chat::{send_msg, ChatId};
use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH}; use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH};
@@ -581,7 +581,7 @@ mod tests {
dc_tools::IsNoneOrEmpty, dc_tools::IsNoneOrEmpty,
}; };
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_ephemeral_messages() { async fn test_stock_ephemeral_messages() {
let context = TestContext::new().await; let context = TestContext::new().await;
@@ -711,7 +711,7 @@ mod tests {
} }
/// Test enabling and disabling ephemeral timer remotely. /// Test enabling and disabling ephemeral timer remotely.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ephemeral_enable_disable() -> Result<()> { async fn test_ephemeral_enable_disable() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -743,7 +743,7 @@ mod tests {
} }
/// Test that timer is enabled even if the message explicitly enabling the timer is lost. /// Test that timer is enabled even if the message explicitly enabling the timer is lost.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ephemeral_enable_lost() -> Result<()> { async fn test_ephemeral_enable_lost() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -785,7 +785,7 @@ mod tests {
/// Test that Alice replying to the chat without a timer at the same time as Bob enables the /// Test that Alice replying to the chat without a timer at the same time as Bob enables the
/// timer does not result in disabling the timer on the Bob's side. /// timer does not result in disabling the timer on the Bob's side.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ephemeral_timer_rollback() -> Result<()> { async fn test_ephemeral_timer_rollback() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -859,7 +859,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ephemeral_delete_msgs() -> Result<()> { async fn test_ephemeral_delete_msgs() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let self_chat = t.get_self_chat().await; let self_chat = t.get_self_chat().await;
@@ -985,7 +985,7 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete_expired_imap_messages() -> Result<()> { async fn test_delete_expired_imap_messages() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
const HOUR: i64 = 60 * 60; const HOUR: i64 = 60 * 60;
@@ -1096,7 +1096,7 @@ mod tests {
} }
// Regression test for a bug in the timer rollback protection. // Regression test for a bug in the timer rollback protection.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ephemeral_timer_references() -> Result<()> { async fn test_ephemeral_timer_references() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;

View File

@@ -1,7 +1,8 @@
//! # Events specification. //! # Events specification.
use async_std::channel::{self, Receiver, Sender, TrySendError}; use std::path::PathBuf;
use async_std::path::PathBuf;
use async_channel::{self as channel, Receiver, Sender, TrySendError};
use crate::chat::ChatId; use crate::chat::ChatId;
use crate::contact::ContactId; use crate::contact::ContactId;
@@ -61,23 +62,18 @@ impl Events {
/// ///
/// [`Context`]: crate::context::Context /// [`Context`]: crate::context::Context
/// [`Context::get_event_emitter`]: crate::context::Context::get_event_emitter /// [`Context::get_event_emitter`]: crate::context::Context::get_event_emitter
/// [`Stream`]: async_std::stream::Stream /// [`Stream`]: futures::stream::Stream
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EventEmitter(Receiver<Event>); pub struct EventEmitter(Receiver<Event>);
impl EventEmitter { impl EventEmitter {
/// Blocking recv of an event. Return `None` if the `Sender` has been droped.
pub fn recv_sync(&self) -> Option<Event> {
async_std::task::block_on(self.recv())
}
/// Async recv of an event. Return `None` if the `Sender` has been droped. /// Async recv of an event. Return `None` if the `Sender` has been droped.
pub async fn recv(&self) -> Option<Event> { pub async fn recv(&self) -> Option<Event> {
self.0.recv().await.ok() self.0.recv().await.ok()
} }
} }
impl async_std::stream::Stream for EventEmitter { impl futures::stream::Stream for EventEmitter {
type Item = Event; type Item = Event;
fn poll_next( fn poll_next(

View File

@@ -203,7 +203,7 @@ mod tests {
assert_eq!(format_flowed_quote(quote), expected); assert_eq!(format_flowed_quote(quote), expected);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_quotes() -> anyhow::Result<()> { async fn test_send_quotes() -> anyhow::Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;

View File

@@ -284,7 +284,7 @@ mod tests {
use crate::message::{MessengerMessage, Viewtype}; use crate::message::{MessengerMessage, Viewtype};
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_plain_unspecified() { async fn test_htmlparse_plain_unspecified() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml"); let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml");
@@ -300,7 +300,7 @@ This message does not have Content-Type nor Subject.<br/>
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_plain_iso88591() { async fn test_htmlparse_plain_iso88591() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_plain_iso88591.eml"); let raw = include_bytes!("../test-data/message/text_plain_iso88591.eml");
@@ -316,7 +316,7 @@ message with a non-UTF-8 encoding: äöüßÄÖÜ<br/>
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_plain_flowed() { async fn test_htmlparse_plain_flowed() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_plain_flowed.eml"); let raw = include_bytes!("../test-data/message/text_plain_flowed.eml");
@@ -336,7 +336,7 @@ and will be wrapped as usual.<br/>
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_alt_plain() { async fn test_htmlparse_alt_plain() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_alt_plain.eml"); let raw = include_bytes!("../test-data/message/text_alt_plain.eml");
@@ -355,7 +355,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_html() { async fn test_htmlparse_html() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_html.eml"); let raw = include_bytes!("../test-data/message/text_html.eml");
@@ -373,7 +373,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_alt_html() { async fn test_htmlparse_alt_html() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_alt_html.eml"); let raw = include_bytes!("../test-data/message/text_alt_html.eml");
@@ -388,7 +388,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_alt_plain_html() { async fn test_htmlparse_alt_plain_html() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml"); let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
@@ -405,7 +405,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_htmlparse_apple_cid_jpg() { async fn test_htmlparse_apple_cid_jpg() {
// load raw mime html-data with related image-part (cid:) // load raw mime html-data with related image-part (cid:)
// and make sure, Content-Id has angle-brackets that are removed correctly. // and make sure, Content-Id has angle-brackets that are removed correctly.
@@ -424,14 +424,14 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
assert!(!parser.html.contains("cid:")); assert!(!parser.html.contains("cid:"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_html_invalid_msgid() { async fn test_get_html_invalid_msgid() {
let t = TestContext::new().await; let t = TestContext::new().await;
let msg_id = MsgId::new(100); let msg_id = MsgId::new(100);
assert!(msg_id.get_html(&t).await.is_err()) assert!(msg_id.get_html(&t).await.is_err())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_forwarding() { async fn test_html_forwarding() {
// alice receives a non-delta html-message // alice receives a non-delta html-message
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -478,7 +478,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
assert!(html.contains("this is <b>html</b>")); assert!(html.contains("this is <b>html</b>"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_html_forwarding_encrypted() { async fn test_html_forwarding_encrypted() {
// Alice receives a non-delta html-message // Alice receives a non-delta html-message
// (`ShowEmails=1` lets Alice actually receive non-delta messages for known contacts, // (`ShowEmails=1` lets Alice actually receive non-delta messages for known contacts,
@@ -515,7 +515,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
assert!(html.contains("this is <b>html</b>")); assert!(html.contains("this is <b>html</b>"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_html() { async fn test_set_html() {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -547,7 +547,7 @@ test some special html-characters as &lt; &gt; and &amp; but also &quot; and &#x
assert!(html.contains("<b>html</b> text")); assert!(html.contains("<b>html</b> text"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cp1252_html() -> Result<()> { async fn test_cp1252_html() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;

View File

@@ -11,11 +11,11 @@ use std::{
}; };
use anyhow::{bail, format_err, Context as _, Result}; use anyhow::{bail, format_err, Context as _, Result};
use async_channel::Receiver;
use async_imap::types::{ use async_imap::types::{
Fetch, Flag, Mailbox, Name, NameAttribute, Quota, QuotaRoot, UnsolicitedResponse, Fetch, Flag, Mailbox, Name, NameAttribute, Quota, QuotaRoot, UnsolicitedResponse,
}; };
use async_std::channel::Receiver; use futures::StreamExt;
use async_std::prelude::*;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use crate::chat::{self, ChatId, ChatIdBlocked}; use crate::chat::{self, ChatId, ChatIdBlocked};
@@ -1922,7 +1922,7 @@ fn get_folder_meaning_by_name(folder_name: &str) -> FolderMeaning {
fn get_folder_meaning(folder_name: &Name) -> FolderMeaning { fn get_folder_meaning(folder_name: &Name) -> FolderMeaning {
for attr in folder_name.attributes() { for attr in folder_name.attributes() {
if let NameAttribute::Custom(ref label) = attr { if let NameAttribute::Extension(ref label) = attr {
match label.as_ref() { match label.as_ref() {
"\\Trash" => return FolderMeaning::Other, "\\Trash" => return FolderMeaning::Other,
"\\Sent" => return FolderMeaning::Sent, "\\Sent" => return FolderMeaning::Sent,
@@ -2388,7 +2388,7 @@ mod tests {
assert_eq!(get_folder_meaning_by_name("SPAM"), FolderMeaning::Spam); assert_eq!(get_folder_meaning_by_name("SPAM"), FolderMeaning::Spam);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_uid_next_validity() { async fn test_set_uid_next_validity() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
assert_eq!(get_uid_next(&t.ctx, "Inbox").await.unwrap(), 0); assert_eq!(get_uid_next(&t.ctx, "Inbox").await.unwrap(), 0);
@@ -2570,7 +2570,7 @@ mod tests {
("Spam", true, true, "DeltaChat"), ("Spam", true, true, "DeltaChat"),
]; ];
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_target_folder_incoming_accepted() -> Result<()> { async fn test_target_folder_incoming_accepted() -> Result<()> {
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_ACCEPTED_CHAT { for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_ACCEPTED_CHAT {
check_target_folder_combination( check_target_folder_combination(
@@ -2587,7 +2587,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_target_folder_incoming_request() -> Result<()> { async fn test_target_folder_incoming_request() -> Result<()> {
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_REQUEST { for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_REQUEST {
check_target_folder_combination( check_target_folder_combination(
@@ -2604,7 +2604,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_target_folder_outgoing() -> Result<()> { async fn test_target_folder_outgoing() -> Result<()> {
// Test outgoing emails // Test outgoing emails
for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_ACCEPTED_CHAT { for (folder, mvbox_move, chat_msg, expected_destination) in COMBINATIONS_ACCEPTED_CHAT {
@@ -2622,7 +2622,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_target_folder_setupmsg() -> Result<()> { async fn test_target_folder_setupmsg() -> Result<()> {
// Test setupmessages // Test setupmessages
for (folder, mvbox_move, chat_msg, _expected_destination) in COMBINATIONS_ACCEPTED_CHAT { for (folder, mvbox_move, chat_msg, _expected_destination) in COMBINATIONS_ACCEPTED_CHAT {
@@ -2640,7 +2640,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_imap_search_command() -> Result<()> { async fn test_get_imap_search_command() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
assert_eq!( assert_eq!(

View File

@@ -8,7 +8,7 @@ use anyhow::{Context as _, Result};
use async_imap::Client as ImapClient; use async_imap::Client as ImapClient;
use async_smtp::ServerAddress; use async_smtp::ServerAddress;
use async_std::net::{self, TcpStream}; use tokio::net::{self, TcpStream};
use super::session::Session; use super::session::Session;
use crate::login_param::{dc_build_tls, Socks5Config}; use crate::login_param::{dc_build_tls, Socks5Config};

View File

@@ -2,7 +2,7 @@ use super::Imap;
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use async_imap::extensions::idle::IdleResponse; use async_imap::extensions::idle::IdleResponse;
use async_std::prelude::*; use futures_lite::FutureExt;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use crate::{context::Context, scheduler::InterruptInfo}; use crate::{context::Context, scheduler::InterruptInfo};
@@ -87,9 +87,7 @@ impl Imap {
} }
} }
let session = handle let session = tokio::time::timeout(Duration::from_secs(15), handle.done())
.done()
.timeout(Duration::from_secs(15))
.await? .await?
.context("IMAP IDLE protocol timed out")?; .context("IMAP IDLE protocol timed out")?;
self.session = Some(Session { inner: session }); self.session = Some(Session { inner: session });
@@ -121,7 +119,7 @@ impl Imap {
// check every minute if there are new messages // check every minute if there are new messages
// TODO: grow sleep durations / make them more flexible // TODO: grow sleep durations / make them more flexible
let mut interval = async_std::stream::interval(Duration::from_secs(60)); let mut interval = tokio::time::interval(Duration::from_secs(60));
enum Event { enum Event {
Tick, Tick,
@@ -131,7 +129,7 @@ impl Imap {
let info = loop { let info = loop {
use futures::future::FutureExt; use futures::future::FutureExt;
match interval match interval
.next() .tick()
.map(|_| Event::Tick) .map(|_| Event::Tick)
.race( .race(
self.idle_interrupt self.idle_interrupt

View File

@@ -1,14 +1,13 @@
use std::{collections::BTreeMap, time::Instant}; use std::{collections::BTreeMap, time::Instant};
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use futures::stream::StreamExt;
use crate::config::Config; use crate::config::Config;
use crate::imap::Imap; use crate::imap::Imap;
use crate::log::LogExt; use crate::log::LogExt;
use crate::{context::Context, imap::FolderMeaning}; use crate::{context::Context, imap::FolderMeaning};
use async_std::stream::StreamExt;
use super::{get_folder_meaning, get_folder_meaning_by_name}; use super::{get_folder_meaning, get_folder_meaning_by_name};
impl Imap { impl Imap {
@@ -104,7 +103,7 @@ impl Imap {
let list = session let list = session
.list(Some(""), Some("*")) .list(Some(""), Some("*"))
.await? .await?
.filter_map(|f| f.ok_or_log_msg(context, "list_folders() can't get folder")); .filter_map(|f| async { f.ok_or_log_msg(context, "list_folders() can't get folder") });
Ok(list.collect().await) Ok(list.collect().await)
} }
} }

View File

@@ -2,8 +2,8 @@ use std::ops::{Deref, DerefMut};
use async_imap::Session as ImapSession; use async_imap::Session as ImapSession;
use async_native_tls::TlsStream; use async_native_tls::TlsStream;
use async_std::net::TcpStream;
use fast_socks5::client::Socks5Stream; use fast_socks5::client::Socks5Stream;
use tokio::net::TcpStream;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Session { pub(crate) struct Session {
@@ -11,7 +11,7 @@ pub(crate) struct Session {
} }
pub(crate) trait SessionStream: pub(crate) trait SessionStream:
async_std::io::Read + async_std::io::Write + Unpin + Send + Sync + std::fmt::Debug tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + Sync + std::fmt::Debug
{ {
} }

View File

@@ -2,16 +2,15 @@
use std::any::Any; use std::any::Any;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use ::pgp::types::KeyTrait; use ::pgp::types::KeyTrait;
use anyhow::{bail, ensure, format_err, Context as _, Result}; use anyhow::{bail, ensure, format_err, Context as _, Result};
use async_std::{ use futures::{StreamExt, TryStreamExt};
fs::{self, File}, use futures_lite::FutureExt;
path::{Path, PathBuf},
prelude::*,
};
use async_tar::Archive;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use tokio::fs::{self, File};
use tokio_tar::Archive;
use crate::blob::BlobObject; use crate::blob::BlobObject;
use crate::chat::{self, delete_and_reset_all_device_msgs, ChatId}; use crate::chat::{self, delete_and_reset_all_device_msgs, ChatId};
@@ -109,24 +108,22 @@ pub async fn imex(
/// Returns the filename of the backup found (otherwise an error) /// Returns the filename of the backup found (otherwise an error)
pub async fn has_backup(_context: &Context, dir_name: &Path) -> Result<String> { pub async fn has_backup(_context: &Context, dir_name: &Path) -> Result<String> {
let mut dir_iter = async_std::fs::read_dir(dir_name).await?; let mut dir_iter = tokio::fs::read_dir(dir_name).await?;
let mut newest_backup_name = "".to_string(); let mut newest_backup_name = "".to_string();
let mut newest_backup_path: Option<PathBuf> = None; let mut newest_backup_path: Option<PathBuf> = None;
while let Some(dirent) = dir_iter.next().await { while let Ok(Some(dirent)) = dir_iter.next_entry().await {
if let Ok(dirent) = dirent { let path = dirent.path();
let path = dirent.path(); let name = dirent.file_name();
let name = dirent.file_name(); let name: String = name.to_string_lossy().into();
let name: String = name.to_string_lossy().into(); if name.starts_with("delta-chat")
if name.starts_with("delta-chat") && name.ends_with(".tar")
&& name.ends_with(".tar") && (newest_backup_name.is_empty() || name > newest_backup_name)
&& (newest_backup_name.is_empty() || name > newest_backup_name) {
{ // We just use string comparison to determine which backup is newer.
// We just use string comparison to determine which backup is newer. // This works fine because the filenames have the form ...delta-chat-backup-2020-07-24-00.tar
// This works fine because the filenames have the form ...delta-chat-backup-2020-07-24-00.tar newest_backup_path = Some(path);
newest_backup_path = Some(path); newest_backup_name = name;
newest_backup_name = name;
}
} }
} }
@@ -177,7 +174,7 @@ async fn do_initiate_key_transfer(context: &Context) -> Result<String> {
let msg_id = chat::send_msg(context, chat_id, &mut msg).await?; let msg_id = chat::send_msg(context, chat_id, &mut msg).await?;
info!(context, "Wait for setup message being sent ...",); info!(context, "Wait for setup message being sent ...",);
while !context.shall_stop_ongoing().await { while !context.shall_stop_ongoing().await {
async_std::task::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
if let Ok(msg) = Message::load_from_db(context, msg_id).await { if let Ok(msg) = Message::load_from_db(context, msg_id).await {
if msg.is_sent() { if msg.is_sent() {
info!(context, "... setup message sent.",); info!(context, "... setup message sent.",);
@@ -446,7 +443,7 @@ async fn import_backup(
context.sql.config_cache.write().await.clear(); context.sql.config_cache.write().await.clear();
let archive = Archive::new(backup_file); let mut archive = Archive::new(backup_file);
let mut entries = archive.entries()?; let mut entries = archive.entries()?;
let mut last_progress = 0; let mut last_progress = 0;
@@ -477,7 +474,7 @@ async fn import_backup(
// async_tar will unpack to blobdir/BLOBS_BACKUP_NAME, so we move the file afterwards. // async_tar will unpack to blobdir/BLOBS_BACKUP_NAME, so we move the file afterwards.
f.unpack_in(context.get_blobdir()).await?; f.unpack_in(context.get_blobdir()).await?;
let from_path = context.get_blobdir().join(f.path()?); let from_path = context.get_blobdir().join(f.path()?);
if from_path.is_file().await { if from_path.is_file() {
if let Some(name) = from_path.file_name() { if let Some(name) = from_path.file_name() {
fs::rename(&from_path, context.get_blobdir().join(name)).await?; fs::rename(&from_path, context.get_blobdir().join(name)).await?;
} else { } else {
@@ -499,10 +496,7 @@ async fn import_backup(
/// Returns Ok((temp_db_path, temp_path, dest_path)) on success. Unencrypted database can be /// Returns Ok((temp_db_path, temp_path, dest_path)) on success. Unencrypted database can be
/// written to temp_db_path. The backup can then be written to temp_path. If the backup succeeded, /// written to temp_db_path. The backup can then be written to temp_path. If the backup succeeded,
/// it can be renamed to dest_path. This guarantees that the backup is complete. /// it can be renamed to dest_path. This guarantees that the backup is complete.
async fn get_next_backup_path( fn get_next_backup_path(folder: &Path, backup_time: i64) -> Result<(PathBuf, PathBuf, PathBuf)> {
folder: &Path,
backup_time: i64,
) -> Result<(PathBuf, PathBuf, PathBuf)> {
let folder = PathBuf::from(folder); let folder = PathBuf::from(folder);
let stem = chrono::NaiveDateTime::from_timestamp(backup_time, 0) let stem = chrono::NaiveDateTime::from_timestamp(backup_time, 0)
// Don't change this file name format, in has_backup() we use string comparison to determine which backup is newer: // Don't change this file name format, in has_backup() we use string comparison to determine which backup is newer:
@@ -520,7 +514,7 @@ async fn get_next_backup_path(
let mut destfile = folder.clone(); let mut destfile = folder.clone();
destfile.push(format!("{}-{:02}.tar", stem, i)); destfile.push(format!("{}-{:02}.tar", stem, i));
if !tempdbfile.exists().await && !tempfile.exists().await && !destfile.exists().await { if !tempdbfile.exists() && !tempfile.exists() && !destfile.exists() {
return Ok((tempdbfile, tempfile, destfile)); return Ok((tempdbfile, tempfile, destfile));
} }
} }
@@ -530,7 +524,7 @@ async fn get_next_backup_path(
async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Result<()> { async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Result<()> {
// get a fine backup file name (the name includes the date so that multiple backup instances are possible) // get a fine backup file name (the name includes the date so that multiple backup instances are possible)
let now = time(); let now = time();
let (temp_db_path, temp_path, dest_path) = get_next_backup_path(dir, now).await?; let (temp_db_path, temp_path, dest_path) = get_next_backup_path(dir, now)?;
let _d1 = DeleteOnDrop(temp_db_path.clone()); let _d1 = DeleteOnDrop(temp_db_path.clone());
let _d2 = DeleteOnDrop(temp_path.clone()); let _d2 = DeleteOnDrop(temp_path.clone());
@@ -584,7 +578,8 @@ impl Drop for DeleteOnDrop {
fn drop(&mut self) { fn drop(&mut self) {
let file = self.0.clone(); let file = self.0.clone();
// Not using dc_delete_file() here because it would send a DeletedBlobFile event // Not using dc_delete_file() here because it would send a DeletedBlobFile event
async_std::task::block_on(fs::remove_file(file)).ok(); // Hack to avoid panic in nested runtime calls of tokio
std::fs::remove_file(file).ok();
} }
} }
@@ -595,19 +590,21 @@ async fn export_backup_inner(
) -> Result<()> { ) -> Result<()> {
let file = File::create(temp_path).await?; let file = File::create(temp_path).await?;
let mut builder = async_tar::Builder::new(file); let mut builder = tokio_tar::Builder::new(file);
builder builder
.append_path_with_name(temp_db_path, DBFILE_BACKUP_NAME) .append_path_with_name(temp_db_path, DBFILE_BACKUP_NAME)
.await?; .await?;
let read_dir: Vec<_> = fs::read_dir(context.get_blobdir()).await?.collect().await; let read_dir: Vec<_> =
tokio_stream::wrappers::ReadDirStream::new(fs::read_dir(context.get_blobdir()).await?)
.try_collect()
.await?;
let count = read_dir.len(); let count = read_dir.len();
let mut written_files = 0; let mut written_files = 0;
let mut last_progress = 0; let mut last_progress = 0;
for entry in read_dir.into_iter() { for entry in read_dir.into_iter() {
let entry = entry?;
let name = entry.file_name(); let name = entry.file_name();
if !entry.file_type().await?.is_file() { if !entry.file_type().await?.is_file() {
warn!( warn!(
@@ -648,9 +645,9 @@ async fn import_self_keys(context: &Context, dir: &Path) -> Result<()> {
let mut imported_cnt = 0; let mut imported_cnt = 0;
let dir_name = dir.to_string_lossy(); let dir_name = dir.to_string_lossy();
let mut dir_handle = async_std::fs::read_dir(&dir).await?; let mut dir_handle = tokio::fs::read_dir(&dir).await?;
while let Some(entry) = dir_handle.next().await { while let Ok(Some(entry)) = dir_handle.next_entry().await {
let entry_fn = entry?.file_name(); let entry_fn = entry.file_name();
let name_f = entry_fn.to_string_lossy(); let name_f = entry_fn.to_string_lossy();
let path_plus_name = dir.join(&entry_fn); let path_plus_name = dir.join(&entry_fn);
match dc_get_filesuffix_lc(&name_f) { match dc_get_filesuffix_lc(&name_f) {
@@ -800,7 +797,7 @@ mod tests {
use ::pgp::armor::BlockType; use ::pgp::armor::BlockType;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_render_setup_file() { async fn test_render_setup_file() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let msg = render_setup_file(&t, "hello").await.unwrap(); let msg = render_setup_file(&t, "hello").await.unwrap();
@@ -817,7 +814,7 @@ mod tests {
assert!(msg.contains("-----END PGP MESSAGE-----\n")); assert!(msg.contains("-----END PGP MESSAGE-----\n"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_render_setup_file_newline_replace() { async fn test_render_setup_file_newline_replace() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_stock_translation(StockMessage::AcSetupMsgBody, "hello\r\nthere".to_string()) t.set_stock_translation(StockMessage::AcSetupMsgBody, "hello\r\nthere".to_string())
@@ -828,7 +825,7 @@ mod tests {
assert!(msg.contains("<p>hello<br>there</p>")); assert!(msg.contains("<p>hello<br>there</p>"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_setup_code() { async fn test_create_setup_code() {
let t = TestContext::new().await; let t = TestContext::new().await;
let setupcode = create_setup_code(&t); let setupcode = create_setup_code(&t);
@@ -843,7 +840,7 @@ mod tests {
assert_eq!(setupcode.chars().nth(39).unwrap(), '-'); assert_eq!(setupcode.chars().nth(39).unwrap(), '-');
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_export_public_key_to_asc_file() { async fn test_export_public_key_to_asc_file() {
let context = TestContext::new().await; let context = TestContext::new().await;
let key = alice_keypair().public; let key = alice_keypair().public;
@@ -853,12 +850,12 @@ mod tests {
.is_ok()); .is_ok());
let blobdir = context.ctx.get_blobdir().to_str().unwrap(); let blobdir = context.ctx.get_blobdir().to_str().unwrap();
let filename = format!("{}/public-key-default.asc", blobdir); let filename = format!("{}/public-key-default.asc", blobdir);
let bytes = async_std::fs::read(&filename).await.unwrap(); let bytes = tokio::fs::read(&filename).await.unwrap();
assert_eq!(bytes, key.to_asc(None).into_bytes()); assert_eq!(bytes, key.to_asc(None).into_bytes());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_export_private_key_to_asc_file() { async fn test_export_private_key_to_asc_file() {
let context = TestContext::new().await; let context = TestContext::new().await;
let key = alice_keypair().secret; let key = alice_keypair().secret;
@@ -868,12 +865,12 @@ mod tests {
.is_ok()); .is_ok());
let blobdir = context.ctx.get_blobdir().to_str().unwrap(); let blobdir = context.ctx.get_blobdir().to_str().unwrap();
let filename = format!("{}/private-key-default.asc", blobdir); let filename = format!("{}/private-key-default.asc", blobdir);
let bytes = async_std::fs::read(&filename).await.unwrap(); let bytes = tokio::fs::read(&filename).await.unwrap();
assert_eq!(bytes, key.to_asc(None).into_bytes()); assert_eq!(bytes, key.to_asc(None).into_bytes());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_export_and_import_key() { async fn test_export_and_import_key() {
let context = TestContext::new_alice().await; let context = TestContext::new_alice().await;
let blobdir = context.ctx.get_blobdir(); let blobdir = context.ctx.get_blobdir();
@@ -887,7 +884,7 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_export_and_import_backup() -> Result<()> { async fn test_export_and_import_backup() -> Result<()> {
let backup_dir = tempfile::tempdir().unwrap(); let backup_dir = tempfile::tempdir().unwrap();
@@ -896,26 +893,21 @@ mod tests {
let context2 = TestContext::new().await; let context2 = TestContext::new().await;
assert!(!context2.is_configured().await?); assert!(!context2.is_configured().await?);
assert!(has_backup(&context2, backup_dir.path().as_ref()) assert!(has_backup(&context2, backup_dir.path()).await.is_err());
.await
.is_err());
// export from context1 // export from context1
assert!(imex( assert!(
&context1, imex(&context1, ImexMode::ExportBackup, backup_dir.path(), None)
ImexMode::ExportBackup, .await
backup_dir.path().as_ref(), .is_ok()
None, );
)
.await
.is_ok());
let _event = context1 let _event = context1
.evtracker .evtracker
.get_matching(|evt| matches!(evt, EventType::ImexProgress(1000))) .get_matching(|evt| matches!(evt, EventType::ImexProgress(1000)))
.await; .await;
// import to context2 // import to context2
let backup = has_backup(&context2, backup_dir.path().as_ref()).await?; let backup = has_backup(&context2, backup_dir.path()).await?;
// Import of unencrypted backup with incorrect "foobar" backup passphrase fails. // Import of unencrypted backup with incorrect "foobar" backup passphrase fails.
assert!(imex( assert!(imex(
@@ -961,7 +953,7 @@ mod tests {
const S_EM_SETUPCODE: &str = "1742-0185-6197-1303-7016-8412-3581-4441-0597"; const S_EM_SETUPCODE: &str = "1742-0185-6197-1303-7016-8412-3581-4441-0597";
const S_EM_SETUPFILE: &str = include_str!("../test-data/message/stress.txt"); const S_EM_SETUPFILE: &str = include_str!("../test-data/message/stress.txt");
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_split_and_decrypt() { async fn test_split_and_decrypt() {
let buf_1 = S_EM_SETUPFILE.as_bytes().to_vec(); let buf_1 = S_EM_SETUPFILE.as_bytes().to_vec();
let (typ, headers, base64) = split_armored_data(&buf_1).unwrap(); let (typ, headers, base64) = split_armored_data(&buf_1).unwrap();
@@ -984,20 +976,20 @@ mod tests {
assert!(headers.get(HEADER_SETUPCODE).is_none()); assert!(headers.get(HEADER_SETUPCODE).is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_key_transfer() -> Result<()> { async fn test_key_transfer() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let alice_clone = alice.clone(); let alice_clone = alice.clone();
let key_transfer_task = async_std::task::spawn(async move { let key_transfer_task = tokio::task::spawn(async move {
let ctx = alice_clone; let ctx = alice_clone;
initiate_key_transfer(&ctx).await initiate_key_transfer(&ctx).await
}); });
// Wait for the message to be added to the queue. // Wait for the message to be added to the queue.
async_std::task::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let sent = alice.pop_sent_msg().await; let sent = alice.pop_sent_msg().await;
let setup_code = key_transfer_task.await?; let setup_code = key_transfer_task.await??;
// Alice sets up a second device. // Alice sets up a second device.
let alice2 = TestContext::new().await; let alice2 = TestContext::new().await;

View File

@@ -448,7 +448,7 @@ mod tests {
.unwrap(); .unwrap();
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_load_next_job_two() -> Result<()> { async fn test_load_next_job_two() -> Result<()> {
// We want to ensure that loading jobs skips over jobs which // We want to ensure that loading jobs skips over jobs which
// fails to load from the database instead of failing to load // fails to load from the database instead of failing to load
@@ -464,7 +464,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_load_next_job_one() -> Result<()> { async fn test_load_next_job_one() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;

View File

@@ -11,6 +11,7 @@ use num_traits::FromPrimitive;
use pgp::composed::Deserializable; use pgp::composed::Deserializable;
use pgp::ser::Serialize; use pgp::ser::Serialize;
use pgp::types::{KeyTrait, SecretKeyTrait}; use pgp::types::{KeyTrait, SecretKeyTrait};
use tokio::runtime::Handle;
use crate::config::Config; use crate::config::Config;
use crate::constants::KeyGenType; use crate::constants::KeyGenType;
@@ -219,9 +220,10 @@ async fn generate_keypair(context: &Context) -> Result<KeyPair> {
let keytype = KeyGenType::from_i32(context.get_config_int(Config::KeyGenType).await?) let keytype = KeyGenType::from_i32(context.get_config_int(Config::KeyGenType).await?)
.unwrap_or_default(); .unwrap_or_default();
info!(context, "Generating keypair with type {}", keytype); info!(context, "Generating keypair with type {}", keytype);
let keypair = let keypair = Handle::current()
async_std::task::spawn_blocking(move || crate::pgp::create_keypair(addr, keytype)) .spawn_blocking(move || crate::pgp::create_keypair(addr, keytype))
.await?; .await??;
store_self_keypair(context, &keypair, KeyPairUse::Default).await?; store_self_keypair(context, &keypair, KeyPairUse::Default).await?;
info!( info!(
context, context,
@@ -397,8 +399,8 @@ mod tests {
use super::*; use super::*;
use crate::test_utils::{alice_keypair, TestContext}; use crate::test_utils::{alice_keypair, TestContext};
use async_std::sync::Arc;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::Arc;
static KEYPAIR: Lazy<KeyPair> = Lazy::new(alice_keypair); static KEYPAIR: Lazy<KeyPair> = Lazy::new(alice_keypair);
@@ -520,7 +522,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
assert_eq!(key, key2); assert_eq!(key, key2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_load_self_existing() { async fn test_load_self_existing() {
let alice = alice_keypair(); let alice = alice_keypair();
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -530,7 +532,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
assert_eq!(alice.secret, seckey); assert_eq!(alice.secret, seckey);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_load_self_generate_public() { async fn test_load_self_generate_public() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.set_config(Config::ConfiguredAddr, Some("alice@example.org")) t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
@@ -540,7 +542,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
assert!(key.is_ok()); assert!(key.is_ok());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_load_self_generate_secret() { async fn test_load_self_generate_secret() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.set_config(Config::ConfiguredAddr, Some("alice@example.org")) t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
@@ -550,7 +552,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
assert!(key.is_ok()); assert!(key.is_ok());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_load_self_generate_concurrent() { async fn test_load_self_generate_concurrent() {
use std::thread; use std::thread;
@@ -560,11 +562,19 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
.unwrap(); .unwrap();
let thr0 = { let thr0 = {
let ctx = t.clone(); let ctx = t.clone();
thread::spawn(move || async_std::task::block_on(SignedPublicKey::load_self(&ctx))) thread::spawn(move || {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(SignedPublicKey::load_self(&ctx))
})
}; };
let thr1 = { let thr1 = {
let ctx = t.clone(); let ctx = t.clone();
thread::spawn(move || async_std::task::block_on(SignedPublicKey::load_self(&ctx))) thread::spawn(move || {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(SignedPublicKey::load_self(&ctx))
})
}; };
let res0 = thr0.join().unwrap(); let res0 = thr0.join().unwrap();
let res1 = thr1.join().unwrap(); let res1 = thr1.join().unwrap();
@@ -577,7 +587,7 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
assert_eq!(pubkey.primary_key, KEYPAIR.public.primary_key); assert_eq!(pubkey.primary_key, KEYPAIR.public.primary_key);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_save_self_key_twice() { async fn test_save_self_key_twice() {
// Saving the same key twice should result in only one row in // Saving the same key twice should result in only one row in
// the keypairs table. // the keypairs table.

View File

@@ -76,7 +76,7 @@ mod tests {
assert_eq!(sec_ring.keys(), [alice.secret]); assert_eq!(sec_ring.keys(), [alice.secret]);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_keyring_load_self() { async fn test_keyring_load_self() {
// new_self() implies load_self() // new_self() implies load_self()
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;

View File

@@ -1,5 +1,6 @@
//! # Delta Chat Core Library. //! # Delta Chat Core Library.
#![recursion_limit = "256"]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![deny( #![deny(
unused, unused,

View File

@@ -1,12 +1,12 @@
//! Location handling. //! Location handling.
use std::convert::TryFrom; use std::convert::TryFrom;
use std::time::Duration;
use anyhow::{ensure, Context as _, Result}; use anyhow::{ensure, Context as _, Result};
use async_std::channel::Receiver; use async_channel::Receiver;
use async_std::future::timeout;
use bitflags::bitflags; use bitflags::bitflags;
use quick_xml::events::{BytesEnd, BytesStart, BytesText}; use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use std::time::Duration; use tokio::time::timeout;
use crate::chat::{self, ChatId}; use crate::chat::{self, ChatId};
use crate::contact::ContactId; use crate::contact::ContactId;
@@ -731,7 +731,7 @@ mod tests {
use crate::dc_receive_imf::dc_receive_imf; use crate::dc_receive_imf::dc_receive_imf;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_kml_parse() { async fn test_kml_parse() {
let context = TestContext::new().await; let context = TestContext::new().await;
@@ -763,7 +763,7 @@ mod tests {
assert_eq!(locations_ref[1].timestamp, 1544739072); assert_eq!(locations_ref[1].timestamp, 1544739072);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_message_kml() { async fn test_get_message_kml() {
let context = TestContext::new().await; let context = TestContext::new().await;
let timestamp = 1598490000; let timestamp = 1598490000;
@@ -791,7 +791,7 @@ mod tests {
} }
/// Tests that location.kml is hidden. /// Tests that location.kml is hidden.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn receive_location_kml() -> Result<()> { async fn receive_location_kml() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;

View File

@@ -1,7 +1,6 @@
//! # Logging. //! # Logging.
use crate::context::Context; use crate::context::Context;
use async_std::task::block_on;
#[macro_export] #[macro_export]
macro_rules! info { macro_rules! info {
@@ -49,15 +48,13 @@ impl Context {
/// Set last error string. /// Set last error string.
/// Implemented as blocking as used from macros in different, not always async blocks. /// Implemented as blocking as used from macros in different, not always async blocks.
pub fn set_last_error(&self, error: &str) { pub fn set_last_error(&self, error: &str) {
block_on(async move { let mut last_error = self.last_error.write().unwrap();
let mut last_error = self.last_error.write().await; *last_error = error.to_string();
*last_error = error.to_string();
});
} }
/// Get last error string. /// Get last error string.
pub async fn get_last_error(&self) -> String { pub fn get_last_error(&self) -> String {
let last_error = &*self.last_error.read().await; let last_error = &*self.last_error.read().unwrap();
last_error.clone() last_error.clone()
} }
} }
@@ -159,24 +156,24 @@ mod tests {
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
use anyhow::Result; use anyhow::Result;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_last_error() -> Result<()> { async fn test_get_last_error() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!(t.get_last_error().await, ""); assert_eq!(t.get_last_error(), "");
error!(t, "foo-error"); error!(t, "foo-error");
assert_eq!(t.get_last_error().await, "foo-error"); assert_eq!(t.get_last_error(), "foo-error");
warn!(t, "foo-warning"); warn!(t, "foo-warning");
assert_eq!(t.get_last_error().await, "foo-error"); assert_eq!(t.get_last_error(), "foo-error");
info!(t, "foo-info"); info!(t, "foo-info");
assert_eq!(t.get_last_error().await, "foo-error"); assert_eq!(t.get_last_error(), "foo-error");
error!(t, "bar-error"); error!(t, "bar-error");
error!(t, "baz-error"); error!(t, "baz-error");
assert_eq!(t.get_last_error().await, "baz-error"); assert_eq!(t.get_last_error(), "baz-error");
Ok(()) Ok(())
} }

View File

@@ -4,18 +4,16 @@ use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::time::Duration; use std::time::Duration;
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_NORMAL, DC_LP_AUTH_OAUTH2};
use crate::provider::{get_provider_by_id, Provider};
use crate::{context::Context, provider::Socket};
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use async_std::io;
use async_std::net::TcpStream;
use async_native_tls::Certificate; use async_native_tls::Certificate;
pub use async_smtp::ServerAddress; pub use async_smtp::ServerAddress;
use fast_socks5::client::Socks5Stream; use fast_socks5::client::Socks5Stream;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::{io, net::TcpStream};
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_NORMAL, DC_LP_AUTH_OAUTH2};
use crate::provider::{get_provider_by_id, Provider};
use crate::{context::Context, provider::Socket};
#[derive(Copy, Clone, Debug, Display, FromPrimitive, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Display, FromPrimitive, PartialEq, Eq)]
#[repr(u32)] #[repr(u32)]
@@ -424,7 +422,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_save_load_login_param() -> Result<()> { async fn test_save_load_login_param() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -460,7 +458,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_build_tls() -> Result<()> { async fn test_build_tls() -> Result<()> {
// we are using some additional root certificates. // we are using some additional root certificates.
// make sure, they do not break construction of TlsConnector // make sure, they do not break construction of TlsConnector

View File

@@ -1,9 +1,9 @@
//! # Messages and their identifiers. //! # Messages and their identifiers.
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use anyhow::{ensure, format_err, Context as _, Result}; use anyhow::{ensure, format_err, Context as _, Result};
use async_std::path::{Path, PathBuf};
use deltachat_derive::{FromSql, ToSql}; use deltachat_derive::{FromSql, ToSql};
use rusqlite::types::ValueRef; use rusqlite::types::ValueRef;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -1857,7 +1857,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_prepare_message_and_send() { async fn test_prepare_message_and_send() {
use crate::config::Config; use crate::config::Config;
@@ -1879,7 +1879,7 @@ mod tests {
} }
/// Tests that message cannot be prepared if account has no configured address. /// Tests that message cannot be prepared if account has no configured address.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_prepare_not_configured() { async fn test_prepare_not_configured() {
let d = test::TestContext::new().await; let d = test::TestContext::new().await;
let ctx = &d.ctx; let ctx = &d.ctx;
@@ -1891,7 +1891,7 @@ mod tests {
assert!(chat::prepare_msg(ctx, chat.id, &mut msg).await.is_err()); assert!(chat::prepare_msg(ctx, chat.id, &mut msg).await.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_webrtc_instance() { async fn test_parse_webrtc_instance() {
let (webrtc_type, url) = Message::parse_webrtc_instance("basicwebrtc:https://foo/bar"); let (webrtc_type, url) = Message::parse_webrtc_instance("basicwebrtc:https://foo/bar");
assert_eq!(webrtc_type, VideochatType::BasicWebrtc); assert_eq!(webrtc_type, VideochatType::BasicWebrtc);
@@ -1910,7 +1910,7 @@ mod tests {
assert_eq!(url, "https://j.si/foo"); assert_eq!(url, "https://j.si/foo");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_webrtc_instance() { async fn test_create_webrtc_instance() {
// webrtc_instance may come from an input field of the ui, be pretty tolerant on input // webrtc_instance may come from an input field of the ui, be pretty tolerant on input
let instance = Message::create_webrtc_instance("https://meet.jit.si/", "123"); let instance = Message::create_webrtc_instance("https://meet.jit.si/", "123");
@@ -1947,7 +1947,7 @@ mod tests {
assert_eq!(instance, "basicwebrtc:https://basic.stuff/12345ab"); assert_eq!(instance, "basicwebrtc:https://basic.stuff/12345ab");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_webrtc_instance_noroom() { async fn test_create_webrtc_instance_noroom() {
// webrtc_instance may come from an input field of the ui, be pretty tolerant on input // webrtc_instance may come from an input field of the ui, be pretty tolerant on input
let instance = Message::create_webrtc_instance("bla.foo$NOROOM", "123"); let instance = Message::create_webrtc_instance("bla.foo$NOROOM", "123");
@@ -1967,7 +1967,7 @@ mod tests {
assert_eq!(instance, "https://bla.foo/?$NOROOM=123"); assert_eq!(instance, "https://bla.foo/?$NOROOM=123");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_width_height() { async fn test_get_width_height() {
let t = test::TestContext::new().await; let t = test::TestContext::new().await;
@@ -1997,7 +1997,7 @@ mod tests {
assert!(has_image); assert!(has_image);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_quote() { async fn test_quote() {
use crate::config::Config; use crate::config::Config;
@@ -2033,7 +2033,7 @@ mod tests {
assert!(quoted_msg.get_text() == msg2.quoted_text()); assert!(quoted_msg.get_text() == msg2.quoted_text());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_chat_id() { async fn test_get_chat_id() {
// Alice receives a message that pops up as a contact request // Alice receives a message that pops up as a contact request
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -2057,7 +2057,7 @@ mod tests {
assert_eq!(msg.get_text().unwrap(), "hello".to_string()); assert_eq!(msg.get_text().unwrap(), "hello".to_string());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_override_sender_name() { async fn test_set_override_sender_name() {
// send message with overridden sender name // send message with overridden sender name
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -2105,7 +2105,7 @@ mod tests {
assert_ne!(chat.typ, Chattype::Mailinglist); assert_ne!(chat.typ, Chattype::Mailinglist);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_markseen_msgs() -> Result<()> { async fn test_markseen_msgs() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -2178,7 +2178,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_state() -> Result<()> { async fn test_get_state() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -2234,7 +2234,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_is_bot() -> Result<()> { async fn test_is_bot() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;

View File

@@ -3,9 +3,9 @@
use std::convert::TryInto; use std::convert::TryInto;
use anyhow::{bail, ensure, Context as _, Result}; use anyhow::{bail, ensure, Context as _, Result};
use async_std::fs;
use chrono::TimeZone; use chrono::TimeZone;
use lettre_email::{mime, Address, Header, MimeMultipartType, PartBuilder}; use lettre_email::{mime, Address, Header, MimeMultipartType, PartBuilder};
use tokio::fs;
use crate::blob::BlobObject; use crate::blob::BlobObject;
use crate::chat::Chat; use crate::chat::Chat;
@@ -1451,9 +1451,9 @@ fn maybe_encode_words(words: &str) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use async_std::fs::File;
use async_std::prelude::*;
use mailparse::{addrparse_header, MailHeaderMap}; use mailparse::{addrparse_header, MailHeaderMap};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use crate::chat::ChatId; use crate::chat::ChatId;
use crate::chat::{ use crate::chat::{
@@ -1556,7 +1556,7 @@ mod tests {
assert_eq!(maybe_encode_words("äöü"), "=?utf-8?b?w6TDtsO8?="); assert_eq!(maybe_encode_words("äöü"), "=?utf-8?b?w6TDtsO8?=");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_from_mua() { async fn test_subject_from_mua() {
// 1.: Receive a mail from an MUA // 1.: Receive a mail from an MUA
assert_eq!( assert_eq!(
@@ -1590,7 +1590,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_from_dc() { async fn test_subject_from_dc() {
// 2. Receive a message from Delta Chat // 2. Receive a message from Delta Chat
assert_eq!( assert_eq!(
@@ -1610,7 +1610,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_outgoing() { async fn test_subject_outgoing() {
// 3. Send the first message to a new contact // 3. Send the first message to a new contact
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -1624,7 +1624,7 @@ mod tests {
assert_eq!(first_subject_str(t).await, "Message from Alice"); assert_eq!(first_subject_str(t).await, "Message from Alice");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_unicode() { async fn test_subject_unicode() {
// 4. Receive messages with unicode characters and make sure that we do not panic (we do not care about the result) // 4. Receive messages with unicode characters and make sure that we do not panic (we do not care about the result)
msg_to_subject_str( msg_to_subject_str(
@@ -1656,7 +1656,7 @@ mod tests {
.await; .await;
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_mdn() { async fn test_subject_mdn() {
// 5. Receive an mdn (read receipt) and make sure the mdn's subject is not used // 5. Receive an mdn (read receipt) and make sure the mdn's subject is not used
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -1706,7 +1706,7 @@ mod tests {
assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap()); assert_eq!("Re: Hello, Bob", mf.subject_str(&t).await.unwrap());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_subject_in_group() -> Result<()> { async fn test_subject_in_group() -> Result<()> {
async fn send_msg_get_subject( async fn send_msg_get_subject(
t: &TestContext, t: &TestContext,
@@ -1914,7 +1914,7 @@ mod tests {
new_msg new_msg
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
// This test could still be extended // This test could still be extended
async fn test_render_reply() { async fn test_render_reply() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -2008,7 +2008,7 @@ mod tests {
assert!(!headers.lines().any(|l| l.trim().is_empty())); assert!(!headers.lines().any(|l| l.trim().is_empty()));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_unencrypted() -> anyhow::Result<()> { async fn test_selfavatar_unencrypted() -> anyhow::Result<()> {
// create chat with bob, set selfavatar // create chat with bob, set selfavatar
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -2065,7 +2065,7 @@ mod tests {
} }
/// Test that removed member address does not go into the `To:` field. /// Test that removed member address does not go into the `To:` field.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remove_member_bcc() -> Result<()> { async fn test_remove_member_bcc() -> Result<()> {
// Alice creates a group with Bob and Claire and then removes Bob. // Alice creates a group with Bob and Claire and then removes Bob.
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -2096,7 +2096,7 @@ mod tests {
} }
/// Tests that standard IMF header "From:" comes before non-standard "Autocrypt:" header. /// Tests that standard IMF header "From:" comes before non-standard "Autocrypt:" header.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_from_before_autocrypt() -> Result<()> { async fn test_from_before_autocrypt() -> Result<()> {
// create chat with bob // create chat with bob
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;

View File

@@ -2,7 +2,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::future::Future; use std::future::Future;
use std::io::Cursor;
use std::pin::Pin; use std::pin::Pin;
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
@@ -1029,9 +1028,8 @@ impl MimeMessage {
if decoded_data.is_empty() { if decoded_data.is_empty() {
return; return;
} }
let reader = Cursor::new(decoded_data);
let msg_type = if context let msg_type = if context
.is_webxdc_file(filename, reader) .is_webxdc_file(filename, decoded_data)
.await .await
.unwrap_or(false) .unwrap_or(false)
{ {
@@ -1750,7 +1748,7 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mimeparser_fromheader() { async fn test_mimeparser_fromheader() {
let ctx = TestContext::new_alice().await; let ctx = TestContext::new_alice().await;
@@ -1808,7 +1806,7 @@ mod tests {
assert_eq!(contact.display_name, Some("Götz C".to_string())); assert_eq!(contact.display_name, Some("Götz C".to_string()));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dc_mimeparser_crash() { async fn test_dc_mimeparser_crash() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = include_bytes!("../test-data/message/issue_523.txt"); let raw = include_bytes!("../test-data/message/issue_523.txt");
@@ -1820,7 +1818,7 @@ mod tests {
assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_rfc724_mid_exists() { async fn test_get_rfc724_mid_exists() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = include_bytes!("../test-data/message/mail_with_message_id.txt"); let raw = include_bytes!("../test-data/message/mail_with_message_id.txt");
@@ -1834,7 +1832,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_rfc724_mid_not_exists() { async fn test_get_rfc724_mid_not_exists() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = include_bytes!("../test-data/message/issue_523.txt"); let raw = include_bytes!("../test-data/message/issue_523.txt");
@@ -1876,7 +1874,7 @@ mod tests {
mail mail
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename() { async fn test_get_attachment_filename() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1887,7 +1885,7 @@ mod tests {
assert_eq!(filename, Some("test.html".to_string())) assert_eq!(filename, Some("test.html".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_encoded_words() { async fn test_get_attachment_filename_encoded_words() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1898,7 +1896,7 @@ mod tests {
assert_eq!(filename, Some("Maßnahmen Okt. 2020.html".to_string())) assert_eq!(filename, Some("Maßnahmen Okt. 2020.html".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_encoded_words_binary() { async fn test_get_attachment_filename_encoded_words_binary() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1909,7 +1907,7 @@ mod tests {
assert_eq!(filename, Some(" § 165 Abs".to_string())) assert_eq!(filename, Some(" § 165 Abs".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_encoded_words_windows1251() { async fn test_get_attachment_filename_encoded_words_windows1251() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1920,7 +1918,7 @@ mod tests {
assert_eq!(filename, Some("file Что нового 2020.pdf".to_string())) assert_eq!(filename, Some("file Что нового 2020.pdf".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_encoded_words_cont() { async fn test_get_attachment_filename_encoded_words_cont() {
// test continued encoded-words and also test apostropes work that way // test continued encoded-words and also test apostropes work that way
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -1932,7 +1930,7 @@ mod tests {
assert_eq!(filename, Some("Maßn'ah'men Okt. 2020.html".to_string())) assert_eq!(filename, Some("Maßn'ah'men Okt. 2020.html".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_encoded_words_bad_delimiter() { async fn test_get_attachment_filename_encoded_words_bad_delimiter() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1944,7 +1942,7 @@ mod tests {
assert_eq!(filename, Some("=?utf-8?q?foo?=.bar".to_string())) assert_eq!(filename, Some("=?utf-8?q?foo?=.bar".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_apostrophed() { async fn test_get_attachment_filename_apostrophed() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1955,7 +1953,7 @@ mod tests {
assert_eq!(filename, Some("Maßnahmen Okt. 2021.html".to_string())) assert_eq!(filename, Some("Maßnahmen Okt. 2021.html".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_apostrophed_cont() { async fn test_get_attachment_filename_apostrophed_cont() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1966,7 +1964,7 @@ mod tests {
assert_eq!(filename, Some("Maßnahmen März 2022.html".to_string())) assert_eq!(filename, Some("Maßnahmen März 2022.html".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_apostrophed_windows1251() { async fn test_get_attachment_filename_apostrophed_windows1251() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1977,7 +1975,7 @@ mod tests {
assert_eq!(filename, Some("программирование.HTM".to_string())) assert_eq!(filename, Some("программирование.HTM".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_apostrophed_cp1252() { async fn test_get_attachment_filename_apostrophed_cp1252() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1988,7 +1986,7 @@ mod tests {
assert_eq!(filename, Some("Auftragsbestätigung.pdf".to_string())) assert_eq!(filename, Some("Auftragsbestätigung.pdf".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_apostrophed_invalid() { async fn test_get_attachment_filename_apostrophed_invalid() {
let t = TestContext::new().await; let t = TestContext::new().await;
let mail = load_mail_with_attachment( let mail = load_mail_with_attachment(
@@ -1999,7 +1997,7 @@ mod tests {
assert_eq!(filename, Some("somedäüta.html.zip".to_string())) assert_eq!(filename, Some("somedäüta.html.zip".to_string()))
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_attachment_filename_combined() { async fn test_get_attachment_filename_combined() {
// test that if `filename` and `filename*0` are given, the filename is not doubled // test that if `filename` and `filename*0` are given, the filename is not doubled
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -2024,7 +2022,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_first_addr() { async fn test_parse_first_addr() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"From: hello@one.org, world@two.org\n\ let raw = b"From: hello@one.org, world@two.org\n\
@@ -2045,7 +2043,7 @@ mod tests {
assert!(mimeparser.chat_disposition_notification_to.is_none()); assert!(mimeparser.chat_disposition_notification_to.is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_parent_timestamp() { async fn test_get_parent_timestamp() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"From: foo@example.org\n\ let raw = b"From: foo@example.org\n\
@@ -2078,7 +2076,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mimeparser_with_context() { async fn test_mimeparser_with_context() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"From: hello\n\ let raw = b"From: hello\n\
@@ -2130,7 +2128,7 @@ mod tests {
.is_none()); .is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mimeparser_with_avatars() { async fn test_mimeparser_with_avatars() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -2171,7 +2169,7 @@ mod tests {
assert!(mimeparser.group_avatar.unwrap().is_change()); assert!(mimeparser.group_avatar.unwrap().is_change());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mimeparser_with_videochat() { async fn test_mimeparser_with_videochat() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -2193,7 +2191,7 @@ mod tests {
assert_eq!(mimeparser.group_avatar, None); assert_eq!(mimeparser.group_avatar, None);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mimeparser_message_kml() { async fn test_mimeparser_message_kml() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"Chat-Version: 1.0\n\ let raw = b"Chat-Version: 1.0\n\
@@ -2238,7 +2236,7 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\
assert_eq!(mimeparser.parts.len(), 1); assert_eq!(mimeparser.parts.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_mdn() { async fn test_parse_mdn() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\ let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
@@ -2288,7 +2286,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
/// ///
/// RFC 6522 specifically allows MDNs to be nested inside /// RFC 6522 specifically allows MDNs to be nested inside
/// multipart MIME messages. /// multipart MIME messages.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_multiple_mdns() { async fn test_parse_multiple_mdns() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\ let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
@@ -2364,7 +2362,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
assert_eq!(message.mdn_reports.len(), 2); assert_eq!(message.mdn_reports.len(), 2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_mdn_with_additional_message_ids() { async fn test_parse_mdn_with_additional_message_ids() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\ let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
@@ -2419,7 +2417,7 @@ Additional-Message-IDs: <foo@example.com> <foo@example.net>\n\
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_inline_attachment() { async fn test_parse_inline_attachment() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC) let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC)
@@ -2459,7 +2457,7 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
assert_eq!(message.parts[0].msg, "Mail with inline attachment Hello!"); assert_eq!(message.parts[0].msg, "Mail with inline attachment Hello!");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_hide_html_without_content() { async fn test_hide_html_without_content() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC) let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC)
@@ -2503,12 +2501,12 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
let f = async_std::fs::File::open(blob.to_abs_path()).await.unwrap(); let f = tokio::fs::File::open(blob.to_abs_path()).await.unwrap();
let size = f.metadata().await.unwrap().len(); let size = f.metadata().await.unwrap().len();
assert_eq!(size, 154); assert_eq!(size, 154);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn parse_inline_image() { async fn parse_inline_image() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br#"Message-ID: <foobar@example.org> let raw = br#"Message-ID: <foobar@example.org>
@@ -2554,7 +2552,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
assert_eq!(message.parts[0].msg, "example Test"); assert_eq!(message.parts[0].msg, "example Test");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn parse_thunderbird_html_embedded_image() { async fn parse_thunderbird_html_embedded_image() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br#"To: Alice <alice@example.org> let raw = br#"To: Alice <alice@example.org>
@@ -2627,7 +2625,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
} }
// Outlook specifies filename in the "name" attribute of Content-Type // Outlook specifies filename in the "name" attribute of Content-Type
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn parse_outlook_html_embedded_image() { async fn parse_outlook_html_embedded_image() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br##"From: Anonymous <anonymous@example.org> let raw = br##"From: Anonymous <anonymous@example.org>
@@ -2766,7 +2764,7 @@ CWt6wx7fiLp0qS9RrX75g6Gqw7nfCs6EcBERcIPt7DTe8VStJwf3LWqVwxl4gQl46yhfoqwEO+I=
assert!(test.is_empty()); assert!(test.is_empty());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn parse_format_flowed_quote() { async fn parse_format_flowed_quote() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
@@ -2802,7 +2800,7 @@ Reply
assert_eq!(message.parts[0].msg, "Reply"); assert_eq!(message.parts[0].msg, "Reply");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn parse_quote_without_reply() { async fn parse_quote_without_reply() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
@@ -2834,7 +2832,7 @@ From: alice <alice@example.org>
assert_eq!(message.parts[0].msg, ""); assert_eq!(message.parts[0].msg, "");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn parse_quote_top_posting() { async fn parse_quote_top_posting() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
@@ -2865,7 +2863,7 @@ On 2020-10-25, Bob wrote:
assert_eq!(message.parts[0].msg, "A reply."); assert_eq!(message.parts[0].msg, "A reply.");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_attachment_quote() { async fn test_attachment_quote() {
let context = TestContext::new().await; let context = TestContext::new().await;
let raw = include_bytes!("../test-data/message/quote_attach.eml"); let raw = include_bytes!("../test-data/message/quote_attach.eml");
@@ -2883,7 +2881,7 @@ On 2020-10-25, Bob wrote:
assert_eq!(mimeparser.parts[0].typ, Viewtype::File); assert_eq!(mimeparser.parts[0].typ, Viewtype::File);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_quote_div() { async fn test_quote_div() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/gmx-quote.eml"); let raw = include_bytes!("../test-data/message/gmx-quote.eml");
@@ -2892,7 +2890,7 @@ On 2020-10-25, Bob wrote:
assert_eq!(mimeparser.parts[0].param.get(Param::Quote).unwrap(), "Now?"); assert_eq!(mimeparser.parts[0].param.get(Param::Quote).unwrap(), "Now?");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_allinkl_blockquote() { async fn test_allinkl_blockquote() {
// all-inkl.com puts quotes into `<blockquote> </blockquote>`. // all-inkl.com puts quotes into `<blockquote> </blockquote>`.
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -2905,7 +2903,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_subj_to_multimedia_msg() { async fn test_add_subj_to_multimedia_msg() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await.unwrap(); t.set_config(Config::ShowEmails, Some("2")).await.unwrap();
@@ -2938,7 +2936,7 @@ On 2020-10-25, Bob wrote:
assert_eq!(msg.get_filemime().unwrap(), "image/png"); assert_eq!(msg.get_filemime().unwrap(), "image/png");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mime_modified_plain() { async fn test_mime_modified_plain() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml"); let raw = include_bytes!("../test-data/message/text_plain_unspecified.eml");
@@ -2950,7 +2948,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mime_modified_alt_plain_html() { async fn test_mime_modified_alt_plain_html() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml"); let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
@@ -2962,7 +2960,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mime_modified_alt_plain() { async fn test_mime_modified_alt_plain() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_alt_plain.eml"); let raw = include_bytes!("../test-data/message/text_alt_plain.eml");
@@ -2977,7 +2975,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mime_modified_alt_html() { async fn test_mime_modified_alt_html() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_alt_html.eml"); let raw = include_bytes!("../test-data/message/text_alt_html.eml");
@@ -2989,7 +2987,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mime_modified_html() { async fn test_mime_modified_html() {
let t = TestContext::new().await; let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/text_html.eml"); let raw = include_bytes!("../test-data/message/text_html.eml");
@@ -3001,7 +2999,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mime_modified_large_plain() { async fn test_mime_modified_large_plain() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -3022,7 +3020,7 @@ On 2020-10-25, Bob wrote:
assert!(mimemsg.parts[0].msg.len() <= DC_DESIRED_TEXT_LEN + DC_ELLIPSIS.len()); assert!(mimemsg.parts[0].msg.len() <= DC_DESIRED_TEXT_LEN + DC_ELLIPSIS.len());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_x_microsoft_original_message_id() { async fn test_x_microsoft_original_message_id() {
let t = TestContext::new().await; let t = TestContext::new().await;
let message = MimeMessage::from_bytes(&t, b"Date: Wed, 17 Feb 2021 15:45:15 +0000\n\ let message = MimeMessage::from_bytes(&t, b"Date: Wed, 17 Feb 2021 15:45:15 +0000\n\
@@ -3045,7 +3043,7 @@ On 2020-10-25, Bob wrote:
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_long_in_reply_to() -> Result<()> { async fn test_long_in_reply_to() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -3088,7 +3086,7 @@ Some reply
} }
// Test that WantsMdn parameter is not set on outgoing messages. // Test that WantsMdn parameter is not set on outgoing messages.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_outgoing_wants_mdn() -> Result<()> { async fn test_outgoing_wants_mdn() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -3119,7 +3117,7 @@ Message.
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ignore_read_receipt_to_self() -> Result<()> { async fn test_ignore_read_receipt_to_self() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -3189,7 +3187,7 @@ Message.
/// ///
/// It does not have required Original-Message-ID field, so it is useless, but we want to /// It does not have required Original-Message-ID field, so it is useless, but we want to
/// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message. /// recognize it as MDN nevertheless to avoid displaying it in the chat as normal message.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_ms_exchange_mdn() -> Result<()> { async fn test_ms_exchange_mdn() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;

View File

@@ -156,21 +156,21 @@ pub async fn dc_get_oauth2_access_token(
} }
// ... and POST // ... and POST
let mut req = surf::post(post_url).build(); let client = reqwest::Client::new();
if let Err(err) = req.body_form(&post_param) {
warn!(context, "Error calling OAuth2 at {}: {:?}", token_url, err);
return Ok(None);
}
let client = surf::Client::new(); let response: Response = match client.post(post_url).form(&post_param).send().await {
let parsed: Result<Response, _> = client.recv_json(req).await; Ok(resp) => match resp.json().await {
let response = match parsed { Ok(response) => response,
Ok(response) => response, Err(err) => {
warn!(
context,
"Failed to parse OAuth2 JSON response from {}: error: {}", token_url, err
);
return Ok(None);
}
},
Err(err) => { Err(err) => {
warn!( warn!(context, "Error calling OAuth2 at {}: {:?}", token_url, err);
context,
"Failed to parse OAuth2 JSON response from {}: error: {}", token_url, err
);
return Ok(None); return Ok(None);
} }
}; };
@@ -288,8 +288,14 @@ impl Oauth2 {
// "verified_email": true, // "verified_email": true,
// "picture": "https://lh4.googleusercontent.com/-Gj5jh_9R0BY/AAAAAAAAAAI/AAAAAAAAAAA/IAjtjfjtjNA/photo.jpg" // "picture": "https://lh4.googleusercontent.com/-Gj5jh_9R0BY/AAAAAAAAAAI/AAAAAAAAAAA/IAjtjfjtjNA/photo.jpg"
// } // }
let response: Result<HashMap<String, serde_json::Value>, surf::Error> = let response = match reqwest::get(userinfo_url).await {
surf::get(userinfo_url).recv_json().await; Ok(response) => response,
Err(err) => {
warn!(context, "failed to get userinfo: {}", err);
return None;
}
};
let response: Result<HashMap<String, serde_json::Value>, _> = response.json().await;
let parsed = match response { let parsed = match response {
Ok(parsed) => parsed, Ok(parsed) => parsed,
Err(err) => { Err(err) => {
@@ -360,7 +366,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_oauth_from_address() { async fn test_oauth_from_address() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!( assert_eq!(
@@ -382,7 +388,7 @@ mod tests {
assert_eq!(Oauth2::from_address(&t, "hello@web.de", false).await, None); assert_eq!(Oauth2::from_address(&t, "hello@web.de", false).await, None);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_oauth_from_mx() { async fn test_oauth_from_mx() {
// youtube staff seems to use "google workspace with oauth2", figures this out by MX lookup // youtube staff seems to use "google workspace with oauth2", figures this out by MX lookup
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -397,7 +403,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dc_get_oauth2_addr() { async fn test_dc_get_oauth2_addr() {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
let addr = "dignifiedquire@gmail.com"; let addr = "dignifiedquire@gmail.com";
@@ -407,7 +413,7 @@ mod tests {
assert_eq!(res, None); assert_eq!(res, None);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dc_get_oauth2_url() { async fn test_dc_get_oauth2_url() {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
let addr = "dignifiedquire@gmail.com"; let addr = "dignifiedquire@gmail.com";
@@ -419,7 +425,7 @@ mod tests {
assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878%2D4mvtgf6feshskf7695nfln6002mom908%2Eapps%2Egoogleusercontent%2Ecom&redirect_uri=chat%2Edelta%3A%2Fcom%2Eb44t%2Emessenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into())); assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878%2D4mvtgf6feshskf7695nfln6002mom908%2Eapps%2Egoogleusercontent%2Ecom&redirect_uri=chat%2Edelta%3A%2Fcom%2Eb44t%2Emessenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into()));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dc_get_oauth2_token() { async fn test_dc_get_oauth2_token() {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
let addr = "dignifiedquire@gmail.com"; let addr = "dignifiedquire@gmail.com";

View File

@@ -1,9 +1,9 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use std::path::PathBuf;
use std::str; use std::str;
use anyhow::{bail, Error, Result}; use anyhow::{bail, Error, Result};
use async_std::path::PathBuf;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -434,12 +434,13 @@ impl<'a> ParamsFile<'a> {
mod tests { mod tests {
use super::*; use super::*;
use std::path::Path;
use std::str::FromStr;
use anyhow::Result; use anyhow::Result;
use async_std::fs; use tokio::fs;
use async_std::path::Path;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
use std::str::FromStr;
#[test] #[test]
fn test_dc_param() { fn test_dc_param() {
@@ -485,7 +486,7 @@ mod tests {
assert_eq!(params.to_string().parse::<Params>().unwrap(), params); assert_eq!(params.to_string().parse::<Params>().unwrap(), params);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_params_file_fs_path() { async fn test_params_file_fs_path() {
let t = TestContext::new().await; let t = TestContext::new().await;
if let ParamsFile::FsPath(p) = ParamsFile::from_param(&t, "/foo/bar/baz").unwrap() { if let ParamsFile::FsPath(p) = ParamsFile::from_param(&t, "/foo/bar/baz").unwrap() {
@@ -495,7 +496,7 @@ mod tests {
} }
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_params_file_blob() { async fn test_params_file_blob() {
let t = TestContext::new().await; let t = TestContext::new().await;
if let ParamsFile::Blob(b) = ParamsFile::from_param(&t, "$BLOBDIR/foo").unwrap() { if let ParamsFile::Blob(b) = ParamsFile::from_param(&t, "$BLOBDIR/foo").unwrap() {
@@ -506,7 +507,7 @@ mod tests {
} }
// Tests for Params::get_file(), Params::get_path() and Params::get_blob(). // Tests for Params::get_file(), Params::get_path() and Params::get_blob().
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_params_get_fileparam() { async fn test_params_get_fileparam() {
let t = TestContext::new().await; let t = TestContext::new().await;
let fname = t.dir.path().join("foo"); let fname = t.dir.path().join("foo");
@@ -514,10 +515,9 @@ mod tests {
p.set(Param::File, fname.to_str().unwrap()); p.set(Param::File, fname.to_str().unwrap());
let file = p.get_file(Param::File, &t).unwrap().unwrap(); let file = p.get_file(Param::File, &t).unwrap().unwrap();
assert_eq!(file, ParamsFile::FsPath(fname.clone().into())); assert_eq!(file, ParamsFile::FsPath(fname.clone()));
let path: PathBuf = p.get_path(Param::File, &t).unwrap().unwrap(); let path: PathBuf = p.get_path(Param::File, &t).unwrap().unwrap();
let fname: PathBuf = fname.into();
assert_eq!(path, fname); assert_eq!(path, fname);
// Blob does not exist yet, expect error. // Blob does not exist yet, expect error.
@@ -539,7 +539,7 @@ mod tests {
assert!(p.get_blob(Param::File, &t, false).await.unwrap().is_none()); assert!(p.get_blob(Param::File, &t, false).await.unwrap().is_none());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_params_unknown_key() -> Result<()> { async fn test_params_unknown_key() -> Result<()> {
// 'Z' is used as a key that is known to be unused; these keys should be ignored silently by definition. // 'Z' is used as a key that is known to be unused; these keys should be ignored silently by definition.
let p = Params::from_str("w=12\nZ=13\nh=14")?; let p = Params::from_str("w=12\nZ=13\nh=14")?;

View File

@@ -552,7 +552,7 @@ mod tests {
use super::*; use super::*;
use crate::test_utils::alice_keypair; use crate::test_utils::alice_keypair;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_peerstate_save_to_db() { async fn test_peerstate_save_to_db() {
let ctx = crate::test_utils::TestContext::new().await; let ctx = crate::test_utils::TestContext::new().await;
let addr = "hello@mail.com"; let addr = "hello@mail.com";
@@ -596,7 +596,7 @@ mod tests {
assert_eq!(peerstate, peerstate_new2); assert_eq!(peerstate, peerstate_new2);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_peerstate_double_create() { async fn test_peerstate_double_create() {
let ctx = crate::test_utils::TestContext::new().await; let ctx = crate::test_utils::TestContext::new().await;
let addr = "hello@mail.com"; let addr = "hello@mail.com";
@@ -628,7 +628,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_peerstate_with_empty_gossip_key_save_to_db() { async fn test_peerstate_with_empty_gossip_key_save_to_db() {
let ctx = crate::test_utils::TestContext::new().await; let ctx = crate::test_utils::TestContext::new().await;
let addr = "hello@mail.com"; let addr = "hello@mail.com";
@@ -665,7 +665,7 @@ mod tests {
assert_eq!(Some(peerstate), peerstate_new); assert_eq!(Some(peerstate), peerstate_new);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_peerstate_load_db_defaults() { async fn test_peerstate_load_db_defaults() {
let ctx = crate::test_utils::TestContext::new().await; let ctx = crate::test_utils::TestContext::new().await;
let addr = "hello@mail.com"; let addr = "hello@mail.com";
@@ -694,7 +694,7 @@ mod tests {
assert_eq!(peerstate.verified_key_fingerprint, None); assert_eq!(peerstate.verified_key_fingerprint, None);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_peerstate_degrade_reordering() { async fn test_peerstate_degrade_reordering() {
let addr = "example@example.org"; let addr = "example@example.org";
let pub_key = alice_keypair().public; let pub_key = alice_keypair().public;

View File

@@ -15,6 +15,7 @@ use pgp::types::{
CompressionAlgorithm, KeyTrait, Mpi, PublicKeyTrait, SecretKeyTrait, StringToKey, CompressionAlgorithm, KeyTrait, Mpi, PublicKeyTrait, SecretKeyTrait, StringToKey,
}; };
use rand::{thread_rng, CryptoRng, Rng}; use rand::{thread_rng, CryptoRng, Rng};
use tokio::runtime::Handle;
use crate::constants::KeyGenType; use crate::constants::KeyGenType;
use crate::dc_tools::EmailAddress; use crate::dc_tools::EmailAddress;
@@ -224,32 +225,33 @@ pub async fn pk_encrypt(
) -> Result<String> { ) -> Result<String> {
let lit_msg = Message::new_literal_bytes("", plain); let lit_msg = Message::new_literal_bytes("", plain);
async_std::task::spawn_blocking(move || { Handle::current()
let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption .spawn_blocking(move || {
.keys() let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption
.iter() .keys()
.filter_map(select_pk_for_encryption) .iter()
.collect(); .filter_map(select_pk_for_encryption)
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect(); .collect();
let pkeys_refs: Vec<&SignedPublicKeyOrSubkey> = pkeys.iter().collect();
let mut rng = thread_rng(); let mut rng = thread_rng();
// TODO: measure time // TODO: measure time
let encrypted_msg = if let Some(ref skey) = private_key_for_signing { let encrypted_msg = if let Some(ref skey) = private_key_for_signing {
lit_msg lit_msg
.sign(skey, || "".into(), Default::default()) .sign(skey, || "".into(), Default::default())
.and_then(|msg| msg.compress(CompressionAlgorithm::ZLIB)) .and_then(|msg| msg.compress(CompressionAlgorithm::ZLIB))
.and_then(|msg| msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs)) .and_then(|msg| msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs))
} else { } else {
lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs) lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys_refs)
}; };
let msg = encrypted_msg?; let msg = encrypted_msg?;
let encoded_msg = msg.to_armored_string(None)?; let encoded_msg = msg.to_armored_string(None)?;
Ok(encoded_msg) Ok(encoded_msg)
}) })
.await .await?
} }
/// Decrypts the message with keys from the private key keyring. /// Decrypts the message with keys from the private key keyring.
@@ -268,7 +270,7 @@ pub async fn pk_decrypt(
) -> Result<(Vec<u8>, HashSet<Fingerprint>)> { ) -> Result<(Vec<u8>, HashSet<Fingerprint>)> {
let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default(); let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
let msgs = async_std::task::spawn_blocking(move || { let msgs = tokio::task::spawn_blocking(move || {
let cursor = Cursor::new(ctext); let cursor = Cursor::new(ctext);
let (msg, _) = Message::from_armor_single(cursor)?; let (msg, _) = Message::from_armor_single(cursor)?;
@@ -277,7 +279,7 @@ pub async fn pk_decrypt(
let (decryptor, _) = msg.decrypt(|| "".into(), || "".into(), &skeys[..])?; let (decryptor, _) = msg.decrypt(|| "".into(), || "".into(), &skeys[..])?;
decryptor.collect::<pgp::errors::Result<Vec<_>>>() decryptor.collect::<pgp::errors::Result<Vec<_>>>()
}) })
.await?; .await??;
if let Some(msg) = msgs.into_iter().next() { if let Some(msg) = msgs.into_iter().next() {
// get_content() will decompress the message if needed, // get_content() will decompress the message if needed,
@@ -342,7 +344,7 @@ pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
let lit_msg = Message::new_literal_bytes("", plain); let lit_msg = Message::new_literal_bytes("", plain);
let passphrase = passphrase.to_string(); let passphrase = passphrase.to_string();
async_std::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut rng = thread_rng(); let mut rng = thread_rng();
let s2k = StringToKey::new_default(&mut rng); let s2k = StringToKey::new_default(&mut rng);
let msg = let msg =
@@ -352,7 +354,7 @@ pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
Ok(encoded_msg) Ok(encoded_msg)
}) })
.await .await?
} }
/// Symmetric decryption. /// Symmetric decryption.
@@ -363,7 +365,7 @@ pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
let (enc_msg, _) = Message::from_armor_single(ctext)?; let (enc_msg, _) = Message::from_armor_single(ctext)?;
let passphrase = passphrase.to_string(); let passphrase = passphrase.to_string();
async_std::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let decryptor = enc_msg.decrypt_with_password(|| passphrase)?; let decryptor = enc_msg.decrypt_with_password(|| passphrase)?;
let msgs = decryptor.collect::<pgp::errors::Result<Vec<_>>>()?; let msgs = decryptor.collect::<pgp::errors::Result<Vec<_>>>()?;
@@ -376,7 +378,7 @@ pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
bail!("No valid messages found") bail!("No valid messages found")
} }
}) })
.await .await?
} }
#[cfg(test)] #[cfg(test)]
@@ -487,7 +489,7 @@ mod tests {
assert!(CTEXT_UNSIGNED.starts_with("-----BEGIN PGP MESSAGE-----")); assert!(CTEXT_UNSIGNED.starts_with("-----BEGIN PGP MESSAGE-----"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decrypt_singed() { async fn test_decrypt_singed() {
// Check decrypting as Alice // Check decrypting as Alice
let mut decrypt_keyring: Keyring<SignedSecretKey> = Keyring::new(); let mut decrypt_keyring: Keyring<SignedSecretKey> = Keyring::new();
@@ -520,7 +522,7 @@ mod tests {
assert_eq!(valid_signatures.len(), 1); assert_eq!(valid_signatures.len(), 1);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decrypt_no_sig_check() { async fn test_decrypt_no_sig_check() {
let mut keyring = Keyring::new(); let mut keyring = Keyring::new();
keyring.add(KEYS.alice_secret.clone()); keyring.add(KEYS.alice_secret.clone());
@@ -533,7 +535,7 @@ mod tests {
assert_eq!(valid_signatures.len(), 0); assert_eq!(valid_signatures.len(), 0);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decrypt_signed_no_key() { async fn test_decrypt_signed_no_key() {
// The validation does not have the public key of the signer. // The validation does not have the public key of the signer.
let mut decrypt_keyring = Keyring::new(); let mut decrypt_keyring = Keyring::new();
@@ -551,7 +553,7 @@ mod tests {
assert_eq!(valid_signatures.len(), 0); assert_eq!(valid_signatures.len(), 0);
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decrypt_unsigned() { async fn test_decrypt_unsigned() {
let mut decrypt_keyring = Keyring::new(); let mut decrypt_keyring = Keyring::new();
decrypt_keyring.add(KEYS.bob_secret.clone()); decrypt_keyring.add(KEYS.bob_secret.clone());

View File

@@ -99,7 +99,7 @@ impl PlainText {
mod tests { mod tests {
use super::*; use super::*;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html() { async fn test_plain_to_html() {
let html = PlainText { let html = PlainText {
text: r##"line 1 text: r##"line 1
@@ -127,7 +127,7 @@ line with <a href="https://link-mid-of-line.org">https://link-mid-of-line.org</a
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html_encapsulated() { async fn test_plain_to_html_encapsulated() {
let html = PlainText { let html = PlainText {
text: r#"line with <http://encapsulated.link/?foo=_bar> here!"#.to_string(), text: r#"line with <http://encapsulated.link/?foo=_bar> here!"#.to_string(),
@@ -146,7 +146,7 @@ line with &lt;<a href="http://encapsulated.link/?foo=_bar">http://encapsulated.l
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html_nolink() { async fn test_plain_to_html_nolink() {
let html = PlainText { let html = PlainText {
text: r#"line with nohttp://no.link here"#.to_string(), text: r#"line with nohttp://no.link here"#.to_string(),
@@ -165,7 +165,7 @@ line with nohttp://no.link here<br/>
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html_mailto() { async fn test_plain_to_html_mailto() {
let html = PlainText { let html = PlainText {
text: r#"just an address: foo@bar.org another@one.de"#.to_string(), text: r#"just an address: foo@bar.org another@one.de"#.to_string(),
@@ -184,7 +184,7 @@ just an address: <a href="mailto:foo@bar.org">foo@bar.org</a> <a href="mailto:an
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html_flowed() { async fn test_plain_to_html_flowed() {
let html = PlainText { let html = PlainText {
text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(), text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(),
@@ -206,7 +206,7 @@ line still line<br/>
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html_flowed_delsp() { async fn test_plain_to_html_flowed_delsp() {
let html = PlainText { let html = PlainText {
text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(), text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(),
@@ -228,7 +228,7 @@ linestill line<br/>
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_plain_to_html_fixed() { async fn test_plain_to_html_fixed() {
let html = PlainText { let html = PlainText {
text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(), text: "line \nstill line\n>quote \n>still quote\n >no quote".to_string(),

View File

@@ -6,8 +6,8 @@ use crate::config::Config;
use crate::context::Context; use crate::context::Context;
use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED}; use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED};
use anyhow::Result; use anyhow::Result;
use async_std_resolver::{config, resolver, resolver_from_system_conf, AsyncStdResolver};
use chrono::{NaiveDateTime, NaiveTime}; use chrono::{NaiveDateTime, NaiveTime};
use trust_dns_resolver::{config, AsyncResolver, TokioAsyncResolver};
#[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)] #[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
#[repr(u8)] #[repr(u8)]
@@ -85,18 +85,17 @@ pub struct Provider {
/// Get resolver to query MX records. /// Get resolver to query MX records.
/// ///
/// We first try resolver_from_system_conf() which reads the system's resolver from `/etc/resolv.conf`. /// We first try to read the system's resolver from `/etc/resolv.conf`.
/// This does not work at least on some Androids, therefore we use use ResolverConfig::default() /// This does not work at least on some Androids, therefore we fallback
/// which default eg. to google's 8.8.8.8 or 8.8.4.4 as a fallback. /// to the default `ResolverConfig` which uses eg. to google's `8.8.8.8` or `8.8.4.4`.
async fn get_resolver() -> Result<AsyncStdResolver> { fn get_resolver() -> Result<TokioAsyncResolver> {
if let Ok(resolver) = resolver_from_system_conf().await { if let Ok(resolver) = AsyncResolver::tokio_from_system_conf() {
return Ok(resolver); return Ok(resolver);
} }
let resolver = resolver( let resolver = AsyncResolver::tokio(
config::ResolverConfig::default(), config::ResolverConfig::default(),
config::ResolverOpts::default(), config::ResolverOpts::default(),
) )?;
.await?;
Ok(resolver) Ok(resolver)
} }
@@ -141,7 +140,7 @@ pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> {
/// ///
/// For security reasons, only Gmail can be configured this way. /// For security reasons, only Gmail can be configured this way.
pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'static Provider> { pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'static Provider> {
if let Ok(resolver) = get_resolver().await { if let Ok(resolver) = get_resolver() {
let mut fqdn: String = domain.to_string(); let mut fqdn: String = domain.to_string();
if !fqdn.ends_with('.') { if !fqdn.ends_with('.') {
fqdn.push('.'); fqdn.push('.');
@@ -242,7 +241,7 @@ mod tests {
assert!(provider.id == "gmail"); assert!(provider.id == "gmail");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_provider_info() { async fn test_get_provider_info() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!(get_provider_info(&t, "", false).await.is_none()); assert!(get_provider_info(&t, "", false).await.is_none());
@@ -270,9 +269,9 @@ mod tests {
assert!(get_provider_update_timestamp() > timestamp_past); assert!(get_provider_update_timestamp() > timestamp_past);
} }
#[async_std::test] #[test]
async fn test_get_resolver() -> Result<()> { fn test_get_resolver() -> Result<()> {
assert!(get_resolver().await.is_ok()); assert!(get_resolver().is_ok());
Ok(()) Ok(())
} }
} }

View File

@@ -1,6 +1,6 @@
//! # QR code module. //! # QR code module.
use anyhow::{bail, ensure, format_err, Context as _, Error, Result}; use anyhow::{bail, ensure, Context as _, Error, Result};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use percent_encoding::percent_decode_str; use percent_encoding::percent_decode_str;
use serde::Deserialize; use serde::Deserialize;
@@ -355,13 +355,13 @@ struct CreateAccountResponse {
async fn set_account_from_qr(context: &Context, qr: &str) -> Result<()> { async fn set_account_from_qr(context: &Context, qr: &str) -> Result<()> {
let url_str = &qr[DCACCOUNT_SCHEME.len()..]; let url_str = &qr[DCACCOUNT_SCHEME.len()..];
let parsed: CreateAccountResponse = surf::post(url_str).recv_json().await.map_err(|err| { let parsed: CreateAccountResponse = reqwest::Client::new()
format_err!( .post(url_str)
"Cannot create account, request to {:?} failed: {}", .send()
url_str, .await?
err .json()
) .await
})?; .with_context(|| format!("Cannot create account, request to {:?} failed", url_str))?;
context context
.set_config(Config::Addr, Some(&parsed.email)) .set_config(Config::Addr, Some(&parsed.email))
@@ -565,7 +565,7 @@ mod tests {
use crate::test_utils::{alice_keypair, TestContext}; use crate::test_utils::{alice_keypair, TestContext};
use anyhow::Result; use anyhow::Result;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_http() -> Result<()> { async fn test_decode_http() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -580,7 +580,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_https() -> Result<()> { async fn test_decode_https() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -595,7 +595,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_text() -> Result<()> { async fn test_decode_text() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -610,7 +610,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_vcard() -> Result<()> { async fn test_decode_vcard() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -632,7 +632,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_matmsg() -> Result<()> { async fn test_decode_matmsg() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -652,7 +652,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_mailto() -> Result<()> { async fn test_decode_mailto() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -682,7 +682,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_smtp() -> Result<()> { async fn test_decode_smtp() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -698,7 +698,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_openpgp_group() -> Result<()> { async fn test_decode_openpgp_group() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
let qr = check_qr( let qr = check_qr(
@@ -741,7 +741,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_openpgp_secure_join() -> Result<()> { async fn test_decode_openpgp_secure_join() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -788,7 +788,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_openpgp_fingerprint() -> Result<()> { async fn test_decode_openpgp_fingerprint() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -850,7 +850,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_openpgp_without_addr() -> Result<()> { async fn test_decode_openpgp_without_addr() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -886,7 +886,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_withdraw_verifycontact() -> Result<()> { async fn test_withdraw_verifycontact() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let qr = dc_get_securejoin_qr(&alice, None).await?; let qr = dc_get_securejoin_qr(&alice, None).await?;
@@ -920,7 +920,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_withdraw_verifygroup() -> Result<()> { async fn test_withdraw_verifygroup() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "foo").await?;
@@ -953,7 +953,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_account() -> Result<()> { async fn test_decode_account() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -985,7 +985,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_webrtc_instance() -> Result<()> { async fn test_decode_webrtc_instance() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
@@ -1011,7 +1011,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decode_account_bad_scheme() { async fn test_decode_account_bad_scheme() {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;
let res = check_qr( let res = check_qr(
@@ -1030,7 +1030,7 @@ mod tests {
assert!(res.is_err()); assert!(res.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_config_from_qr() -> Result<()> { async fn test_set_config_from_qr() -> Result<()> {
let ctx = TestContext::new().await; let ctx = TestContext::new().await;

View File

@@ -25,7 +25,7 @@ async fn generate_join_group_qr_code(context: &Context, chat_id: ChatId) -> Resu
let avatar = match chat.get_profile_image(context).await? { let avatar = match chat.get_profile_image(context).await? {
Some(path) => { Some(path) => {
let avatar_blob = BlobObject::from_path(context, &path)?; let avatar_blob = BlobObject::from_path(context, &path)?;
Some(std::fs::read(avatar_blob.to_abs_path())?) Some(tokio::fs::read(avatar_blob.to_abs_path()).await?)
} }
None => None, None => None,
}; };
@@ -45,7 +45,7 @@ async fn generate_verification_qr(context: &Context) -> Result<String> {
let avatar = match contact.get_profile_image(context).await? { let avatar = match contact.get_profile_image(context).await? {
Some(path) => { Some(path) => {
let avatar_blob = BlobObject::from_path(context, &path)?; let avatar_blob = BlobObject::from_path(context, &path)?;
Some(std::fs::read(avatar_blob.to_abs_path())?) Some(tokio::fs::read(avatar_blob.to_abs_path()).await?)
} }
None => None, None => None,
}; };
@@ -266,7 +266,7 @@ fn inner_generate_secure_join_qr_code(
mod tests { mod tests {
use super::*; use super::*;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_svg_escaping() { async fn test_svg_escaping() {
let svg = inner_generate_secure_join_qr_code( let svg = inner_generate_secure_join_qr_code(
"descr123 \" < > &", "descr123 \" < > &",

View File

@@ -180,7 +180,7 @@ mod tests {
QUOTA_WARN_THRESHOLD_PERCENTAGE, QUOTA_WARN_THRESHOLD_PERCENTAGE,
}; };
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_needs_quota_warning() -> Result<()> { async fn test_needs_quota_warning() -> Result<()> {
assert!(!needs_quota_warning(0, 0)); assert!(!needs_quota_warning(0, 0));
assert!(!needs_quota_warning(10, 0)); assert!(!needs_quota_warning(10, 0));
@@ -199,7 +199,7 @@ mod tests {
} }
#[allow(clippy::assertions_on_constants)] #[allow(clippy::assertions_on_constants)]
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_quota_thresholds() -> anyhow::Result<()> { async fn test_quota_thresholds() -> anyhow::Result<()> {
assert!(QUOTA_ALLCLEAR_PERCENTAGE > 50); assert!(QUOTA_ALLCLEAR_PERCENTAGE > 50);
assert!(QUOTA_ALLCLEAR_PERCENTAGE < QUOTA_WARN_THRESHOLD_PERCENTAGE); assert!(QUOTA_ALLCLEAR_PERCENTAGE < QUOTA_WARN_THRESHOLD_PERCENTAGE);

View File

@@ -1,9 +1,8 @@
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use async_std::prelude::*; use async_channel::{self as channel, Receiver, Sender};
use async_std::{ use futures::{join, try_join};
channel::{self, Receiver, Sender}, use futures_lite::FutureExt;
future, task, use tokio::task;
};
use crate::config::Config; use crate::config::Config;
use crate::context::Context; use crate::context::Context;
@@ -338,7 +337,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
"smtp got rate limited, waiting for {} until can send again", "smtp got rate limited, waiting for {} until can send again",
duration_to_str(duration_until_can_send) duration_to_str(duration_until_can_send)
); );
async_std::future::timeout(duration_until_can_send, async { tokio::time::timeout(duration_until_can_send, async {
idle_interrupt_receiver.recv().await.unwrap_or_default() idle_interrupt_receiver.recv().await.unwrap_or_default()
}) })
.await .await
@@ -367,7 +366,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
"smtp has messages to retry, planning to retry {} seconds later", timeout "smtp has messages to retry, planning to retry {} seconds later", timeout
); );
let duration = std::time::Duration::from_secs(timeout); let duration = std::time::Duration::from_secs(timeout);
async_std::future::timeout(duration, async { tokio::time::timeout(duration, async {
idle_interrupt_receiver.recv().await.unwrap_or_default() idle_interrupt_receiver.recv().await.unwrap_or_default()
}) })
.await .await
@@ -493,13 +492,12 @@ impl Scheduler {
}; };
// wait for all loops to be started // wait for all loops to be started
if let Err(err) = inbox_start_recv if let Err(err) = try_join!(
.recv() inbox_start_recv.recv(),
.try_join(mvbox_start_recv.recv()) mvbox_start_recv.recv(),
.try_join(sentbox_start_recv.recv()) sentbox_start_recv.recv(),
.try_join(smtp_start_recv.recv()) smtp_start_recv.recv()
.await ) {
{
bail!("failed to start scheduler: {}", err); bail!("failed to start scheduler: {}", err);
} }
@@ -508,19 +506,21 @@ impl Scheduler {
} }
async fn maybe_network(&self) { async fn maybe_network(&self) {
self.interrupt_inbox(InterruptInfo::new(true)) join!(
.join(self.interrupt_mvbox(InterruptInfo::new(true))) self.interrupt_inbox(InterruptInfo::new(true)),
.join(self.interrupt_sentbox(InterruptInfo::new(true))) self.interrupt_mvbox(InterruptInfo::new(true)),
.join(self.interrupt_smtp(InterruptInfo::new(true))) self.interrupt_sentbox(InterruptInfo::new(true)),
.await; self.interrupt_smtp(InterruptInfo::new(true))
);
} }
async fn maybe_network_lost(&self) { async fn maybe_network_lost(&self) {
self.interrupt_inbox(InterruptInfo::new(false)) join!(
.join(self.interrupt_mvbox(InterruptInfo::new(false))) self.interrupt_inbox(InterruptInfo::new(false)),
.join(self.interrupt_sentbox(InterruptInfo::new(false))) self.interrupt_mvbox(InterruptInfo::new(false)),
.join(self.interrupt_smtp(InterruptInfo::new(false))) self.interrupt_sentbox(InterruptInfo::new(false)),
.await; self.interrupt_smtp(InterruptInfo::new(false))
);
} }
async fn interrupt_inbox(&self, info: InterruptInfo) { async fn interrupt_inbox(&self, info: InterruptInfo) {
@@ -564,24 +564,24 @@ impl Scheduler {
// Actually shutdown tasks. // Actually shutdown tasks.
let timeout_duration = std::time::Duration::from_secs(30); let timeout_duration = std::time::Duration::from_secs(30);
future::timeout(timeout_duration, self.inbox_handle) tokio::time::timeout(timeout_duration, self.inbox_handle)
.await .await
.ok_or_log(context); .ok_or_log(context);
if let Some(mvbox_handle) = self.mvbox_handle.take() { if let Some(mvbox_handle) = self.mvbox_handle.take() {
future::timeout(timeout_duration, mvbox_handle) tokio::time::timeout(timeout_duration, mvbox_handle)
.await .await
.ok_or_log(context); .ok_or_log(context);
} }
if let Some(sentbox_handle) = self.sentbox_handle.take() { if let Some(sentbox_handle) = self.sentbox_handle.take() {
future::timeout(timeout_duration, sentbox_handle) tokio::time::timeout(timeout_duration, sentbox_handle)
.await .await
.ok_or_log(context); .ok_or_log(context);
} }
future::timeout(timeout_duration, self.smtp_handle) tokio::time::timeout(timeout_duration, self.smtp_handle)
.await .await
.ok_or_log(context); .ok_or_log(context);
self.ephemeral_handle.cancel().await; self.ephemeral_handle.abort();
self.location_handle.cancel().await; self.location_handle.abort();
} }
} }

View File

@@ -1,7 +1,7 @@
use core::fmt; use core::fmt;
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
use async_std::sync::{Mutex, RwLockReadGuard}; use tokio::sync::{Mutex, RwLockReadGuard};
use crate::dc_tools::time; use crate::dc_tools::time;
use crate::events::EventType; use crate::events::EventType;
@@ -239,7 +239,7 @@ pub(crate) async fn maybe_network_lost(
impl fmt::Debug for ConnectivityStore { impl fmt::Debug for ConnectivityStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(guard) = self.0.try_lock() { if let Ok(guard) = self.0.try_lock() {
write!(f, "ConnectivityStore {:?}", &*guard) write!(f, "ConnectivityStore {:?}", &*guard)
} else { } else {
write!(f, "ConnectivityStore [LOCKED]") write!(f, "ConnectivityStore [LOCKED]")

View File

@@ -696,7 +696,7 @@ mod tests {
use crate::peerstate::Peerstate; use crate::peerstate::Peerstate;
use crate::test_utils::{TestContext, TestContextManager}; use crate::test_utils::{TestContext, TestContextManager};
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_setup_contact() { async fn test_setup_contact() {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;
@@ -903,14 +903,14 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_setup_contact_bad_qr() { async fn test_setup_contact_bad_qr() {
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
let ret = dc_join_securejoin(&bob.ctx, "not a qr code").await; let ret = dc_join_securejoin(&bob.ctx, "not a qr code").await;
assert!(ret.is_err()); assert!(ret.is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_setup_contact_bob_knows_alice() -> Result<()> { async fn test_setup_contact_bob_knows_alice() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;
@@ -1035,7 +1035,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_setup_contact_concurrent_calls() -> Result<()> { async fn test_setup_contact_concurrent_calls() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;
@@ -1066,7 +1066,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_secure_join() -> Result<()> { async fn test_secure_join() -> Result<()> {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;
@@ -1290,7 +1290,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_adhoc_group_no_qr() -> Result<()> { async fn test_adhoc_group_no_qr() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.set_config(Config::ShowEmails, Some("2")).await?; alice.set_config(Config::ShowEmails, Some("2")).await?;

View File

@@ -8,7 +8,7 @@ use anyhow::{bail, format_err, Context as _, Error, Result};
use async_smtp::smtp::client::net::ClientTlsParameters; use async_smtp::smtp::client::net::ClientTlsParameters;
use async_smtp::smtp::response::{Category, Code, Detail}; use async_smtp::smtp::response::{Category, Code, Detail};
use async_smtp::{smtp, EmailAddress, ServerAddress}; use async_smtp::{smtp, EmailAddress, ServerAddress};
use async_std::task; use tokio::task;
use crate::config::Config; use crate::config::Config;
use crate::contact::{Contact, ContactId}; use crate::contact::{Contact, ContactId};

View File

@@ -1,16 +1,14 @@
//! # SQLite wrapper. //! # SQLite wrapper.
use async_std::path::Path;
use async_std::sync::RwLock;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::path::Path;
use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use anyhow::{bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use async_std::path::PathBuf;
use async_std::prelude::*;
use rusqlite::{config::DbConfig, Connection, OpenFlags}; use rusqlite::{config::DbConfig, Connection, OpenFlags};
use tokio::sync::RwLock;
use crate::blob::BlobObject; use crate::blob::BlobObject;
use crate::chat::{add_device_msg, update_device_icon, update_saved_messages_icon}; use crate::chat::{add_device_msg, update_device_icon, update_saved_messages_icon};
@@ -126,18 +124,21 @@ impl Sql {
.to_str() .to_str()
.with_context(|| format!("path {:?} is not valid unicode", path))?; .with_context(|| format!("path {:?} is not valid unicode", path))?;
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
conn.execute( tokio::task::block_in_place(move || {
"ATTACH DATABASE ? AS backup KEY ?", conn.execute(
paramsv![path_str, passphrase], "ATTACH DATABASE ? AS backup KEY ?",
) paramsv![path_str, passphrase],
.context("failed to attach backup database")?; )
let res = conn .context("failed to attach backup database")?;
.query_row("SELECT sqlcipher_export('backup')", [], |_row| Ok(())) let res = conn
.context("failed to export to attached backup database"); .query_row("SELECT sqlcipher_export('backup')", [], |_row| Ok(()))
conn.execute("DETACH DATABASE backup", []) .context("failed to export to attached backup database");
.context("failed to detach backup database")?; conn.execute("DETACH DATABASE backup", [])
res?; .context("failed to detach backup database")?;
Ok(()) res?;
Ok(())
})
} }
/// Imports the database from a separate file with the given passphrase. /// Imports the database from a separate file with the given passphrase.
@@ -147,40 +148,42 @@ impl Sql {
.with_context(|| format!("path {:?} is not valid unicode", path))?; .with_context(|| format!("path {:?} is not valid unicode", path))?;
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
// Check that backup passphrase is correct before resetting our database. tokio::task::block_in_place(move || {
conn.execute( // Check that backup passphrase is correct before resetting our database.
"ATTACH DATABASE ? AS backup KEY ?", conn.execute(
paramsv![path_str, passphrase], "ATTACH DATABASE ? AS backup KEY ?",
) paramsv![path_str, passphrase],
.context("failed to attach backup database")?; )
if let Err(err) = conn .context("failed to attach backup database")?;
.query_row("SELECT count(*) FROM sqlite_master", [], |_row| Ok(())) if let Err(err) = conn
.context("backup passphrase is not correct") .query_row("SELECT count(*) FROM sqlite_master", [], |_row| Ok(()))
{ .context("backup passphrase is not correct")
{
conn.execute("DETACH DATABASE backup", [])
.context("failed to detach backup database")?;
return Err(err);
}
// Reset the database without reopening it. We don't want to reopen the database because we
// don't have main database passphrase at this point.
// See <https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html> for documentation.
// Without resetting import may fail due to existing tables.
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)
.context("failed to set SQLITE_DBCONFIG_RESET_DATABASE")?;
conn.execute("VACUUM", [])
.context("failed to vacuum the database")?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)
.context("failed to unset SQLITE_DBCONFIG_RESET_DATABASE")?;
let res = conn
.query_row("SELECT sqlcipher_export('main', 'backup')", [], |_row| {
Ok(())
})
.context("failed to import from attached backup database");
conn.execute("DETACH DATABASE backup", []) conn.execute("DETACH DATABASE backup", [])
.context("failed to detach backup database")?; .context("failed to detach backup database")?;
return Err(err); res?;
} Ok(())
})
// Reset the database without reopening it. We don't want to reopen the database because we
// don't have main database passphrase at this point.
// See <https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html> for documentation.
// Without resetting import may fail due to existing tables.
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)
.context("failed to set SQLITE_DBCONFIG_RESET_DATABASE")?;
conn.execute("VACUUM", [])
.context("failed to vacuum the database")?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)
.context("failed to unset SQLITE_DBCONFIG_RESET_DATABASE")?;
let res = conn
.query_row("SELECT sqlcipher_export('main', 'backup')", [], |_row| {
Ok(())
})
.context("failed to import from attached backup database");
conn.execute("DETACH DATABASE backup", [])
.context("failed to detach backup database")?;
res?;
Ok(())
} }
fn new_pool( fn new_pool(
@@ -224,20 +227,22 @@ impl Sql {
{ {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
tokio::task::block_in_place(move || -> Result<()> {
// Try to enable auto_vacuum. This will only be
// applied if the database is new or after successful
// VACUUM, which usually happens before backup export.
// When auto_vacuum is INCREMENTAL, it is possible to
// use PRAGMA incremental_vacuum to return unused
// database pages to the filesystem.
conn.pragma_update(None, "auto_vacuum", &"INCREMENTAL".to_string())?;
// Try to enable auto_vacuum. This will only be // journal_mode is persisted, it is sufficient to change it only for one handle.
// applied if the database is new or after successful conn.pragma_update(None, "journal_mode", &"WAL".to_string())?;
// VACUUM, which usually happens before backup export.
// When auto_vacuum is INCREMENTAL, it is possible to
// use PRAGMA incremental_vacuum to return unused
// database pages to the filesystem.
conn.pragma_update(None, "auto_vacuum", &"INCREMENTAL".to_string())?;
// journal_mode is persisted, it is sufficient to change it only for one handle. // Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode.
conn.pragma_update(None, "journal_mode", &"WAL".to_string())?; conn.pragma_update(None, "synchronous", &"NORMAL".to_string())?;
Ok(())
// Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode. })?;
conn.pragma_update(None, "synchronous", &"NORMAL".to_string())?;
} }
self.run_migrations(context).await?; self.run_migrations(context).await?;
@@ -343,15 +348,19 @@ impl Sql {
/// Execute the given query, returning the number of affected rows. /// Execute the given query, returning the number of affected rows.
pub async fn execute(&self, query: &str, params: impl rusqlite::Params) -> Result<usize> { pub async fn execute(&self, query: &str, params: impl rusqlite::Params) -> Result<usize> {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
let res = conn.execute(query, params)?; tokio::task::block_in_place(move || {
Ok(res) let res = conn.execute(query, params)?;
Ok(res)
})
} }
/// Executes the given query, returning the last inserted row ID. /// Executes the given query, returning the last inserted row ID.
pub async fn insert(&self, query: &str, params: impl rusqlite::Params) -> Result<i64> { pub async fn insert(&self, query: &str, params: impl rusqlite::Params) -> Result<i64> {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
conn.execute(query, params)?; tokio::task::block_in_place(move || {
Ok(conn.last_insert_rowid()) conn.execute(query, params)?;
Ok(conn.last_insert_rowid())
})
} }
/// Prepares and executes the statement and maps a function over the resulting rows. /// Prepares and executes the statement and maps a function over the resulting rows.
@@ -369,9 +378,11 @@ impl Sql {
G: FnMut(rusqlite::MappedRows<F>) -> Result<H>, G: FnMut(rusqlite::MappedRows<F>) -> Result<H>,
{ {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
let mut stmt = conn.prepare(sql)?; tokio::task::block_in_place(move || {
let res = stmt.query_map(params, f)?; let mut stmt = conn.prepare(sql)?;
g(res) let res = stmt.query_map(params, f)?;
g(res)
})
} }
pub async fn get_conn( pub async fn get_conn(
@@ -408,8 +419,10 @@ impl Sql {
F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>, F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
{ {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
let res = conn.query_row(query, params, f)?; tokio::task::block_in_place(move || {
Ok(res) let res = conn.query_row(query, params, f)?;
Ok(res)
})
} }
/// Execute the function inside a transaction. /// Execute the function inside a transaction.
@@ -422,49 +435,55 @@ impl Sql {
G: Send + 'static + FnOnce(&mut rusqlite::Transaction<'_>) -> anyhow::Result<H>, G: Send + 'static + FnOnce(&mut rusqlite::Transaction<'_>) -> anyhow::Result<H>,
{ {
let mut conn = self.get_conn().await?; let mut conn = self.get_conn().await?;
let mut transaction = conn.transaction()?; tokio::task::block_in_place(move || {
let ret = callback(&mut transaction); let mut transaction = conn.transaction()?;
let ret = callback(&mut transaction);
match ret { match ret {
Ok(ret) => { Ok(ret) => {
transaction.commit()?; transaction.commit()?;
Ok(ret) Ok(ret)
}
Err(err) => {
transaction.rollback()?;
Err(err)
}
} }
Err(err) => { })
transaction.rollback()?;
Err(err)
}
}
} }
/// Query the database if the requested table already exists. /// Query the database if the requested table already exists.
pub async fn table_exists(&self, name: &str) -> anyhow::Result<bool> { pub async fn table_exists(&self, name: &str) -> anyhow::Result<bool> {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
let mut exists = false; tokio::task::block_in_place(move || {
conn.pragma(None, "table_info", &name.to_string(), |_row| { let mut exists = false;
// will only be executed if the info was found conn.pragma(None, "table_info", &name.to_string(), |_row| {
exists = true; // will only be executed if the info was found
Ok(()) exists = true;
})?; Ok(())
})?;
Ok(exists) Ok(exists)
})
} }
/// Check if a column exists in a given table. /// Check if a column exists in a given table.
pub async fn col_exists(&self, table_name: &str, col_name: &str) -> anyhow::Result<bool> { pub async fn col_exists(&self, table_name: &str, col_name: &str) -> anyhow::Result<bool> {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
let mut exists = false; tokio::task::block_in_place(move || {
// `PRAGMA table_info` returns one row per column, let mut exists = false;
// each row containing 0=cid, 1=name, 2=type, 3=notnull, 4=dflt_value // `PRAGMA table_info` returns one row per column,
conn.pragma(None, "table_info", &table_name.to_string(), |row| { // each row containing 0=cid, 1=name, 2=type, 3=notnull, 4=dflt_value
let curr_name: String = row.get(1)?; conn.pragma(None, "table_info", &table_name.to_string(), |row| {
if col_name == curr_name { let curr_name: String = row.get(1)?;
exists = true; if col_name == curr_name {
} exists = true;
Ok(()) }
})?; Ok(())
})?;
Ok(exists) Ok(exists)
})
} }
/// Execute a query which is expected to return zero or one row. /// Execute a query which is expected to return zero or one row.
@@ -478,12 +497,15 @@ impl Sql {
F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>, F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
{ {
let conn = self.get_conn().await?; let conn = self.get_conn().await?;
let res = match conn.query_row(sql.as_ref(), params, f) { let res =
Ok(res) => Ok(Some(res)), tokio::task::block_in_place(move || match conn.query_row(sql.as_ref(), params, f) {
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), Ok(res) => Ok(Some(res)),
Err(rusqlite::Error::InvalidColumnType(_, _, rusqlite::types::Type::Null)) => Ok(None), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(err) => Err(err), Err(rusqlite::Error::InvalidColumnType(_, _, rusqlite::types::Type::Null)) => {
}?; Ok(None)
}
Err(err) => Err(err),
})?;
Ok(res) Ok(res)
} }
@@ -717,7 +739,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
info!(context, "{} files in use.", files_in_use.len(),); info!(context, "{} files in use.", files_in_use.len(),);
/* go through directory and delete unused files */ /* go through directory and delete unused files */
let p = context.get_blobdir(); let p = context.get_blobdir();
match async_std::fs::read_dir(p).await { match tokio::fs::read_dir(p).await {
Ok(mut dir_handle) => { Ok(mut dir_handle) => {
/* avoid deletion of files that are just created to build a message object */ /* avoid deletion of files that are just created to build a message object */
let diff = std::time::Duration::from_secs(60 * 60); let diff = std::time::Duration::from_secs(60 * 60);
@@ -725,11 +747,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
.checked_sub(diff) .checked_sub(diff)
.unwrap_or(std::time::SystemTime::UNIX_EPOCH); .unwrap_or(std::time::SystemTime::UNIX_EPOCH);
while let Some(entry) = dir_handle.next().await { while let Ok(Some(entry)) = dir_handle.next_entry().await {
let entry = match entry {
Ok(entry) => entry,
Err(_) => break,
};
let name_f = entry.file_name(); let name_f = entry.file_name();
let name_s = name_f.to_string_lossy(); let name_s = name_f.to_string_lossy();
@@ -743,7 +761,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
unreferenced_count += 1; unreferenced_count += 1;
if let Ok(stats) = async_std::fs::metadata(entry.path()).await { if let Ok(stats) = tokio::fs::metadata(entry.path()).await {
let recently_created = let recently_created =
stats.created().map_or(false, |t| t > keep_files_newer_than); stats.created().map_or(false, |t| t > keep_files_newer_than);
let recently_modified = stats let recently_modified = stats
@@ -860,8 +878,9 @@ pub fn repeat_vars(count: usize) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use async_std::channel; use async_channel as channel;
use async_std::fs::File; use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use crate::config::Config; use crate::config::Config;
use crate::{test_utils::TestContext, EventType}; use crate::{test_utils::TestContext, EventType};
@@ -894,14 +913,14 @@ mod tests {
assert!(is_file_in_use(&files, Some("-suffix"), "world.txt-suffix")); assert!(is_file_in_use(&files, Some("-suffix"), "world.txt-suffix"));
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_table_exists() { async fn test_table_exists() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!(t.ctx.sql.table_exists("msgs").await.unwrap()); assert!(t.ctx.sql.table_exists("msgs").await.unwrap());
assert!(!t.ctx.sql.table_exists("foobar").await.unwrap()); assert!(!t.ctx.sql.table_exists("foobar").await.unwrap());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_col_exists() { async fn test_col_exists() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!(t.ctx.sql.col_exists("msgs", "mime_modified").await.unwrap()); assert!(t.ctx.sql.col_exists("msgs", "mime_modified").await.unwrap());
@@ -910,7 +929,7 @@ mod tests {
} }
/// Tests that auto_vacuum is enabled for new databases. /// Tests that auto_vacuum is enabled for new databases.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_auto_vacuum() -> Result<()> { async fn test_auto_vacuum() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -925,7 +944,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_housekeeping_db_closed() { async fn test_housekeeping_db_closed() {
let t = TestContext::new().await; let t = TestContext::new().await;
@@ -945,14 +964,14 @@ mod tests {
t.add_event_sender(event_sink).await; t.add_event_sender(event_sink).await;
let a = t.get_config(Config::Selfavatar).await.unwrap().unwrap(); let a = t.get_config(Config::Selfavatar).await.unwrap().unwrap();
assert_eq!(avatar_bytes, &async_std::fs::read(&a).await.unwrap()[..]); assert_eq!(avatar_bytes, &tokio::fs::read(&a).await.unwrap()[..]);
t.sql.close().await; t.sql.close().await;
housekeeping(&t).await.unwrap_err(); // housekeeping should fail as the db is closed housekeeping(&t).await.unwrap_err(); // housekeeping should fail as the db is closed
t.sql.open(&t, "".to_string()).await.unwrap(); t.sql.open(&t, "".to_string()).await.unwrap();
let a = t.get_config(Config::Selfavatar).await.unwrap().unwrap(); let a = t.get_config(Config::Selfavatar).await.unwrap().unwrap();
assert_eq!(avatar_bytes, &async_std::fs::read(&a).await.unwrap()[..]); assert_eq!(avatar_bytes, &tokio::fs::read(&a).await.unwrap()[..]);
while let Ok(event) = event_source.try_recv() { while let Ok(event) = event_source.try_recv() {
match event.typ { match event.typ {
@@ -969,7 +988,7 @@ mod tests {
/// Regression test for a bug where housekeeping deleted drafts since their /// Regression test for a bug where housekeeping deleted drafts since their
/// `hidden` flag is set. /// `hidden` flag is set.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_housekeeping_dont_delete_drafts() { async fn test_housekeeping_dont_delete_drafts() {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -996,7 +1015,7 @@ mod tests {
/// ///
/// Statements were not finalized due to a bug in sqlx: /// Statements were not finalized due to a bug in sqlx:
/// <https://github.com/launchbadge/sqlx/issues/1147> /// <https://github.com/launchbadge/sqlx/issues/1147>
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_db_reopen() -> Result<()> { async fn test_db_reopen() -> Result<()> {
use tempfile::tempdir; use tempfile::tempdir;
@@ -1006,7 +1025,7 @@ mod tests {
// Create a separate empty database for testing. // Create a separate empty database for testing.
let dir = tempdir()?; let dir = tempdir()?;
let dbfile = dir.path().join("testdb.sqlite"); let dbfile = dir.path().join("testdb.sqlite");
let sql = Sql::new(dbfile.into()); let sql = Sql::new(dbfile);
// Create database with all the tables. // Create database with all the tables.
sql.open(&t, "".to_string()).await.unwrap(); sql.open(&t, "".to_string()).await.unwrap();
@@ -1028,7 +1047,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_migration_flags() -> Result<()> { async fn test_migration_flags() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
t.evtracker.get_info_contains("Opened database").await; t.evtracker.get_info_contains("Opened database").await;
@@ -1067,7 +1086,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_check_passphrase() -> Result<()> { async fn test_check_passphrase() -> Result<()> {
use tempfile::tempdir; use tempfile::tempdir;
@@ -1077,7 +1096,7 @@ mod tests {
// Create a separate empty database for testing. // Create a separate empty database for testing.
let dir = tempdir()?; let dir = tempdir()?;
let dbfile = dir.path().join("testdb.sqlite"); let dbfile = dir.path().join("testdb.sqlite");
let sql = Sql::new(dbfile.clone().into()); let sql = Sql::new(dbfile.clone());
sql.check_passphrase("foo".to_string()).await?; sql.check_passphrase("foo".to_string()).await?;
sql.open(&t, "foo".to_string()) sql.open(&t, "foo".to_string())
@@ -1086,7 +1105,7 @@ mod tests {
sql.close().await; sql.close().await;
// Reopen the database // Reopen the database
let sql = Sql::new(dbfile.into()); let sql = Sql::new(dbfile);
// Test that we can't open encrypted database without a passphrase. // Test that we can't open encrypted database without a passphrase.
assert!(sql.open(&t, "".to_string()).await.is_err()); assert!(sql.open(&t, "".to_string()).await.is_err());

View File

@@ -1167,7 +1167,7 @@ mod tests {
assert_eq!(StockMessage::NoMessages.fallback(), "No messages."); assert_eq!(StockMessage::NoMessages.fallback(), "No messages.");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_stock_translation() { async fn test_set_stock_translation() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.set_stock_translation(StockMessage::NoMessages, "xyz".to_string()) t.set_stock_translation(StockMessage::NoMessages, "xyz".to_string())
@@ -1176,7 +1176,7 @@ mod tests {
assert_eq!(no_messages(&t).await, "xyz") assert_eq!(no_messages(&t).await, "xyz")
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_set_stock_translation_wrong_replacements() { async fn test_set_stock_translation_wrong_replacements() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!(t assert!(t
@@ -1191,13 +1191,13 @@ mod tests {
.is_err()); .is_err());
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_str() { async fn test_stock_str() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!(no_messages(&t).await, "No messages."); assert_eq!(no_messages(&t).await, "No messages.");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_string_repl_str() { async fn test_stock_string_repl_str() {
let t = TestContext::new().await; let t = TestContext::new().await;
let contact_id = Contact::create(&t.ctx, "Someone", "someone@example.org") let contact_id = Contact::create(&t.ctx, "Someone", "someone@example.org")
@@ -1212,13 +1212,13 @@ mod tests {
// We have no string using %1$d to test... // We have no string using %1$d to test...
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_string_repl_str2() { async fn test_stock_string_repl_str2() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!(msg_action_by_user(&t, "foo", "bar").await, "foo by bar."); assert_eq!(msg_action_by_user(&t, "foo", "bar").await, "foo by bar.");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_system_msg_simple() { async fn test_stock_system_msg_simple() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!( assert_eq!(
@@ -1227,7 +1227,7 @@ mod tests {
) )
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_system_msg_add_member_by_me() { async fn test_stock_system_msg_add_member_by_me() {
let t = TestContext::new().await; let t = TestContext::new().await;
assert_eq!( assert_eq!(
@@ -1236,7 +1236,7 @@ mod tests {
) )
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_system_msg_add_member_by_me_with_displayname() { async fn test_stock_system_msg_add_member_by_me_with_displayname() {
let t = TestContext::new().await; let t = TestContext::new().await;
Contact::create(&t, "Alice", "alice@example.org") Contact::create(&t, "Alice", "alice@example.org")
@@ -1248,7 +1248,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_stock_system_msg_add_member_by_other_with_displayname() { async fn test_stock_system_msg_add_member_by_other_with_displayname() {
let t = TestContext::new().await; let t = TestContext::new().await;
let contact_id = { let contact_id = {
@@ -1265,7 +1265,7 @@ mod tests {
); );
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_quota_exceeding_stock_str() -> anyhow::Result<()> { async fn test_quota_exceeding_stock_str() -> anyhow::Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let str = quota_exceeding(&t, 81).await; let str = quota_exceeding(&t, 81).await;
@@ -1275,7 +1275,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_partial_download_msg_body() -> anyhow::Result<()> { async fn test_partial_download_msg_body() -> anyhow::Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let str = partial_download_msg_body(&t, 1024 * 1024).await; let str = partial_download_msg_body(&t, 1024 * 1024).await;
@@ -1283,7 +1283,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_update_device_chats() { async fn test_update_device_chats() {
let t = TestContext::new().await; let t = TestContext::new().await;
t.update_device_chats().await.ok(); t.update_device_chats().await.ok();

View File

@@ -189,7 +189,7 @@ mod tests {
use super::*; use super::*;
use crate::test_utils as test; use crate::test_utils as test;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_summary_text() { async fn test_get_summary_text() {
let d = test::TestContext::new().await; let d = test::TestContext::new().await;
let ctx = &d.ctx; let ctx = &d.ctx;

View File

@@ -267,7 +267,7 @@ mod tests {
use crate::token::Namespace; use crate::token::Namespace;
use anyhow::bail; use anyhow::bail;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_is_sync_sending_enabled() -> Result<()> { async fn test_is_sync_sending_enabled() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
assert!(!t.is_sync_sending_enabled().await?); assert!(!t.is_sync_sending_enabled().await?);
@@ -278,7 +278,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_build_sync_json() -> Result<()> { async fn test_build_sync_json() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config_bool(Config::SendSyncMsgs, true).await?; t.set_config_bool(Config::SendSyncMsgs, true).await?;
@@ -323,7 +323,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_build_sync_json_sync_msgs_off() -> Result<()> { async fn test_build_sync_json_sync_msgs_off() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config_bool(Config::SendSyncMsgs, false).await?; t.set_config_bool(Config::SendSyncMsgs, false).await?;
@@ -337,7 +337,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_sync_items() -> Result<()> { async fn test_parse_sync_items() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -422,7 +422,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_execute_sync_items() -> Result<()> { async fn test_execute_sync_items() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -451,7 +451,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_sync_msg() -> Result<()> { async fn test_send_sync_msg() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.set_config_bool(Config::SendSyncMsgs, true).await?; alice.set_config_bool(Config::SendSyncMsgs, true).await?;

View File

@@ -6,17 +6,17 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::ops::Deref; use std::ops::Deref;
use std::panic; use std::panic;
use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use ansi_term::Color; use ansi_term::Color;
use async_std::channel::{self, Receiver, Sender}; use async_channel::{self as channel, Receiver, Sender};
use async_std::prelude::*;
use async_std::sync::{Arc, RwLock};
use async_std::task;
use chat::ChatItem; use chat::ChatItem;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::Rng; use rand::Rng;
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
use tokio::sync::RwLock;
use tokio::task;
use crate::chat::{self, Chat, ChatId}; use crate::chat::{self, Chat, ChatId};
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
@@ -250,6 +250,17 @@ impl TestContext {
Self::builder().configure_fiona().build().await Self::builder().configure_fiona().build().await
} }
/// Print current chat state.
pub async fn print_chats(&self) {
println!("\n========== Chats of {}: ==========", self.name());
if let Ok(chats) = Chatlist::try_load(self, 0, None, None).await {
for (chat, _) in chats.iter() {
self.print_chat(*chat).await;
}
}
println!();
}
/// Internal constructor. /// Internal constructor.
/// ///
/// `name` is used to identify this context in e.g. log output. This is useful mostly /// `name` is used to identify this context in e.g. log output. This is useful mostly
@@ -266,7 +277,7 @@ impl TestContext {
let mut context_names = CONTEXT_NAMES.write().unwrap(); let mut context_names = CONTEXT_NAMES.write().unwrap();
context_names.insert(id, name); context_names.insert(id, name);
} }
let ctx = Context::new(dbfile.into(), id, Events::new()) let ctx = Context::new(&dbfile, id, Events::new())
.await .await
.expect("failed to create context"); .expect("failed to create context");
@@ -382,7 +393,7 @@ impl TestContext {
break row; break row;
} }
if start.elapsed() < Duration::from_secs(3) { if start.elapsed() < Duration::from_secs(3) {
async_std::task::sleep(Duration::from_millis(100)).await; tokio::time::sleep(Duration::from_millis(100)).await;
} else { } else {
panic!("no sent message found in jobs table"); panic!("no sent message found in jobs table");
} }
@@ -670,20 +681,6 @@ impl Deref for TestContext {
} }
} }
impl Drop for TestContext {
fn drop(&mut self) {
async_std::task::block_on(async {
println!("\n========== Chats of {}: ==========", self.name());
if let Ok(chats) = Chatlist::try_load(self, 0, None, None).await {
for (chat, _) in chats.iter() {
self.print_chat(*chat).await;
}
}
println!();
});
}
}
pub enum LogEvent { pub enum LogEvent {
/// Logged event. /// Logged event.
Event(Event), Event(Event),
@@ -828,15 +825,14 @@ impl EventTracker {
/// If no matching events are ready this will wait for new events to arrive and time out /// If no matching events are ready this will wait for new events to arrive and time out
/// after 10 seconds. /// after 10 seconds.
pub async fn get_matching<F: Fn(&EventType) -> bool>(&self, event_matcher: F) -> EventType { pub async fn get_matching<F: Fn(&EventType) -> bool>(&self, event_matcher: F) -> EventType {
async move { tokio::time::timeout(Duration::from_secs(10), async move {
loop { loop {
let event = self.0.recv().await.unwrap(); let event = self.0.recv().await.unwrap();
if event_matcher(&event.typ) { if event_matcher(&event.typ) {
return event.typ; return event.typ;
} }
} }
} })
.timeout(Duration::from_secs(10))
.await .await
.expect("timeout waiting for event match") .expect("timeout waiting for event match")
} }
@@ -1011,21 +1007,21 @@ mod tests {
// The following three tests demonstrate, when made to fail, the log output being // The following three tests demonstrate, when made to fail, the log output being
// directed to the correct test output. // directed to the correct test output.
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_with_alice() { async fn test_with_alice() {
let alice = TestContext::builder().configure_alice().build().await; let alice = TestContext::builder().configure_alice().build().await;
alice.ctx.emit_event(EventType::Info("hello".into())); alice.ctx.emit_event(EventType::Info("hello".into()));
// panic!("Alice fails"); // panic!("Alice fails");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_with_bob() { async fn test_with_bob() {
let bob = TestContext::builder().configure_bob().build().await; let bob = TestContext::builder().configure_bob().build().await;
bob.ctx.emit_event(EventType::Info("there".into())); bob.ctx.emit_event(EventType::Info("there".into()));
// panic!("Bob fails"); // panic!("Bob fails");
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_with_both() { async fn test_with_both() {
let mut tcm = TestContextManager::new().await; let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await; let alice = tcm.alice().await;

View File

@@ -63,7 +63,7 @@ mod tests {
use crate::dc_tools::time; use crate::dc_tools::time;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_params_update_timestamp() -> Result<()> { async fn test_params_update_timestamp() -> Result<()> {
let mut params = Params::new(); let mut params = Params::new();
let ts = time(); let ts = time();
@@ -85,7 +85,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_out_of_order_subject() -> Result<()> { async fn test_out_of_order_subject() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
@@ -126,7 +126,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_out_of_order_group_name() -> Result<()> { async fn test_out_of_order_group_name() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;

View File

@@ -1,26 +1,27 @@
//! # Handle webxdc messages. //! # Handle webxdc messages.
use std::convert::TryFrom;
use std::path::PathBuf;
use anyhow::{anyhow, bail, ensure, format_err, Result};
use deltachat_derive::FromSql;
use lettre_email::mime;
use lettre_email::PartBuilder;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::io::AsyncReadExt;
use crate::chat::Chat; use crate::chat::Chat;
use crate::contact::ContactId; use crate::contact::ContactId;
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::{dc_create_smeared_timestamp, dc_open_file_std}; use crate::dc_tools::dc_create_smeared_timestamp;
use crate::dc_tools::dc_get_abs_path;
use crate::message::{Message, MessageState, MsgId, Viewtype}; use crate::message::{Message, MessageState, MsgId, Viewtype};
use crate::mimeparser::SystemMessage; use crate::mimeparser::SystemMessage;
use crate::param::Param; use crate::param::Param;
use crate::param::Params; use crate::param::Params;
use crate::scheduler::InterruptInfo; use crate::scheduler::InterruptInfo;
use crate::{chat, EventType}; use crate::{chat, EventType};
use anyhow::{bail, ensure, format_err, Result};
use async_std::path::PathBuf;
use deltachat_derive::FromSql;
use lettre_email::mime;
use lettre_email::PartBuilder;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::convert::TryFrom;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use zip::ZipArchive;
/// The current API version. /// The current API version.
/// If `min_api` in manifest.toml is set to a larger value, /// If `min_api` in manifest.toml is set to a larger value,
@@ -139,16 +140,12 @@ pub(crate) struct StatusUpdateItemAndSerial {
impl Context { impl Context {
/// check if a file is an acceptable webxdc for sending or receiving. /// check if a file is an acceptable webxdc for sending or receiving.
pub(crate) async fn is_webxdc_file<R>(&self, filename: &str, mut reader: R) -> Result<bool> pub(crate) async fn is_webxdc_file(&self, filename: &str, file: &[u8]) -> Result<bool> {
where
R: Read + Seek,
{
if !filename.ends_with(WEBXDC_SUFFIX) { if !filename.ends_with(WEBXDC_SUFFIX) {
return Ok(false); return Ok(false);
} }
let size = reader.seek(SeekFrom::End(0))?; if file.len() as u64 > WEBXDC_RECEIVING_LIMIT {
if size > WEBXDC_RECEIVING_LIMIT {
info!( info!(
self, self,
"{} exceeds receiving limit of {} bytes", &filename, WEBXDC_RECEIVING_LIMIT "{} exceeds receiving limit of {} bytes", &filename, WEBXDC_RECEIVING_LIMIT
@@ -156,8 +153,7 @@ impl Context {
return Ok(false); return Ok(false);
} }
reader.seek(SeekFrom::Start(0))?; let archive = match async_zip::read::mem::ZipFileReader::new(file).await {
let mut archive = match zip::ZipArchive::new(reader) {
Ok(archive) => archive, Ok(archive) => archive,
Err(_) => { Err(_) => {
info!(self, "{} cannot be opened as zip-file", &filename); info!(self, "{} cannot be opened as zip-file", &filename);
@@ -165,7 +161,7 @@ impl Context {
} }
}; };
if archive.by_name("index.html").is_err() { if archive.entry("index.html").is_none() {
info!(self, "{} misses index.html", &filename); info!(self, "{} misses index.html", &filename);
return Ok(false); return Ok(false);
} }
@@ -176,18 +172,12 @@ impl Context {
/// ensure that a file is an acceptable webxdc for sending /// ensure that a file is an acceptable webxdc for sending
/// (sending has more strict size limits). /// (sending has more strict size limits).
pub(crate) async fn ensure_sendable_webxdc_file(&self, path: &PathBuf) -> Result<()> { pub(crate) async fn ensure_sendable_webxdc_file(&self, path: &PathBuf) -> Result<()> {
let mut file = std::fs::File::open(path)?; let filename = path.to_str().unwrap_or_default();
if !self if !filename.ends_with(WEBXDC_SUFFIX) {
.is_webxdc_file(path.to_str().unwrap_or_default(), &mut file) bail!("{} is not a valid webxdc file", filename);
.await?
{
bail!(
"{} is not a valid webxdc file",
path.to_str().unwrap_or_default()
);
} }
let size = file.seek(SeekFrom::End(0))?; let size = tokio::fs::metadata(path).await?.len();
if size > WEBXDC_SENDING_LIMIT { if size > WEBXDC_SENDING_LIMIT {
bail!( bail!(
"webxdc {} exceeds acceptable size of {} bytes", "webxdc {} exceeds acceptable size of {} bytes",
@@ -195,6 +185,26 @@ impl Context {
WEBXDC_SENDING_LIMIT WEBXDC_SENDING_LIMIT
); );
} }
let valid = match async_zip::read::fs::ZipFileReader::new(path).await {
Ok(archive) => {
if archive.entry("index.html").is_none() {
info!(self, "{} misses index.html", filename);
false
} else {
true
}
}
Err(_) => {
info!(self, "{} cannot be opened as zip-file", filename);
false
}
};
if !valid {
bail!("{} is not a valid webxdc file", filename);
}
Ok(()) Ok(())
} }
@@ -588,22 +598,28 @@ fn parse_webxdc_manifest(bytes: &[u8]) -> Result<WebxdcManifest> {
Ok(manifest) Ok(manifest)
} }
fn get_blob(archive: &mut ZipArchive<File>, name: &str) -> Result<Vec<u8>> { async fn get_blob(archive: &mut async_zip::read::fs::ZipFileReader, name: &str) -> Result<Vec<u8>> {
let mut file = archive.by_name(name)?; let (i, _) = archive
.entry(name)
.ok_or_else(|| anyhow!("no entry found for {}", name))?;
let mut reader = archive.entry_reader(i).await?;
let mut buf = Vec::new(); let mut buf = Vec::new();
file.read_to_end(&mut buf)?; reader.read_to_end(&mut buf).await?;
Ok(buf) Ok(buf)
} }
impl Message { impl Message {
/// Get handle to a webxdc ZIP-archive. /// Get handle to a webxdc ZIP-archive.
/// To check for file existance use archive.by_name(), to read a file, use get_blob(archive). /// To check for file existance use archive.by_name(), to read a file, use get_blob(archive).
async fn get_webxdc_archive(&self, context: &Context) -> Result<ZipArchive<File>> { async fn get_webxdc_archive(
&self,
context: &Context,
) -> Result<async_zip::read::fs::ZipFileReader> {
let path = self let path = self
.get_file(context) .get_file(context)
.ok_or_else(|| format_err!("No webxdc instance file."))?; .ok_or_else(|| format_err!("No webxdc instance file."))?;
let file = dc_open_file_std(context, path)?; let path_abs = dc_get_abs_path(context, &path);
let archive = zip::ZipArchive::new(file)?; let archive = async_zip::read::fs::ZipFileReader::new(path_abs).await?;
Ok(archive) Ok(archive)
} }
@@ -627,7 +643,7 @@ impl Message {
let mut archive = self.get_webxdc_archive(context).await?; let mut archive = self.get_webxdc_archive(context).await?;
if name == "index.html" { if name == "index.html" {
if let Ok(bytes) = get_blob(&mut archive, "manifest.toml") { if let Ok(bytes) = get_blob(&mut archive, "manifest.toml").await {
if let Ok(manifest) = parse_webxdc_manifest(&bytes) { if let Ok(manifest) = parse_webxdc_manifest(&bytes) {
if let Some(min_api) = manifest.min_api { if let Some(min_api) = manifest.min_api {
if min_api > WEBXDC_API_VERSION { if min_api > WEBXDC_API_VERSION {
@@ -640,7 +656,7 @@ impl Message {
} }
} }
get_blob(&mut archive, name) get_blob(&mut archive, name).await
} }
/// Return info from manifest.toml or from fallbacks. /// Return info from manifest.toml or from fallbacks.
@@ -648,7 +664,7 @@ impl Message {
ensure!(self.viewtype == Viewtype::Webxdc, "No webxdc instance."); ensure!(self.viewtype == Viewtype::Webxdc, "No webxdc instance.");
let mut archive = self.get_webxdc_archive(context).await?; let mut archive = self.get_webxdc_archive(context).await?;
let mut manifest = if let Ok(bytes) = get_blob(&mut archive, "manifest.toml") { let mut manifest = if let Ok(bytes) = get_blob(&mut archive, "manifest.toml").await {
if let Ok(manifest) = parse_webxdc_manifest(&bytes) { if let Ok(manifest) = parse_webxdc_manifest(&bytes) {
manifest manifest
} else { } else {
@@ -680,9 +696,9 @@ impl Message {
} else { } else {
self.get_filename().unwrap_or_default() self.get_filename().unwrap_or_default()
}, },
icon: if archive.by_name("icon.png").is_ok() { icon: if archive.entry("icon.png").is_some() {
"icon.png".to_string() "icon.png".to_string()
} else if archive.by_name("icon.jpg").is_ok() { } else if archive.entry("icon.jpg").is_some() {
"icon.jpg".to_string() "icon.jpg".to_string()
} else { } else {
WEBXDC_DEFAULT_ICON.to_string() WEBXDC_DEFAULT_ICON.to_string()
@@ -708,10 +724,8 @@ impl Message {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::Cursor; use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use async_std::fs::File;
use async_std::io::WriteExt;
use crate::chat::{ use crate::chat::{
add_contact_to_chat, create_group_chat, forward_msgs, resend_msgs, send_msg, send_text_msg, add_contact_to_chat, create_group_chat, forward_msgs, resend_msgs, send_msg, send_text_msg,
@@ -726,7 +740,7 @@ mod tests {
use super::*; use super::*;
#[allow(clippy::assertions_on_constants)] #[allow(clippy::assertions_on_constants)]
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_file_limits() -> Result<()> { async fn test_webxdc_file_limits() -> Result<()> {
assert!(WEBXDC_SENDING_LIMIT >= 32768); assert!(WEBXDC_SENDING_LIMIT >= 32768);
assert!(WEBXDC_SENDING_LIMIT < 16777216); assert!(WEBXDC_SENDING_LIMIT < 16777216);
@@ -735,41 +749,41 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_is_webxdc_file() -> Result<()> { async fn test_is_webxdc_file() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!( assert!(
!t.is_webxdc_file( !t.is_webxdc_file(
"bad-ext-no-zip.txt", "bad-ext-no-zip.txt",
Cursor::new(include_bytes!("../test-data/message/issue_523.txt")) include_bytes!("../test-data/message/issue_523.txt")
) )
.await? .await?
); );
assert!( assert!(
!t.is_webxdc_file( !t.is_webxdc_file(
"bad-ext-good-zip.txt", "bad-ext-good-zip.txt",
Cursor::new(include_bytes!("../test-data/webxdc/minimal.xdc")) include_bytes!("../test-data/webxdc/minimal.xdc")
) )
.await? .await?
); );
assert!( assert!(
!t.is_webxdc_file( !t.is_webxdc_file(
"good-ext-no-zip.xdc", "good-ext-no-zip.xdc",
Cursor::new(include_bytes!("../test-data/message/issue_523.txt")) include_bytes!("../test-data/message/issue_523.txt")
) )
.await? .await?
); );
assert!( assert!(
!t.is_webxdc_file( !t.is_webxdc_file(
"good-ext-no-index-html.xdc", "good-ext-no-index-html.xdc",
Cursor::new(include_bytes!("../test-data/webxdc/no-index-html.xdc")) include_bytes!("../test-data/webxdc/no-index-html.xdc")
) )
.await? .await?
); );
assert!( assert!(
t.is_webxdc_file( t.is_webxdc_file(
"good-ext-good-zip.xdc", "good-ext-good-zip.xdc",
Cursor::new(include_bytes!("../test-data/webxdc/minimal.xdc")) include_bytes!("../test-data/webxdc/minimal.xdc")
) )
.await? .await?
); );
@@ -796,7 +810,7 @@ mod tests {
Message::load_from_db(t, instance_msg_id).await Message::load_from_db(t, instance_msg_id).await
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_webxdc_instance() -> Result<()> { async fn test_send_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -820,7 +834,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_invalid_webxdc() -> Result<()> { async fn test_send_invalid_webxdc() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -852,7 +866,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_forward_webxdc_instance() -> Result<()> { async fn test_forward_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -895,7 +909,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resend_webxdc_instance_and_info() -> Result<()> { async fn test_resend_webxdc_instance_and_info() -> Result<()> {
// Alice uses webxdc in a group // Alice uses webxdc in a group
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
@@ -940,7 +954,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_receive_webxdc_instance() -> Result<()> { async fn test_receive_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
dc_receive_imf( dc_receive_imf(
@@ -966,7 +980,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_contact_request() -> Result<()> { async fn test_webxdc_contact_request() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -1010,7 +1024,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete_webxdc_instance() -> Result<()> { async fn test_delete_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1048,7 +1062,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_status_update_record() -> Result<()> { async fn test_create_status_update_record() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1142,7 +1156,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_receive_status_update() -> Result<()> { async fn test_receive_status_update() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1238,7 +1252,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_webxdc_status_update() -> Result<()> { async fn test_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
alice.set_config_bool(Config::BccSelf, true).await?; alice.set_config_bool(Config::BccSelf, true).await?;
@@ -1338,7 +1352,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_render_webxdc_status_update_object() -> Result<()> { async fn test_render_webxdc_status_update_object() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
@@ -1364,7 +1378,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_render_webxdc_status_update_object_range() -> Result<()> { async fn test_render_webxdc_status_update_object_range() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
@@ -1402,7 +1416,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pop_status_update() -> Result<()> { async fn test_pop_status_update() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
@@ -1462,7 +1476,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_draft_and_send_webxdc_status_update() -> Result<()> { async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -1529,7 +1543,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_webxdc_status_update_to_non_webxdc() -> Result<()> { async fn test_send_webxdc_status_update_to_non_webxdc() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1541,7 +1555,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_webxdc_blob() -> Result<()> { async fn test_get_webxdc_blob() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1558,7 +1572,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_webxdc_blob_default_icon() -> Result<()> { async fn test_get_webxdc_blob_default_icon() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1570,7 +1584,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_webxdc_blob_with_absolute_paths() -> Result<()> { async fn test_get_webxdc_blob_with_absolute_paths() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1583,7 +1597,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_webxdc_blob_with_subdirs() -> Result<()> { async fn test_get_webxdc_blob_with_subdirs() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1624,7 +1638,7 @@ mod tests {
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_webxdc_manifest() -> Result<()> { async fn test_parse_webxdc_manifest() -> Result<()> {
let result = parse_webxdc_manifest(r#"key = syntax error"#.as_bytes()); let result = parse_webxdc_manifest(r#"key = syntax error"#.as_bytes());
assert!(result.is_err()); assert!(result.is_err());
@@ -1655,7 +1669,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_webxdc_manifest_min_api() -> Result<()> { async fn test_parse_webxdc_manifest_min_api() -> Result<()> {
let manifest = parse_webxdc_manifest(r#"min_api = 3"#.as_bytes())?; let manifest = parse_webxdc_manifest(r#"min_api = 3"#.as_bytes())?;
assert_eq!(manifest.min_api, Some(3)); assert_eq!(manifest.min_api, Some(3));
@@ -1669,7 +1683,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_webxdc_manifest_source_code_url() -> Result<()> { async fn test_parse_webxdc_manifest_source_code_url() -> Result<()> {
let result = parse_webxdc_manifest(r#"source_code_url = 3"#.as_bytes()); let result = parse_webxdc_manifest(r#"source_code_url = 3"#.as_bytes());
assert!(result.is_err()); assert!(result.is_err());
@@ -1683,7 +1697,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_min_api_too_large() -> Result<()> { async fn test_webxdc_min_api_too_large() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
@@ -1702,7 +1716,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_webxdc_info() -> Result<()> { async fn test_get_webxdc_info() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
@@ -1786,7 +1800,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_info_summary() -> Result<()> { async fn test_webxdc_info_summary() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -1852,7 +1866,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_document_name() -> Result<()> { async fn test_webxdc_document_name() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -1894,7 +1908,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_info_msg() -> Result<()> { async fn test_webxdc_info_msg() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -1985,7 +1999,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_info_msg_cleanup_series() -> Result<()> { async fn test_webxdc_info_msg_cleanup_series() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -2023,7 +2037,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_info_msg_no_cleanup_on_interrupted_series() -> Result<()> { async fn test_webxdc_info_msg_no_cleanup_on_interrupted_series() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "c").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "c").await?;
@@ -2041,7 +2055,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_opportunistic_encryption() -> Result<()> { async fn test_webxdc_opportunistic_encryption() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
@@ -2093,7 +2107,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_chatlist_summary() -> Result<()> { async fn test_webxdc_chatlist_summary() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
@@ -2113,7 +2127,7 @@ sth_for_the = "future""#
Ok(()) Ok(())
} }
#[async_std::test] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_and_text() -> Result<()> { async fn test_webxdc_and_text() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;