File indexing completed on 2024-04-14 04:47:53

0001 /*
0002     SPDX-FileCopyrightText: 2022 Eric Jiang
0003     SPDX-FileCopyrightText: 2017-2019 Nicolas Carion <french.ebook.lover@gmail.com>
0004     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 #include "catch.hpp"
0007 #include "test_utils.hpp"
0008 // test specific headers
0009 #include <QString>
0010 #include <cmath>
0011 #include <iostream>
0012 #include <tuple>
0013 #include <unordered_set>
0014 
0015 #include "abstractmodel/abstracttreemodel.hpp"
0016 #include "abstractmodel/treeitem.hpp"
0017 #include "effects/effectlist/model/effecttreemodel.hpp"
0018 #include "effects/effectlist/model/effectfilter.hpp"
0019 
0020 TEST_CASE("Basic tree testing", "[TreeModel]")
0021 {
0022     auto model = AbstractTreeModel::construct();
0023 
0024     REQUIRE(model->checkConsistency());
0025     REQUIRE(model->rowCount() == 0);
0026 
0027     SECTION("Item creation Test")
0028     {
0029         auto item = TreeItem::construct(QList<QVariant>{QString("test")}, model, false);
0030         int id = item->getId();
0031         REQUIRE(item->depth() == 0);
0032         REQUIRE(model->checkConsistency());
0033 
0034         // check that a valid Id has been assigned
0035         REQUIRE(id != -1);
0036 
0037         // check that the item is not yet registered (not valid parent)
0038         REQUIRE(model->m_allItems.size() == 1);
0039 
0040         // Assign this to a parent
0041         model->getRoot()->appendChild(item);
0042         REQUIRE(model->checkConsistency());
0043         // Now the item should be registered, we query it
0044         REQUIRE(model->m_allItems.size() == 2);
0045         REQUIRE(model->getItemById(id) == item);
0046         REQUIRE(item->depth() == 1);
0047         REQUIRE(model->rowCount() == 1);
0048         REQUIRE(model->rowCount(model->getIndexFromItem(item)) == 0);
0049 
0050         // Retrieve data member
0051         REQUIRE(model->data(model->getIndexFromItem(item), 0) == QStringLiteral("test"));
0052 
0053         // Try joint creation / assignation
0054         auto item2 = item->appendChild(QList<QVariant>{QString("test2")});
0055         auto state = [&]() {
0056             REQUIRE(model->checkConsistency());
0057             REQUIRE(item->depth() == 1);
0058             REQUIRE(item2->depth() == 2);
0059             REQUIRE(model->rowCount() == 1);
0060             REQUIRE(item->row() == 0);
0061             REQUIRE(item2->row() == 0);
0062             REQUIRE(model->data(model->getIndexFromItem(item2), 0) == QStringLiteral("test2"));
0063             REQUIRE(model->rowCount(model->getIndexFromItem(item2)) == 0);
0064         };
0065         state();
0066         REQUIRE(model->rowCount(model->getIndexFromItem(item)) == 1);
0067         REQUIRE(model->m_allItems.size() == 3);
0068 
0069         // Add a second child to item to check if everything collapses
0070         auto item3 = item->appendChild(QList<QVariant>{QString("test3")});
0071         state();
0072         REQUIRE(model->rowCount(model->getIndexFromItem(item3)) == 0);
0073         REQUIRE(model->rowCount(model->getIndexFromItem(item)) == 2);
0074         REQUIRE(model->m_allItems.size() == 4);
0075         REQUIRE(item3->depth() == 2);
0076         REQUIRE(item3->row() == 1);
0077         REQUIRE(model->data(model->getIndexFromItem(item3), 0) == QStringLiteral("test3"));
0078     }
0079 
0080     SECTION("Invalid moves")
0081     {
0082         auto item = model->getRoot()->appendChild(QList<QVariant>{QString("test")});
0083         auto state = [&]() {
0084             REQUIRE(model->checkConsistency());
0085             REQUIRE(model->rowCount() == 1);
0086             REQUIRE(item->depth() == 1);
0087             REQUIRE(item->row() == 0);
0088             REQUIRE(model->data(model->getIndexFromItem(item), 0) == QStringLiteral("test"));
0089         };
0090         state();
0091         REQUIRE(model->rowCount(model->getIndexFromItem(item)) == 0);
0092 
0093         // Try to move the root
0094         REQUIRE_FALSE(item->appendChild(model->getRoot()));
0095         state();
0096         REQUIRE(model->rowCount(model->getIndexFromItem(item)) == 0);
0097 
0098         auto item2 = item->appendChild(QList<QVariant>{QString("test2")});
0099         auto item3 = item2->appendChild(QList<QVariant>{QString("test3")});
0100         auto item4 = item3->appendChild(QList<QVariant>{QString("test4")});
0101         auto state2 = [&]() {
0102             state();
0103             REQUIRE(item2->depth() == 2);
0104             REQUIRE(item2->row() == 0);
0105             REQUIRE(model->data(model->getIndexFromItem(item2), 0) == QStringLiteral("test2"));
0106             REQUIRE(item3->depth() == 3);
0107             REQUIRE(item3->row() == 0);
0108             REQUIRE(model->data(model->getIndexFromItem(item3), 0) == QStringLiteral("test3"));
0109             REQUIRE(item4->depth() == 4);
0110             REQUIRE(item4->row() == 0);
0111             REQUIRE(model->data(model->getIndexFromItem(item4), 0) == QStringLiteral("test4"));
0112         };
0113         state2();
0114 
0115         // Try to make a loop
0116         REQUIRE_FALSE(item->changeParent(item3));
0117         state2();
0118         REQUIRE_FALSE(item->changeParent(item4));
0119         state2();
0120 
0121         // Try to append a child that already have a parent
0122         REQUIRE_FALSE(item->appendChild(item4));
0123         state2();
0124 
0125         // valid move
0126         REQUIRE(item4->changeParent(item2));
0127         REQUIRE(model->checkConsistency());
0128     }
0129 
0130     SECTION("Deregistration tests")
0131     {
0132         // we construct a non trivial structure
0133         auto item = model->getRoot()->appendChild(QList<QVariant>{QString("test")});
0134         auto item2 = item->appendChild(QList<QVariant>{QString("test2")});
0135         auto item3 = item2->appendChild(QList<QVariant>{QString("test3")});
0136         auto item4 = item3->appendChild(QList<QVariant>{QString("test4")});
0137         auto item5 = item2->appendChild(QList<QVariant>{QString("test5")});
0138         auto state = [&]() {
0139             REQUIRE(model->checkConsistency());
0140             REQUIRE(model->rowCount() == 1);
0141             REQUIRE(item->depth() == 1);
0142             REQUIRE(item->row() == 0);
0143             REQUIRE(model->data(model->getIndexFromItem(item), 0) == QStringLiteral("test"));
0144             REQUIRE(model->rowCount(model->getIndexFromItem(item)) == 1);
0145             REQUIRE(item2->depth() == 2);
0146             REQUIRE(item2->row() == 0);
0147             REQUIRE(model->data(model->getIndexFromItem(item2), 0) == QStringLiteral("test2"));
0148             REQUIRE(model->rowCount(model->getIndexFromItem(item2)) == 2);
0149             REQUIRE(item3->depth() == 3);
0150             REQUIRE(item3->row() == 0);
0151             REQUIRE(model->data(model->getIndexFromItem(item3), 0) == QStringLiteral("test3"));
0152             REQUIRE(model->rowCount(model->getIndexFromItem(item3)) == 1);
0153             REQUIRE(item4->depth() == 4);
0154             REQUIRE(item4->row() == 0);
0155             REQUIRE(model->data(model->getIndexFromItem(item4), 0) == QStringLiteral("test4"));
0156             REQUIRE(model->rowCount(model->getIndexFromItem(item4)) == 0);
0157             REQUIRE(item5->depth() == 3);
0158             REQUIRE(item5->row() == 1);
0159             REQUIRE(model->data(model->getIndexFromItem(item5), 0) == QStringLiteral("test5"));
0160             REQUIRE(model->rowCount(model->getIndexFromItem(item5)) == 0);
0161             REQUIRE(model->m_allItems.size() == 6);
0162             REQUIRE(item->isInModel());
0163             REQUIRE(item2->isInModel());
0164             REQUIRE(item3->isInModel());
0165             REQUIRE(item4->isInModel());
0166             REQUIRE(item5->isInModel());
0167         };
0168         state();
0169 
0170         // deregister the topmost item, should also deregister its children
0171         item->changeParent(std::shared_ptr<TreeItem>());
0172         REQUIRE(model->m_allItems.size() == 1);
0173         REQUIRE(model->rowCount() == 0);
0174         REQUIRE(!item->isInModel());
0175         REQUIRE(!item2->isInModel());
0176         REQUIRE(!item3->isInModel());
0177         REQUIRE(!item4->isInModel());
0178         REQUIRE(!item5->isInModel());
0179 
0180         // reinsert
0181         REQUIRE(model->getRoot()->appendChild(item));
0182         state();
0183 
0184         item2->removeChild(item5);
0185         REQUIRE(!item5->isInModel());
0186         REQUIRE(model->rowCount(model->getIndexFromItem(item2)) == 1);
0187         REQUIRE(model->m_allItems.size() == 5);
0188 
0189         // reinsert
0190         REQUIRE(item5->changeParent(item2));
0191         state();
0192     }
0193 }
0194 
0195 // Tests the logic for matching the user-supplied search string against the list
0196 // of items. The actual logic is in AssetFilter but since it's an abstract
0197 // class, we test EffectFilter instead.
0198 TEST_CASE("Effect filter text-matching logic")
0199 {
0200     auto model = EffectTreeModel::construct("", nullptr);
0201     EffectFilter filter{nullptr};
0202     // rootData copied from effecttreemodel.cpp
0203     QList<QVariant> rootData{"Name", "ID", "Type", "isFav"};
0204 
0205     SECTION("Handles basic alphanum search")
0206     {
0207         auto item = TreeItem::construct(rootData, model, true);
0208         item->setData(AssetTreeModel::IdCol, "Hello World");
0209         item->setData(AssetTreeModel::NameCol, "This is K-denlive");
0210 
0211         filter.setFilterName(true, "wORL");
0212         CHECK(filter.filterName(item) == true);
0213 
0214         // should not search across spaces
0215         filter.setFilterName(true, "Thisis");
0216         CHECK(filter.filterName(item) == false);
0217 
0218         // should ignore punctuation
0219         filter.setFilterName(true, "Kden");
0220         CHECK(filter.filterName(item) == true);
0221     }
0222 
0223     // Tests for bug 432699 where filtering the effects list in Chinese fails
0224     // because the filter only works for ascii alphanum characters.
0225     SECTION("Handles Chinese/Japanese search")
0226     {
0227         auto item = TreeItem::construct(rootData, model, true);
0228         item->setData(AssetTreeModel::IdCol, "静音");
0229         item->setData(AssetTreeModel::NameCol, "ミュート");
0230 
0231         filter.setFilterName(true, "音");
0232         CHECK(filter.filterName(item) == true);
0233         filter.setFilterName(true, "默");
0234         CHECK(filter.filterName(item) == false);
0235 
0236         // should ignore punctuation
0237         filter.setFilterName(true, "ミュ");
0238         CHECK(filter.filterName(item) == true);
0239     }
0240 
0241     SECTION("Ignores diacritics")
0242     {
0243         auto item = TreeItem::construct(rootData, model, true);
0244         item->setData(AssetTreeModel::IdCol, "rgb parade");
0245         item->setData(AssetTreeModel::NameCol, "Défilé RVB");
0246 
0247         // should be able to search with and without diacritics
0248         filter.setFilterName(true, "defile");
0249         CHECK(filter.filterName(item) == true);
0250         filter.setFilterName(true, "ilé");
0251         CHECK(filter.filterName(item) == true);
0252     }
0253 }