aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mainwindow.cpp21
-rw-r--r--src/mainwindow.h5
-rw-r--r--tests/test_mainwindow.cpp50
3 files changed, 49 insertions, 27 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index c62013d..59edf53 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -4312,7 +4312,6 @@ void MainWindow::onSyncFinished(bool success, int exitCode)
// assert the edits had reached the mail store when the sync is exactly
// what failed to put them there.
m_pendingTagEdits.clear();
- m_unnettablePendingEdits = 0;
// Only what this run actually carried, per the snapshot above. An
// account added by flushHeldEdits() stays, because its edit reaches the
@@ -4446,14 +4445,6 @@ void MainWindow::onTagsApplied(const TagChange &change)
recordPendingEdit(messageId, tag, false);
}
- // A change carrying no message ids cannot be netted against anything, and
- // must still register: losing an edit understates the indicator, which is
- // the direction that costs the user work.
- if (change.messageIds.isEmpty()
- && !(change.added.isEmpty() && change.removed.isEmpty())) {
- ++m_unnettablePendingEdits;
- }
-
updatePendingIndicator();
// Item 71. Armed here, where a write is CONFIRMED and the pending count is
@@ -4808,7 +4799,6 @@ void MainWindow::onExternalSyncStateChanged(SyncMonitor::State state)
// is the absence of evidence rather than evidence of success.
if (MailSync::lastRunOutcome(m_config.syncLog()) == SyncOutcome::Ok) {
m_pendingTagEdits.clear();
- m_unnettablePendingEdits = 0;
// Cleared HERE, before flushHeldEdits() below, and the ordering is
// load-bearing for the reason spelled out on the local path at
@@ -5051,6 +5041,14 @@ int MainWindow::pendingEditCount() const
// an edit waiting on a lock is precisely the work quitting would lose.
// Each held edit counts as one whatever its size, since it carries thread
// ids rather than message ids and cannot be netted against the map.
+ //
+ // There is no fourth term. A counter for confirmed changes carrying no
+ // message ids stood here until item 119 looked for what it held and found
+ // nothing: NotmuchWorker::applyTags() is the only emitter of tagsApplied()
+ // and returns early on an empty id list, so the change that counter
+ // existed for cannot reach this window. Every pending change can name the
+ // messages it touches, which is what lets the indicator be opened and
+ // listed in full.
const int held = int(m_heldEdits.size());
// Held MOVES count for exactly the same reason, and were missed. With no
// tag edit queued the count was 0, so the indicator stayed hidden and
@@ -5059,8 +5057,7 @@ int MainWindow::pendingEditCount() const
// is item 106's data loss, and worse here, because a dropped move leaves
// the file in the folder the user asked it out of.
const int heldMoves = int(m_heldMoves.size());
- return m_pendingTagEdits.size() + m_unnettablePendingEdits + held
- + heldMoves;
+ return m_pendingTagEdits.size() + held + heldMoves;
}
void MainWindow::updatePendingIndicator()
diff --git a/src/mainwindow.h b/src/mainwindow.h
index e65c07d..25a9a7f 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -1541,11 +1541,6 @@ private:
/// behind and the map cannot grow without bound.
QHash<QString, bool> m_pendingTagEdits;
- /// Confirmed changes carrying no message ids, which cannot be netted
- /// against anything. Counted separately rather than dropped: understating
- /// the indicator is the direction that costs the user work.
- int m_unnettablePendingEdits = 0;
-
/// Marks the open thread read once it has been on screen long enough.
///
/// Single-shot and RESTARTED on every selection change, never stacked:
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp
index 5d2316f..a120ce6 100644
--- a/tests/test_mainwindow.cpp
+++ b/tests/test_mainwindow.cpp
@@ -415,7 +415,7 @@ private slots:
void theSelectionCountIsStateAndDoesNotExpire();
void anEditUndoneNettsBackToZero();
void aDifferentTagOnTheSameMessageStillCounts();
- void anEditWithNoMessageIdsStillCounts();
+ void everyPendingChangeCanNameItsMessages();
void anEditDuringABackgroundSyncIsNotSentYet();
void aHeldEditIsSentWhenTheBackgroundSyncEnds();
void aHeldEditCountsAsUnsynced();
@@ -2239,6 +2239,9 @@ void TestMainWindow::pendingEditCountSurvivesAQuery()
// Confirm a write the way the worker really does, by emitting the signal
// the window listens to. No test-only entry point on MainWindow.
TagChange change;
+ // A real edit always names the messages it touched: the worker's only
+ // emitter of tagsApplied() returns early without them (item 119).
+ change.messageIds = { QStringLiteral("pq1@example.org") };
change.added = { QStringLiteral("deleted") };
change.description = QStringLiteral("Delete");
QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied",
@@ -2271,6 +2274,8 @@ void TestMainWindow::aFailedSyncDoesNotClearThePendingCount()
QVERIFY(label);
TagChange change;
+ // See item 119: a change with no message ids never reaches this slot.
+ change.messageIds = { QStringLiteral("fs1@example.org") };
change.added = { QStringLiteral("flagged") };
QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied",
Q_ARG(TagChange, change)));
@@ -2362,6 +2367,8 @@ void TestMainWindow::syncOnExitNeverClosesSilently()
// Give it something to lose, so this is not passing for the same reason
// the previous test does.
TagChange change;
+ // See item 119: a change with no message ids never reaches this slot.
+ change.messageIds = { QStringLiteral("se1@example.org") };
change.added = { QStringLiteral("deleted") };
QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied",
Q_ARG(TagChange, change)));
@@ -6437,26 +6444,49 @@ void TestMainWindow::aDifferentTagOnTheSameMessageStillCounts()
"two different tags on one message cancelled each other");
}
-void TestMainWindow::anEditWithNoMessageIdsStillCounts()
+void TestMainWindow::everyPendingChangeCanNameItsMessages()
{
- // A TagChange carrying no message ids cannot be netted against anything,
- // and must still register rather than silently counting as zero. Losing an
- // edit understates the indicator, which is the direction that costs the
- // user work.
+ // Item 119. The count summed a fourth term, a bare int for confirmed
+ // changes carrying no message ids, and that term is what made the count
+ // impossible to open and list: three groups could name what they held and
+ // the remainder could not.
+ //
+ // The remainder was empty. NotmuchWorker::applyTags() is the only emitter
+ // of tagsApplied() and returns early on an empty id list, which is the
+ // exact condition the counter required, so the change it existed for
+ // cannot reach this window. Measured before removing it: a qFatal in the
+ // branch fired in 4 of 70 cases here, all four invoking the slot directly
+ // with a hand-built TagChange, and an assertion before the worker's emit
+ // never fired across the whole suite.
+ //
+ // The guard itself is pinned in test_notmuchworker by
+ // applyTagsWithNoIdsDoesNothing(), which is where it lives. What this test
+ // holds is the consequence: a change that reaches the indicator names its
+ // messages, so the indicator can be listed in full.
const Config config;
MainWindow window(config);
auto *label = window.findChild<QLabel *>(QStringLiteral("pendingEdits"));
QVERIFY(label);
+ QVERIFY(label->isHidden());
+ // A change with ids counts, and the count is the number of (message, tag)
+ // pairs it carries rather than one per signal.
TagChange change;
- change.added = { QStringLiteral("deleted") };
- change.description = QStringLiteral("Delete");
+ change.messageIds = { QStringLiteral("a@example.org"),
+ QStringLiteral("b@example.org") };
+ change.added = { QStringLiteral("flagged") };
+ change.description = QStringLiteral("Mark important");
QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied",
Q_ARG(TagChange, change)));
+ QVERIFY(!label->isHidden());
- QVERIFY2(!label->isHidden(),
- "an edit with no message ids was not counted at all");
+ // And its inverse nets it back to nothing, which is the property the
+ // fourth term could never have: an unnettable count only ever grew.
+ QVERIFY(QMetaObject::invokeMethod(&window, "onTagsApplied",
+ Q_ARG(TagChange, change.inverted())));
+ QVERIFY2(label->isHidden(),
+ "an edit and its inverse left the indicator claiming work");
}
// Item 37. A tag edit made while a background sync holds notmuch's write lock