Compare commits

..

1 Commits

Author SHA1 Message Date
link2xt
552de04bfb ci: increase timeout in test_logged_ac_process_ffi_failure
This test keeps failing on macOS CI,
likely because it does not get enough resources
to finish in 10 seconds.
2025-01-03 01:05:11 +00:00
8 changed files with 64 additions and 169 deletions

25
Cargo.lock generated
View File

@@ -5384,9 +5384,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.20"
version = "0.23.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
dependencies = [
"log",
"once_cell",
@@ -5636,9 +5636,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.217"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
@@ -5673,9 +5673,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.217"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
@@ -5695,9 +5695,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.134"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"itoa",
"memchr",
@@ -6253,13 +6253,12 @@ dependencies = [
[[package]]
name = "testdir"
version = "0.9.3"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ffa013be124f7e8e648876190de818e3a87088ed97ccd414a398b403aec8c8"
checksum = "ee79e927b64d193f5abb60d20a0eb56be0ee5a242fdeb8ce3bf054177006de52"
dependencies = [
"anyhow",
"backtrace",
"cargo-platform",
"cargo_metadata",
"once_cell",
"sysinfo",
@@ -6386,9 +6385,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.42.0"
version = "1.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
dependencies = [
"backtrace",
"bytes",

View File

@@ -86,7 +86,7 @@ regex = { workspace = true }
rusqlite = { workspace = true, features = ["sqlcipher"] }
rust-hsluv = "0.1"
rustls-pki-types = "1.10.1"
rustls = { version = "0.23.20", default-features = false }
rustls = { version = "0.23.19", default-features = false }
sanitize-filename = { workspace = true }
serde_json = { workspace = true }
serde_urlencoded = "0.7.1"
@@ -120,7 +120,7 @@ nu-ansi-term = { workspace = true }
pretty_assertions = "1.4.1"
proptest = { version = "1", default-features = false, features = ["std"] }
tempfile = { workspace = true }
testdir = "0.9.3"
testdir = "0.9.0"
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
[workspace]

View File

@@ -285,43 +285,6 @@ def test_message(acfactory) -> None:
assert reactions == snapshot.reactions
def test_reaction_seen_on_another_dev(acfactory, tmp_path) -> None:
alice, bob = acfactory.get_online_accounts(2)
alice.export_backup(tmp_path)
files = list(tmp_path.glob("*.tar"))
alice2 = acfactory.get_unconfigured_account()
alice2.import_backup(files[0])
alice2.start_io()
bob_addr = bob.get_config("addr")
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
alice_chat_bob = alice_contact_bob.create_chat()
alice_chat_bob.send_text("Hello!")
event = bob.wait_for_incoming_msg_event()
msg_id = event.msg_id
message = bob.get_message_by_id(msg_id)
snapshot = message.get_snapshot()
snapshot.chat.accept()
message.send_reaction("😎")
for a in [alice, alice2]:
while True:
event = a.wait_for_event()
if event.kind == EventType.INCOMING_REACTION:
break
alice_chat_bob.mark_noticed()
while True:
event = alice2.wait_for_event()
if event.kind == EventType.MSGS_NOTICED:
chat_id = event.chat_id
break
alice2_contact_bob = alice2.get_contact_by_addr(bob_addr)
alice2_chat_bob = alice2_contact_bob.create_chat()
assert chat_id == alice2_chat_bob.id
def test_is_bot(acfactory) -> None:
"""Test that we can recognize messages submitted by bots."""
alice, bob = acfactory.get_online_accounts(2)

View File

@@ -216,7 +216,7 @@ def test_logged_ac_process_ffi_failure(acfactory):
ac1.add_account_plugin(FailPlugin())
# cause any event eg contact added/changed
ac1.create_contact("something@example.org")
res = cap.get(timeout=10)
res = cap.get(timeout=600)
assert "ac_process_ffi_event" in res
assert "ZeroDivisionError" in res
assert "Traceback" in res

View File

@@ -39,7 +39,6 @@ use crate::mimeparser::SystemMessage;
use crate::param::{Param, Params};
use crate::peerstate::Peerstate;
use crate::receive_imf::ReceivedMsg;
use crate::rusqlite::OptionalExtension;
use crate::securejoin::BobState;
use crate::smtp::send_msg_to_smtp;
use crate::sql;
@@ -3244,16 +3243,15 @@ pub async fn get_chat_msgs_ex(
/// Marks all messages in the chat as noticed.
/// If the given chat-id is the archive-link, marks all messages in all archived chats as noticed.
pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()> {
// `WHERE` statements below use the index `(state, hidden, chat_id)`, that's why we enumerate
// `hidden` values, see `get_fresh_msg_cnt()` for reasoning.
// The additional `SELECT` statement may speed up things as no write-blocking is needed.
// "WHERE" below uses the index `(state, hidden, chat_id)`, see get_fresh_msg_cnt() for reasoning
// the additional SELECT statement may speed up things as no write-blocking is needed.
if chat_id.is_archived_link() {
let chat_ids_in_archive = context
.sql
.query_map(
"SELECT DISTINCT(m.chat_id) FROM msgs m
LEFT JOIN chats c ON m.chat_id=c.id
WHERE m.state=10 AND m.hidden IN (0,1) AND m.chat_id>9 AND c.archived=1",
WHERE m.state=10 AND m.hidden=0 AND m.chat_id>9 AND c.archived=1",
(),
|row| row.get::<_, ChatId>(0),
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
@@ -3267,7 +3265,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
.sql
.transaction(|transaction| {
let mut stmt = transaction.prepare(
"UPDATE msgs SET state=13 WHERE state=10 AND hidden IN (0,1) AND chat_id=?",
"UPDATE msgs SET state=13 WHERE state=10 AND hidden=0 AND chat_id = ?",
)?;
for chat_id_in_archive in &chat_ids_in_archive {
stmt.execute((chat_id_in_archive,))?;
@@ -3283,56 +3281,22 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
}
} else {
start_chat_ephemeral_timers(context, chat_id).await?;
let conn_fn = |conn: &mut rusqlite::Connection| {
// This is to trigger emitting `MsgsNoticed` on other devices when reactions are noticed
// locally. We filter out `InNoticed` messages because they are normally a result of
// `mark_old_messages_as_noticed()` which happens on all devices anyway. Also we limit
// this to one message because the effect is the same anyway.
//
// Even if `message::markseen_msgs()` fails then, in the worst case other devices won't
// emit `MsgsNoticed` and app notifications won't be removed. The bigger problem is that
// another device may have more reactions received and not yet seen notifications are
// removed from it, but the same problem already exists for "usual" messages, so let's
// not solve it for now.
let mut stmt = conn.prepare(
"SELECT id, state FROM msgs
WHERE (state=? OR state=? OR state=?)
AND hidden=1
AND chat_id=?
ORDER BY id DESC LIMIT 1",
)?;
let id_to_markseen = stmt
.query_row(
(
MessageState::InFresh,
MessageState::InNoticed,
MessageState::InSeen,
chat_id,
),
|row| {
let id: MsgId = row.get(0)?;
let state: MessageState = row.get(1)?;
Ok((id, state))
},
)
.optional()?
.filter(|&(_, state)| state == MessageState::InFresh)
.map(|(id, _)| id);
let nr_msgs_noticed = conn.execute(
if context
.sql
.execute(
"UPDATE msgs
SET state=?
WHERE state=? AND hidden IN (0,1) AND chat_id=?",
SET state=?
WHERE state=?
AND hidden=0
AND chat_id=?;",
(MessageState::InNoticed, MessageState::InFresh, chat_id),
)?;
Ok((nr_msgs_noticed, id_to_markseen))
};
let (nr_msgs_noticed, id_to_markseen) = context.sql.call_write(conn_fn).await?;
if nr_msgs_noticed == 0 {
)
.await?
== 0
{
return Ok(());
}
if let Some(id) = id_to_markseen {
message::markseen_msgs(context, vec![id]).await?;
}
}
context.emit_event(EventType::MsgsNoticed(chat_id));
@@ -3373,12 +3337,11 @@ pub(crate) async fn mark_old_messages_as_noticed(
.transaction(|transaction| {
let mut changed_chats = Vec::new();
for (_, msg) in msgs_by_chat {
// NB: Enumerate `hidden` values to employ the index `(state, hidden, chat_id)`.
let changed_rows = transaction.execute(
"UPDATE msgs
SET state=?
WHERE state=?
AND hidden IN (0,1)
AND hidden=0
AND chat_id=?
AND timestamp<=?;",
(

View File

@@ -397,7 +397,7 @@ mod tests {
use deltachat_contact_tools::ContactAddress;
use super::*;
use crate::chat::{forward_msgs, get_chat_msgs, marknoticed_chat, send_text_msg};
use crate::chat::{forward_msgs, get_chat_msgs, send_text_msg};
use crate::chatlist::Chatlist;
use crate::config::Config;
use crate::contact::{Contact, Origin};
@@ -663,8 +663,7 @@ Here's my footer -- bob@example.net"
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
let bob_reaction_msg = bob.pop_sent_msg().await;
let alice_reaction_msg = alice.recv_msg_hidden(&bob_reaction_msg).await;
assert_eq!(alice_reaction_msg.state, MessageState::InFresh);
alice.recv_msg_trash(&bob_reaction_msg).await;
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
@@ -681,20 +680,6 @@ Here's my footer -- bob@example.net"
expect_incoming_reactions_event(&alice, alice_msg.sender_msg_id, *bob_id, "👍").await?;
expect_no_unwanted_events(&alice).await;
marknoticed_chat(&alice, chat_alice.id).await?;
assert_eq!(
alice_reaction_msg.id.get_state(&alice).await?,
MessageState::InSeen
);
// Reactions don't request MDNs.
assert_eq!(
alice
.sql
.count("SELECT COUNT(*) FROM smtp_mdns", ())
.await?,
0
);
// Alice reacts to own message.
send_reaction(&alice, alice_msg.sender_msg_id, "👍 😀")
.await
@@ -734,7 +719,7 @@ Here's my footer -- bob@example.net"
bob_msg1.chat_id.accept(&bob).await?;
send_reaction(&bob, bob_msg1.id, "👍").await?;
let bob_send_reaction = bob.pop_sent_msg().await;
alice.recv_msg_hidden(&bob_send_reaction).await;
alice.recv_msg_trash(&bob_send_reaction).await;
expect_incoming_reactions_event(&alice, alice_msg1.sender_msg_id, alice_bob_id, "👍")
.await?;
expect_no_unwanted_events(&alice).await;
@@ -897,7 +882,7 @@ Here's my footer -- bob@example.net"
let bob_reaction_msg = bob.pop_sent_msg().await;
// Alice receives a reaction.
alice.recv_msg_hidden(&bob_reaction_msg).await;
alice.recv_msg_trash(&bob_reaction_msg).await;
let reactions = get_msg_reactions(&alice, alice_msg_id).await?;
assert_eq!(reactions.to_string(), "👍1");
@@ -949,7 +934,7 @@ Here's my footer -- bob@example.net"
{
send_reaction(&alice2, alice2_msg.id, "👍").await?;
let msg = alice2.pop_sent_msg().await;
alice1.recv_msg_hidden(&msg).await;
alice1.recv_msg_trash(&msg).await;
}
// Check that the status is still the same.
@@ -971,7 +956,7 @@ Here's my footer -- bob@example.net"
let alice1_msg = alice1.recv_msg(&alice0.pop_sent_msg().await).await;
send_reaction(&alice0, alice0_msg_id, "👀").await?;
alice1.recv_msg_hidden(&alice0.pop_sent_msg().await).await;
alice1.recv_msg_trash(&alice0.pop_sent_msg().await).await;
expect_reactions_changed_event(&alice0, chat_id, alice0_msg_id, ContactId::SELF).await?;
expect_reactions_changed_event(&alice1, alice1_msg.chat_id, alice1_msg.id, ContactId::SELF)

View File

@@ -54,9 +54,6 @@ pub struct ReceivedMsg {
/// Received message state.
pub state: MessageState,
/// Whether the message is hidden.
pub hidden: bool,
/// Message timestamp for sorting.
pub sort_timestamp: i64,
@@ -195,7 +192,6 @@ pub(crate) async fn receive_imf_inner(
return Ok(Some(ReceivedMsg {
chat_id: DC_CHAT_ID_TRASH,
state: MessageState::Undefined,
hidden: false,
sort_timestamp: 0,
msg_ids,
needs_delete_job: false,
@@ -377,7 +373,6 @@ pub(crate) async fn receive_imf_inner(
received_msg = Some(ReceivedMsg {
chat_id: DC_CHAT_ID_TRASH,
state: MessageState::InSeen,
hidden: false,
sort_timestamp: mime_parser.timestamp_sent,
msg_ids: vec![msg_id],
needs_delete_job: res == securejoin::HandshakeMessage::Done,
@@ -616,11 +611,7 @@ pub(crate) async fn receive_imf_inner(
} else if !chat_id.is_trash() {
let fresh = received_msg.state == MessageState::InFresh;
for msg_id in &received_msg.msg_ids {
chat_id.emit_msg_event(
context,
*msg_id,
mime_parser.incoming && fresh && !received_msg.hidden,
);
chat_id.emit_msg_event(context, *msg_id, mime_parser.incoming && fresh);
}
}
context.new_msgs_notify.notify_one();
@@ -773,7 +764,7 @@ async fn add_parts(
// (of course, the user can add other chats manually later)
let to_id: ContactId;
let state: MessageState;
let mut hidden = is_reaction;
let mut hidden = false;
let mut needs_delete_job = false;
let mut restore_protection = false;
@@ -787,11 +778,6 @@ async fn add_parts(
}
}
if chat_id.is_none() && is_mdn {
chat_id = Some(DC_CHAT_ID_TRASH);
info!(context, "Message is an MDN (TRASH).",);
}
if mime_parser.incoming {
to_id = ContactId::SELF;
@@ -803,6 +789,11 @@ async fn add_parts(
markseen_on_imap_table(context, rfc724_mid).await.ok();
}
if chat_id.is_none() && is_mdn {
chat_id = Some(DC_CHAT_ID_TRASH);
info!(context, "Message is an MDN (TRASH).",);
}
let create_blocked_default = if is_bot {
Blocked::Not
} else {
@@ -986,6 +977,7 @@ async fn add_parts(
// the 1:1 chat accordingly.
let chat = match is_partial_download.is_none()
&& mime_parser.get_header(HeaderDef::SecureJoin).is_none()
&& !is_mdn
{
true => Some(Chat::load_from_db(context, chat_id).await?)
.filter(|chat| chat.typ == Chattype::Single),
@@ -1030,8 +1022,11 @@ async fn add_parts(
}
}
state = if seen || fetching_existing_messages || is_mdn || chat_id_blocked == Blocked::Yes
// No check for `hidden` because only reactions are such and they should be `InFresh`.
state = if seen
|| fetching_existing_messages
|| is_mdn
|| is_reaction
|| chat_id_blocked == Blocked::Yes
{
MessageState::InSeen
} else {
@@ -1241,10 +1236,14 @@ async fn add_parts(
}
let orig_chat_id = chat_id;
let mut chat_id = chat_id.unwrap_or_else(|| {
info!(context, "No chat id for message (TRASH).");
let mut chat_id = if is_mdn || is_reaction {
DC_CHAT_ID_TRASH
});
} else {
chat_id.unwrap_or_else(|| {
info!(context, "No chat id for message (TRASH).");
DC_CHAT_ID_TRASH
})
};
// Extract ephemeral timer from the message or use the existing timer if the message is not fully downloaded.
let mut ephemeral_timer = if is_partial_download.is_some() {
@@ -1602,10 +1601,10 @@ RETURNING id
state,
is_dc_message,
if trash { "" } else { msg },
if trash || hidden { None } else { message::normalize_text(msg) },
if trash || hidden { "" } else { &subject },
if trash { None } else { message::normalize_text(msg) },
if trash { "" } else { &subject },
// txt_raw might contain invalid utf8
if trash || hidden { "" } else { &txt_raw },
if trash { "" } else { &txt_raw },
if trash {
"".to_string()
} else {
@@ -1621,7 +1620,7 @@ RETURNING id
mime_in_reply_to,
mime_references,
save_mime_modified,
if trash || hidden { "" } else { part.error.as_deref().unwrap_or_default() },
part.error.as_deref().unwrap_or_default(),
ephemeral_timer,
ephemeral_timestamp,
if is_partial_download.is_some() {
@@ -1631,7 +1630,7 @@ RETURNING id
} else {
DownloadState::Done
},
if trash || hidden { "" } else { &mime_parser.hop_info },
mime_parser.hop_info
],
|row| {
let msg_id: MsgId = row.get(0)?;
@@ -1695,7 +1694,7 @@ RETURNING id
"Message has {icnt} parts and is assigned to chat #{chat_id}."
);
if !chat_id.is_trash() {
if !is_mdn {
let mut chat = Chat::load_from_db(context, chat_id).await?;
// In contrast to most other update-timestamps,
@@ -1733,7 +1732,6 @@ RETURNING id
Ok(ReceivedMsg {
chat_id,
state,
hidden,
sort_timestamp,
msg_ids: created_db_entries,
needs_delete_job,

View File

@@ -606,19 +606,6 @@ impl TestContext {
msg
}
/// Receive a message using the `receive_imf()` pipeline. Panics if it's not hidden.
pub async fn recv_msg_hidden(&self, msg: &SentMessage<'_>) -> Message {
let received = self
.recv_msg_opt(msg)
.await
.expect("receive_imf() seems not to have added a new message to the db");
let msg = Message::load_from_db(self, *received.msg_ids.last().unwrap())
.await
.unwrap();
assert!(msg.hidden);
msg
}
/// Receive a message using the `receive_imf()` pipeline. This is similar
/// to `recv_msg()`, but doesn't assume that the message is shown in the chat.
pub async fn recv_msg_opt(