File indexing completed on 2024-04-21 03:58:26

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2002 David Faure <david@mandrakesoft.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "kreplacetest.h"
0009 
0010 #include <assert.h>
0011 #include <stdlib.h>
0012 
0013 #include <QApplication>
0014 #include <QDebug>
0015 #include <QEventLoop>
0016 #include <QPushButton>
0017 
0018 #include <kreplace.h>
0019 #include <kreplacedialog.h>
0020 
0021 void KReplaceTest::enterLoop()
0022 {
0023     QEventLoop eventLoop;
0024     connect(this, &KReplaceTest::exitLoop, &eventLoop, &QEventLoop::quit);
0025     eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
0026 }
0027 
0028 KReplaceTest::KReplaceTest(const QStringList &text, const QString &buttonName)
0029     : QObject(nullptr)
0030     , m_text(text)
0031     , m_replace(nullptr)
0032     , m_buttonName(buttonName)
0033 {
0034 }
0035 
0036 KReplaceTest::~KReplaceTest()
0037 {
0038 }
0039 
0040 void KReplaceTest::replace(const QString &pattern, const QString &replacement, long options)
0041 {
0042     m_needEventLoop = false;
0043     // This creates a replace-next-prompt dialog if needed.
0044     m_replace.reset(new KReplace(pattern, replacement, options));
0045 
0046     // Connect highlight (or textFound) signal to code which handles highlighting of found text.
0047     connect(m_replace.get(), &KFind::textFound, this, &KReplaceTest::slotHighlight);
0048 
0049     // Connect findNext signal - called when pressing the button in the dialog
0050     connect(m_replace.get(), &KFind::findNext, this, &KReplaceTest::slotReplaceNext);
0051 
0052     // Connect replace signal - called when doing a replacement
0053     connect(m_replace.get(), &KReplace::textReplaced, this, &KReplaceTest::slotReplace);
0054 
0055     // Go to initial position
0056     if ((options & KFind::FromCursor) == 0) {
0057         if (m_text.isEmpty()) {
0058             return;
0059         }
0060         if (m_replace->options() & KFind::FindBackwards) {
0061             m_currentPos = --m_text.end();
0062         } else {
0063             m_currentPos = m_text.begin();
0064         }
0065     }
0066 
0067     // Launch first replacement
0068     slotReplaceNext();
0069 
0070     if (m_needEventLoop) {
0071         enterLoop();
0072     }
0073 }
0074 
0075 void KReplaceTest::slotHighlight(const QString &str, int matchingIndex, int matchedLength)
0076 {
0077     qDebug() << "slotHighlight Index:" << matchingIndex << " Length:" << matchedLength << " Substr:" << str.mid(matchingIndex, matchedLength);
0078     // Emulate the user saying yes
0079     // We need Qt::QueuedConnection (and the enterloop/exitloop)
0080     // otherwise we get an infinite loop (Match never returned,
0081     // so slotReplaceNext never returns)
0082     if (m_replace->options() & KReplaceDialog::PromptOnReplace) {
0083         QDialog *dlg = m_replace->replaceNextDialog(false);
0084         disconnect(dlg, &QDialog::finished, m_replace.get(), nullptr); // hack to avoid slotDialogClosed being called
0085         dlg->hide();
0086 
0087         QPushButton *button = dlg->findChild<QPushButton *>(m_buttonName);
0088         auto clickFunc = [button]() {
0089             button->click();
0090         };
0091         QMetaObject::invokeMethod(button, clickFunc, Qt::QueuedConnection);
0092 
0093         m_needEventLoop = true;
0094     }
0095 }
0096 
0097 void KReplaceTest::slotReplace(const QString &text, int replacementIndex, int replacedLength, int matchedLength)
0098 {
0099     Q_UNUSED(replacementIndex);
0100     Q_UNUSED(replacedLength);
0101     Q_UNUSED(matchedLength);
0102     // qDebug() << "index=" << replacementIndex << " replacedLength=" << replacedLength << " matchedLength=" << matchedLength << " text=" << text.left( 50 );
0103     *m_currentPos = text; // KReplace hacked the replacement into 'text' in already.
0104 }
0105 
0106 void KReplaceTest::slotReplaceNext()
0107 {
0108     // qDebug();
0109     KFind::Result res = KFind::NoMatch;
0110     int backwards = m_replace->options() & KFind::FindBackwards;
0111     while (res == KFind::NoMatch) {
0112         if (m_replace->needData()) {
0113             m_replace->setData(*m_currentPos);
0114         }
0115 
0116         // Let KReplace inspect the text fragment, and display a dialog if a match is found
0117         res = m_replace->replace();
0118 
0119         if (res == KFind::NoMatch) {
0120             QStringList::iterator lastItem = backwards ? m_text.begin() : --m_text.end();
0121             if (m_currentPos == lastItem) {
0122                 break;
0123             }
0124             if (m_replace->options() & KFind::FindBackwards) {
0125                 m_currentPos--;
0126             } else {
0127                 m_currentPos++;
0128             }
0129         }
0130     }
0131 
0132 #if 0 // commented out so that this test doesn't require interaction
0133     if (res == KFind::NoMatch)   // i.e. at end
0134         if (m_replace->shouldRestart()) {
0135             if (m_replace->options() & KFind::FindBackwards) {
0136                 m_currentPos = m_text.fromLast();
0137             } else {
0138                 m_currentPos = m_text.begin();
0139             }
0140             slotReplaceNext();
0141         }
0142 #endif
0143     if (res == KFind::NoMatch && m_needEventLoop) {
0144         Q_EMIT exitLoop();
0145     }
0146 }
0147 
0148 void KReplaceTest::print()
0149 {
0150     QStringList::Iterator it = m_text.begin();
0151     for (; it != m_text.end(); ++it) {
0152         qDebug() << *it;
0153     }
0154 }
0155 
0156 /* button is the button that we emulate pressing, when options includes PromptOnReplace.
0157    Valid possibilities are User1 (replace all) and User3 (replace) */
0158 static void testReplaceSimple(int options, const QString &buttonName = QString())
0159 {
0160     qDebug() << "testReplaceSimple: " << options;
0161     KReplaceTest test(QStringList() << QStringLiteral("hellohello"), buttonName);
0162     test.replace(QStringLiteral("hello"), QStringLiteral("HELLO"), options);
0163     QStringList textLines = test.textLines();
0164     assert(textLines.count() == 1);
0165     if (textLines[0] != QLatin1String("HELLOHELLO")) {
0166         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of 'HELLOHELLO'";
0167         exit(1);
0168     }
0169 }
0170 
0171 // Replacing "a" with "".
0172 // input="aaaaaa", expected output=""
0173 static void testReplaceBlank(int options, const QString &buttonName = QString())
0174 {
0175     qDebug() << "testReplaceBlank: " << options;
0176     KReplaceTest test(QStringList() << QStringLiteral("aaaaaa"), buttonName);
0177     test.replace(QStringLiteral("a"), QString(), options);
0178     QStringList textLines = test.textLines();
0179     assert(textLines.count() == 1);
0180     if (!textLines[0].isEmpty()) {
0181         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of ''";
0182         exit(1);
0183     }
0184 }
0185 
0186 // Replacing "" with "foo"
0187 // input="bbbb", expected output="foobfoobfoobfoobfoo"
0188 static void testReplaceBlankSearch(int options, const QString &buttonName = QString())
0189 {
0190     qDebug() << "testReplaceBlankSearch: " << options;
0191     KReplaceTest test(QStringList() << QStringLiteral("bbbb"), buttonName);
0192     test.replace(QString(), QStringLiteral("foo"), options);
0193     QStringList textLines = test.textLines();
0194     assert(textLines.count() == 1);
0195     if (textLines[0] != QLatin1String("foobfoobfoobfoobfoo")) {
0196         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of 'foobfoobfoobfoobfoo'";
0197         exit(1);
0198     }
0199 }
0200 
0201 static void testReplaceLonger(int options, const QString &buttonName = QString())
0202 {
0203     qDebug() << "testReplaceLonger: " << options;
0204     // Standard test of a replacement string longer than the matched string
0205     KReplaceTest test(QStringList() << QStringLiteral("aaaa"), buttonName);
0206     test.replace(QStringLiteral("a"), QStringLiteral("bb"), options);
0207     QStringList textLines = test.textLines();
0208     assert(textLines.count() == 1);
0209     if (textLines[0] != QLatin1String("bbbbbbbb")) {
0210         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of 'bbbbbbbb'";
0211         exit(1);
0212     }
0213 }
0214 
0215 static void testReplaceLongerInclude(int options, const QString &buttonName = QString())
0216 {
0217     qDebug() << "testReplaceLongerInclude: " << options;
0218     // Similar test, where the replacement string includes the search string
0219     KReplaceTest test(QStringList() << QStringLiteral("a foo b"), buttonName);
0220     test.replace(QStringLiteral("foo"), QStringLiteral("foobar"), options);
0221     QStringList textLines = test.textLines();
0222     assert(textLines.count() == 1);
0223     if (textLines[0] != QLatin1String("a foobar b")) {
0224         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of 'a foobar b'";
0225         exit(1);
0226     }
0227 }
0228 
0229 static void testReplaceLongerInclude2(int options, const QString &buttonName = QString())
0230 {
0231     qDebug() << "testReplaceLongerInclude2: " << options;
0232     // Similar test, but with more chances of matches inside the replacement string
0233     KReplaceTest test(QStringList() << QStringLiteral("aaaa"), buttonName);
0234     test.replace(QStringLiteral("a"), QStringLiteral("aa"), options);
0235     QStringList textLines = test.textLines();
0236     assert(textLines.count() == 1);
0237     if (textLines[0] != QLatin1String("aaaaaaaa")) {
0238         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of 'aaaaaaaa'";
0239         exit(1);
0240     }
0241 }
0242 
0243 // Test for the \0 backref
0244 static void testReplaceBackRef(int options, const QString &buttonName = QString())
0245 {
0246     KReplaceTest test(QStringList() << QStringLiteral("abc def"), buttonName);
0247     test.replace(QStringLiteral("abc"), QStringLiteral("(\\0)"), options);
0248     QStringList textLines = test.textLines();
0249     assert(textLines.count() == 1);
0250     QString expected = options & KReplaceDialog::BackReference ? QStringLiteral("(abc) def") : QStringLiteral("(\\0) def");
0251     if (textLines[0] != expected) {
0252         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of '" << expected << "'";
0253         exit(1);
0254     }
0255 }
0256 
0257 // Test for other backrefs
0258 static void testReplaceBackRef1(int options, const QString &buttonName = QString())
0259 {
0260     KReplaceTest test(QStringList() << QStringLiteral("a1 b2 a3"), buttonName);
0261     test.replace(QStringLiteral("([ab])([\\d])"), QStringLiteral("\\1 and \\2 in (\\0)"), options);
0262     QStringList textLines = test.textLines();
0263     assert(textLines.count() == 1);
0264     QString expected = QStringLiteral("a and 1 in (a1) b and 2 in (b2) a and 3 in (a3)");
0265     if (textLines[0] != expected) {
0266         qCritical() << "ASSERT FAILED: replaced text is '" << textLines[0] << "' instead of '" << expected << "'";
0267         exit(1);
0268     }
0269 }
0270 
0271 static void testReplacementHistory(const QStringList &findHistory, const QStringList &replaceHistory)
0272 {
0273     KReplaceDialog dlg(nullptr, 0, findHistory, replaceHistory);
0274     dlg.show();
0275     qDebug() << "testReplacementHistory:" << dlg.replacementHistory();
0276     assert(dlg.replacementHistory() == replaceHistory);
0277 }
0278 
0279 static void testReplacementHistory()
0280 {
0281     QStringList findHistory;
0282     QStringList replaceHistory;
0283     findHistory << QStringLiteral("foo") << QStringLiteral("bar");
0284     replaceHistory << QStringLiteral("FOO") << QStringLiteral("BAR");
0285     testReplacementHistory(findHistory, replaceHistory);
0286 
0287     findHistory.clear();
0288     replaceHistory.clear();
0289     findHistory << QStringLiteral("foo") << QStringLiteral("bar");
0290     replaceHistory << QString() << QStringLiteral("baz"); // #130831
0291     testReplacementHistory(findHistory, replaceHistory);
0292 }
0293 
0294 int main(int argc, char **argv)
0295 {
0296     QApplication::setApplicationName(QStringLiteral("kreplacetest"));
0297     QApplication app(argc, argv);
0298 
0299     testReplacementHistory(); // #130831
0300 
0301     testReplaceBlank(0);
0302     testReplaceBlank(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0303     testReplaceBlank(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0304     testReplaceBlank(KFind::FindBackwards);
0305     testReplaceBlank(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0306     testReplaceBlank(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0307 
0308     testReplaceBlankSearch(0);
0309     testReplaceBlankSearch(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0310     testReplaceBlankSearch(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0311     testReplaceBlankSearch(KFind::FindBackwards);
0312     testReplaceBlankSearch(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0313     testReplaceBlankSearch(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0314 
0315     testReplaceSimple(0);
0316     testReplaceSimple(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0317     testReplaceSimple(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0318     testReplaceSimple(KFind::FindBackwards);
0319     testReplaceSimple(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0320     testReplaceSimple(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0321 
0322     testReplaceLonger(0);
0323     testReplaceLonger(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0324     testReplaceLonger(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0325     testReplaceLonger(KFind::FindBackwards);
0326     testReplaceLonger(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0327     testReplaceLonger(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0328 
0329     testReplaceLongerInclude(0);
0330     testReplaceLongerInclude(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0331     testReplaceLongerInclude(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0332     testReplaceLongerInclude(KFind::FindBackwards);
0333     testReplaceLongerInclude(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0334     testReplaceLongerInclude(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0335 
0336     testReplaceLongerInclude2(0);
0337     testReplaceLongerInclude2(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0338     testReplaceLongerInclude2(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0339     testReplaceLongerInclude2(KFind::FindBackwards);
0340     testReplaceLongerInclude2(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0341     testReplaceLongerInclude2(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0342 
0343     testReplaceBackRef(0);
0344     testReplaceBackRef(KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0345     testReplaceBackRef(KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0346 
0347     testReplaceBackRef(KFind::FindBackwards);
0348     testReplaceBackRef(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0349     testReplaceBackRef(KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0350     testReplaceBackRef(KReplaceDialog::BackReference | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0351     testReplaceBackRef(KReplaceDialog::BackReference | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0352     testReplaceBackRef(KReplaceDialog::BackReference | KFind::FindBackwards);
0353     testReplaceBackRef(KReplaceDialog::BackReference | KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("replaceButton")); // replace
0354     testReplaceBackRef(KReplaceDialog::BackReference | KFind::FindBackwards | KReplaceDialog::PromptOnReplace, QStringLiteral("allButton")); // replace all
0355 
0356     testReplaceBackRef1(KReplaceDialog::BackReference | KFind::RegularExpression, QStringLiteral("replaceButton")); // replace
0357     testReplaceBackRef1(KReplaceDialog::BackReference | KFind::RegularExpression, QStringLiteral("allButton")); // replace all
0358 
0359     QString text = QLatin1String("This file is part of the KDE project.\n") + QLatin1String("This library is free software; you can redistribute it and/or\n")
0360         + QLatin1String("modify it under the terms of the GNU Library General Public\n")
0361         + QLatin1String("License version 2, as published by the Free Software Foundation.\n") + QLatin1Char('\n')
0362         + QLatin1String("    This library is distributed in the hope that it will be useful,\n")
0363         + QLatin1String("    but WITHOUT ANY WARRANTY; without even the implied warranty of\n")
0364         + QLatin1String("    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n")
0365         + QLatin1String("    Library General Public License for more details.\n") + QLatin1Char('\n')
0366         + QLatin1String("    You should have received a copy of the GNU Library General Public License\n")
0367         + QLatin1String("    along with this library; see the file COPYING.LIB.  If not, write to\n")
0368         + QLatin1String("    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n") + QLatin1String("    Boston, MA 02110-1301, USA.\n")
0369         + QLatin1String("More tests:\n") + QLatin1String("ThisThis This, This. This\n") + QLatin1String("aGNU\n") + QLatin1String("free");
0370     KReplaceTest test(text.split(QLatin1Char('\n')), QStringLiteral("0"));
0371 
0372     test.replace(QStringLiteral("GNU"), QStringLiteral("KDE"), 0);
0373     test.replace(QStringLiteral("free"), QStringLiteral("*free*"), 0);
0374     test.replace(QStringLiteral("This"), QStringLiteral("THIS*"), KFind::FindBackwards);
0375 
0376     test.print();
0377     // return app.exec();
0378     return 0;
0379 }
0380 
0381 #include "moc_kreplacetest.cpp"