File indexing completed on 2025-02-09 05:45:47
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 }