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