File indexing completed on 2024-04-07 04:45:07

0001 /*
0002     SPDX-FileCopyrightText: 2018-2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
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 "test_utils.hpp"
0007 // test specific headers
0008 #include "bin/model/markerlistmodel.hpp"
0009 #include "core.h"
0010 #include "doc/kdenlivedoc.h"
0011 #include "kdenlivesettings.h"
0012 #include "timeline2/model/snapmodel.hpp"
0013 
0014 using Marker = std::tuple<GenTime, QString, int>;
0015 double fps;
0016 
0017 void checkMarkerList(const std::shared_ptr<MarkerListModel> &model, const std::vector<Marker> &l, const std::shared_ptr<SnapModel> &snaps)
0018 {
0019     auto list = l;
0020     // std::sort(list.begin(), list.end(), [](const Marker &a, const Marker &b) { return std::get<0>(a) < std::get<0>(b); });
0021 
0022     REQUIRE(model->rowCount() == (int)list.size());
0023     if (model->rowCount() == 0) {
0024         REQUIRE(snaps->getClosestPoint(0) == -1);
0025     }
0026     for (int i = 0; i < model->rowCount(); ++i) {
0027         Marker m;
0028         // Model markers and List do not necessarily use the same order
0029         for (size_t j = 0; j < list.size(); j++) {
0030             if (qAbs(std::get<0>(list[j]).seconds() - model->data(model->index(i), MarkerListModel::PosRole).toDouble()) < 0.9 / fps) {
0031                 m = list[j];
0032                 list.erase(std::remove(list.begin(), list.end(), m), list.end());
0033                 break;
0034             }
0035         }
0036         REQUIRE(qAbs(std::get<0>(m).seconds() - model->data(model->index(i), MarkerListModel::PosRole).toDouble()) < 0.9 / fps);
0037         REQUIRE(std::get<1>(m) == model->data(model->index(i), MarkerListModel::CommentRole).toString());
0038         REQUIRE(std::get<2>(m) == model->data(model->index(i), MarkerListModel::TypeRole).toInt());
0039         REQUIRE(pCore->markerTypes.value(std::get<2>(m)).color == model->data(model->index(i), MarkerListModel::ColorRole).value<QColor>());
0040 
0041         // check for marker existence
0042         int frame = std::get<0>(m).frames(fps);
0043         REQUIRE(model->hasMarker(frame));
0044 
0045         // cheap way to check for snap
0046         REQUIRE(snaps->getClosestPoint(frame) == frame);
0047     }
0048 }
0049 
0050 void checkStates(const std::shared_ptr<DocUndoStack> &undoStack, const std::shared_ptr<MarkerListModel> &model, const std::vector<std::vector<Marker>> &states,
0051                  const std::shared_ptr<SnapModel> &snaps)
0052 {
0053     for (size_t i = 0; i < states.size(); ++i) {
0054         checkMarkerList(model, states[states.size() - 1 - i], snaps);
0055         if (i != states.size() - 1) {
0056             undoStack->undo();
0057         }
0058     }
0059     for (size_t i = 1; i < states.size(); ++i) {
0060         undoStack->redo();
0061         checkMarkerList(model, states[i], snaps);
0062     }
0063 }
0064 
0065 TEST_CASE("Marker model", "[MarkerListModel]")
0066 {
0067     auto binModel = pCore->projectItemModel();
0068     fps = pCore->getCurrentFps();
0069     GenTime::setFps(fps);
0070     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
0071     KdenliveDoc document(undoStack);
0072     Mock<KdenliveDoc> docMock(document);
0073     KdenliveDoc &mockedDoc = docMock.get();
0074 
0075     // We mock the project class so that the undoStack function returns our undoStack, and our mocked document
0076     Mock<ProjectManager> pmMock;
0077     When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
0078     When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
0079     When(Method(pmMock, current)).AlwaysReturn(&mockedDoc);
0080     ProjectManager &mocked = pmMock.get();
0081     pCore->m_projectManager = &mocked;
0082     mocked.m_project = &mockedDoc;
0083     QDateTime documentDate = QDateTime::currentDateTime();
0084     mocked.updateTimeline(false, QString(), QString(), documentDate, 0);
0085     auto timeline = mockedDoc.getTimeline(mockedDoc.uuid());
0086     mocked.m_activeTimelineModel = timeline;
0087     mocked.testSetActiveDocument(&mockedDoc, timeline);
0088 
0089     std::shared_ptr<MarkerListModel> model = timeline->getGuideModel();
0090     std::shared_ptr<SnapModel> snaps = std::make_shared<SnapModel>();
0091     model->registerSnapModel(snaps);
0092 
0093     SECTION("Basic Manipulation")
0094     {
0095         std::vector<Marker> list;
0096         checkMarkerList(model, list, snaps);
0097 
0098         // add markers
0099         list.emplace_back(GenTime(1.3), QLatin1String("test marker"), 3);
0100         REQUIRE(model->addMarker(GenTime(1.3), QLatin1String("test marker"), 3));
0101         checkMarkerList(model, list, snaps);
0102         auto state1 = list;
0103 
0104         checkStates(undoStack, model, {{}, state1}, snaps);
0105 
0106         list.emplace_back(GenTime(0.3), QLatin1String("test marker2"), 0);
0107         REQUIRE(model->addMarker(GenTime(0.3), QLatin1String("test marker2"), 0));
0108         checkMarkerList(model, list, snaps);
0109         auto state2 = list;
0110 
0111         checkStates(undoStack, model, {{}, state1, state2}, snaps);
0112 
0113         // delete unexisting marker shouldn't work
0114         REQUIRE_FALSE(model->removeMarker(GenTime(42.)));
0115         checkMarkerList(model, list, snaps);
0116         checkStates(undoStack, model, {{}, state1, state2}, snaps);
0117 
0118         // rename markers
0119         std::get<1>(list[0]) = QLatin1String("new comment");
0120         std::get<2>(list[0]) = 1;
0121         REQUIRE(model->addMarker(GenTime(1.3), QLatin1String("new comment"), 1));
0122         checkMarkerList(model, list, snaps);
0123         auto state3 = list;
0124         checkStates(undoStack, model, {{}, state1, state2, state3}, snaps);
0125 
0126         // edit marker
0127         GenTime oldPos = std::get<0>(list[1]);
0128         std::get<0>(list[1]) = GenTime(42.8);
0129         std::get<1>(list[1]) = QLatin1String("edited comment");
0130         std::get<2>(list[1]) = 3;
0131         REQUIRE(model->editMarker(oldPos, GenTime(42.8), QLatin1String("edited comment"), 3));
0132         checkMarkerList(model, list, snaps);
0133         auto state4 = list;
0134         checkStates(undoStack, model, {{}, state1, state2, state3, state4}, snaps);
0135 
0136         // delete markers
0137         std::swap(list[0], list[1]);
0138         list.pop_back();
0139         REQUIRE(model->removeMarker(GenTime(1.3)));
0140         checkMarkerList(model, list, snaps);
0141         auto state5 = list;
0142         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5}, snaps);
0143         GenTime old = std::get<0>(list.back());
0144         list.pop_back();
0145         REQUIRE(model->removeMarker(old));
0146         checkMarkerList(model, list, snaps);
0147         auto state6 = list;
0148         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6}, snaps);
0149 
0150         // add some back
0151         list.emplace_back(GenTime(1.7), QLatin1String("test marker6"), KdenliveSettings::default_marker_type());
0152         REQUIRE(model->addMarker(GenTime(1.7), QLatin1String("test marker6"), -1));
0153         auto state7 = list;
0154         list.emplace_back(GenTime(2), QLatin1String("auieuansr"), 3);
0155         REQUIRE(model->addMarker(GenTime(2), QLatin1String("auieuansr"), 3));
0156         auto state8 = list;
0157         list.emplace_back(GenTime(0), QLatin1String("sasenust"), 1);
0158         REQUIRE(model->addMarker(GenTime(0), QLatin1String("sasenust"), 1));
0159         checkMarkerList(model, list, snaps);
0160         auto state9 = list;
0161         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6, state7, state8, state9}, snaps);
0162 
0163         // try spurious model registration
0164         // std::shared_ptr<SnapInterface> spurious;
0165         // REQUIRE(ABORTS(&MarkerListModel::registerSnapModel, model, spurious));
0166 
0167         // try real model registration
0168         std::shared_ptr<SnapModel> other_snaps = std::make_shared<SnapModel>();
0169         model->registerSnapModel(other_snaps);
0170         checkMarkerList(model, list, other_snaps);
0171 
0172         // remove all
0173         REQUIRE(model->removeAllMarkers());
0174         checkMarkerList(model, {}, snaps);
0175         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6, state7, state8, state9, {}}, snaps);
0176     }
0177 
0178     SECTION("Test Categories")
0179     {
0180         std::vector<Marker> list;
0181         checkMarkerList(model, list, snaps);
0182 
0183         // add markers
0184         list.emplace_back(GenTime(1.3), QLatin1String("test marker"), 3);
0185         REQUIRE(model->addMarker(GenTime(1.3), QLatin1String("test marker"), 3));
0186         REQUIRE(model->addMarker(GenTime(0.3), QLatin1String("test marker2"), 0));
0187 
0188         QStringList categories = model->categoriesToStringList();
0189         // We have 9 marker categories by default
0190         Q_ASSERT(categories.count() == 9);
0191 
0192         QStringList newCategories = categories;
0193         newCategories.removeFirst();
0194 
0195         REQUIRE(model->rowCount() == int(snaps->_snaps().size()));
0196         REQUIRE(model->rowCount() == 2);
0197         model->loadCategoriesWithUndo(newCategories, categories);
0198         REQUIRE(model->rowCount() == int(snaps->_snaps().size()));
0199         REQUIRE(model->rowCount() == 1);
0200 
0201         // Reset to default categories
0202         undoStack->undo();
0203         REQUIRE(model->rowCount() == int(snaps->_snaps().size()));
0204         REQUIRE(model->rowCount() == 2);
0205 
0206         REQUIRE(model->removeAllMarkers());
0207         checkMarkerList(model, {}, snaps);
0208     }
0209 
0210     SECTION("Json identity test")
0211     {
0212         std::vector<Marker> list;
0213         checkMarkerList(model, list, snaps);
0214 
0215         // add markers
0216         list.emplace_back(GenTime(1.3), QLatin1String("test marker"), 3);
0217         model->addMarker(GenTime(1.3), QLatin1String("test marker"), 3);
0218         list.emplace_back(GenTime(0.3), QLatin1String("test marker2"), 0);
0219         model->addMarker(GenTime(0.3), QLatin1String("test marker2"), 0);
0220         list.emplace_back(GenTime(3), QLatin1String("test marker3"), 0);
0221         model->addMarker(GenTime(3), QLatin1String("test marker3"), 0);
0222         checkMarkerList(model, list, snaps);
0223 
0224         // export
0225         QString json = model->toJson();
0226 
0227         // clean
0228         model->removeMarker(GenTime(0.3));
0229         model->removeMarker(GenTime(3));
0230         model->removeMarker(GenTime(1.3));
0231         checkMarkerList(model, {}, snaps);
0232 
0233         // Reimport
0234         REQUIRE(model->importFromJson(json, false));
0235         checkMarkerList(model, list, snaps);
0236 
0237         // undo/redo
0238         undoStack->undo();
0239         checkMarkerList(model, {}, snaps);
0240         undoStack->redo();
0241         checkMarkerList(model, list, snaps);
0242 
0243         // now we try the same thing with non-empty model
0244         undoStack->undo();
0245         checkMarkerList(model, {}, snaps);
0246         // non - conflicting marker
0247         list.emplace_back(GenTime(5), QLatin1String("non conflicting"), 0);
0248         std::vector<Marker> otherMarkers;
0249         otherMarkers.emplace_back(GenTime(5), QLatin1String("non conflicting"), 0);
0250         model->addMarker(GenTime(5), QLatin1String("non conflicting"), 0);
0251         REQUIRE(model->importFromJson(json, false));
0252         checkMarkerList(model, list, snaps);
0253         undoStack->undo();
0254         checkMarkerList(model, otherMarkers, snaps);
0255         undoStack->redo();
0256         checkMarkerList(model, list, snaps);
0257         undoStack->undo();
0258 
0259         // conflicting marker
0260         otherMarkers.emplace_back(GenTime(1.3), QLatin1String("conflicting"), 1);
0261         model->addMarker(GenTime(1.3), QLatin1String("conflicting"), 1);
0262         checkMarkerList(model, otherMarkers, snaps);
0263         REQUIRE_FALSE(model->importFromJson(json, false));
0264         checkMarkerList(model, otherMarkers, snaps);
0265         REQUIRE(model->importFromJson(json, true));
0266         checkMarkerList(model, list, snaps);
0267         undoStack->undo();
0268         checkMarkerList(model, otherMarkers, snaps);
0269         undoStack->redo();
0270         checkMarkerList(model, list, snaps);
0271     }
0272     snaps.reset();
0273     // undoStack->clear();
0274     binModel->clean();
0275     pCore->m_projectManager = nullptr;
0276 }