File indexing completed on 2024-04-21 03:57:16

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 "templatehandler_test.h"
0009 
0010 #include <KTextEditor/Cursor>
0011 #include <kateconfig.h>
0012 #include <katedocument.h>
0013 #include <kateglobal.h>
0014 #include <katetemplatehandler.h>
0015 #include <kateview.h>
0016 
0017 #include <QString>
0018 #include <QTest>
0019 #include <QTestKeyEvent>
0020 
0021 QTEST_MAIN(TemplateHandlerTest)
0022 
0023 using namespace KTextEditor;
0024 
0025 TemplateHandlerTest::TemplateHandlerTest()
0026     : QObject()
0027 {
0028     KTextEditor::EditorPrivate::enableUnitTestMode();
0029 }
0030 
0031 void TemplateHandlerTest::testUndo()
0032 {
0033     const QString snippet = QStringLiteral(
0034         "for (${type=\"int\"} ${index=\"i\"} = ; ${index} < ; ++${index})\n"
0035         "{\n"
0036         "    ${index}\n"
0037         "}");
0038 
0039     auto doc = new KTextEditor::DocumentPrivate();
0040     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0041 
0042     // fixed indentation options
0043     doc->config()->setTabWidth(8);
0044     doc->config()->setIndentationWidth(4);
0045     doc->config()->setReplaceTabsDyn(true);
0046 
0047     view->insertTemplate(KTextEditor::Cursor(0, 0), snippet);
0048 
0049     const QString result = QStringLiteral(
0050         "for (int i = ; i < ; ++i)\n"
0051         "{\n"
0052         "    i\n"
0053         "}");
0054     QCOMPARE(doc->text(), result);
0055 
0056     doc->replaceText(Range(0, 9, 0, 10), QStringLiteral("j"));
0057 
0058     const QString result2 = QStringLiteral(
0059         "for (int j = ; j < ; ++j)\n"
0060         "{\n"
0061         "    j\n"
0062         "}");
0063     QCOMPARE(doc->text(), result2);
0064 
0065     doc->undo();
0066 
0067     QCOMPARE(doc->text(), result);
0068 
0069     doc->redo();
0070 
0071     QCOMPARE(doc->text(), result2);
0072 
0073     doc->insertText(Cursor(0, 10), QStringLiteral("j"));
0074     doc->insertText(Cursor(0, 11), QStringLiteral("j"));
0075 
0076     const QString result3 = QStringLiteral(
0077         "for (int jjj = ; jjj < ; ++jjj)\n"
0078         "{\n"
0079         "    jjj\n"
0080         "}");
0081     QCOMPARE(doc->text(), result3);
0082 
0083     doc->undo();
0084 
0085     QCOMPARE(doc->text(), result);
0086 
0087     doc->redo();
0088 
0089     QCOMPARE(doc->text(), result3);
0090 
0091     doc->undo();
0092     QCOMPARE(doc->text(), result);
0093 
0094     doc->undo();
0095     QCOMPARE(doc->text(), QString());
0096 }
0097 
0098 void TemplateHandlerTest::testEscapes()
0099 {
0100     auto doc = new KTextEditor::DocumentPrivate();
0101     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0102     view->insertTemplate({0, 0}, QStringLiteral("\\${field} ${bar} \\${foo=3} \\\\${baz=7}"));
0103     QCOMPARE(doc->text(), QStringLiteral("${field} bar ${foo=3} \\${baz=7}"));
0104 }
0105 
0106 void TemplateHandlerTest::testSimpleMirror()
0107 {
0108     QFETCH(QString, text);
0109 
0110     auto doc = new KTextEditor::DocumentPrivate();
0111     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0112     view->insertTemplate({0, 0}, text);
0113 
0114     QCOMPARE(doc->text(), QString(text).replace(QStringLiteral("${foo}"), QStringLiteral("foo")));
0115 
0116     doc->insertText({0, 0}, QStringLiteral("xx"));
0117     QCOMPARE(doc->text(), QString(text).replace(QStringLiteral("${foo}"), QStringLiteral("xxfoo")));
0118 
0119     doc->removeText(KTextEditor::Range({0, 0}, {0, 2}));
0120     QCOMPARE(doc->text(), QString(text).replace(QStringLiteral("${foo}"), QStringLiteral("foo")));
0121 
0122     delete doc;
0123 }
0124 
0125 void TemplateHandlerTest::testSimpleMirror_data()
0126 {
0127     QTest::addColumn<QString>("text");
0128 
0129     QTest::newRow("one") << QStringLiteral("${foo}");
0130     QTest::newRow("several") << QStringLiteral("${foo} ${foo} Foo ${foo}");
0131 }
0132 
0133 void TemplateHandlerTest::testAlignC()
0134 {
0135     QFETCH(QString, input);
0136     QFETCH(QString, expected);
0137 
0138     auto doc = new KTextEditor::DocumentPrivate();
0139     doc->setHighlightingMode(QStringLiteral("C"));
0140     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0141     view->insertTemplate({0, 0}, input);
0142 
0143     QCOMPARE(doc->text(), expected);
0144 
0145     delete doc;
0146 }
0147 
0148 void TemplateHandlerTest::testAlignC_data()
0149 {
0150     QTest::addColumn<QString>("input");
0151     QTest::addColumn<QString>("expected");
0152 
0153     QTest::newRow("one") << QStringLiteral("/* ${foo} */") << QStringLiteral("/* foo */");
0154     QTest::newRow("simple") << QStringLiteral("/**\n* ${foo}\n*/") << QStringLiteral("/**\n * foo\n */");
0155     QTest::newRow("complex") << QStringLiteral("/**\n* @brief: ${...}\n* \n*/") << QStringLiteral("/**\n * @brief: ...\n * \n */");
0156 }
0157 
0158 void TemplateHandlerTest::testAdjacentRanges()
0159 {
0160     auto doc = new KTextEditor::DocumentPrivate();
0161     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0162 
0163     view->insertTemplate({0, 0}, QStringLiteral("${foo} ${foo}"));
0164     QCOMPARE(doc->text(), QStringLiteral("foo foo"));
0165     doc->removeText(KTextEditor::Range({0, 3}, {0, 4}));
0166     QCOMPARE(doc->text(), QStringLiteral("foofoo"));
0167     doc->insertText({0, 1}, QStringLiteral("x"));
0168     QCOMPARE(doc->text(), QStringLiteral("fxoofxoo"));
0169     doc->insertText({0, 4}, QStringLiteral("y"));
0170     QCOMPARE(doc->text(), QStringLiteral("fxooyfxooy"));
0171     doc->removeText(KTextEditor::Range({0, 4}, {0, 5}));
0172     QCOMPARE(doc->text(), QStringLiteral("fxoofxoo"));
0173 
0174     delete doc;
0175 }
0176 
0177 void TemplateHandlerTest::testTab()
0178 {
0179     QFETCH(QString, tpl);
0180     QFETCH(int, cursor);
0181 
0182     auto doc = new KTextEditor::DocumentPrivate();
0183     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0184 
0185     view->insertTemplate({0, 0}, tpl);
0186     view->setCursorPosition({0, cursor});
0187 
0188     // no idea why the event needs to be posted to the focus proxy
0189     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0190     QTEST(view->cursorPosition().column(), "expected_cursor");
0191 
0192     QTest::keyClick(view->focusProxy(), Qt::Key_Tab, Qt::ShiftModifier);
0193     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0194     QTEST(view->cursorPosition().column(), "expected_cursor");
0195 
0196     delete doc;
0197 }
0198 
0199 void TemplateHandlerTest::testTab_data()
0200 {
0201     QTest::addColumn<QString>("tpl");
0202     QTest::addColumn<int>("cursor");
0203     QTest::addColumn<int>("expected_cursor");
0204 
0205     QTest::newRow("simple_start") << "${foo} ${bar}" << 0 << 4;
0206     QTest::newRow("simple_mid") << "${foo} ${bar}" << 2 << 4;
0207     QTest::newRow("simple_end") << "${foo} ${bar}" << 3 << 4;
0208     QTest::newRow("wrap_start") << "${foo} ${bar}" << 4 << 0;
0209     QTest::newRow("wrap_mid") << "${foo} ${bar}" << 5 << 0;
0210     QTest::newRow("wrap_end") << "${foo} ${bar}" << 6 << 0;
0211     QTest::newRow("non_editable_start") << "${foo} ${foo}" << 0 << 0;
0212     QTest::newRow("non_editable_mid") << "${foo} ${foo}" << 2 << 0;
0213     QTest::newRow("non_editable_end") << "${foo} ${foo}" << 3 << 0;
0214     QTest::newRow("skip_non_editable") << "${foo} ${foo} ${bar}" << 0 << 8;
0215     QTest::newRow("skip_non_editable_at_end") << "${foo} ${bar} ${foo}" << 4 << 0;
0216     QTest::newRow("jump_to_cursor") << "${foo} ${cursor}" << 0 << 4;
0217     QTest::newRow("jump_to_cursor_last") << "${foo} ${cursor} ${bar}" << 0 << 5;
0218     QTest::newRow("jump_to_cursor_last2") << "${foo} ${cursor} ${bar}" << 5 << 4;
0219 }
0220 
0221 void TemplateHandlerTest::testExitAtCursor()
0222 {
0223     auto doc = new KTextEditor::DocumentPrivate();
0224     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0225 
0226     view->insertTemplate({0, 0}, QStringLiteral("${foo} ${bar} ${cursor} ${foo}"));
0227     view->setCursorPosition({0, 0});
0228 
0229     // check it jumps to the cursor
0230     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0231     QCOMPARE(view->cursorPosition().column(), 4);
0232     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0233     QCOMPARE(view->cursorPosition().column(), 8);
0234 
0235     // insert an a at cursor position
0236     QTest::keyClick(view->focusProxy(), Qt::Key_A);
0237     // check it was inserted
0238     QCOMPARE(doc->text(), QStringLiteral("foo bar a foo"));
0239 
0240     // required to process the deleteLater() used to exit the template handler
0241     QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
0242     QApplication::processEvents();
0243 
0244     // go to the first field and verify it's not mirrored any more (i.e. the handler exited)
0245     view->setCursorPosition({0, 0});
0246     QTest::keyClick(view->focusProxy(), Qt::Key_A);
0247     QCOMPARE(doc->text(), QStringLiteral("afoo bar a foo"));
0248 
0249     delete doc;
0250 }
0251 
0252 void TemplateHandlerTest::testDefaultMirror()
0253 {
0254     auto doc = new KTextEditor::DocumentPrivate();
0255     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0256 
0257     view->insertTemplate({0, 0},
0258                          QStringLiteral("${foo=uppercase(\"hi\")} ${bar=3} ${foo}"),
0259                          QStringLiteral("function uppercase(x) { return x.toUpperCase(); }"));
0260     QCOMPARE(doc->text(), QStringLiteral("HI 3 HI"));
0261     doc->insertText({0, 0}, QStringLiteral("xy@"));
0262     QCOMPARE(doc->text(), QStringLiteral("xy@HI 3 xy@HI"));
0263 
0264     delete doc;
0265 }
0266 
0267 void TemplateHandlerTest::testFunctionMirror()
0268 {
0269     auto doc = new KTextEditor::DocumentPrivate();
0270     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0271 
0272     view->insertTemplate({0, 0}, QStringLiteral("${foo} hi ${uppercase(foo)}"), QStringLiteral("function uppercase(x) { return x.toUpperCase(); }"));
0273     QCOMPARE(doc->text(), QStringLiteral("foo hi FOO"));
0274     doc->insertText({0, 0}, QStringLiteral("xy@"));
0275     QCOMPARE(doc->text(), QStringLiteral("xy@foo hi XY@FOO"));
0276 
0277     delete doc;
0278 }
0279 
0280 void TemplateHandlerTest::testAutoSelection()
0281 {
0282     auto doc = new KTextEditor::DocumentPrivate();
0283     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0284 
0285     view->insertTemplate({0, 0}, QStringLiteral("${foo} ${bar} ${bar} ${cursor} ${baz}"));
0286     QCOMPARE(doc->text(), QStringLiteral("foo bar bar  baz"));
0287     QCOMPARE(view->selectionText(), QStringLiteral("foo"));
0288 
0289     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0290     QCOMPARE(view->selectionText(), QStringLiteral("bar"));
0291 
0292     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0293     QCOMPARE(view->selectionText(), QStringLiteral("baz"));
0294 
0295     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0296     QVERIFY(view->selectionRange().isEmpty());
0297 
0298     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0299     QCOMPARE(view->selectionText(), QStringLiteral("foo"));
0300     QTest::keyClick(view->focusProxy(), Qt::Key_A);
0301     QCOMPARE(doc->text(), QStringLiteral("a bar bar  baz"));
0302 
0303     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0304     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0305     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0306     QTest::keyClick(view->focusProxy(), Qt::Key_Tab);
0307     QVERIFY(view->selectionRange().isEmpty());
0308 }
0309 
0310 void TemplateHandlerTest::testNotEditableFields()
0311 {
0312     QFETCH(QString, input);
0313     QFETCH(int, change_offset);
0314 
0315     auto doc = new KTextEditor::DocumentPrivate();
0316     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0317     view->insertTemplate({0, 0}, input);
0318 
0319     doc->insertText({0, change_offset}, QStringLiteral("xxx"));
0320     QTEST(doc->text(), "expected");
0321 }
0322 
0323 void TemplateHandlerTest::testNotEditableFields_data()
0324 {
0325     QTest::addColumn<QString>("input");
0326     QTest::addColumn<int>("change_offset");
0327     QTest::addColumn<QString>("expected");
0328 
0329     QTest::newRow("mirror") << QStringLiteral("${foo} ${foo}") << 6 << "foo foxxxo";
0330 }
0331 
0332 void TemplateHandlerTest::testCanRetrieveSelection()
0333 {
0334     auto doc = new KTextEditor::DocumentPrivate();
0335     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0336     view->insertText(QStringLiteral("hi world"));
0337     view->setSelection(KTextEditor::Range(0, 1, 0, 4));
0338     view->insertTemplate({0, 1}, QStringLiteral("xx${foo=sel()}xx"), QStringLiteral("function sel() { return view.selectedText(); }"));
0339     QCOMPARE(doc->text(), QStringLiteral("hxxi wxxorld"));
0340 }
0341 
0342 void TemplateHandlerTest::testDefaults_data()
0343 {
0344     QTest::addColumn<QString>("input");
0345     QTest::addColumn<QString>("expected");
0346     QTest::addColumn<QString>("function");
0347 
0348     using S = QString;
0349     QTest::newRow("empty") << S() << S() << S();
0350     QTest::newRow("foo") << QStringLiteral("${foo}") << QStringLiteral("foo") << S();
0351     QTest::newRow("foo=3") << QStringLiteral("${foo=3}") << QStringLiteral("3") << S();
0352     QTest::newRow("${foo=3+5}") << QStringLiteral("${foo=3+5}") << QStringLiteral("8") << S();
0353     QTest::newRow("string") << QStringLiteral("${foo=\"3+5\"}") << QStringLiteral("3+5") << S();
0354     QTest::newRow("string_mirror") << QStringLiteral("${foo=\"Bar\"} ${foo}") << QStringLiteral("Bar Bar") << S();
0355     QTest::newRow("func_simple") << QStringLiteral("${foo=myfunc()}") << QStringLiteral("hi") << QStringLiteral("function myfunc() { return 'hi'; }");
0356     QTest::newRow("func_fixed") << QStringLiteral("${myfunc()}") << QStringLiteral("hi") << QStringLiteral("function myfunc() { return 'hi'; }");
0357     QTest::newRow("func_constant_arg") << QStringLiteral("${foo=uppercase(\"Foo\")}") << QStringLiteral("FOO")
0358                                        << QStringLiteral("function uppercase(x) { return x.toUpperCase(); }");
0359     QTest::newRow("func_constant_arg_mirror") << QStringLiteral("${foo=uppercase(\"hi\")} ${bar=3} ${foo}") << QStringLiteral("HI 3 HI")
0360                                               << QStringLiteral("function uppercase(x) { return x.toUpperCase(); }");
0361     QTest::newRow("cursor") << QStringLiteral("${foo} ${cursor}") << QStringLiteral("foo ") << S();
0362     QTest::newRow("only_cursor") << QStringLiteral("${cursor}") << QStringLiteral("") << S();
0363     QTest::newRow("only_cursor_stuff") << QStringLiteral("fdas ${cursor} asdf") << QStringLiteral("fdas  asdf") << S();
0364 }
0365 
0366 void TemplateHandlerTest::testDefaults()
0367 {
0368     auto doc = new KTextEditor::DocumentPrivate();
0369     auto view = static_cast<KTextEditor::ViewPrivate *>(doc->createView(nullptr));
0370 
0371     QFETCH(QString, input);
0372     QFETCH(QString, function);
0373 
0374     view->insertTemplate(KTextEditor::Cursor(0, 0), input, function);
0375     QTEST(doc->text(), "expected");
0376 
0377     view->selectAll();
0378     view->keyDelete();
0379     QCOMPARE(doc->text(), QString());
0380 
0381     delete doc;
0382 }
0383 
0384 #include "moc_templatehandler_test.cpp"