File indexing completed on 2024-12-01 09:55:47

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2010 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "regexpsearch_test.h"
0009 #include "moc_regexpsearch_test.cpp"
0010 
0011 #include <katedocument.h>
0012 #include <kateglobal.h>
0013 #include <kateregexpsearch.h>
0014 
0015 #include <QRegularExpression>
0016 
0017 #include <QtTestWidgets>
0018 
0019 QTEST_MAIN(RegExpSearchTest)
0020 
0021 #define testNewRow() (QTest::newRow(QString("line %1").arg(__LINE__).toLatin1().data()))
0022 
0023 using namespace KTextEditor;
0024 
0025 RegExpSearchTest::RegExpSearchTest()
0026     : QObject()
0027 {
0028     KTextEditor::EditorPrivate::enableUnitTestMode();
0029 }
0030 
0031 RegExpSearchTest::~RegExpSearchTest()
0032 {
0033 }
0034 
0035 void RegExpSearchTest::testReplaceEscapeSequences_data()
0036 {
0037     QTest::addColumn<QString>("pattern");
0038     QTest::addColumn<QString>("expected");
0039 
0040     testNewRow() << "\\"
0041                  << "\\";
0042     testNewRow() << "\\0"
0043                  << "0";
0044     testNewRow() << "\\00"
0045                  << "00";
0046     testNewRow() << "\\000"
0047                  << "000";
0048     testNewRow() << "\\0000" << QString(QChar(0));
0049     testNewRow() << "\\0377" << QString(QChar(0377));
0050     testNewRow() << "\\0378"
0051                  << "0378";
0052     testNewRow() << "\\a"
0053                  << "\a";
0054     testNewRow() << "\\f"
0055                  << "\f";
0056     testNewRow() << "\\n"
0057                  << "\n";
0058     testNewRow() << "\\r"
0059                  << "\r";
0060     testNewRow() << "\\t"
0061                  << "\t";
0062     testNewRow() << "\\v"
0063                  << "\v";
0064     testNewRow() << "\\x"
0065                  << "x";
0066     testNewRow() << "\\x0"
0067                  << "x0";
0068     testNewRow() << "\\x00"
0069                  << "x00";
0070     testNewRow() << "\\x000"
0071                  << "x000";
0072     testNewRow() << "\\x0000" << QString(QChar(0x0000));
0073     testNewRow() << "\\x00000" << QString(QChar(0x0000) + '0');
0074     testNewRow() << "\\xaaaa" << QString(QChar(0xaaaa));
0075     testNewRow() << "\\xFFFF" << QString(QChar(0xFFFF));
0076     testNewRow() << "\\xFFFg"
0077                  << "xFFFg";
0078 }
0079 
0080 void RegExpSearchTest::testReplaceEscapeSequences()
0081 {
0082     QFETCH(QString, pattern);
0083     QFETCH(QString, expected);
0084 
0085     const QString result1 = KateRegExpSearch::escapePlaintext(pattern);
0086     const QString result2 = KateRegExpSearch::buildReplacement(pattern, QStringList(), 0);
0087 
0088     QCOMPARE(result1, expected);
0089     QCOMPARE(result2, expected);
0090 }
0091 
0092 void RegExpSearchTest::testReplacementReferences_data()
0093 {
0094     QTest::addColumn<QString>("pattern");
0095     QTest::addColumn<QString>("expected");
0096     QTest::addColumn<QStringList>("capturedTexts");
0097 
0098     testNewRow() << "\\0"
0099                  << "b" << (QStringList() << "b");
0100     testNewRow() << "\\00"
0101                  << "b0" << (QStringList() << "b");
0102     testNewRow() << "\\000"
0103                  << "b00" << (QStringList() << "b");
0104     testNewRow() << "\\0000" << QString(QChar(0)) << (QStringList() << "b");
0105     testNewRow() << "\\1"
0106                  << "1" << (QStringList() << "b");
0107     testNewRow() << "\\0"
0108                  << "b"
0109                  << (QStringList() << "b"
0110                                    << "c");
0111     testNewRow() << "\\1"
0112                  << "c"
0113                  << (QStringList() << "b"
0114                                    << "c");
0115 }
0116 
0117 void RegExpSearchTest::testReplacementReferences()
0118 {
0119     QFETCH(QString, pattern);
0120     QFETCH(QString, expected);
0121     QFETCH(QStringList, capturedTexts);
0122 
0123     const QString result = KateRegExpSearch::buildReplacement(pattern, capturedTexts, 1);
0124 
0125     QCOMPARE(result, expected);
0126 }
0127 
0128 void RegExpSearchTest::testReplacementCaseConversion_data()
0129 {
0130     QTest::addColumn<QString>("pattern");
0131     QTest::addColumn<QString>("expected");
0132 
0133     testNewRow() << "a\\Uaa"
0134                  << "aAA";
0135     testNewRow() << "a\\UAa"
0136                  << "aAA";
0137     testNewRow() << "a\\UaA"
0138                  << "aAA";
0139 
0140     testNewRow() << "a\\Uáa"
0141                  << "aÁA";
0142     testNewRow() << "a\\UAá"
0143                  << "aAÁ";
0144     testNewRow() << "a\\UaÁ"
0145                  << "aAÁ";
0146 
0147     testNewRow() << "a\\uaa"
0148                  << "aAa";
0149     testNewRow() << "a\\uAa"
0150                  << "aAa";
0151     testNewRow() << "a\\uaA"
0152                  << "aAA";
0153 
0154     testNewRow() << "a\\uáa"
0155                  << "aÁa";
0156     testNewRow() << "a\\uÁa"
0157                  << "aÁa";
0158     testNewRow() << "a\\uáA"
0159                  << "aÁA";
0160 
0161     testNewRow() << "A\\LAA"
0162                  << "Aaa";
0163     testNewRow() << "A\\LaA"
0164                  << "Aaa";
0165     testNewRow() << "A\\LAa"
0166                  << "Aaa";
0167 
0168     testNewRow() << "A\\LÁA"
0169                  << "Aáa";
0170     testNewRow() << "A\\LaÁ"
0171                  << "Aaá";
0172     testNewRow() << "A\\LÁa"
0173                  << "Aáa";
0174 
0175     testNewRow() << "A\\lAA"
0176                  << "AaA";
0177     testNewRow() << "A\\lAa"
0178                  << "Aaa";
0179     testNewRow() << "A\\laA"
0180                  << "AaA";
0181 
0182     testNewRow() << "A\\lÁA"
0183                  << "AáA";
0184     testNewRow() << "A\\lÁa"
0185                  << "Aáa";
0186     testNewRow() << "A\\láA"
0187                  << "AáA";
0188 
0189     testNewRow() << "a\\Ubb\\EaA"
0190                  << "aBBaA";
0191     testNewRow() << "A\\LBB\\EAa"
0192                  << "AbbAa";
0193 
0194     testNewRow() << "a\\Ubb\\EáA"
0195                  << "aBBáA";
0196     testNewRow() << "A\\LBB\\EÁa"
0197                  << "AbbÁa";
0198 }
0199 
0200 void RegExpSearchTest::testReplacementCaseConversion()
0201 {
0202     QFETCH(QString, pattern);
0203     QFETCH(QString, expected);
0204 
0205     const QString result = KateRegExpSearch::buildReplacement(pattern, QStringList(), 1);
0206 
0207     QCOMPARE(result, expected);
0208 }
0209 
0210 void RegExpSearchTest::testReplacementCounter_data()
0211 {
0212     QTest::addColumn<QString>("pattern");
0213     QTest::addColumn<int>("counter");
0214     QTest::addColumn<QString>("expected");
0215 
0216     testNewRow() << "a\\#b" << 1 << "a1b";
0217     testNewRow() << "a\\#b" << 10 << "a10b";
0218     testNewRow() << "a\\#####b" << 1 << "a00001b";
0219 }
0220 
0221 void RegExpSearchTest::testReplacementCounter()
0222 {
0223     QFETCH(QString, pattern);
0224     QFETCH(int, counter);
0225     QFETCH(QString, expected);
0226 
0227     const QString result = KateRegExpSearch::buildReplacement(pattern, QStringList(), counter);
0228 
0229     QCOMPARE(result, expected);
0230 }
0231 
0232 void RegExpSearchTest::testAnchoredRegexp_data()
0233 {
0234     QTest::addColumn<QString>("pattern");
0235     QTest::addColumn<Range>("inputRange");
0236     QTest::addColumn<bool>("backwards");
0237     QTest::addColumn<Range>("expected");
0238 
0239     testNewRow() << "fe" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 2);
0240     testNewRow() << "fe" << Range(0, 0, 0, 8) << true << Range(0, 6, 0, 8);
0241 
0242     testNewRow() << "^fe" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 2);
0243     testNewRow() << "^fe" << Range(0, 0, 0, 1) << false << Range::invalid();
0244     testNewRow() << "^fe" << Range(0, 0, 0, 2) << false << Range(0, 0, 0, 2);
0245     testNewRow() << "^fe" << Range(0, 3, 0, 8) << false << Range::invalid(); // only match at line start
0246     testNewRow() << "^fe" << Range(0, 0, 0, 2) << true << Range(0, 0, 0, 2);
0247     testNewRow() << "^fe" << Range(0, 0, 0, 1) << true << Range::invalid();
0248     testNewRow() << "^fe" << Range(0, 0, 0, 2) << true << Range(0, 0, 0, 2);
0249     testNewRow() << "^fe" << Range(0, 3, 0, 8) << true << Range::invalid();
0250 
0251     testNewRow() << "fe$" << Range(0, 0, 0, 8) << false << Range(0, 6, 0, 8);
0252     testNewRow() << "fe$" << Range(0, 7, 0, 8) << false << Range::invalid();
0253     testNewRow() << "fe$" << Range(0, 6, 0, 8) << false << Range(0, 6, 0, 8);
0254     testNewRow() << "fe$" << Range(0, 0, 0, 5) << false << Range::invalid(); // only match at line end, fails
0255     testNewRow() << "fe$" << Range(0, 0, 0, 8) << true << Range(0, 6, 0, 8);
0256     testNewRow() << "fe$" << Range(0, 7, 0, 8) << true << Range::invalid();
0257     testNewRow() << "fe$" << Range(0, 6, 0, 8) << true << Range(0, 6, 0, 8);
0258     testNewRow() << "fe$" << Range(0, 0, 0, 5) << true << Range::invalid();
0259 
0260     testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8);
0261     testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 8) << false << Range::invalid();
0262     testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 5) << false << Range::invalid();
0263     testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 5) << false << Range::invalid();
0264     testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8);
0265     testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 8) << true << Range::invalid();
0266     testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 5) << true << Range::invalid();
0267     testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 5) << true << Range::invalid();
0268 
0269     testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8);
0270     testNewRow() << "^fe( fe)*" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8);
0271     testNewRow() << "fe( fe)*$" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8);
0272     testNewRow() << "fe( fe)*" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8);
0273     testNewRow() << "^fe( fe)*$" << Range(0, 3, 0, 8) << false << Range::invalid();
0274     testNewRow() << "fe( fe)*$" << Range(0, 3, 0, 8) << false << Range(0, 3, 0, 8);
0275     testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 5) << false << Range::invalid();
0276     // fails because the whole line is fed to QRegularExpression, then matches
0277     // that end beyond the search range are rejected, see KateRegExpSearch::searchText()
0278     // testNewRow() << "^fe( fe)*"  << Range(0, 0, 0, 5) << false << Range(0, 0, 0, 5);
0279 
0280     testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8);
0281     testNewRow() << "^fe( fe)*" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8);
0282     testNewRow() << "fe( fe)*$" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8);
0283     testNewRow() << "fe( fe)*" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8);
0284     testNewRow() << "^fe( fe)*$" << Range(0, 3, 0, 8) << true << Range::invalid();
0285     testNewRow() << "fe( fe)*$" << Range(0, 3, 0, 8) << true << Range(0, 3, 0, 8);
0286     testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 5) << true << Range::invalid();
0287 
0288     testNewRow() << "^fe|fe$" << Range(0, 0, 0, 5) << false << Range(0, 0, 0, 2);
0289     testNewRow() << "^fe|fe$" << Range(0, 3, 0, 8) << false << Range(0, 6, 0, 8);
0290     testNewRow() << "^fe|fe$" << Range(0, 0, 0, 5) << true << Range(0, 0, 0, 2);
0291     testNewRow() << "^fe|fe$" << Range(0, 3, 0, 8) << true << Range(0, 6, 0, 8);
0292 }
0293 
0294 void RegExpSearchTest::testAnchoredRegexp()
0295 {
0296     QFETCH(QString, pattern);
0297     QFETCH(Range, inputRange);
0298     QFETCH(bool, backwards);
0299     QFETCH(Range, expected);
0300 
0301     KTextEditor::DocumentPrivate doc;
0302     doc.setText("fe fe fe");
0303 
0304     KateRegExpSearch searcher(&doc);
0305 
0306     static int i = 0;
0307     if (i == 34 || i == 36) {
0308         qDebug() << i;
0309     }
0310     i++;
0311 
0312     const Range result = searcher.search(pattern, inputRange, backwards, QRegularExpression::CaseInsensitiveOption)[0];
0313 
0314     QCOMPARE(result, expected);
0315 }
0316 
0317 void RegExpSearchTest::testSearchForward()
0318 {
0319     KTextEditor::DocumentPrivate doc;
0320     doc.setText("  \\piinfercong");
0321 
0322     KateRegExpSearch searcher(&doc);
0323     QVector<KTextEditor::Range> result = searcher.search("\\\\piinfer(\\w)", Range(0, 2, 0, 15), false);
0324 
0325     QCOMPARE(result.at(0), Range(0, 2, 0, 11));
0326     QCOMPARE(doc.text(result.at(1)), QLatin1Char('c'));
0327 
0328     // Test Unicode
0329     doc.setText("  \\piinferćong");
0330     result = searcher.search("\\\\piinfer(\\w)", Range(0, 2, 0, 15), false);
0331 
0332     QCOMPARE(result.at(0), Range(0, 2, 0, 11));
0333     QCOMPARE(doc.text(result.at(1)), QStringLiteral("ć"));
0334 }
0335 
0336 void RegExpSearchTest::testSearchBackwardInSelection()
0337 {
0338     KTextEditor::DocumentPrivate doc;
0339     doc.setText("foobar foo bar foo bar foo");
0340 
0341     KateRegExpSearch searcher(&doc);
0342     const Range result = searcher.search("foo", Range(0, 0, 0, 15), true)[0];
0343 
0344     QCOMPARE(result, Range(0, 7, 0, 10));
0345 }
0346 
0347 void RegExpSearchTest::test()
0348 {
0349     KTextEditor::DocumentPrivate doc;
0350     doc.setText("\\newcommand{\\piReductionOut}");
0351 
0352     KateRegExpSearch searcher(&doc);
0353     const QVector<Range> result = searcher.search("\\\\piReduction(\\S)", Range(0, 10, 0, 28), true);
0354 
0355     QCOMPARE(result.size(), 2);
0356     QCOMPARE(result[0], Range(0, 12, 0, 25));
0357     QCOMPARE(result[1], Range(0, 24, 0, 25));
0358     QCOMPARE(doc.text(result[0]), QString("\\piReductionO"));
0359     QCOMPARE(doc.text(result[1]), QString("O"));
0360 }
0361 
0362 void RegExpSearchTest::testUnicode()
0363 {
0364     KTextEditor::DocumentPrivate doc;
0365     doc.setText("\\newcommand{\\piReductionOÓut}");
0366 
0367     KateRegExpSearch searcher(&doc);
0368     const QVector<Range> result = searcher.search("\\\\piReduction(\\w)(\\w)", Range(0, 10, 0, 28), true);
0369 
0370     QCOMPARE(result.size(), 3);
0371     QCOMPARE(result.at(0), Range(0, 12, 0, 26));
0372     QCOMPARE(result.at(1), Range(0, 24, 0, 25));
0373     QCOMPARE(result.at(2), Range(0, 25, 0, 26));
0374     QCOMPARE(doc.text(result.at(0)), QStringLiteral("\\piReductionOÓ"));
0375     QCOMPARE(doc.text(result.at(1)), QStringLiteral("O"));
0376     QCOMPARE(doc.text(result.at(2)), QStringLiteral("Ó"));
0377 }