File indexing completed on 2024-04-21 03:53:39

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 #include "kfuzzymatchertest.h"
0009 
0010 #include <QString>
0011 #include <QStringList>
0012 #include <QTest>
0013 
0014 #include <algorithm>
0015 
0016 #include "kfuzzymatcher.h"
0017 
0018 QTEST_MAIN(KFuzzyMatcherTest)
0019 
0020 void KFuzzyMatcherTest::testMatchSimple_data()
0021 {
0022     QTest::addColumn<QString>("pattern");
0023     QTest::addColumn<QString>("inputstr");
0024     QTest::addColumn<bool>("expected");
0025 
0026     QTest::newRow("AbcD") << QStringLiteral("AbcD") << QStringLiteral("AbCdefg") << true;
0027     QTest::newRow("WithSpace") << QStringLiteral("Wa qa") << QStringLiteral("Wa qar") << true;
0028     QTest::newRow("RTL") << QStringLiteral("ارو") << QStringLiteral("اردو") << true;
0029     QTest::newRow("WithSep") << QStringLiteral("tf") << QStringLiteral("the_file") << true;
0030     QTest::newRow("Umlaut") << QStringLiteral("Häu") << QStringLiteral("Häuser") << true;
0031     QTest::newRow("Unmatched") << QStringLiteral("Name") << QStringLiteral("Nam") << false;
0032     QTest::newRow("Empty Pattern") << QStringLiteral("") << QStringLiteral("Nam") << true;
0033 }
0034 
0035 void KFuzzyMatcherTest::testMatchSimple()
0036 {
0037     QFETCH(QString, pattern);
0038     QFETCH(QString, inputstr);
0039     QFETCH(bool, expected);
0040 
0041     QVERIFY(KFuzzyMatcher::matchSimple(pattern, inputstr) == expected);
0042 }
0043 
0044 void KFuzzyMatcherTest::testMatch_data()
0045 {
0046     QTest::addColumn<QString>("pattern");
0047     QTest::addColumn<QStringList>("input");
0048     QTest::addColumn<QStringList>("expected");
0049     QTest::addColumn<int>("size");
0050     // clang-format off
0051     QTest::newRow("pattern=sort") << QStringLiteral("sort")
0052                           << QStringList{
0053                                 QStringLiteral("Sort"),
0054                                 QStringLiteral("Some other right test"),
0055                                 QStringLiteral("Soup rate"),
0056                                 QStringLiteral("Someother"),
0057                                 QStringLiteral("irrelevant"),
0058                               }
0059                           << QStringList{
0060                                 QStringLiteral("Sort"),
0061                                 QStringLiteral("Some other right test"),
0062                                 QStringLiteral("Soup rate"),
0063                               }
0064                           << 3;
0065 
0066 
0067     QTest::newRow("pattern=kateapp") << QStringLiteral("kaapp")
0068                           << QStringList{
0069                                 QStringLiteral("kateapp.cpp"),
0070                                 QStringLiteral("kate_application"),
0071                                 QStringLiteral("kateapp.h"),
0072                                 QStringLiteral("katepap.c")
0073                               }
0074                           << QStringList{
0075                                 QStringLiteral("kate_application"),
0076                                 QStringLiteral("kateapp.h"),
0077                                 QStringLiteral("kateapp.cpp")
0078                              }
0079                           << 3;
0080 
0081     QTest::newRow("pattern=this") << QStringLiteral("this")
0082                           << QStringList{
0083                                 QStringLiteral("th"),
0084                                 QStringLiteral("ths"),
0085                                 QStringLiteral("thsi")
0086                               }
0087                           << QStringList{
0088                              }
0089                           << 0;
0090 
0091     QTest::newRow("pattern=marath") << QStringLiteral("marath")
0092                           << QStringList{
0093                              QStringLiteral("Maralen of the Mornsong"),
0094                              QStringLiteral("Silumgar, the Drifting Death"),
0095                              QStringLiteral("Maralen of the Mornsong Avatar"),
0096                              QStringLiteral("Marshaling the Troops"),
0097                              QStringLiteral("Homeward Path"),
0098                              QStringLiteral("Marath, Will of the Wild"),
0099                              QStringLiteral("Marshal's Anthem"),
0100                              QStringLiteral("Marchesa, the Black Rose"),
0101                              QStringLiteral("Mark for Death"),
0102                              QStringLiteral("Master Apothecary"),
0103                              QStringLiteral("Mazirek, Kraul Death Priest"),
0104                              QStringLiteral("Akroma, Angel of Wrath"),
0105                              QStringLiteral("Akroma, Angel of Wrath Avatar"),
0106                              QStringLiteral("Commander's Authority"),
0107                              QStringLiteral("Shaman of the Great Hunt"),
0108                              QStringLiteral("Halimar Wavewatch"),
0109                              QStringLiteral("Pyromancer's Swath")
0110                               }
0111                           << QStringList{
0112                              QStringLiteral("Marath, Will of the Wild"),
0113                              QStringLiteral("Maralen of the Mornsong"),
0114                              QStringLiteral("Maralen of the Mornsong Avatar"),
0115                              QStringLiteral("Marshal's Anthem"),
0116                              QStringLiteral("Marshaling the Troops"),
0117                              QStringLiteral("Marchesa, the Black Rose"),
0118                              QStringLiteral("Mark for Death"),
0119                              QStringLiteral("Master Apothecary"),
0120                              QStringLiteral("Mazirek, Kraul Death Priest"),
0121                              QStringLiteral("Akroma, Angel of Wrath"),
0122                              QStringLiteral("Akroma, Angel of Wrath Avatar"),
0123                              QStringLiteral("Commander's Authority"),
0124                              QStringLiteral("Homeward Path"),
0125                              QStringLiteral("Shaman of the Great Hunt"),
0126                              QStringLiteral("Halimar Wavewatch"),
0127                              QStringLiteral("Pyromancer's Swath"),
0128                              QStringLiteral("Silumgar, the Drifting Death")
0129                              }
0130                           << 17;
0131 
0132     // This tests our recursive best match
0133     QTest::newRow("pattern=lll") << QStringLiteral("lll")
0134                           << QStringList{
0135                                 QStringLiteral("SVisualLoggerLogsList.h"),
0136                                 QStringLiteral("SimpleFileLogger.cpp"),
0137                                 QStringLiteral("StringHandlerLogList.txt"),
0138                                 QStringLiteral("LeapFromLostAllan"),
0139                                 QStringLiteral("BumpLLL"),
0140                               }
0141                           << QStringList{
0142                              QStringLiteral("SVisualLoggerLogsList.h"),
0143                              QStringLiteral("LeapFromLostAllan"),
0144                              QStringLiteral("BumpLLL"),
0145                              QStringLiteral("StringHandlerLogList.txt"),
0146                              QStringLiteral("SimpleFileLogger.cpp"),
0147                              }
0148                           << 5;
0149 
0150     QTest::newRow("pattern=") << QStringLiteral("")
0151                           << QStringList{
0152                                 QStringLiteral("th"),
0153                                 QStringLiteral("ths"),
0154                                 QStringLiteral("thsi")
0155                               }
0156                           << QStringList{
0157                              QStringLiteral("th"),
0158                              QStringLiteral("ths"),
0159                              QStringLiteral("thsi")
0160                              }
0161                           << 3;
0162     // clang-format on
0163 }
0164 
0165 static QStringList matchHelper(const QString &pattern, const QStringList &input)
0166 {
0167     QList<QPair<QString, int>> actual;
0168     for (int i = 0; i < input.size(); ++i) {
0169         KFuzzyMatcher::Result res = KFuzzyMatcher::match(pattern, input.at(i));
0170         if (res.matched) {
0171             actual.push_back({input.at(i), res.score});
0172         }
0173     }
0174 
0175     // sort descending based on score
0176     std::sort(actual.begin(), actual.end(), [](const QPair<QString, int> &l, const QPair<QString, int> &r) {
0177         return l.second > r.second;
0178     });
0179 
0180     QStringList actualOut;
0181     for (const auto &s : actual) {
0182         actualOut << s.first;
0183     }
0184     return actualOut;
0185 }
0186 
0187 void KFuzzyMatcherTest::testMatch()
0188 {
0189     QFETCH(QString, pattern);
0190     QFETCH(QStringList, input);
0191     QFETCH(QStringList, expected);
0192     QFETCH(int, size);
0193 
0194     const QStringList actual = matchHelper(pattern, input);
0195 
0196     QCOMPARE(actual.size(), size);
0197     QCOMPARE(actual, expected);
0198 }
0199 
0200 void KFuzzyMatcherTest::testMatchedRanges_data()
0201 {
0202     QTest::addColumn<QString>("pattern");
0203     QTest::addColumn<QString>("string");
0204 
0205     using Range = QPair<int, int>;
0206     QTest::addColumn<QList<Range>>("expectedRanges");
0207 
0208     QTest::addColumn<bool>("matchingOnly");
0209 
0210     QTest::newRow("Emtpy") << QStringLiteral("") << QStringLiteral("") << QList<Range>{} << true;
0211     QTest::newRow("Hello") << QStringLiteral("Hlo") << QStringLiteral("Hello") << QList<Range>{{0, 1}, {3, 2}} << true;
0212     QTest::newRow("lll") << QStringLiteral("lll") << QStringLiteral("SVisualLoggerLogsList") << QList<Range>{{7, 1}, {13, 1}, {17, 1}} << true;
0213     QTest::newRow("Sort") << QStringLiteral("sort") << QStringLiteral("SorT") << QList<Range>{{0, 4}} << true;
0214     QTest::newRow("Unmatching") << QStringLiteral("git") << QStringLiteral("gti") << QList<Range>{} << true;
0215     QTest::newRow("UnmatchingWithAllMatches") << QStringLiteral("git") << QStringLiteral("gti") << QList<Range>{{0, 1}, {2, 1}} << false;
0216 }
0217 
0218 void KFuzzyMatcherTest::testMatchedRanges()
0219 {
0220     QFETCH(QString, pattern);
0221     QFETCH(QString, string);
0222     QFETCH(bool, matchingOnly);
0223     using Range = QPair<int, int>;
0224     QFETCH(QList<Range>, expectedRanges);
0225 
0226     const auto matchMode = matchingOnly ? KFuzzyMatcher::RangeType::FullyMatched : KFuzzyMatcher::RangeType::All;
0227 
0228     auto resultRanges = KFuzzyMatcher::matchedRanges(pattern, string, matchMode);
0229     QCOMPARE(resultRanges.size(), expectedRanges.size());
0230 
0231     bool res = std::equal(expectedRanges.begin(), expectedRanges.end(), resultRanges.begin(), [](const Range &l, const KFuzzyMatcher::Range &r) {
0232         return l.first == r.start && l.second == r.length;
0233     });
0234     QVERIFY(res);
0235 }
0236 
0237 #include "moc_kfuzzymatchertest.cpp"