File indexing completed on 2024-04-21 03:57:12
0001 /* 0002 This file is part of the Kate project. 0003 SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org> 0004 SPDX-FileCopyrightText: 2010-2018 Dominik Haumann <dhaumann@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "katetextbuffertest.h" 0010 #include "katebuffer.h" 0011 #include "katedocument.h" 0012 #include "katetextfolding.h" 0013 #include <kateglobal.h> 0014 #include <ktexteditor/movingcursor.h> 0015 0016 QTEST_MAIN(KateTextBufferTest) 0017 0018 KateTextBufferTest::KateTextBufferTest() 0019 : QObject() 0020 { 0021 KTextEditor::EditorPrivate::enableUnitTestMode(); 0022 } 0023 0024 KateTextBufferTest::~KateTextBufferTest() 0025 { 0026 } 0027 0028 void KateTextBufferTest::basicBufferTest() 0029 { 0030 // construct an empty text buffer 0031 KTextEditor::DocumentPrivate doc; 0032 Kate::TextBuffer &buffer = doc.buffer(); 0033 0034 // one line per default 0035 QVERIFY(buffer.lines() == 1); 0036 QVERIFY(buffer.text() == QString()); 0037 0038 // FIXME: use QTestLib macros for checking the correct state 0039 // start editing 0040 buffer.startEditing(); 0041 0042 // end editing 0043 buffer.finishEditing(); 0044 } 0045 0046 void KateTextBufferTest::wrapLineTest() 0047 { 0048 // construct an empty text buffer 0049 KTextEditor::DocumentPrivate doc; 0050 Kate::TextBuffer &buffer = doc.buffer(); 0051 0052 // wrap first empty line -> we should have two empty lines 0053 buffer.startEditing(); 0054 buffer.wrapLine(KTextEditor::Cursor(0, 0)); 0055 buffer.finishEditing(); 0056 buffer.debugPrint(QStringLiteral("Two empty lines")); 0057 QVERIFY(buffer.text() == QLatin1String("\n")); 0058 0059 // unwrap again -> only one empty line 0060 buffer.startEditing(); 0061 buffer.unwrapLine(1); 0062 buffer.finishEditing(); 0063 0064 // print debug 0065 buffer.debugPrint(QStringLiteral("Empty Buffer")); 0066 QVERIFY(buffer.text() == QString()); 0067 } 0068 0069 void KateTextBufferTest::insertRemoveTextTest() 0070 { 0071 // construct an empty text buffer 0072 KTextEditor::DocumentPrivate doc; 0073 Kate::TextBuffer &buffer = doc.buffer(); 0074 0075 // wrap first line 0076 buffer.startEditing(); 0077 buffer.wrapLine(KTextEditor::Cursor(0, 0)); 0078 buffer.finishEditing(); 0079 buffer.debugPrint(QStringLiteral("Two empty lines")); 0080 QVERIFY(buffer.text() == QLatin1String("\n")); 0081 0082 // remember second line 0083 Kate::TextLine second = buffer.line(1); 0084 0085 // unwrap second line 0086 buffer.startEditing(); 0087 buffer.unwrapLine(1); 0088 buffer.finishEditing(); 0089 buffer.debugPrint(QStringLiteral("One empty line")); 0090 QVERIFY(buffer.text() == QString()); 0091 0092 // second text line should be still there 0093 // const QString &secondText = second->text (); 0094 // QVERIFY (secondText == "") 0095 0096 // insert text 0097 buffer.startEditing(); 0098 buffer.insertText(KTextEditor::Cursor(0, 0), QStringLiteral("testremovetext")); 0099 buffer.finishEditing(); 0100 buffer.debugPrint(QStringLiteral("One line")); 0101 QVERIFY(buffer.text() == QLatin1String("testremovetext")); 0102 0103 // remove text 0104 buffer.startEditing(); 0105 buffer.removeText(KTextEditor::Range(KTextEditor::Cursor(0, 4), KTextEditor::Cursor(0, 10))); 0106 buffer.finishEditing(); 0107 buffer.debugPrint(QStringLiteral("One line")); 0108 QVERIFY(buffer.text() == QLatin1String("testtext")); 0109 0110 // wrap text 0111 buffer.startEditing(); 0112 buffer.wrapLine(KTextEditor::Cursor(0, 2)); 0113 buffer.finishEditing(); 0114 buffer.debugPrint(QStringLiteral("Two line")); 0115 QVERIFY(buffer.text() == QLatin1String("te\nsttext")); 0116 0117 // unwrap text 0118 buffer.startEditing(); 0119 buffer.unwrapLine(1); 0120 buffer.finishEditing(); 0121 buffer.debugPrint(QStringLiteral("One line")); 0122 QVERIFY(buffer.text() == QLatin1String("testtext")); 0123 } 0124 0125 void KateTextBufferTest::cursorTest() 0126 { 0127 // last buffer content, for consistence checks 0128 QString lastBufferContent; 0129 0130 // construct an empty text buffer 0131 KTextEditor::DocumentPrivate doc; 0132 Kate::TextBuffer &buffer = doc.buffer(); 0133 0134 // wrap first line 0135 buffer.startEditing(); 0136 buffer.insertText(KTextEditor::Cursor(0, 0), QStringLiteral("sfdfjdsklfjlsdfjlsdkfjskldfjklsdfjklsdjkfl")); 0137 buffer.wrapLine(KTextEditor::Cursor(0, 8)); 0138 buffer.wrapLine(KTextEditor::Cursor(1, 8)); 0139 buffer.wrapLine(KTextEditor::Cursor(2, 8)); 0140 buffer.finishEditing(); 0141 buffer.debugPrint(QStringLiteral("Cursor buffer")); 0142 0143 // construct cursor 0144 KTextEditor::MovingCursor *cursor1 = doc.newMovingCursor(KTextEditor::Cursor(0, 0), KTextEditor::MovingCursor::MoveOnInsert); 0145 QVERIFY(cursor1->toCursor() == KTextEditor::Cursor(0, 0)); 0146 // printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); 0147 0148 // insert text 0149 buffer.startEditing(); 0150 buffer.insertText(KTextEditor::Cursor(0, 0), QStringLiteral("hallo")); 0151 buffer.finishEditing(); 0152 buffer.debugPrint(QStringLiteral("Cursor buffer")); 0153 0154 // printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); 0155 QVERIFY(cursor1->toCursor() == KTextEditor::Cursor(0, 5)); 0156 0157 // remove text 0158 buffer.startEditing(); 0159 buffer.removeText(KTextEditor::Range(KTextEditor::Cursor(0, 4), KTextEditor::Cursor(0, 10))); 0160 buffer.finishEditing(); 0161 buffer.debugPrint(QStringLiteral("Cursor buffer")); 0162 0163 // printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); 0164 QVERIFY(cursor1->toCursor() == KTextEditor::Cursor(0, 4)); 0165 0166 // wrap line 0167 buffer.startEditing(); 0168 buffer.wrapLine(KTextEditor::Cursor(0, 3)); 0169 buffer.finishEditing(); 0170 buffer.debugPrint(QStringLiteral("Cursor buffer")); 0171 0172 // printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); 0173 QVERIFY(cursor1->toCursor() == KTextEditor::Cursor(1, 1)); 0174 0175 // unwrap line 0176 buffer.startEditing(); 0177 buffer.unwrapLine(1); 0178 buffer.finishEditing(); 0179 buffer.debugPrint(QStringLiteral("Cursor buffer")); 0180 0181 // printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); 0182 QVERIFY(cursor1->toCursor() == KTextEditor::Cursor(0, 4)); 0183 0184 // remember content 0185 lastBufferContent = buffer.text(); 0186 // verify content 0187 QVERIFY(lastBufferContent == buffer.text()); 0188 } 0189 0190 void KateTextBufferTest::foldingTest() 0191 { 0192 // construct an empty text buffer & folding info 0193 KTextEditor::DocumentPrivate doc; 0194 KateBuffer &buffer = doc.buffer(); 0195 Kate::TextFolding folding(buffer); 0196 0197 // insert some text 0198 buffer.startEditing(); 0199 for (int i = 0; i < 100; ++i) { 0200 buffer.insertText(KTextEditor::Cursor(i, 0), QStringLiteral("1234567890")); 0201 if (i < 99) { 0202 buffer.wrapLine(KTextEditor::Cursor(i, 10)); 0203 } 0204 } 0205 buffer.finishEditing(); 0206 QVERIFY(buffer.lines() == 100); 0207 0208 // starting with empty folding! 0209 folding.debugPrint(QStringLiteral("Empty Folding")); 0210 QVERIFY(folding.debugDump() == QLatin1String("tree - folded ")); 0211 0212 // check visibility 0213 QVERIFY(folding.isLineVisible(0)); 0214 QVERIFY(folding.isLineVisible(99)); 0215 0216 // all visible 0217 QVERIFY(folding.visibleLines() == 100); 0218 0219 // we shall be able to insert new range 0220 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(5, 0), KTextEditor::Cursor(10, 0))) == 0); 0221 0222 // we shall have now exactly one range toplevel, that is not folded! 0223 folding.debugPrint(QStringLiteral("One Toplevel Fold")); 0224 QVERIFY(folding.debugDump() == QLatin1String("tree [5:0 10:0] - folded ")); 0225 0226 // fold the range! 0227 QVERIFY(folding.foldRange(0)); 0228 0229 folding.debugPrint(QStringLiteral("One Toplevel Fold - Folded")); 0230 QVERIFY(folding.debugDump() == QLatin1String("tree [5:0 f 10:0] - folded [5:0 f 10:0]")); 0231 0232 // check visibility 0233 QVERIFY(folding.isLineVisible(5)); 0234 for (int i = 6; i <= 10; ++i) { 0235 QVERIFY(!folding.isLineVisible(i)); 0236 } 0237 QVERIFY(folding.isLineVisible(11)); 0238 0239 // 5 lines are hidden 0240 QVERIFY(folding.visibleLines() == (100 - 5)); 0241 0242 // check line mapping 0243 QVERIFY(folding.visibleLineToLine(5) == 5); 0244 for (int i = 6; i <= 50; ++i) { 0245 QVERIFY(folding.visibleLineToLine(i) == (i + 5)); 0246 } 0247 0248 // there shall be one range starting at 5 0249 QList<QPair<qint64, Kate::TextFolding::FoldingRangeFlags>> forLine = folding.foldingRangesStartingOnLine(5); 0250 QVERIFY(forLine.size() == 1); 0251 QVERIFY(forLine[0].first == 0); 0252 QVERIFY(forLine[0].second & Kate::TextFolding::Folded); 0253 0254 // we shall be able to insert new range 0255 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(20, 0), KTextEditor::Cursor(30, 0)), Kate::TextFolding::Folded) == 1); 0256 0257 // we shall have now exactly two range toplevel 0258 folding.debugPrint(QStringLiteral("Two Toplevel Folds")); 0259 QVERIFY(folding.debugDump() == QLatin1String("tree [5:0 f 10:0] [20:0 f 30:0] - folded [5:0 f 10:0] [20:0 f 30:0]")); 0260 0261 // check visibility 0262 QVERIFY(folding.isLineVisible(5)); 0263 for (int i = 6; i <= 10; ++i) { 0264 QVERIFY(!folding.isLineVisible(i)); 0265 } 0266 QVERIFY(folding.isLineVisible(11)); 0267 0268 QVERIFY(folding.isLineVisible(20)); 0269 for (int i = 21; i <= 30; ++i) { 0270 QVERIFY(!folding.isLineVisible(i)); 0271 } 0272 QVERIFY(folding.isLineVisible(31)); 0273 0274 // 15 lines are hidden 0275 QVERIFY(folding.visibleLines() == (100 - 5 - 10)); 0276 0277 // check line mapping 0278 QVERIFY(folding.visibleLineToLine(5) == 5); 0279 for (int i = 6; i <= 15; ++i) { 0280 QVERIFY(folding.visibleLineToLine(i) == (i + 5)); 0281 } 0282 for (int i = 16; i <= 50; ++i) { 0283 QVERIFY(folding.visibleLineToLine(i) == (i + 15)); 0284 } 0285 0286 // check line mapping 0287 QVERIFY(folding.lineToVisibleLine(5) == 5); 0288 for (int i = 11; i <= 20; ++i) { 0289 QVERIFY(folding.lineToVisibleLine(i) == (i - 5)); 0290 } 0291 for (int i = 31; i <= 40; ++i) { 0292 QVERIFY(folding.lineToVisibleLine(i) == (i - 15)); 0293 } 0294 0295 // there shall be one range starting at 20 0296 forLine = folding.foldingRangesStartingOnLine(20); 0297 QVERIFY(forLine.size() == 1); 0298 QVERIFY(forLine[0].first == 1); 0299 QVERIFY(forLine[0].second & Kate::TextFolding::Folded); 0300 0301 // this shall fail to be inserted, as it badly overlaps with the first range! 0302 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(6, 0), KTextEditor::Cursor(15, 0)), Kate::TextFolding::Folded) == -1); 0303 0304 // this shall fail to be inserted, as it badly overlaps with the second range! 0305 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(15, 0), KTextEditor::Cursor(25, 0)), Kate::TextFolding::Folded) == -1); 0306 0307 // we shall still have now exactly two range toplevel 0308 folding.debugPrint(QStringLiteral("Still Two Toplevel Folds")); 0309 QVERIFY(folding.debugDump() == QLatin1String("tree [5:0 f 10:0] [20:0 f 30:0] - folded [5:0 f 10:0] [20:0 f 30:0]")); 0310 0311 // still 15 lines are hidden 0312 QVERIFY(folding.visibleLines() == (100 - 5 - 10)); 0313 0314 // we shall be able to insert new range, should lead to nested folds! 0315 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(15, 0), KTextEditor::Cursor(35, 0)), Kate::TextFolding::Folded) == 2); 0316 0317 // we shall have now exactly two range toplevel and one embedded fold 0318 folding.debugPrint(QStringLiteral("Two Toplevel Folds, One Nested Fold")); 0319 QVERIFY(folding.debugDump() == QLatin1String("tree [5:0 f 10:0] [15:0 f [20:0 f 30:0] 35:0] - folded [5:0 f 10:0] [15:0 f 35:0]")); 0320 0321 // 25 lines are hidden 0322 QVERIFY(folding.visibleLines() == (100 - 5 - 20)); 0323 0324 // check line mapping 0325 QVERIFY(folding.lineToVisibleLine(5) == 5); 0326 for (int i = 11; i <= 15; ++i) { 0327 QVERIFY(folding.lineToVisibleLine(i) == (i - 5)); 0328 } 0329 0330 // special case: hidden lines, should fall ack to last visible one! 0331 for (int i = 16; i <= 35; ++i) { 0332 QVERIFY(folding.lineToVisibleLine(i) == 10); 0333 } 0334 0335 for (int i = 36; i <= 40; ++i) { 0336 QVERIFY(folding.lineToVisibleLine(i) == (i - 25)); 0337 } 0338 0339 // we shall be able to insert new range, should lead to nested folds! 0340 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(0, 0), KTextEditor::Cursor(50, 0)), Kate::TextFolding::Folded) == 3); 0341 0342 // we shall have now exactly one range toplevel and many embedded fold 0343 folding.debugPrint(QStringLiteral("One Toplevel + Embedded Folds")); 0344 QVERIFY(folding.debugDump() == QLatin1String("tree [0:0 f [5:0 f 10:0] [15:0 f [20:0 f 30:0] 35:0] 50:0] - folded [0:0 f 50:0]")); 0345 0346 // there shall still be one range starting at 20 0347 forLine = folding.foldingRangesStartingOnLine(20); 0348 QVERIFY(forLine.size() == 1); 0349 QVERIFY(forLine[0].first == 1); 0350 QVERIFY(forLine[0].second & Kate::TextFolding::Folded); 0351 0352 // add more regions starting at 20 0353 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(20, 5), KTextEditor::Cursor(24, 0)), Kate::TextFolding::Folded) == 4); 0354 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(20, 3), KTextEditor::Cursor(25, 0)), Kate::TextFolding::Folded) == 5); 0355 folding.debugPrint(QStringLiteral("More ranges at 20")); 0356 0357 // there shall still be three ranges starting at 20 0358 forLine = folding.foldingRangesStartingOnLine(20); 0359 QVERIFY(forLine.size() == 3); 0360 QVERIFY(forLine[0].first == 1); 0361 QVERIFY(forLine[0].second & Kate::TextFolding::Folded); 0362 QVERIFY(forLine[1].first == 5); 0363 QVERIFY(forLine[1].second & Kate::TextFolding::Folded); 0364 QVERIFY(forLine[2].first == 4); 0365 QVERIFY(forLine[2].second & Kate::TextFolding::Folded); 0366 0367 // 50 lines are hidden 0368 QVERIFY(folding.visibleLines() == (100 - 50)); 0369 0370 // save state 0371 QJsonDocument folds = folding.exportFoldingRanges(); 0372 QString textDump = folding.debugDump(); 0373 0374 // clear folds 0375 folding.clear(); 0376 QVERIFY(folding.debugDump() == QLatin1String("tree - folded ")); 0377 0378 // restore state 0379 folding.importFoldingRanges(folds); 0380 QCOMPARE(folding.debugDump(), textDump); 0381 } 0382 0383 void KateTextBufferTest::nestedFoldingTest() 0384 { 0385 // construct an empty text buffer & folding info 0386 KTextEditor::DocumentPrivate doc; 0387 Kate::TextBuffer &buffer = doc.buffer(); 0388 Kate::TextFolding folding(buffer); 0389 0390 // insert two nested folds in 5 lines 0391 buffer.startEditing(); 0392 for (int i = 0; i < 4; ++i) { 0393 buffer.wrapLine(KTextEditor::Cursor(0, 0)); 0394 } 0395 buffer.finishEditing(); 0396 0397 QVERIFY(buffer.lines() == 5); 0398 0399 // folding for line 1 0400 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(0, 0), KTextEditor::Cursor(3, 0)), Kate::TextFolding::Folded) == 0); 0401 QVERIFY(folding.newFoldingRange(KTextEditor::Range(KTextEditor::Cursor(1, 0), KTextEditor::Cursor(2, 0)), Kate::TextFolding::Folded) == 1); 0402 0403 QVERIFY(folding.foldRange(1)); 0404 QVERIFY(folding.foldRange(0)); 0405 0406 QVERIFY(folding.unfoldRange(0)); 0407 QVERIFY(folding.unfoldRange(1)); 0408 } 0409 0410 void KateTextBufferTest::saveFileInUnwritableFolder() 0411 { 0412 // create temp dir and get file name inside 0413 QTemporaryDir dir; 0414 QVERIFY(dir.isValid()); 0415 const QString folder_name = dir.path(); 0416 const QString file_path = folder_name + QLatin1String("/foo"); 0417 0418 QFile f(file_path); 0419 QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); 0420 f.write("1234567890"); 0421 QVERIFY(f.flush()); 0422 f.close(); 0423 0424 QFile::setPermissions(folder_name, QFile::ExeOwner); 0425 0426 KTextEditor::DocumentPrivate doc; 0427 Kate::TextBuffer &buffer = doc.buffer(); 0428 buffer.setTextCodec(QStringLiteral("UTF-8")); 0429 buffer.setFallbackTextCodec(QStringLiteral("UTF-8")); 0430 bool a; 0431 bool b; 0432 int c; 0433 buffer.load(file_path, a, b, c, true); 0434 buffer.clear(); 0435 buffer.startEditing(); 0436 buffer.insertText(KTextEditor::Cursor(0, 0), QStringLiteral("ABC")); 0437 buffer.finishEditing(); 0438 buffer.save(file_path); 0439 0440 f.open(QIODevice::ReadOnly); 0441 QCOMPARE(f.readAll(), QByteArray("ABC")); 0442 f.close(); 0443 0444 QFile::setPermissions(folder_name, QFile::WriteOwner | QFile::ExeOwner); 0445 QVERIFY(f.remove()); 0446 QVERIFY(dir.remove()); 0447 } 0448 0449 void KateTextBufferTest::lineLengthLimit() 0450 { 0451 // create temp dir and get file name inside 0452 QTemporaryDir dir; 0453 QVERIFY(dir.isValid()); 0454 const QString file_path = dir.path() + QLatin1String("/foo"); 0455 0456 // create some 'long' lines 0457 QFile f(file_path); 0458 QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); 0459 f.write("0123456789\n0123456 789\n01234 56789"); 0460 QVERIFY(f.flush()); 0461 f.close(); 0462 0463 // load with long line limit 8 and inspect results 0464 KTextEditor::DocumentPrivate doc; 0465 Kate::TextBuffer buffer(&doc, true); 0466 buffer.setTextCodec(QStringLiteral("UTF-8")); 0467 buffer.setFallbackTextCodec(QStringLiteral("UTF-8")); 0468 buffer.setLineLengthLimit(8); 0469 bool encodingErrors = false; 0470 bool tooLongLinesWrapped = false; 0471 int longestLineLoaded = 0; 0472 buffer.load(file_path, encodingErrors, tooLongLinesWrapped, longestLineLoaded, true); 0473 QVERIFY(!encodingErrors); 0474 QVERIFY(tooLongLinesWrapped); 0475 QCOMPARE(longestLineLoaded, 11); 0476 QCOMPARE(buffer.lines(), 6); 0477 QCOMPARE(buffer.line(0).text(), QLatin1String("01234567")); 0478 QCOMPARE(buffer.line(1).text(), QLatin1String("89")); 0479 QCOMPARE(buffer.line(2).text(), QLatin1String("0123456 ")); 0480 QCOMPARE(buffer.line(3).text(), QLatin1String("789")); 0481 QCOMPARE(buffer.line(4).text(), QLatin1String("01234 56")); 0482 QCOMPARE(buffer.line(5).text(), QLatin1String("789")); 0483 } 0484 0485 #if HAVE_KAUTH 0486 void KateTextBufferTest::saveFileWithElevatedPrivileges() 0487 { 0488 // create temp dir and get file name inside 0489 QTemporaryDir dir; 0490 QVERIFY(dir.isValid()); 0491 const QString file_path = dir.path() + QLatin1String("/foo"); 0492 0493 QFile f(file_path); 0494 QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); 0495 f.write("1234567890"); 0496 QVERIFY(f.flush()); 0497 f.close(); 0498 0499 KTextEditor::DocumentPrivate doc; 0500 Kate::TextBuffer buffer(&doc, true); 0501 buffer.setTextCodec(QStringLiteral("UTF-8")); 0502 buffer.setFallbackTextCodec(QStringLiteral("UTF-8")); 0503 bool a; 0504 bool b; 0505 int c; 0506 buffer.load(file_path, a, b, c, true); 0507 buffer.clear(); 0508 buffer.startEditing(); 0509 buffer.insertText(KTextEditor::Cursor(0, 0), QStringLiteral("ABC")); 0510 buffer.finishEditing(); 0511 qDebug() << buffer.text(); 0512 buffer.save(file_path); 0513 0514 f.open(QIODevice::ReadOnly); 0515 QCOMPARE(f.readAll(), QByteArray("ABC")); 0516 f.close(); 0517 0518 QVERIFY(f.remove()); 0519 QVERIFY(dir.remove()); 0520 } 0521 #endif 0522 0523 #include "moc_katetextbuffertest.cpp"