diff --git a/src/context.rs b/src/context.rs index 6616e3fdd..d97d1684e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -541,18 +541,10 @@ impl Context { } // update quota (to send warning if full) - but only check it once in a while - let quota_needs_update = { - let quota = self.quota.read().await; - quota - .as_ref() - .filter(|quota| { - time_elapsed("a.modified) - < Duration::from_secs(DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT) - }) - .is_none() - }; - - if quota_needs_update { + if self + .quota_needs_update(DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT) + .await + { if let Err(err) = self.update_recent_quota(&mut session).await { warn!(self, "Failed to update quota: {err:#}."); } diff --git a/src/quota.rs b/src/quota.rs index 620c3a5dc..d0cce66eb 100644 --- a/src/quota.rs +++ b/src/quota.rs @@ -1,6 +1,7 @@ //! # Support for IMAP QUOTA extension. use std::collections::BTreeMap; +use std::time::Duration; use anyhow::{anyhow, Context as _, Result}; use async_imap::types::{Quota, QuotaResource}; @@ -11,7 +12,7 @@ use crate::context::Context; use crate::imap::scan_folders::get_watched_folders; use crate::imap::session::Session as ImapSession; use crate::message::{Message, Viewtype}; -use crate::tools; +use crate::tools::{self, time_elapsed}; use crate::{stock_str, EventType}; /// warn about a nearly full mailbox after this usage percentage is reached. @@ -102,6 +103,16 @@ pub fn needs_quota_warning(curr_percentage: u64, warned_at_percentage: u64) -> b } impl Context { + /// Returns whether the quota value needs an update. If so, `update_recent_quota()` should be + /// called. + pub(crate) async fn quota_needs_update(&self, ratelimit_secs: u64) -> bool { + let quota = self.quota.read().await; + quota + .as_ref() + .filter(|quota| time_elapsed("a.modified) < Duration::from_secs(ratelimit_secs)) + .is_none() + } + /// Updates `quota.recent`, sets `quota.modified` to the current time /// and emits an event to let the UIs update connectivity view. /// @@ -155,6 +166,7 @@ impl Context { #[cfg(test)] mod tests { use super::*; + use crate::test_utils::TestContextManager; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_needs_quota_warning() -> Result<()> { @@ -183,4 +195,24 @@ mod tests { assert!(QUOTA_ERROR_THRESHOLD_PERCENTAGE < 100); Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_quota_needs_update() { + let mut tcm = TestContextManager::new(); + let t = &tcm.unconfigured().await; + const TIMEOUT: u64 = 60; + assert!(t.quota_needs_update(TIMEOUT).await); + + *t.quota.write().await = Some(QuotaInfo { + recent: Ok(Default::default()), + modified: tools::Time::now() - Duration::from_secs(TIMEOUT + 1), + }); + assert!(t.quota_needs_update(TIMEOUT).await); + + *t.quota.write().await = Some(QuotaInfo { + recent: Ok(Default::default()), + modified: tools::Time::now(), + }); + assert!(!t.quota_needs_update(TIMEOUT).await); + } } diff --git a/src/scheduler.rs b/src/scheduler.rs index 1e9d0bd60..d1eebbf4c 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -2,7 +2,6 @@ use std::cmp; use std::iter::{self, once}; use std::num::NonZeroUsize; use std::sync::atomic::Ordering; -use std::time::Duration; use anyhow::{bail, Context as _, Error, Result}; use async_channel::{self as channel, Receiver, Sender}; @@ -473,14 +472,7 @@ async fn inbox_fetch_idle(ctx: &Context, imap: &mut Imap, mut session: Session) .await?; // Update quota no more than once a minute. - let quota_needs_update = { - let quota = ctx.quota.read().await; - quota - .as_ref() - .filter(|quota| time_elapsed("a.modified) < Duration::from_secs(60)) - .is_none() - }; - if quota_needs_update { + if ctx.quota_needs_update(60).await { if let Err(err) = ctx.update_recent_quota(&mut session).await { warn!(ctx, "Failed to update quota: {:#}.", err); }