aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_searchterm.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-14 17:58:22 +0200
committerDanilo M. <danix@danix.xyz>2026-08-14 17:58:22 +0200
commit7781f437a05b4e860353c012cb2f31860fd48cf6 (patch)
tree57b1bf1d6006127bf02bb41f40ae3ff2afe16125 /tests/test_searchterm.cpp
parentdb770e8d77a1b2482249e3315a227b9b017ab3f1 (diff)
downloadqtmaildir-7781f437a05b4e860353c012cb2f31860fd48cf6.tar.gz
qtmaildir-7781f437a05b4e860353c012cb2f31860fd48cf6.zip
feat(search): add SearchTerm::exclude
Parenthesises both sides, as extend() does: unparenthesised, a disjunction in the query bar binds so the exclusion covers only its last term and leaves the excluded mail on screen, with nothing reporting an error. An empty existing query returns empty rather than the addition alone, which is where this deliberately differs from extend(). Excluding from nothing means the whole Maildir minus one value; the menus will grey the entry out and this is the second layer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_searchterm.cpp')
-rw-r--r--tests/test_searchterm.cpp39
1 files changed, 39 insertions, 0 deletions
diff --git a/tests/test_searchterm.cpp b/tests/test_searchterm.cpp
index e0bdb60..53185f3 100644
--- a/tests/test_searchterm.cpp
+++ b/tests/test_searchterm.cpp
@@ -41,6 +41,9 @@ private slots:
void tagIsNotQuoted();
void extendParenthesisesBothSides();
void extendOntoAnEmptyQueryIsAReplace();
+ void excludeParenthesisesBothSides();
+ void excludeFromAnEmptyQueryIsEmpty();
+ void excludeWithNothingToExcludeLeavesTheQuery();
};
void TestSearchTerm::quotesAPlainValue()
@@ -142,5 +145,41 @@ void TestSearchTerm::extendOntoAnEmptyQueryIsAReplace()
QStringLiteral("tag:inbox"));
}
+void TestSearchTerm::excludeParenthesisesBothSides()
+{
+ // The same trap as extendParenthesisesBothSides, and worse in this
+ // direction. Unparenthesised, `a or b AND NOT c` binds as
+ // `a or (b AND NOT c)`: the exclusion covers only the second term, so
+ // every message matching `a` stays on screen INCLUDING the ones the user
+ // asked to be rid of. notmuch reports no error for either form, so this
+ // assertion is the only thing that fails.
+ QCOMPARE(SearchTerm::exclude(QStringLiteral("tag:inbox or tag:flagged"),
+ QStringLiteral("from:foo@example.org")),
+ QStringLiteral(
+ "(tag:inbox or tag:flagged) AND NOT (from:foo@example.org)"));
+}
+
+void TestSearchTerm::excludeFromAnEmptyQueryIsEmpty()
+{
+ // Deliberately NOT extend()'s behaviour. extend() returns the addition
+ // alone, because narrowing nothing by x sensibly means x. Excluding from
+ // nothing would mean the whole Maildir minus one value: a legitimate
+ // query, and an implausible thing to have meant by right-clicking a value
+ // in a fresh window. The menus grey the entry out; this is the second
+ // layer, against a caller that forgets the guard.
+ QVERIFY(SearchTerm::exclude(QString(), QStringLiteral("tag:inbox"))
+ .isEmpty());
+ QVERIFY(SearchTerm::exclude(QStringLiteral(" "),
+ QStringLiteral("tag:inbox"))
+ .isEmpty());
+}
+
+void TestSearchTerm::excludeWithNothingToExcludeLeavesTheQuery()
+{
+ QCOMPARE(SearchTerm::exclude(QStringLiteral("tag:inbox"), QString()),
+ QStringLiteral("tag:inbox"));
+ QVERIFY(SearchTerm::exclude(QString(), QString()).isEmpty());
+}
+
QTEST_MAIN(TestSearchTerm)
#include "test_searchterm.moc"