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"