diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-31 12:14:12 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-31 12:14:12 +0100 |
| commit | a30b4c2f2c0b11b7484ae91574755deb5b9a5be9 (patch) | |
| tree | 709b9adb5449bbabba897916a74fdc7d52145be4 /tests/test_mainwindow.cpp | |
| parent | b1f6660b9f1471659ead81cf527d13d93dd57146 (diff) | |
| download | qtmaildir-a30b4c2f2c0b11b7484ae91574755deb5b9a5be9.tar.gz qtmaildir-a30b4c2f2c0b11b7484ae91574755deb5b9a5be9.zip | |
fix: credit a tag confirmation to the command whose tags it names
onTagsApplied() handed each confirmation to the head of the waiting
queue. The worker stays silent for a write that moved nothing, so when
one operation of a rule found nothing to do (another writer got there
between the count and the write), the next operation's ids were
credited to it, and its undo inverted the wrong tag on the wrong
messages: item 176 through a race.
A confirmation now goes to the first waiting command whose add and
remove sets equal the change's, and the commands ahead of it are
dropped with no ids, so their undo stays a no-op. Equal-tag commands,
the two halves of a mixed selection, still match in send order. No
match credits nobody. TagCommand carries its own add and remove to make
the comparison.
Items 176 and 212.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_mainwindow.cpp')
| -rw-r--r-- | tests/test_mainwindow.cpp | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index 4887647..918fe00 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -299,6 +299,7 @@ private slots: void aWorkerBackedWindowReturnsRealThreads(); void aWriteSaysItIsApplyingUntilTheWorkerHasRunIt(); void applyingARuleConfirmsWithTheCountAndUndoesExactly(); + void aSilentWriteDoesNotStealTheNextConfirmation(); void aPurgeTakesTheRowsOutOfTheViewWithoutARefresh(); void theDashboardFollowsAWriteToTheConversationItShows(); void anEditHeldByASyncSaysSoInsteadOfClaimingItLanded(); @@ -10020,6 +10021,35 @@ void TestMainWindow::applyingARuleConfirmsWithTheCountAndUndoesExactly() QStringLiteral("1")); } +void TestMainWindow::aSilentWriteDoesNotStealTheNextConfirmation() +{ + // Items 176 and 212. A rule is written one tag at a time, each operation + // its own armed command. If the first moves nothing (another writer got + // there between the count and the write), the worker stays silent for it, + // and the second operation's confirmation must not be credited to the + // first: its undo would then invert `+x` on messages that only lost `y`. + const Config config; + MainWindow window(config); + + const QList<const TagCommand *> commands = window.armTagCommandsForTesting( + { { { QStringLiteral("x") }, {} }, + { {}, { QStringLiteral("y") } } }); + QCOMPARE(commands.size(), 2); + + // Only the SECOND operation's confirmation arrives. + TagChange change; + change.messageIds = { QStringLiteral("moved@example.org") }; + change.removed = { QStringLiteral("y") }; + change.description = QStringLiteral("Apply rule"); + QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied", + Q_ARG(TagChange, change))); + + QVERIFY2(commands.at(0)->changedMessageIds().isEmpty(), + qPrintable(commands.at(0)->changedMessageIds().join(u','))); + QCOMPARE(commands.at(1)->changedMessageIds(), + QStringList{ QStringLiteral("moved@example.org") }); +} + /// Item 181, from the user: "the thread dashboard doesn't update live with the /// modifications applied to the list pane. If I mark the thread as read, the /// dash still reports N unread". |
