From 6dec989e9690c2f00f5e17c147be951c15f1fdb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Sun, 20 Sep 2026 23:12:27 +0200 Subject: [PATCH] [Filter] Restore exact, case-insensitive set code search (#7332, #7333) --- .../libcockatrice/filters/filter_string.cpp | 13 +++- tests/carddatabase/filter_string_test.cpp | 63 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/libcockatrice_filters/libcockatrice/filters/filter_string.cpp b/libcockatrice_filters/libcockatrice/filters/filter_string.cpp index aaf391c03..26415ef38 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_string.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_string.cpp @@ -20,7 +20,7 @@ SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart QueryPart <- NotQuery / SetQuery / RarityQuery / CMCQuery / FormatQuery / PowerQuery / ToughnessQuery / ColorQuery / TypeQuery / OracleQuery / FieldQuery / GenericQuery NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart -SetQuery <- ('e'/'set') SetExpression / ([:] FlexStringValue) +SetQuery <- ('e'/'set') ([=:] FlexStringValue) / ('e'/'set') (<[!][=]?> FlexStringValue) / ('e'/'set') SetExpression OracleQuery <- 'o' [:] MatcherString @@ -104,7 +104,7 @@ static void setupParserRules() return [=](const CardData &x) -> bool { return matcher(x->getCardType()); }; }; search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter { - if (sv.choice() == 1) { + if (sv.choice() == 0) { auto matcher = std::any_cast(sv[0]); return [=](const CardData &x) -> bool { QList sets = x->getSets().keys(); @@ -113,6 +113,15 @@ static void setupParserRules() return std::any_of(sets.begin(), sets.end(), matchesSet); }; } + if (sv.choice() == 1) { + auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { + QList sets = x->getSets().keys(); + + auto matchesSet = [&matcher](const QString &set) { return matcher(set); }; + return std::none_of(sets.begin(), sets.end(), matchesSet); + }; + } auto matcher = std::any_cast(sv[0]); return [=](const CardData &x) -> bool { diff --git a/tests/carddatabase/filter_string_test.cpp b/tests/carddatabase/filter_string_test.cpp index c6d68be1f..56f33825c 100644 --- a/tests/carddatabase/filter_string_test.cpp +++ b/tests/carddatabase/filter_string_test.cpp @@ -2,6 +2,8 @@ #include "test_card_database_path_provider.h" #include "gtest/gtest.h" +#include +#include #include #include #include @@ -71,10 +73,71 @@ QUERY(Color2, cat, "c:gw", true) QUERY(Color3, cat, "c!g", true) QUERY(Color4, cat, "c!gw", false) +QUERY(SetCodeCaseInsensitive1, cat, "set:cat", true) +QUERY(SetCodeCaseInsensitive2, cat, "set:CAT", true) +QUERY(SetCodeCaseInsensitive3, cat, "set:CAt", true) +QUERY(SetCodeShortForm, cat, "e:cat", true) +QUERY(SetCodeWrongSet, cat, "set:who", false) +QUERY(SetCodeWrongSet2, doctor, "set:cat", false) + QUERY(BracketNextToUnquotedString, cat, "(o:woof OR o:meow)", true) } // namespace +class SetQuery : public ::testing::Test +{ +protected: + void SetUp() override + { + // EOE and EOC share a release date, like a set and its Commander counterpart. + const QDate sharedReleaseDate(2026, 3, 13); + mainSet = CardSet::newInstance(&controller, "EOE", "Edge of Eternities", "expansion", sharedReleaseDate, + CardSet::PriorityPrimary); + commanderSet = CardSet::newInstance(&controller, "EOC", "Edge of Eternities Commander", "commander", + sharedReleaseDate, CardSet::PrioritySecondary); + + inBothSets = newCardWithPrintings({{"EOE", mainSet}, {"EOC", commanderSet}}); + onlyInCommanderSet = newCardWithPrintings({{"EOC", commanderSet}}); + } + + CardInfoPtr newCardWithPrintings(const QList> &printings) + { + SetToPrintingsMap setsInfo; + for (const auto &printing : printings) { + setsInfo[printing.first].append(PrintingInfo(printing.second)); + } + return CardInfo::newInstance("Test Card", "", false, {}, {}, {}, setsInfo, CardInfo::UiAttributes()); + } + + NoopCardSetPriorityController controller; + CardSetPtr mainSet; + CardSetPtr commanderSet; + CardInfoPtr inBothSets; + CardInfoPtr onlyInCommanderSet; +}; + +TEST_F(SetQuery, ExactMatchSeparatesSameDaySets) +{ + EXPECT_TRUE(FilterString("set:EOE").check(inBothSets)); + EXPECT_TRUE(FilterString("e:EOE").check(inBothSets)); + EXPECT_TRUE(FilterString("set:EOC").check(inBothSets)); + EXPECT_FALSE(FilterString("set:EOE").check(onlyInCommanderSet)); + EXPECT_TRUE(FilterString("set:EOC").check(onlyInCommanderSet)); +} + +TEST_F(SetQuery, ExactMatchIsCaseInsensitive) +{ + EXPECT_TRUE(FilterString("set:eoe").check(inBothSets)); + EXPECT_TRUE(FilterString("set:EoE").check(inBothSets)); + EXPECT_FALSE(FilterString("set:eoe").check(onlyInCommanderSet)); +} + +TEST_F(SetQuery, NotEqualsMatchesPrintingsOutsideTheSet) +{ + EXPECT_FALSE(FilterString("set!EOE").check(inBothSets)); + EXPECT_TRUE(FilterString("set!EOE").check(onlyInCommanderSet)); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv);