File indexing completed on 2024-05-05 12:22:04

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"