File indexing completed on 2024-12-08 12:24:10

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