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 }