File indexing completed on 2024-05-05 05:53:01

0001 /*
0002  *  Copyright 2010 David Faure <faure@kde.org>
0003  *
0004  *  This program is free software; you can redistribute it and/or
0005  *  modify it under the terms of the GNU General Public License as
0006  *  published by the Free Software Foundation; either version 2 of
0007  *  the License or (at your option) version 3 or any later version
0008  *  accepted by the membership of KDE e.V. (or its successor approved
0009  *  by the membership of KDE e.V.), which shall act as a proxy
0010  *  defined in Section 14 of version 3 of the license.
0011  *
0012  *  This program is distributed in the hope that it will be useful,
0013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  *  GNU General Public License for more details.
0016  *
0017  *  You should have received a copy of the GNU General Public License
0018  *  along with this program.  If not, see <https://www.gnu.org/licenses/>
0019  */
0020 
0021 #include <KActionCollection>
0022 #include <QAction>
0023 
0024 #include <KBookmarkManager>
0025 #include <QMimeData>
0026 #include <QStandardPaths>
0027 #include <QTemporaryFile>
0028 #include <QTest>
0029 
0030 #include "../../kbookmarkmodel/commandhistory.h"
0031 #include "../../kbookmarkmodel/commands.h" // TODO provide public API in the model instead?
0032 #include "../../kbookmarkmodel/model.h"
0033 
0034 // Return a list of all bookmark addresses or urls in a KBookmarkManager.
0035 class BookmarkLister : public KBookmarkGroupTraverser
0036 {
0037 public:
0038     BookmarkLister(const KBookmarkGroup &root)
0039     {
0040         traverse(root);
0041     }
0042     static QStringList addressList(KBookmarkManager *mgr)
0043     {
0044         BookmarkLister lister(mgr->root());
0045         return lister.m_addressList;
0046     }
0047     static QStringList urlList(KBookmarkManager *mgr)
0048     {
0049         BookmarkLister lister(mgr->root());
0050         return lister.m_urlList;
0051     }
0052     static QStringList titleList(KBookmarkManager *mgr)
0053     {
0054         BookmarkLister lister(mgr->root());
0055         return lister.m_titleList;
0056     }
0057     void visit(const KBookmark &bk) override
0058     {
0059         m_addressList.append(bk.address());
0060         m_urlList.append(bk.url().url());
0061         m_titleList.append(bk.text());
0062     }
0063     void visitEnter(const KBookmarkGroup &group) override
0064     {
0065         m_addressList.append(group.address() + QLatin1Char('/'));
0066         m_titleList.append(group.text());
0067     }
0068 
0069 private:
0070     QStringList m_addressList;
0071     QStringList m_urlList;
0072     QStringList m_titleList;
0073 };
0074 
0075 class KBookmarkModelTest : public QObject
0076 {
0077     Q_OBJECT
0078 public:
0079     KBookmarkModelTest()
0080         : m_collection(this)
0081     {
0082     }
0083 
0084 private:
0085 private Q_SLOTS:
0086     void initTestCase()
0087     {
0088         const QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/konqueror/bookmarks.xml");
0089         QFile::remove(filename);
0090         m_bookmarkManager = std::make_unique<KBookmarkManager>(filename);
0091         m_cmdHistory = new CommandHistory(this);
0092         m_cmdHistory->setBookmarkManager(m_bookmarkManager.get());
0093         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList());
0094         m_model = new KBookmarkModel(m_bookmarkManager->root(), m_cmdHistory, this);
0095         QCOMPARE(m_model->rowCount(), 1); // the toplevel "Bookmarks" toplevel item
0096         m_rootIndex = m_model->index(0, 0);
0097         QVERIFY(m_rootIndex.isValid());
0098         QCOMPARE(m_model->rowCount(m_rootIndex), 0);
0099         m_cmdHistory->createActions(&m_collection);
0100     }
0101 
0102     // The commands modify the model, so the test code uses the commands
0103     void testAddBookmark()
0104     {
0105         CreateCommand *cmd =
0106             new CreateCommand(m_model, QStringLiteral("/0"), QStringLiteral("test_bk"), QStringLiteral("www"), QUrl(QStringLiteral("https://www.kde.org")));
0107         cmd->redo();
0108         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList() << QStringLiteral("/0"));
0109         QCOMPARE(BookmarkLister::urlList(m_bookmarkManager.get()), QStringList() << QStringLiteral("https://www.kde.org"));
0110         QCOMPARE(m_model->rowCount(m_rootIndex), 1);
0111         cmd->undo();
0112         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList());
0113         QCOMPARE(m_model->rowCount(m_rootIndex), 0);
0114         delete cmd;
0115     }
0116 
0117     void testDeleteBookmark()
0118     {
0119         CreateCommand *cmd =
0120             new CreateCommand(m_model, QStringLiteral("/0"), QStringLiteral("test_bk"), QStringLiteral("www"), QUrl(QStringLiteral("https://www.kde.org")));
0121         cmd->redo();
0122         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList() << QStringLiteral("/0"));
0123         DeleteCommand *deleteCmd = new DeleteCommand(m_model, QStringLiteral("/0"));
0124         deleteCmd->redo();
0125         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList());
0126         deleteCmd->undo();
0127         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList() << QStringLiteral("/0"));
0128         deleteCmd->redo();
0129         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList());
0130 
0131         delete cmd;
0132         delete deleteCmd;
0133     }
0134 
0135     void testCreateFolder() // and test moving stuff around
0136     {
0137         CreateCommand *folderCmd = new CreateCommand(m_model, QStringLiteral("/0"), QStringLiteral("folder"), QStringLiteral("folder"), true /*open*/);
0138         m_cmdHistory->addCommand(folderCmd); // calls redo
0139         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList() << QStringLiteral("/0/"));
0140         QCOMPARE(m_model->rowCount(m_rootIndex), 1);
0141 
0142         const QString kde = QStringLiteral("https://www.kde.org");
0143         CreateCommand *cmd = new CreateCommand(m_model, QStringLiteral("/0/0"), QStringLiteral("test_bk"), QStringLiteral("www"), QUrl(kde));
0144         m_cmdHistory->addCommand(cmd); // calls redo
0145         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList() << QStringLiteral("/0/") << QStringLiteral("/0/0"));
0146 
0147         // Insert before this bookmark
0148         const QString first = QStringLiteral("https://first.example.com");
0149         m_cmdHistory->addCommand(new CreateCommand(m_model, QStringLiteral("/0/0"), QStringLiteral("first_bk"), QStringLiteral("www"), QUrl(first)));
0150         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0151                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/0/0") << QStringLiteral("/0/1"));
0152         QCOMPARE(BookmarkLister::urlList(m_bookmarkManager.get()), QStringList() << first << kde);
0153 
0154         // Move the kde bookmark before the first bookmark
0155         KBookmark kdeBookmark = m_bookmarkManager->findByAddress(QStringLiteral("/0/1"));
0156         QCOMPARE(kdeBookmark.url().url(), kde);
0157         QModelIndex kdeIndex = m_model->indexForBookmark(kdeBookmark);
0158         QCOMPARE(kdeIndex.row(), 1);
0159         QCOMPARE(m_model->rowCount(kdeIndex.parent()), 2);
0160 
0161         QMimeData *mimeData = m_model->mimeData(QModelIndexList() << kdeIndex);
0162         bool ok = m_model->dropMimeData(mimeData, Qt::MoveAction, 0, 0, kdeIndex.parent());
0163         QVERIFY(ok);
0164         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0165                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/0/0") << QStringLiteral("/0/1"));
0166         QCOMPARE(BookmarkLister::urlList(m_bookmarkManager.get()), QStringList() << kde << first);
0167         delete mimeData;
0168 
0169         // Move the kde bookmark after the bookmark called 'first'
0170         kdeBookmark = m_bookmarkManager->findByAddress(QStringLiteral("/0/0"));
0171         kdeIndex = m_model->indexForBookmark(kdeBookmark);
0172         QCOMPARE(kdeIndex.row(), 0);
0173         mimeData = m_model->mimeData(QModelIndexList() << kdeIndex);
0174         ok = m_model->dropMimeData(mimeData, Qt::MoveAction, 2, 0, kdeIndex.parent());
0175         QVERIFY(ok);
0176         QCOMPARE(BookmarkLister::urlList(m_bookmarkManager.get()), QStringList() << first << kde);
0177         delete mimeData;
0178 
0179         // Create new folder, then move both bookmarks into it (#287038)
0180         m_cmdHistory->addCommand(new CreateCommand(m_model, QStringLiteral("/1"), QStringLiteral("folder2"), QStringLiteral("folder2"), true));
0181         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0182                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/0/0") << QStringLiteral("/0/1") << QStringLiteral("/1/"));
0183         QCOMPARE(m_model->rowCount(m_rootIndex), 2);
0184 
0185         moveTwoBookmarks(QStringLiteral("/0/0"), QStringLiteral("/0/1"), QStringLiteral("/1"));
0186         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0187                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/1/") << QStringLiteral("/1/0") << QStringLiteral("/1/1"));
0188         QCOMPARE(BookmarkLister::urlList(m_bookmarkManager.get()), QStringList() << kde << first);
0189 
0190         // Move bookmarks from /1 into subfolder /1/2 (which will become /1/0)
0191         m_cmdHistory->addCommand(new CreateCommand(m_model, QStringLiteral("/1/2"), QStringLiteral("subfolder"), QStringLiteral("subfolder"), true));
0192         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0193                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/1/") << QStringLiteral("/1/0") << QStringLiteral("/1/1")
0194                                << QStringLiteral("/1/2/"));
0195         moveTwoBookmarks(QStringLiteral("/1/0"), QStringLiteral("/1/1"), QStringLiteral("/1/2"));
0196         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0197                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/1/") << QStringLiteral("/1/0/") << QStringLiteral("/1/0/0")
0198                                << QStringLiteral("/1/0/1"));
0199 
0200         // Move them up again
0201         moveTwoBookmarks(QStringLiteral("/1/0/0"), QStringLiteral("/1/0/1"), QStringLiteral("/1"));
0202         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()),
0203                  QStringList() << QStringLiteral("/0/") << QStringLiteral("/1/") << QStringLiteral("/1/0") << QStringLiteral("/1/1")
0204                                << QStringLiteral("/1/2/"));
0205 
0206         undoAll();
0207     }
0208 
0209     void testSort()
0210     {
0211         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList());
0212         CreateCommand *folderCmd = new CreateCommand(m_model, QStringLiteral("/0"), QStringLiteral("folder"), QStringLiteral("folder"), true /*open*/);
0213         m_cmdHistory->addCommand(folderCmd); // calls redo
0214         const QString kde = QStringLiteral("https://www.kde.org");
0215         QStringList bookmarks;
0216         bookmarks << QStringLiteral("Faure") << QStringLiteral("Web") << QStringLiteral("Kde") << QStringLiteral("Avatar") << QStringLiteral("David");
0217         for (int i = 0; i < bookmarks.count(); ++i) {
0218             m_cmdHistory->addCommand(new CreateCommand(m_model, QStringLiteral("/0/") + QString::number(i), bookmarks[i], QStringLiteral("www"), QUrl(kde)));
0219         }
0220         const QStringList addresses = BookmarkLister::addressList(m_bookmarkManager.get());
0221         // qCDebug(KEDITBOOKMARKS_LOG) << addresses;
0222         const QStringList origTitleList = BookmarkLister::titleList(m_bookmarkManager.get());
0223         QCOMPARE(addresses.count(), bookmarks.count() + 1 /* parent folder */);
0224         SortCommand *sortCmd = new SortCommand(m_model, QStringLiteral("Sort"), QStringLiteral("/0"));
0225         m_cmdHistory->addCommand(sortCmd);
0226         QStringList expectedTitleList = bookmarks;
0227         expectedTitleList.sort();
0228         expectedTitleList.prepend(QStringLiteral("folder"));
0229         const QStringList sortedTitles = BookmarkLister::titleList(m_bookmarkManager.get());
0230         // qCDebug(KEDITBOOKMARKS_LOG) << sortedTitles;
0231         QCOMPARE(sortedTitles, expectedTitleList);
0232 
0233         sortCmd->undo();
0234         QCOMPARE(BookmarkLister::titleList(m_bookmarkManager.get()), origTitleList);
0235         sortCmd->redo();
0236         undoAll();
0237     }
0238 
0239     void testSortBug258505()
0240     {
0241         // Given a specific set of bookmarks from XML
0242         const QString testFile(QFINDTESTDATA("kde-bug-258505-bookmarks.xml"));
0243         QVERIFY(!testFile.isEmpty());
0244         //  (copied to a temp file to avoid saving the sorted bookmarks)
0245         QFile xmlFile(testFile);
0246         QVERIFY(xmlFile.open(QIODevice::ReadOnly));
0247         const QByteArray data = xmlFile.readAll();
0248         QTemporaryFile tempFile;
0249         QVERIFY(tempFile.open());
0250         const QString fileName = tempFile.fileName();
0251         tempFile.write(data);
0252         tempFile.close();
0253         KBookmarkManager bookmarkManager(fileName);
0254 
0255         const QStringList addresses = BookmarkLister::addressList(&bookmarkManager);
0256         const QStringList origTitleList = BookmarkLister::titleList(&bookmarkManager);
0257         // qCDebug(KEDITBOOKMARKS_LOG) << addresses << origTitleList;
0258         QCOMPARE(addresses.count(), 53);
0259 
0260         CommandHistory cmdHistory;
0261         cmdHistory.setBookmarkManager(&bookmarkManager);
0262         KBookmarkModel *model = new KBookmarkModel(bookmarkManager.root(), &cmdHistory, this);
0263         QCOMPARE(model->rowCount(), 1); // the toplevel "Bookmarks" toplevel item
0264 
0265         // When sorting
0266         SortCommand *sortCmd = new SortCommand(model, QStringLiteral("Sort"), QStringLiteral("/0"));
0267         cmdHistory.addCommand(sortCmd);
0268 
0269         // Then the contents should be correctly sorted
0270         const QStringList sortedTitles = BookmarkLister::titleList(&bookmarkManager);
0271         QCOMPARE(sortedTitles.at(0), QStringLiteral("Cyclone V"));
0272         QCOMPARE(sortedTitles.at(1), QStringLiteral("Altera SoC design courses"));
0273         QCOMPARE(sortedTitles.at(2), QStringLiteral("Hardware Design Fl...r an ARM-based SoC"));
0274         // ...
0275 
0276         // And when undoing
0277         sortCmd->undo();
0278 
0279         // Then the contents should revert to the orig order
0280         QCOMPARE(BookmarkLister::titleList(&bookmarkManager), origTitleList);
0281 
0282         QFile::remove(fileName + QLatin1String(".bak"));
0283     }
0284 
0285 private:
0286     void moveTwoBookmarks(const QString &src1, const QString &src2, const QString &dest)
0287     {
0288         const QModelIndex firstIndex = m_model->indexForBookmark(m_bookmarkManager->findByAddress(src1));
0289         const QModelIndex secondIndex = m_model->indexForBookmark(m_bookmarkManager->findByAddress(src2));
0290         QMimeData *mimeData = m_model->mimeData(QModelIndexList() << firstIndex << secondIndex);
0291         QModelIndex folder2Index = m_model->indexForBookmark(m_bookmarkManager->findByAddress(dest));
0292         QVERIFY(m_model->dropMimeData(mimeData, Qt::MoveAction, -1, 0, folder2Index));
0293         delete mimeData;
0294     }
0295 
0296     void undoAll()
0297     {
0298         QAction *undoAction = m_collection.action(KStandardAction::name(KStandardAction::Undo));
0299         QVERIFY(undoAction);
0300         while (undoAction->isEnabled()) {
0301             undoAction->trigger();
0302         }
0303         QCOMPARE(BookmarkLister::addressList(m_bookmarkManager.get()), QStringList());
0304     }
0305 
0306     std::unique_ptr<KBookmarkManager> m_bookmarkManager;
0307     KBookmarkModel *m_model;
0308     CommandHistory *m_cmdHistory;
0309     KActionCollection m_collection;
0310     QModelIndex m_rootIndex; // the index of the "Bookmarks" root
0311 };
0312 
0313 QTEST_MAIN(KBookmarkModelTest)
0314 
0315 #include "kbookmarkmodeltest.moc"