This commit is contained in:
Pedro Schreiber 2025-09-17 15:23:46 +08:00 committed by GitHub
commit d5de0a5ebb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 65 additions and 4 deletions

View file

@ -184,17 +184,34 @@ impl CardQueues {
}
}
/// Return the current due counts. If there are no due cards, the learning
/// cutoff is updated to the current time first, and any newly-due learning
/// cards are added to the counts.
/// Return the current due counts. If there are no due cards or if learning
/// cards have become due since the last cutoff update, the learning cutoff
/// is updated to the current time first, and any newly-due learning cards
/// are added to the counts.
pub(crate) fn counts(&mut self) -> Counts {
if self.counts.all_zero() {
if self.counts.all_zero() || self.has_newly_due_learning_cards() {
// we discard the returned undo information in this case
self.update_learning_cutoff_and_count();
}
self.counts
}
/// Check if any learning cards have become due since the last cutoff
/// update.
fn has_newly_due_learning_cards(&self) -> bool {
let current_cutoff = self.current_learning_cutoff;
let now = TimestampSecs::now();
if now <= current_cutoff {
return false;
}
let new_ahead_cutoff = now.adding_secs(self.learn_ahead_secs);
self.intraday_learning
.iter()
.any(|e| e.due > current_cutoff && e.due <= new_ahead_cutoff)
}
fn is_stale(&self, current_day: u32) -> bool {
self.current_day != current_day
}

View file

@ -290,4 +290,48 @@ mod test {
Ok(())
}
#[test]
fn learning_cards_become_due_after_counts_cached() -> Result<()> {
use crate::scheduler::queue::learning::LearningQueueEntry;
let mut col = Collection::new();
if col.timing_today()?.near_cutoff() {
return Ok(());
}
// Add a note with learning cards
add_note(&mut col, true)?;
// Answer to put a card into learning state
col.answer_again();
assert_eq!(col.counts(), [1, 1, 0]);
// Get the current queues to cache the counts
let queues = col.get_queues()?;
let old_cutoff = queues.current_learning_cutoff;
// Manually add a learning card that would be due now but wasn't
// when the cutoff was set (simulating time passing)
let now = crate::timestamp::TimestampSecs::now();
let new_entry = LearningQueueEntry {
due: now, // due right now
id: CardId(999), // fake ID
mtime: now,
};
// Insert the entry directly into the queue to simulate the bug scenario
let queues = col.state.card_queues.as_mut().unwrap();
queues.intraday_learning.push_back(new_entry);
// The old logic would not detect this newly due card because
// counts() only checked all_zero(), but our fix should detect it
let _updated_counts = queues.counts();
// The important thing is that update_learning_cutoff_and_count was called,
// which our fix should trigger. This updates the cutoff to the current time.
assert!(queues.current_learning_cutoff >= old_cutoff);
Ok(())
}
}