File indexing completed on 2024-05-12 05:55:45

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2023 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "searchtooltest.hpp"
0010 
0011 // test object
0012 #include <view/search/searchtool.hpp>
0013 #include <view/search/searchuserqueryable.hpp>
0014 // Okteta Kasten gui
0015 #include <Kasten/Okteta/ByteArrayView>
0016 // Okteta Kasten core
0017 #include <Kasten/Okteta/ByteArrayDocument>
0018 // Okteta Core
0019 #include <Okteta/PieceTableByteArrayModel>
0020 // Qt
0021 #include <QTest>
0022 
0023 Q_DECLARE_METATYPE(Okteta::AddressRange)
0024 namespace Okteta {
0025 char* toString(const AddressRange& addressRange)
0026 {
0027     const QByteArray string(QByteArrayLiteral("Okteta::AddressRange(") +
0028                             QByteArray::number(addressRange.start()) + '-' +
0029                             QByteArray::number(addressRange.end()) + ')');
0030     return qstrdup(string.data());
0031 }
0032 }
0033 
0034 Q_DECLARE_METATYPE(Kasten::FindDirection)
0035 
0036 enum WrapQueryStatus
0037 {
0038     WrapNotQueried,
0039     WrapQueried
0040 };
0041 Q_DECLARE_METATYPE(WrapQueryStatus)
0042 char* toString(const WrapQueryStatus& status)
0043 {
0044     if (status == WrapNotQueried) {
0045         return qstrdup("WrapNotQueried");
0046     }
0047     return qstrdup("WrapQueried");
0048 }
0049 
0050 template<int N> QByteArray byteArrayFromLiteral(const char (&data)[N])
0051 {
0052    return QByteArray::fromRawData(data, N-1);
0053 }
0054 
0055 class TestSearchUserQueryable : public Kasten::If::SearchUserQueryable
0056 {
0057 public:
0058     TestSearchUserQueryable() = default;
0059 
0060 public:
0061     WrapQueryStatus wrapQueryStatus() const { return mWrapQueryStatus; }
0062 
0063 public: // If::SearchUserQueryable API
0064     bool queryContinue(Kasten::FindDirection direction) const override;
0065 
0066 private:
0067     mutable WrapQueryStatus mWrapQueryStatus = WrapNotQueried;
0068 };
0069 
0070 bool TestSearchUserQueryable::queryContinue(Kasten::FindDirection direction) const
0071 {
0072     Q_UNUSED(direction);
0073     mWrapQueryStatus = WrapQueried;
0074     return true;
0075 }
0076 
0077 /*
0078     Markup format:
0079     "_":  cursor, ignored currently when there is a selection -> ViewData.cursorPosition
0080     "[]": selection -> ViewData.selection
0081     "{}": search match result -> ViewData.match
0082 */
0083 ViewData SearchToolTest::parseToViewData(const QByteArray& viewMarkup) const
0084 {
0085     ViewData result;
0086 
0087     bool cursorSeen = false;
0088     bool selectionBeginSeen = false;
0089     bool selectionEndSeen = false;
0090     bool matchBeginSeen = false;
0091     bool matchEndSeen = false;
0092 
0093     for (const char c : viewMarkup) {
0094         if (c == '_') {
0095             Q_ASSERT(!cursorSeen);
0096             result.cursorPosition = result.data.size();
0097             cursorSeen = true;
0098         } else if (c == '[') {
0099             Q_ASSERT(!selectionBeginSeen);
0100             result.selection.setStart(result.data.size());
0101             selectionBeginSeen = true;
0102         } else if (c == ']') {
0103             Q_ASSERT(selectionBeginSeen && !selectionEndSeen);
0104             result.selection.setEnd(result.data.size() - 1);
0105             selectionEndSeen = true;
0106         } else if (c == '{') {
0107             Q_ASSERT(!matchBeginSeen);
0108             result.match.setStart(result.data.size());
0109             matchBeginSeen = true;
0110         } else if (c == '}') {
0111             Q_ASSERT(matchBeginSeen && !matchEndSeen);
0112             result.match.setEnd(result.data.size() - 1);
0113             matchEndSeen = true;
0114         } else  {
0115             result.data.append(c);
0116         }
0117     }
0118 
0119     return result;
0120 }
0121 
0122 void SearchToolTest::testParseToViewData_data()
0123 {
0124     QTest::addColumn<QByteArray>("viewMarkup");
0125     QTest::addColumn<Okteta::Address>("cursorPosition");
0126     QTest::addColumn<Okteta::AddressRange>("selection");
0127     QTest::addColumn<Okteta::AddressRange>("match");
0128     QTest::addColumn<QByteArray>("data");
0129 
0130     QTest::newRow("cursor-begin-selection-none")
0131         << byteArrayFromLiteral("_aaa")
0132         << Okteta::Address(0)
0133         << Okteta::AddressRange()
0134         << Okteta::AddressRange()
0135         << byteArrayFromLiteral("aaa");
0136 
0137     QTest::newRow("cursor-end-selection-none")
0138         << byteArrayFromLiteral("aaa_")
0139         << Okteta::Address(3)
0140         << Okteta::AddressRange()
0141         << Okteta::AddressRange()
0142         << byteArrayFromLiteral("aaa");
0143 
0144     QTest::newRow("cursor-begin-selection-all")
0145         << byteArrayFromLiteral("_[aaa]")
0146         << Okteta::Address(0)
0147         << Okteta::AddressRange(0, 2)
0148         << Okteta::AddressRange()
0149         << byteArrayFromLiteral("aaa");
0150 
0151     QTest::newRow("cursor-end-selection-all")
0152         << byteArrayFromLiteral("[aaa]_")
0153         << Okteta::Address(3)
0154         << Okteta::AddressRange(0, 2)
0155         << Okteta::AddressRange()
0156         << byteArrayFromLiteral("aaa");
0157 
0158     QTest::newRow("cursor-begin-selection-firsthalf")
0159         << byteArrayFromLiteral("_[aaa]bbb")
0160         << Okteta::Address(0)
0161         << Okteta::AddressRange(0, 2)
0162         << Okteta::AddressRange()
0163         << byteArrayFromLiteral("aaabbb");
0164 
0165     QTest::newRow("cursor-end-selection-firsthalf")
0166         << byteArrayFromLiteral("[aaa]_bbb")
0167         << Okteta::Address(3)
0168         << Okteta::AddressRange(0, 2)
0169         << Okteta::AddressRange()
0170         << byteArrayFromLiteral("aaabbb");
0171 
0172     QTest::newRow("cursor-begin-selection-secondhalf")
0173         << byteArrayFromLiteral("{aaa}_[bbb]")
0174         << Okteta::Address(3)
0175         << Okteta::AddressRange(3, 5)
0176         << Okteta::AddressRange(0, 2)
0177         << byteArrayFromLiteral("aaabbb");
0178 
0179     QTest::newRow("cursor-end-selection-secondhalf")
0180         << byteArrayFromLiteral("aaa{[bbb]}_")
0181         << Okteta::Address(6)
0182         << Okteta::AddressRange(3, 5)
0183         << Okteta::AddressRange(3, 5)
0184         << byteArrayFromLiteral("aaabbb");
0185 }
0186 
0187 void SearchToolTest::testParseToViewData()
0188 {
0189     QFETCH(const QByteArray, viewMarkup);
0190     QFETCH(const Okteta::Address, cursorPosition);
0191     QFETCH(const Okteta::AddressRange, selection);
0192     QFETCH(const Okteta::AddressRange, match);
0193     QFETCH(const QByteArray, data);
0194 
0195     const ViewData viewData = parseToViewData(viewMarkup);
0196 
0197     QCOMPARE(viewData.data, data);
0198     QCOMPARE(viewData.cursorPosition, cursorPosition);
0199     QCOMPARE(viewData.selection, selection);
0200     QCOMPARE(viewData.match, match);
0201 }
0202 
0203 void SearchToolTest::testSearch_data()
0204 {
0205     QTest::addColumn<QByteArray>("viewMarkup");
0206     QTest::addColumn<QByteArray>("searchData");
0207     QTest::addColumn<Kasten::FindDirection>("direction");
0208     QTest::addColumn<WrapQueryStatus>("wrapQueryStatus");
0209 
0210     QTest::newRow("cursor-begin-selection-none-forward-single")
0211         << byteArrayFromLiteral("_{a}bbbaaa")
0212         << byteArrayFromLiteral("a")
0213         << Kasten::FindForward
0214         << WrapNotQueried;
0215 
0216     QTest::newRow("cursor-begin-selection-none-forward-single-nonlatin")
0217         << byteArrayFromLiteral("_{\xf0}bbb\xf0\xf0\xf0")
0218         << byteArrayFromLiteral("\xf0")
0219         << Kasten::FindForward
0220         << WrapNotQueried;
0221 
0222     QTest::newRow("cursor-begin-selection-none-forward-multi")
0223         << byteArrayFromLiteral("_{aaa}bbbaaa")
0224         << byteArrayFromLiteral("aaa")
0225         << Kasten::FindForward
0226         << WrapNotQueried;
0227 
0228     QTest::newRow("cursor-begin-selection-none-forward-multi-nonlatin")
0229         << byteArrayFromLiteral("_{aa\xf0}bbbaa\xf0")
0230         << byteArrayFromLiteral("aa\xf0")
0231         << Kasten::FindForward
0232         << WrapNotQueried;
0233 
0234     QTest::newRow("cursor-begin-selection-partmatch-forward-multi")
0235         << byteArrayFromLiteral("_{[a]aa}bbbaaa")
0236         << byteArrayFromLiteral("aaa")
0237         << Kasten::FindForward
0238         << WrapNotQueried;
0239 
0240     QTest::newRow("cursor-begin-selection-match-forward-single")
0241         << byteArrayFromLiteral("_[a]bbb{a}")
0242         << byteArrayFromLiteral("a")
0243         << Kasten::FindForward
0244         << WrapNotQueried;
0245 
0246     QTest::newRow("cursor-begin-selection-match-forward-multi")
0247         << byteArrayFromLiteral("_[aaa]bbb{aaa}")
0248         << byteArrayFromLiteral("aaa")
0249         << Kasten::FindForward
0250         << WrapNotQueried;
0251 
0252     QTest::newRow("cursor-begin-selection-match-overlap-forward-multi")
0253         << byteArrayFromLiteral("_[aaa]abb{aaa}")
0254         << byteArrayFromLiteral("aaa")
0255         << Kasten::FindForward
0256         << WrapNotQueried;
0257 
0258     QTest::newRow("cursor-begin-selection-match-overlap-duplicate-forward-multi")
0259         << byteArrayFromLiteral("_[aaa]{aaa}aaa")
0260         << byteArrayFromLiteral("aaa")
0261         << Kasten::FindForward
0262         << WrapNotQueried;
0263 
0264     QTest::newRow("cursor-beforeend-selection-none-forward-single")
0265         << byteArrayFromLiteral("aaabbb_{a}")
0266         << byteArrayFromLiteral("a")
0267         << Kasten::FindForward
0268         << WrapNotQueried;
0269 
0270     QTest::newRow("cursor-beforeend-selection-none-forward-multi")
0271         << byteArrayFromLiteral("aaabbb_{aaa}")
0272         << byteArrayFromLiteral("aaa")
0273         << Kasten::FindForward
0274         << WrapNotQueried;
0275 
0276     QTest::newRow("cursor-beforeend-selection-partmatch-forward-multi")
0277         << byteArrayFromLiteral("aaabbb{[aa]_a}")
0278         << byteArrayFromLiteral("aaa")
0279         << Kasten::FindForward
0280         << WrapNotQueried;
0281 
0282     QTest::newRow("cursor-beforeend-selection-wrongpartmatch1-forward-multi")
0283         << byteArrayFromLiteral("{aaa}bbbaa[a]_")
0284         << byteArrayFromLiteral("aaa")
0285         << Kasten::FindForward
0286         << WrapQueried;
0287 
0288     QTest::newRow("cursor-beforeend-selection-wrongpartmatch2-forward-multi")
0289         << byteArrayFromLiteral("{aaa}bbba[aa]_")
0290         << byteArrayFromLiteral("aaa")
0291         << Kasten::FindForward
0292         << WrapQueried;
0293 
0294     QTest::newRow("cursor-end-selection-none-forward-single")
0295         << byteArrayFromLiteral("{a}bbbaaa_")
0296         << byteArrayFromLiteral("a")
0297         << Kasten::FindForward
0298         << WrapQueried;
0299 
0300     QTest::newRow("cursor-end-selection-none-forward-multi")
0301         << byteArrayFromLiteral("{aaa}bbbaaa_")
0302         << byteArrayFromLiteral("aaa")
0303         << Kasten::FindForward
0304         << WrapQueried;
0305 
0306     QTest::newRow("cursor-end-selection-match-forward-single")
0307         << byteArrayFromLiteral("{a}bbb[a]_")
0308         << byteArrayFromLiteral("a")
0309         << Kasten::FindForward
0310         << WrapQueried;
0311 
0312     QTest::newRow("cursor-end-selection-match-forward-multi")
0313         << byteArrayFromLiteral("{aaa}bbb[aaa]_")
0314         << byteArrayFromLiteral("aaa")
0315         << Kasten::FindForward
0316         << WrapQueried;
0317 
0318     QTest::newRow("cursor-end-selection-none-backward-single")
0319         << byteArrayFromLiteral("aaabbb{a}_")
0320         << byteArrayFromLiteral("a")
0321         << Kasten::FindBackward
0322         << WrapNotQueried;
0323 
0324     QTest::newRow("cursor-end-selection-none-backward-multi")
0325         << byteArrayFromLiteral("aaabbb{aaa}_")
0326         << byteArrayFromLiteral("aaa")
0327         << Kasten::FindBackward
0328         << WrapNotQueried;
0329 
0330     QTest::newRow("cursor-end-selection-partmatch-backward-multi")
0331         << byteArrayFromLiteral("aaabbb{aa[a]}_")
0332         << byteArrayFromLiteral("aaa")
0333         << Kasten::FindBackward
0334         << WrapNotQueried;
0335 
0336     QTest::newRow("cursor-end-selection-match-backward-single")
0337         << byteArrayFromLiteral("{a}bbb[a]_")
0338         << byteArrayFromLiteral("a")
0339         << Kasten::FindBackward
0340         << WrapNotQueried;
0341 
0342     QTest::newRow("cursor-end-selection-match-backward-multi")
0343         << byteArrayFromLiteral("{aaa}bbb[aaa]_")
0344         << byteArrayFromLiteral("aaa")
0345         << Kasten::FindBackward
0346         << WrapNotQueried;
0347 
0348     QTest::newRow("cursor-end-selection-match-overlap-backward-multi")
0349         << byteArrayFromLiteral("{aaa}bba[aaa]_")
0350         << byteArrayFromLiteral("aaa")
0351         << Kasten::FindBackward
0352         << WrapNotQueried;
0353 
0354     QTest::newRow("cursor-end-selection-match-overlap-duplicate-backward-multi")
0355         << byteArrayFromLiteral("aaa{aaa}[aaa]_")
0356         << byteArrayFromLiteral("aaa")
0357         << Kasten::FindBackward
0358         << WrapNotQueried;
0359 
0360     QTest::newRow("cursor-begin-selection-none-backward-single")
0361         << byteArrayFromLiteral("_aaabbb{a}")
0362         << byteArrayFromLiteral("a")
0363         << Kasten::FindBackward
0364         << WrapQueried;
0365 
0366     QTest::newRow("cursor-begin-selection-none-backward-single-nonlatin")
0367         << byteArrayFromLiteral("_\xf0\xf0\xf0""bbb{\xf0}")
0368         << byteArrayFromLiteral("\xf0")
0369         << Kasten::FindBackward
0370         << WrapQueried;
0371 
0372     QTest::newRow("cursor-begin-selection-none-backward-multi")
0373         << byteArrayFromLiteral("_aaabbb{aaa}")
0374         << byteArrayFromLiteral("aaa")
0375         << Kasten::FindBackward
0376         << WrapQueried;
0377 
0378     QTest::newRow("cursor-begin-selection-wrongpartmatch1-backward-multi")
0379         << byteArrayFromLiteral("[a]_aabbb{aaa}")
0380         << byteArrayFromLiteral("aaa")
0381         << Kasten::FindBackward
0382         << WrapQueried;
0383 
0384     QTest::newRow("cursor-begin-selection-wrongpartmatch2-backward-multi")
0385         << byteArrayFromLiteral("[aa]_abbb{aaa}")
0386         << byteArrayFromLiteral("aaa")
0387         << Kasten::FindBackward
0388         << WrapQueried;
0389 
0390     QTest::newRow("cursor-begin-selection-match-backward-single")
0391         << byteArrayFromLiteral("[a]_bbb{a}")
0392         << byteArrayFromLiteral("a")
0393         << Kasten::FindBackward
0394         << WrapQueried;
0395 
0396     QTest::newRow("cursor-begin-selection-match-backward-multi")
0397         << byteArrayFromLiteral("[aaa]_bbb{aaa}")
0398         << byteArrayFromLiteral("aaa")
0399         << Kasten::FindBackward
0400         << WrapQueried;
0401 }
0402 
0403 void SearchToolTest::testSearch()
0404 {
0405     QFETCH(const QByteArray, viewMarkup);
0406     QFETCH(const QByteArray, searchData);
0407     QFETCH(const Kasten::FindDirection, direction);
0408     QFETCH(const WrapQueryStatus, wrapQueryStatus);
0409 
0410     const ViewData viewData = parseToViewData(viewMarkup);
0411 
0412     auto* byteArray = new Okteta::PieceTableByteArrayModel(viewData.data);
0413     auto* document = new Kasten::ByteArrayDocument(byteArray, QStringLiteral("init"));
0414     auto* view = new Kasten::ByteArrayView(document, nullptr);
0415 
0416     auto* queryAgent = new TestSearchUserQueryable();
0417     auto* tool = new Kasten::SearchTool();
0418     tool->setTargetModel(view);
0419     tool->setSearchData(searchData);
0420     tool->setUserQueryAgent(queryAgent);
0421 
0422     if (viewData.selection.isValid()) {
0423         view->setSelection(viewData.selection.start(), viewData.selection.end());
0424     } else {
0425         view->setCursorPosition(viewData.cursorPosition);
0426     }
0427 
0428     const Okteta::AddressRange beforeMatch = view->selection();
0429 
0430     tool->search(direction, true, false);
0431 
0432     const Okteta::AddressRange match = view->selection();
0433 
0434     QCOMPARE(match, viewData.match);
0435     QCOMPARE(queryAgent->wrapQueryStatus(), wrapQueryStatus);
0436 
0437     delete tool;
0438     delete view;
0439     delete document;
0440     delete queryAgent;
0441 }
0442 
0443 QTEST_MAIN(SearchToolTest)
0444 
0445 #include "moc_searchtooltest.cpp"