File indexing completed on 2024-05-12 05:46:39

0001 /*
0002     Copyright (C) 2004, Arend van Beelen jr. <arend@auton.nl>
0003     Copyright (C) 2010, David Faure <faure@kde.org>
0004 
0005     This file is part of the KDE project
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License version 2, as published by the Free Software Foundation.
0010 
0011     This library is distributed in the hope that it will be useful,
0012     but WITHOUT ANY WARRANTY; without even the implied warranty of
0013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014     Library General Public License for more details.
0015 
0016     You should have received a copy of the GNU Library General Public License
0017     along with this library; see the file COPYING.LGPL-2.  If not, write to
0018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019     Boston, MA 02110-1301, USA.
0020 */
0021 #include "kfindtest.h"
0022 
0023 #include <QTest>
0024 
0025 #include <kfind.h>
0026 
0027 #include <assert.h>
0028 
0029 void KFindRecorder::changeText(int line, const QString &text)
0030 {
0031     Q_ASSERT(line < m_text.count());
0032     Q_ASSERT(m_find != nullptr);
0033 
0034     m_line = line;
0035     m_text[line] = text;
0036     m_find->setData(line, text);
0037 }
0038 
0039 void KFindRecorder::find(const QString &pattern, long options)
0040 {
0041     delete m_find;
0042     m_find = new KFind(pattern, options, nullptr);
0043     // Prevent dialogs from popping up
0044     m_find->closeFindNextDialog();
0045 
0046     connect(m_find, SIGNAL(highlight(QString,int,int)),
0047             SLOT(slotHighlight(QString,int,int)));
0048     connect(m_find, SIGNAL(highlight(int,int,int)),
0049             SLOT(slotHighlight(int,int,int)));
0050 
0051     m_line = 0;
0052     KFind::Result result = KFind::NoMatch;
0053     do {
0054         if (options & KFind::FindIncremental) {
0055             m_find->setData(m_line, m_text[m_line]);
0056         } else {
0057             m_find->setData(m_text[m_line]);
0058         }
0059 
0060         m_line++;
0061 
0062         result = m_find->find();
0063     } while (result == KFind::NoMatch && m_line < m_text.count());
0064 }
0065 
0066 bool KFindRecorder::findNext(const QString &pattern)
0067 {
0068     Q_ASSERT(m_find != nullptr);
0069 
0070     if (!pattern.isNull()) {
0071         m_find->setPattern(pattern);
0072     }
0073 
0074     KFind::Result result = KFind::NoMatch;
0075     do {
0076         //qDebug() << "m_line: " << m_line;
0077 
0078         result = m_find->find();
0079 
0080         if (result == KFind::NoMatch && m_line < m_text.count()) {
0081             //qDebug() << "incrementing m_line...";
0082             if (m_find->options() & KFind::FindIncremental) {
0083                 m_find->setData(m_line, m_text[m_line]);
0084             } else {
0085                 m_find->setData(m_text[m_line]);
0086             }
0087 
0088             m_line++;
0089         }
0090     } while (result == KFind::NoMatch && m_line < m_text.count());
0091     //qDebug() << "find next completed" << m_line;
0092 
0093     return result != KFind::NoMatch;
0094 }
0095 
0096 void KFindRecorder::slotHighlight(const QString &text, int index, int matchedLength)
0097 {
0098     m_hits.append(QLatin1String("line: \"") + text + QLatin1String("\", index: ") + QString::number(index) +
0099                   QLatin1String(", length: ") + QString::number(matchedLength) + QLatin1Char('\n'));
0100 }
0101 
0102 void KFindRecorder::slotHighlight(int id, int index, int matchedLength)
0103 {
0104     m_hits.append(QLatin1String("line: \"") + m_text[id] + QLatin1String("\", index: ") + QString::number(index) +
0105                   QLatin1String(", length: ") + QString::number(matchedLength) + QLatin1Char('\n'));
0106 }
0107 
0108 ////
0109 
0110 TestKFind::TestKFind()
0111     : QObject()
0112 {
0113     m_text = QLatin1String("This file is part of the KDE project.\n") +
0114              QLatin1String("This library is free software; you can redistribute it and/or\n") +
0115              QLatin1String("modify it under the terms of the GNU Library General Public\n") +
0116              QLatin1String("License version 2, as published by the Free Software Foundation.\n") +
0117              QLatin1Char('\n') +
0118              QLatin1String("    This library is distributed in the hope that it will be useful,\n") +
0119              QLatin1String("    but WITHOUT ANY WARRANTY; without even the implied warranty of\n") +
0120              QLatin1String("    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n") +
0121              QLatin1String("    Library General Public License for more details.\n") +
0122              QLatin1Char('\n') +
0123              QLatin1String("    You should have received a copy of the GNU Library General Public License\n") +
0124              QLatin1String("    along with this library; see the file COPYING.LIB.  If not, write to\n") +
0125              QLatin1String("    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n") +
0126              QLatin1String("    Boston, MA 02110-1301, USA.\n");
0127 }
0128 
0129 void TestKFind::testStaticFindString_data()
0130 {
0131     // Tests for the core method "static KFind::find"
0132     QTest::addColumn<QString>("text");
0133     QTest::addColumn<QString>("pattern");
0134     QTest::addColumn<int>("startIndex");
0135     QTest::addColumn<int>("options");
0136     QTest::addColumn<int>("expectedResult");
0137     QTest::addColumn<int>("expectedMatchedLength");
0138 
0139     QTest::newRow("simple (0)") << "abc" << "a" << 0 << 0 << 0 << 1;
0140     QTest::newRow("simple (1)") << "abc" << "b" << 0 << 0 << 1 << 1;
0141     QTest::newRow("not found") << "abca" << "ba" << 0 << 0 << -1 << 0;
0142     QTest::newRow("from index") << "abc bc" << "b" << 3 << 0 << 4 << 1;
0143     QTest::newRow("from exact index") << "abc bc" << "b" << 4 << 0 << 4 << 1;
0144     QTest::newRow("past index (not found)") << "abc bc" << "b" << 5 << 0 << -1 << 0;
0145     QTest::newRow("dot") << "ab." << "b." << 0 << 0 << 1 << 2;
0146     QTest::newRow("^should fail") << "text" << "^tex" << 0 << 0 << -1 << 0;
0147     QTest::newRow("multiline with \\n") << "foo\nbar" << "o\nb" << 0 << 0 << 2 << 3;
0148     QTest::newRow("whole words ok") << "abc bcbc bc bmore be" << "bc" << 0 << int(KFind::WholeWordsOnly) << 9 << 2;
0149     QTest::newRow("whole words not found") << "abab abx" << "ab" << 0 << int(KFind::WholeWordsOnly) << -1 << 0;
0150     QTest::newRow("whole words not found (_)") << "abab ab_" << "ab" << 0 << int(KFind::WholeWordsOnly) << -1 << 0;
0151     QTest::newRow("whole words ok (.)") << "ab." << "ab" << 0 << int(KFind::WholeWordsOnly) << 0 << 2;
0152     QTest::newRow("backwards") << "abc bcbc8bc" << "bc" << 10 << int(KFind::FindBackwards) << 9 << 2;
0153     QTest::newRow("backwards again") << "abc bcbc8bc" << "bc" << 8 << int(KFind::FindBackwards) << 6 << 2;
0154     QTest::newRow("backwards 2") << "abc bcbc8bc" << "bc" << 5 << int(KFind::FindBackwards) << 4 << 2;
0155     QTest::newRow("backwards 3") << "abc bcbc8bc" << "bc" << 3 << int(KFind::FindBackwards) << 1 << 2;
0156     QTest::newRow("empty (0)") << "a" << "" << 0 << int(0) << 0 << 0;
0157     QTest::newRow("empty (1)") << "a" << "" << 1 << int(0) << 1 << 0; // kreplacetest testReplaceBlankSearch relies on this
0158     QTest::newRow("at end, not found") << "a" << "b" << 1 << int(0) << -1 << 0; // just for catching the while(index<text.length()) bug
0159     QTest::newRow("back, not found") << "a" << "b" << 0 << int(KFind::FindBackwards) << -1 << 0;
0160     QTest::newRow("back, at begin, found") << "a" << "a" << 0 << int(KFind::FindBackwards) << 0 << 1;
0161     QTest::newRow("back, at end, found") << "a" << "a" << 1 << int(KFind::FindBackwards) << 0 << 1;
0162     QTest::newRow("back, text shorter than pattern") << "a" << "abcd" << 0 << int(KFind::FindBackwards) << -1 << 0;
0163 }
0164 
0165 void TestKFind::testStaticFindString()
0166 {
0167     // Tests for the core method "static KFind::find(text, regexp)"
0168     QFETCH(QString, text);
0169     QFETCH(QString, pattern);
0170     QFETCH(int, startIndex);
0171     QFETCH(int, options);
0172     QFETCH(int, expectedResult);
0173     QFETCH(int, expectedMatchedLength);
0174 
0175     int matchedLength;
0176     const int result = KFind::find(text, pattern, startIndex, options, &matchedLength);
0177     QCOMPARE(result, expectedResult);
0178     QCOMPARE(matchedLength, expectedMatchedLength);
0179 }
0180 
0181 void TestKFind::testStaticFindRegexp_data()
0182 {
0183     // Tests for the core method "static KFind::find"
0184     QTest::addColumn<QString>("text");
0185     QTest::addColumn<QString>("pattern");
0186     QTest::addColumn<int>("startIndex");
0187     QTest::addColumn<int>("options");
0188     QTest::addColumn<int>("expectedResult");
0189     QTest::addColumn<int>("expectedMatchedLength");
0190 
0191     QTest::newRow("simple (0)") << "abc" << "a" << 0 << 0 << 0 << 1;
0192     QTest::newRow("simple (1)") << "abc" << "b" << 0 << 0 << 1 << 1;
0193     QTest::newRow("not found") << "abca" << "ba" << 0 << 0 << -1 << 0;
0194     QTest::newRow("from index") << "abc bc" << "b" << 3 << 0 << 4 << 1;
0195     QTest::newRow("from exact index") << "abc bc" << "b" << 4 << 0 << 4 << 1;
0196     QTest::newRow("past index (not found)") << "abc bc" << "b" << 5 << 0 << -1 << 0;
0197     QTest::newRow("dot") << "abc" << "b." << 0 << 0 << 1 << 2;
0198     QTest::newRow("^simple") << "text" << "^tex" << 0 << 0 << 0 << 3;
0199     QTest::newRow("^multiline first") << "foo\nbar" << "^f" << 0 << 0 << 0 << 1;
0200     QTest::newRow("^multiline last") << "foo\nbar" << "^bar" << 0 << 0 << 4 << 3;
0201     QTest::newRow("^multiline with index") << "boo\nbar" << "^b" << 1 << 0 << 4 << 1;
0202     QTest::newRow("simple$") << "text" << "xt$" << 0 << 0 << 2 << 2;
0203     QTest::newRow("$ backwards") << "text" << "xt$" << 4 << int(KFind::FindBackwards) << 2 << 2;
0204     QTest::newRow("multiline$") << "foo\nbar" << "oo$" << 0 << 0 << 1 << 2;
0205     QTest::newRow("multiline$ intermediary line") << "foo\nbar\nagain bar" << "r$" << 0 << 0 << 6 << 1;
0206     QTest::newRow("multiline$ with index, last line") << "foo\nbar\nagain bar" << "r$" << 7 << 0 << 16 << 1;
0207     QTest::newRow("multiline$ backwards") << "foo\nbar" << "oo$" << 7 << int(KFind::FindBackwards) << 1 << 2;
0208     QTest::newRow("multiline with \\n") << "foo\nbar" << "o\nb" << 0 << 0 << 2 << 3;
0209     QTest::newRow("whole words ok") << "abc bcbc bc bmore be" << "b." << 0 << int(KFind::WholeWordsOnly) << 9 << 2;
0210     QTest::newRow("whole words not found") << "abab abx" << "ab" << 0 << int(KFind::WholeWordsOnly) << -1 << 0;
0211     QTest::newRow("whole words not found (_)") << "abab ab_" << "ab" << 0 << int(KFind::WholeWordsOnly) << -1 << 0;
0212     QTest::newRow("whole words ok (.)") << "ab." << "ab" << 0 << int(KFind::WholeWordsOnly) << 0 << 2;
0213     QTest::newRow("backwards") << "abc bcbc bc" << "b." << 10 << int(KFind::FindBackwards) << 9 << 2;
0214     QTest::newRow("empty (0)") << "a" << "" << 0 << int(0) << 0 << 0;
0215     QTest::newRow("empty (1)") << "a" << "" << 1 << int(0) << 1 << 0; // kreplacetest testReplaceBlankSearch relies on this
0216     QTest::newRow("at end, not found") << "a" << "b" << 1 << int(0) << -1 << 0; // just for catching the while(index<text.length()) bug
0217     QTest::newRow("back, not found") << "a" << "b" << 0 << int(KFind::FindBackwards) << -1 << 0;
0218     QTest::newRow("back, at begin, found") << "a" << "a" << 0 << int(KFind::FindBackwards) << 0 << 1;
0219     QTest::newRow("back, at end, found") << "a" << "a" << 1 << int(KFind::FindBackwards) << 0 << 1;
0220     QTest::newRow("back, text shorter than pattern") << "a" << "abcd" << 0 << int(KFind::FindBackwards) << -1 << 0;
0221 }
0222 
0223 void TestKFind::testStaticFindRegexp()
0224 {
0225     // Tests for the core method "static KFind::find(text, regexp)"
0226     QFETCH(QString, text);
0227     QFETCH(QString, pattern);
0228     QFETCH(int, startIndex);
0229     QFETCH(int, options);
0230     QFETCH(int, expectedResult);
0231     QFETCH(int, expectedMatchedLength);
0232 
0233     int matchedLength;
0234     const int result = KFind::find(text, QRegExp(pattern), startIndex, options, &matchedLength);
0235     QCOMPARE(result, expectedResult);
0236     QCOMPARE(matchedLength, expectedMatchedLength);
0237 }
0238 
0239 void TestKFind::testSimpleSearch()
0240 {
0241     // first we do a simple text searching the text and doing a few find nexts
0242     KFindRecorder test(m_text.split(QLatin1Char('\n')));
0243     test.find(QStringLiteral("This"), 0);
0244     while (test.findNext()) {}
0245 
0246     const QString output1 =
0247         QLatin1String("line: \"This file is part of the KDE project.\", index: 0, length: 4\n") +
0248         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 0, length: 4\n") +
0249         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 4, length: 4\n") +
0250         QLatin1String("line: \"    along with this library; see the file COPYING.LIB.  If not, write to\", index: 15, length: 4\n");
0251 
0252     QCOMPARE(test.hits().join(QString()), output1);
0253 }
0254 
0255 void TestKFind::testSimpleRegexp()
0256 {
0257     KFindRecorder test(m_text.split(QLatin1Char('\n')));
0258     test.find(QStringLiteral("W.R+ANT[YZ]"), KFind::RegularExpression | KFind::CaseSensitive);
0259     while (test.findNext()) {}
0260     const QString output =
0261         QStringLiteral("line: \"    but WITHOUT ANY WARRANTY; without even the implied warranty of\", index: 20, length: 8\n");
0262     QCOMPARE(test.hits().join(QString()), output);
0263 }
0264 
0265 void TestKFind::testLineBeginRegexp()
0266 {
0267     // Let's see what QRegExp can do on a big text (like in KTextEdit)
0268     {
0269         const QString foobar = QStringLiteral("foo\nbar");
0270         const int idx = foobar.indexOf(QRegExp(QStringLiteral("^bar")));
0271         QCOMPARE(idx, -1); // it doesn't find it. No /m support, as they say. Too bad.
0272     }
0273 
0274     // If we split, it works, but then looking for "foo\nbar" won't work...
0275     KFindRecorder test(m_text.split(QLatin1Char('\n')));
0276     test.find(QStringLiteral("^License"), KFind::RegularExpression);
0277     while (test.findNext()) {}
0278     const QString output =
0279         QStringLiteral("line: \"License version 2, as published by the Free Software Foundation.\", index: 0, length: 7\n");
0280     QCOMPARE(test.hits().join(QString()), output);
0281 }
0282 
0283 void TestKFind::testFindIncremental()
0284 {
0285     // FindIncremental with static contents...
0286 
0287     KFindRecorder test(m_text.split(QLatin1Char('\n')));
0288     test.find(QString(), KFind::FindIncremental);
0289     test.findNext(QStringLiteral("i"));
0290     test.findNext(QStringLiteral("is"));
0291     test.findNext(QStringLiteral("ist"));
0292     test.findNext();
0293     test.findNext(QStringLiteral("istri"));
0294     test.findNext(QStringLiteral("istr"));
0295     test.findNext(QStringLiteral("ist"));
0296     test.findNext(QStringLiteral("is"));
0297     test.findNext(QStringLiteral("W"));
0298     test.findNext(QStringLiteral("WA"));
0299     test.findNext(QStringLiteral("WARRANTY"));
0300     test.findNext(QStringLiteral("Free"));
0301     test.findNext(QStringLiteral("Software Foundation"));
0302 
0303     const QString output2 =
0304         QLatin1String("line: \"This file is part of the KDE project.\", index: 0, length: 0\n") +
0305         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 1\n") +
0306         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 2\n") +
0307         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 42, length: 3\n") +
0308         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 3\n") +
0309         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 5\n") +
0310         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 4\n") +
0311         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 3\n") +
0312         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 2\n") +
0313         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 25, length: 1\n") +
0314         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 25, length: 2\n") +
0315         QLatin1String("line: \"    but WITHOUT ANY WARRANTY; without even the implied warranty of\", index: 20, length: 8\n") +
0316         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 16, length: 4\n") +
0317         QLatin1String("line: \"License version 2, as published by the Free Software Foundation.\", index: 44, length: 19\n");
0318 
0319     QCOMPARE(test.hits().join(QString()), output2);
0320 }
0321 
0322 void TestKFind::testFindIncrementalDynamic()
0323 {
0324     // Now do that again but with pages that change between searches
0325     KFindRecorder test(m_text.split(QLatin1Char('\n')));
0326 
0327     test.find(QString(), KFind::FindIncremental);
0328     test.findNext(QStringLiteral("i"));
0329     test.findNext(QStringLiteral("is"));
0330     test.findNext(QStringLiteral("ist"));
0331     test.findNext(QStringLiteral("istr"));
0332     test.findNext();
0333     test.changeText(1, QStringLiteral("The second line now looks a whole lot different."));
0334     test.findNext(QStringLiteral("istri"));
0335     test.findNext(QStringLiteral("istr"));
0336     test.findNext(QStringLiteral("ist"));
0337     test.findNext(QStringLiteral("is"));
0338     test.findNext(QStringLiteral("i"));
0339     test.findNext(QStringLiteral("W"));
0340     test.findNext(QStringLiteral("WA"));
0341     test.findNext(QStringLiteral("WARRANTY"));
0342     test.changeText(6, QStringLiteral("    but WITHOUT ANY xxxx; without even the implied warranty of"));
0343     test.findNext(QStringLiteral("WARRAN"));
0344     test.findNext(QStringLiteral("Free"));
0345     test.findNext(QStringLiteral("Software Foundation"));
0346 
0347     const QString output3 =
0348         QLatin1String("line: \"This file is part of the KDE project.\", index: 0, length: 0\n") +
0349         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 1\n") +
0350         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 2\n") +
0351         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 42, length: 3\n") +
0352         QLatin1String("line: \"This library is free software; you can redistribute it and/or\", index: 42, length: 4\n") +
0353         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 4\n") +
0354         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 5\n") +
0355         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 4\n") +
0356         QLatin1String("line: \"    This library is distributed in the hope that it will be useful,\", index: 21, length: 3\n") +
0357         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 2\n") +
0358         QLatin1String("line: \"This file is part of the KDE project.\", index: 2, length: 1\n") +
0359         QLatin1String("line: \"The second line now looks a whole lot different.\", index: 18, length: 1\n") +
0360         QLatin1String("line: \"License version 2, as published by the Free Software Foundation.\", index: 48, length: 2\n") +
0361         QLatin1String("line: \"    but WITHOUT ANY WARRANTY; without even the implied warranty of\", index: 20, length: 8\n") +
0362         QLatin1String("line: \"    but WITHOUT ANY xxxx; without even the implied warranty of\", index: 51, length: 6\n") +
0363         QLatin1String("line: \"License version 2, as published by the Free Software Foundation.\", index: 39, length: 4\n") +
0364         QLatin1String("line: \"License version 2, as published by the Free Software Foundation.\", index: 44, length: 19\n");
0365 
0366     QCOMPARE(test.hits().join(QString()), output3);
0367 }
0368 
0369 QTEST_MAIN(TestKFind)
0370