File indexing completed on 2024-09-08 03:40:36
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"