File indexing completed on 2024-12-01 03:42:29

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2012-2018 Dominik Haumann <dhaumann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kte_documentcursor.h"
0009 
0010 #include <katedocument.h>
0011 #include <kateglobal.h>
0012 #include <ktexteditor/documentcursor.h>
0013 #include <ktexteditor/view.h>
0014 
0015 #include <QTest>
0016 
0017 QTEST_MAIN(DocumentCursorTest)
0018 
0019 using namespace KTextEditor;
0020 
0021 DocumentCursorTest::DocumentCursorTest()
0022     : QObject()
0023 {
0024 }
0025 
0026 DocumentCursorTest::~DocumentCursorTest()
0027 {
0028 }
0029 
0030 void DocumentCursorTest::initTestCase()
0031 {
0032     KTextEditor::EditorPrivate::enableUnitTestMode();
0033 }
0034 
0035 void DocumentCursorTest::cleanupTestCase()
0036 {
0037 }
0038 
0039 // tests:
0040 // - atStartOfDocument
0041 // - atStartOfLine
0042 // - atEndOfDocument
0043 // - atEndOfLine
0044 // - move forward with Wrap
0045 // - move forward with NoWrap
0046 // - move backward
0047 // - gotoNextLine
0048 // - gotoPreviousLine
0049 void DocumentCursorTest::testConvenienceApi()
0050 {
0051     KTextEditor::DocumentPrivate doc;
0052     doc.setText(
0053         QStringLiteral("\n"
0054                        "1\n"
0055                        "22\n"
0056                        "333\n"
0057                        "4444\n"
0058                        "55555"));
0059 
0060     // check start and end of document
0061     DocumentCursor startOfDoc(&doc);
0062     startOfDoc.setPosition(Cursor(0, 0));
0063     DocumentCursor endOfDoc(&doc);
0064     endOfDoc.setPosition(Cursor(5, 5));
0065     QVERIFY(startOfDoc.atStartOfDocument());
0066     QVERIFY(startOfDoc.atStartOfLine());
0067     QVERIFY(endOfDoc.atEndOfDocument());
0068     QVERIFY(endOfDoc.atEndOfLine());
0069 
0070     // set cursor to (2, 2) and then move to the left two times
0071     DocumentCursor moving(&doc);
0072     moving.setPosition(Cursor(2, 2));
0073     QVERIFY(moving.atEndOfLine()); // at 2, 2
0074     QVERIFY(moving.move(-1)); // at 2, 1
0075     QCOMPARE(moving.toCursor(), Cursor(2, 1));
0076     QVERIFY(!moving.atEndOfLine());
0077     QVERIFY(moving.move(-1)); // at 2, 0
0078     QCOMPARE(moving.toCursor(), Cursor(2, 0));
0079     QVERIFY(moving.atStartOfLine());
0080 
0081     // now move again to the left, should wrap to (1, 1)
0082     QVERIFY(moving.move(-1)); // at 1, 1
0083     QCOMPARE(moving.toCursor(), Cursor(1, 1));
0084     QVERIFY(moving.atEndOfLine());
0085 
0086     // advance 7 characters to position (3, 3)
0087     QVERIFY(moving.move(7)); // at 3, 3
0088     QCOMPARE(moving.toCursor(), Cursor(3, 3));
0089 
0090     // advance 20 characters in NoWrap mode, then go back 10 characters
0091     QVERIFY(moving.move(20, DocumentCursor::NoWrap)); // at 3, 23
0092     QCOMPARE(moving.toCursor(), Cursor(3, 23));
0093     QVERIFY(moving.move(-10)); // at 3, 13
0094     QCOMPARE(moving.toCursor(), Cursor(3, 13));
0095 
0096     // still at invalid text position. move one char to wrap around
0097     QVERIFY(!moving.isValidTextPosition()); // at 3, 13
0098     QVERIFY(moving.move(1)); // at 4, 0
0099     QCOMPARE(moving.toCursor(), Cursor(4, 0));
0100 
0101     // moving 11 characters in wrap mode moves to (5, 6), which is not a valid
0102     // text position anymore. Hence, moving should be rejected.
0103     QVERIFY(!moving.move(11));
0104     QVERIFY(moving.move(10));
0105     QVERIFY(moving.atEndOfDocument());
0106 
0107     // try to move to next line, which fails. then go to previous line
0108     QVERIFY(!moving.gotoNextLine());
0109     QVERIFY(moving.gotoPreviousLine());
0110     QCOMPARE(moving.toCursor(), Cursor(4, 0));
0111 }
0112 
0113 void DocumentCursorTest::testOperators()
0114 {
0115     KTextEditor::DocumentPrivate doc;
0116     doc.setText(
0117         QStringLiteral("--oo--\n"
0118                        "--oo--\n"
0119                        "--oo--"));
0120 
0121     // create lots of cursors for comparison
0122     Cursor invalid = Cursor::invalid();
0123     Cursor c02(0, 2);
0124     Cursor c04(0, 4);
0125     Cursor c14(1, 4);
0126 
0127     DocumentCursor m02(&doc);
0128     DocumentCursor m04(&doc);
0129     DocumentCursor m14(&doc);
0130 
0131     QVERIFY(m02 == invalid);
0132     QVERIFY(m04 == invalid);
0133     QVERIFY(m14 == invalid);
0134 
0135     m02.setPosition(Cursor(0, 2));
0136     m04.setPosition(Cursor(0, 4));
0137     m14.setPosition(Cursor(1, 4));
0138 
0139     // invalid comparison
0140     // cppcheck-suppress duplicateExpression
0141     QVERIFY(invalid == invalid);
0142     QVERIFY(invalid <= c02);
0143     QVERIFY(invalid < c02);
0144     QVERIFY(!(invalid > c02));
0145     QVERIFY(!(invalid >= c02));
0146 
0147     QVERIFY(!(invalid == m02));
0148     QVERIFY(invalid <= m02);
0149     QVERIFY(invalid < m02);
0150     QVERIFY(!(invalid > m02));
0151     QVERIFY(!(invalid >= m02));
0152 
0153     QVERIFY(!(m02 == invalid));
0154     QVERIFY(!(m02 <= invalid));
0155     QVERIFY(!(m02 < invalid));
0156     QVERIFY(m02 > invalid);
0157     QVERIFY(m02 >= invalid);
0158 
0159     // DocumentCursor <-> DocumentCursor
0160     // cppcheck-suppress duplicateExpression
0161     QVERIFY(m02 == m02);
0162     // cppcheck-suppress duplicateExpression
0163     QVERIFY(m02 <= m02);
0164     // cppcheck-suppress duplicateExpression
0165     QVERIFY(m02 >= m02);
0166     // cppcheck-suppress duplicateExpression
0167     QVERIFY(!(m02 < m02));
0168     // cppcheck-suppress duplicateExpression
0169     QVERIFY(!(m02 > m02));
0170     // cppcheck-suppress duplicateExpression
0171     QVERIFY(!(m02 != m02));
0172 
0173     QVERIFY(!(m02 == m04));
0174     QVERIFY(m02 <= m04);
0175     QVERIFY(!(m02 >= m04));
0176     QVERIFY(m02 < m04);
0177     QVERIFY(!(m02 > m04));
0178     QVERIFY(m02 != m04);
0179 
0180     QVERIFY(!(m04 == m02));
0181     QVERIFY(!(m04 <= m02));
0182     QVERIFY(m04 >= m02);
0183     QVERIFY(!(m04 < m02));
0184     QVERIFY(m04 > m02);
0185     QVERIFY(m04 != m02);
0186 
0187     QVERIFY(!(m02 == m14));
0188     QVERIFY(m02 <= m14);
0189     QVERIFY(!(m02 >= m14));
0190     QVERIFY(m02 < m14);
0191     QVERIFY(!(m02 > m14));
0192     QVERIFY(m02 != m14);
0193 
0194     QVERIFY(!(m14 == m02));
0195     QVERIFY(!(m14 <= m02));
0196     QVERIFY(m14 >= m02);
0197     QVERIFY(!(m14 < m02));
0198     QVERIFY(m14 > m02);
0199     QVERIFY(m14 != m02);
0200 
0201     // DocumentCursor <-> Cursor
0202     QVERIFY(m02 == c02);
0203     QVERIFY(m02 <= c02);
0204     QVERIFY(m02 >= c02);
0205     QVERIFY(!(m02 < c02));
0206     QVERIFY(!(m02 > c02));
0207     QVERIFY(!(m02 != c02));
0208 
0209     QVERIFY(!(m02 == c04));
0210     QVERIFY(m02 <= c04);
0211     QVERIFY(!(m02 >= c04));
0212     QVERIFY(m02 < c04);
0213     QVERIFY(!(m02 > c04));
0214     QVERIFY(m02 != c04);
0215 
0216     QVERIFY(!(m04 == c02));
0217     QVERIFY(!(m04 <= c02));
0218     QVERIFY(m04 >= c02);
0219     QVERIFY(!(m04 < c02));
0220     QVERIFY(m04 > c02);
0221     QVERIFY(m04 != c02);
0222 
0223     QVERIFY(!(m02 == c14));
0224     QVERIFY(m02 <= c14);
0225     QVERIFY(!(m02 >= c14));
0226     QVERIFY(m02 < c14);
0227     QVERIFY(!(m02 > c14));
0228     QVERIFY(m02 != c14);
0229 
0230     QVERIFY(!(m14 == c02));
0231     QVERIFY(!(m14 <= c02));
0232     QVERIFY(m14 >= c02);
0233     QVERIFY(!(m14 < c02));
0234     QVERIFY(m14 > c02);
0235     QVERIFY(m14 != c02);
0236 
0237     // Cursor <-> DocumentCursor
0238     QVERIFY(c02 == m02);
0239     QVERIFY(c02 <= m02);
0240     QVERIFY(c02 >= m02);
0241     QVERIFY(!(c02 < m02));
0242     QVERIFY(!(c02 > m02));
0243     QVERIFY(!(c02 != m02));
0244 
0245     QVERIFY(!(c02 == m04));
0246     QVERIFY(c02 <= m04);
0247     QVERIFY(!(c02 >= m04));
0248     QVERIFY(c02 < m04);
0249     QVERIFY(!(c02 > m04));
0250     QVERIFY(c02 != m04);
0251 
0252     QVERIFY(!(c04 == m02));
0253     QVERIFY(!(c04 <= m02));
0254     QVERIFY(c04 >= m02);
0255     QVERIFY(!(c04 < m02));
0256     QVERIFY(c04 > m02);
0257     QVERIFY(c04 != m02);
0258 
0259     QVERIFY(!(c02 == m14));
0260     QVERIFY(c02 <= m14);
0261     QVERIFY(!(c02 >= m14));
0262     QVERIFY(c02 < m14);
0263     QVERIFY(!(c02 > m14));
0264     QVERIFY(c02 != m14);
0265 
0266     QVERIFY(!(c14 == m02));
0267     QVERIFY(!(c14 <= m02));
0268     QVERIFY(c14 >= m02);
0269     QVERIFY(!(c14 < m02));
0270     QVERIFY(c14 > m02);
0271     QVERIFY(c14 != m02);
0272 }
0273 
0274 void DocumentCursorTest::testValidTextPosition()
0275 {
0276     // test: utf-32 characters that are visually only one single character (Unicode surrogates)
0277     // Inside such a utf-32 character, the text position is not valid.
0278     KTextEditor::DocumentPrivate doc;
0279     DocumentCursor c(&doc);
0280 
0281     // 0xfffe: byte-order-mark (BOM)
0282     // 0x002d: '-'
0283     // 0x3dd8, 0x38de: an utf32-surrogate, see https://www.fileformat.info/info/unicode/char/1f638/browsertest.htm
0284     const char16_t line0[] = {0xfffe, 0x2d00, 0x3dd8, 0x38de, 0x2d00}; // -xx- where xx is one utf32 char
0285     const char16_t line1[] = {0xfffe, 0x2d00, 0x3dd8, 0x2d00, 0x2d00}; // -x-- where x is a high surrogate (broken utf32)
0286     const char16_t line2[] = {0xfffe, 0x2d00, 0x2d00, 0x38de, 0x2d00}; // --x- where x is a low surrogate (broken utf32)
0287     doc.setText(QString::fromUtf16(line0, 5));
0288     doc.insertLine(1, QString::fromUtf16(line1, 5));
0289     doc.insertLine(2, QString::fromUtf16(line2, 5));
0290 
0291     // set to true if you want to see the contents
0292     const bool showView = false;
0293     if (showView) {
0294         doc.createView(nullptr)->show();
0295         QTest::qWait(5000);
0296     }
0297 
0298     // line 0: invalid in utf-32 char
0299     c.setPosition(0, 0);
0300     QVERIFY(c.isValidTextPosition());
0301     c.setPosition(0, 1);
0302     QVERIFY(c.isValidTextPosition());
0303     c.setPosition(0, 2);
0304     QVERIFY(!c.isValidTextPosition());
0305     c.setPosition(0, 3);
0306     QVERIFY(c.isValidTextPosition());
0307     c.setPosition(0, 4);
0308     QVERIFY(c.isValidTextPosition());
0309     c.setPosition(0, 5);
0310     QVERIFY(!c.isValidTextPosition());
0311 
0312     // line 1, valid in broken utf-32 char (2nd part missing)
0313     c.setPosition(1, 0);
0314     QVERIFY(c.isValidTextPosition());
0315     c.setPosition(1, 1);
0316     QVERIFY(c.isValidTextPosition());
0317     c.setPosition(1, 2);
0318     QVERIFY(c.isValidTextPosition());
0319     c.setPosition(1, 3);
0320     QVERIFY(c.isValidTextPosition());
0321     c.setPosition(1, 4);
0322     QVERIFY(c.isValidTextPosition());
0323     c.setPosition(1, 5);
0324     QVERIFY(!c.isValidTextPosition());
0325 
0326     // line 2, valid in broken utf-32 char (1st part missing)
0327     c.setPosition(2, 0);
0328     QVERIFY(c.isValidTextPosition());
0329     c.setPosition(2, 1);
0330     QVERIFY(c.isValidTextPosition());
0331     c.setPosition(2, 2);
0332     QVERIFY(c.isValidTextPosition());
0333     c.setPosition(2, 3);
0334     QVERIFY(c.isValidTextPosition());
0335     c.setPosition(2, 4);
0336     QVERIFY(c.isValidTextPosition());
0337     c.setPosition(2, 5);
0338     QVERIFY(!c.isValidTextPosition());
0339 
0340     // test out-of-range
0341     c.setPosition(-1, 0);
0342     QVERIFY(!c.isValidTextPosition());
0343     c.setPosition(3, 0);
0344     QVERIFY(!c.isValidTextPosition());
0345     c.setPosition(0, -1);
0346     QVERIFY(!c.isValidTextPosition());
0347 }
0348 
0349 #include "moc_kte_documentcursor.cpp"