File indexing completed on 2024-05-19 08:45:58
0001 /* 0002 SPDX-FileCopyrightText: 2018-2022 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 SPDX-FileCopyrightText: 2022 Eric Jiang 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/binplaylist.hpp" 0009 #include "doc/kdenlivedoc.h" 0010 #include "timeline2/model/builders/meltBuilder.hpp" 0011 #include "xml/xml.hpp" 0012 0013 #include <QTemporaryFile> 0014 #include <QUndoGroup> 0015 0016 using namespace fakeit; 0017 0018 TEST_CASE("Open and Close Sequence", "[OCS]") 0019 { 0020 auto binModel = pCore->projectItemModel(); 0021 Q_ASSERT(binModel->clipsCount() == 0); 0022 binModel->clean(); 0023 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0024 0025 SECTION("Create sequence, add grouped clips, close sequence and reopen") 0026 { 0027 // Create document 0028 KdenliveDoc document(undoStack); 0029 Mock<KdenliveDoc> docMock(document); 0030 KdenliveDoc &mockedDoc = docMock.get(); 0031 0032 pCore->projectManager()->m_project = &mockedDoc; 0033 QDateTime documentDate = QDateTime::currentDateTime(); 0034 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0035 auto timeline = mockedDoc.getTimeline(mockedDoc.uuid()); 0036 pCore->projectManager()->m_activeTimelineModel = timeline; 0037 pCore->projectManager()->testSetActiveDocument(&mockedDoc, timeline); 0038 KdenliveDoc::next_id = 0; 0039 QDir dir = QDir::temp(); 0040 QString binId = createProducerWithSound(pCore->getProjectProfile(), binModel); 0041 0042 // Create a new sequence clip 0043 std::pair<int, int> tracks = {2, 2}; 0044 const QString seqId = ClipCreator::createPlaylistClip(QStringLiteral("Seq 2"), tracks, QStringLiteral("-1"), binModel); 0045 REQUIRE(seqId != QLatin1String("-1")); 0046 0047 timeline.reset(); 0048 // Now use the new timeline sequence 0049 QUuid uuid; 0050 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0051 QMapIterator<QUuid, QString> i(allSequences); 0052 while (i.hasNext()) { 0053 // Find clips with the tag 0054 i.next(); 0055 if (i.value() == seqId) { 0056 uuid = i.key(); 0057 } 0058 } 0059 timeline = mockedDoc.getTimeline(uuid); 0060 pCore->projectManager()->m_activeTimelineModel = timeline; 0061 0062 // Insert an AV clip 0063 int tid1 = timeline->getTrackIndexFromPosition(2); 0064 0065 // Setup timeline audio drop info 0066 QMap<int, QString> audioInfo; 0067 audioInfo.insert(1, QStringLiteral("stream1")); 0068 timeline->m_binAudioTargets = audioInfo; 0069 timeline->m_videoTarget = tid1; 0070 // Insert 0071 int cid1 = -1; 0072 REQUIRE(timeline->requestClipInsertion(binId, tid1, 80, cid1, true, true, false)); 0073 0074 // Ensure the clip is grouped (part af an AV group) 0075 REQUIRE(timeline->m_groups->isInGroup(cid1)); 0076 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0077 0078 // Add a few guides 0079 timeline->getGuideModel()->addMarker(GenTime(40, pCore->getCurrentFps()), i18n("guide 1")); 0080 timeline->getGuideModel()->addMarker(GenTime(60, pCore->getCurrentFps()), i18n("guide 2")); 0081 0082 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0083 0084 // Now close timeline, the reopen and check the clips, groups and guides are still here 0085 mockedDoc.setModified(true); 0086 pCore->projectManager()->closeTimeline(uuid); 0087 timeline.reset(); 0088 0089 // Reopen 0090 pCore->projectManager()->openTimeline(seqId, uuid); 0091 timeline = mockedDoc.getTimeline(uuid); 0092 pCore->projectManager()->m_activeTimelineModel = timeline; 0093 tid1 = timeline->getTrackIndexFromPosition(2); 0094 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0095 cid1 = timeline->getClipByStartPosition(tid1, 80); 0096 REQUIRE(cid1 > -1); 0097 REQUIRE(timeline->m_groups->isInGroup(cid1)); 0098 REQUIRE(timeline->getGuideModel()->hasMarker(40)); 0099 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0100 pCore->projectManager()->closeCurrentDocument(false, false); 0101 } 0102 } 0103 0104 TEST_CASE("Save File With 2 Sequences", "[SF2]") 0105 { 0106 auto binModel = pCore->projectItemModel(); 0107 // Q_ASSERT(binModel->clipsCount() == 0); 0108 binModel->clean(); 0109 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0110 0111 SECTION("Simple insert and save") 0112 { 0113 // Create document 0114 KdenliveDoc document(undoStack); 0115 pCore->projectManager()->m_project = &document; 0116 QDateTime documentDate = QDateTime::currentDateTime(); 0117 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0118 auto timeline = document.getTimeline(document.uuid()); 0119 pCore->projectManager()->m_activeTimelineModel = timeline; 0120 pCore->projectManager()->testSetActiveDocument(&document, timeline); 0121 0122 KdenliveDoc::next_id = 0; 0123 QDir dir = QDir::temp(); 0124 0125 QString binId = createProducerWithSound(pCore->getProjectProfile(), binModel); 0126 QString binId2 = createProducer(pCore->getProjectProfile(), "red", binModel, 20, false); 0127 0128 int tid1 = timeline->getTrackIndexFromPosition(2); 0129 0130 // Setup timeline audio drop info 0131 QMap<int, QString> audioInfo; 0132 audioInfo.insert(1, QStringLiteral("stream1")); 0133 timeline->m_binAudioTargets = audioInfo; 0134 timeline->m_videoTarget = tid1; 0135 // Insert 2 clips (length=20, pos = 80 / 100) 0136 int cid1 = -1; 0137 REQUIRE(timeline->requestClipInsertion(binId2, tid1, 80, cid1, true, true, false)); 0138 int l = timeline->getClipPlaytime(cid1); 0139 int cid2 = -1; 0140 REQUIRE(timeline->requestClipInsertion(binId2, tid1, 80 + l, cid2, true, true, false)); 0141 // Resize first clip (length=100) 0142 REQUIRE(timeline->requestItemResize(cid1, 100, false) == 100); 0143 0144 // Group the 2 clips 0145 REQUIRE(timeline->requestClipsGroup({cid1, cid2}, true)); 0146 0147 auto state = [&]() { 0148 REQUIRE(timeline->checkConsistency()); 0149 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0150 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0151 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0152 REQUIRE(timeline->getClipPosition(cid1) == 0); 0153 REQUIRE(timeline->getClipPosition(cid2) == 100); 0154 REQUIRE(timeline->getClipPlaytime(cid1) == 100); 0155 REQUIRE(timeline->getClipPlaytime(cid2) == 20); 0156 }; 0157 state(); 0158 0159 // Add a few guides 0160 timeline->getGuideModel()->addMarker(GenTime(40, pCore->getCurrentFps()), i18n("guide 1")); 0161 timeline->getGuideModel()->addMarker(GenTime(60, pCore->getCurrentFps()), i18n("guide 2")); 0162 timeline->getGuideModel()->addMarker(GenTime(10, pCore->getCurrentFps()), i18n("guide 3")); 0163 0164 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0165 0166 // Add another sequence clip 0167 std::pair<int, int> tracks = {1, 2}; 0168 const QString seqId = ClipCreator::createPlaylistClip(QStringLiteral("Seq 2"), tracks, QStringLiteral("-1"), binModel); 0169 REQUIRE(seqId != QLatin1String("-1")); 0170 0171 QUuid uuid; 0172 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0173 QString firstSeqId = allSequences.value(document.uuid()); 0174 QMapIterator<QUuid, QString> i(allSequences); 0175 while (i.hasNext()) { 0176 // Find clips with the tag 0177 i.next(); 0178 if (i.value() == seqId) { 0179 uuid = i.key(); 0180 } 0181 } 0182 REQUIRE(!uuid.isNull()); 0183 // Make last sequence active 0184 timeline.reset(); 0185 timeline = document.getTimeline(uuid); 0186 timeline->getGuideModel()->addMarker(GenTime(10, pCore->getCurrentFps()), i18n("guide 4")); 0187 0188 // Save and close 0189 pCore->projectManager()->testSaveFileAs(dir.absoluteFilePath(QStringLiteral("test-nest.kdenlive"))); 0190 Q_ASSERT(QFile::exists(dir.absoluteFilePath(QStringLiteral("test-nest.kdenlive")))); 0191 pCore->projectManager()->closeCurrentDocument(false, false); 0192 } 0193 0194 SECTION("Open and check in/out points") 0195 { 0196 // Open document 0197 KdenliveDoc::next_id = 0; 0198 QString saveFile = QDir::temp().absoluteFilePath(QStringLiteral("test-nest.kdenlive")); 0199 QUrl openURL = QUrl::fromLocalFile(saveFile); 0200 0201 Mock<ProjectManager> pmMock; 0202 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack); 0203 When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))); 0204 QUndoGroup *undoGroup = new QUndoGroup(); 0205 undoGroup->addStack(undoStack.get()); 0206 DocOpenResult openResults = KdenliveDoc::Open(openURL, QDir::temp().path(), undoGroup, false, nullptr); 0207 REQUIRE(openResults.isSuccessful() == true); 0208 std::unique_ptr<KdenliveDoc> openedDoc = openResults.getDocument(); 0209 0210 pCore->projectManager()->m_project = openedDoc.get(); 0211 const QUuid uuid = openedDoc->uuid(); 0212 QDateTime documentDate = QFileInfo(openURL.toLocalFile()).lastModified(); 0213 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0214 0215 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0216 const QString firstSeqId = allSequences.value(uuid); 0217 pCore->projectManager()->openTimeline(firstSeqId, uuid); 0218 std::shared_ptr<TimelineItemModel> timeline = openedDoc->getTimeline(uuid); 0219 // Now reopen all timeline sequences 0220 QList<QUuid> allUuids = allSequences.keys(); 0221 allUuids.removeAll(uuid); 0222 pCore->projectManager()->testSetActiveDocument(openedDoc.get(), timeline); 0223 0224 REQUIRE(timeline->getTracksCount() == 4); 0225 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0226 int tid1 = timeline->getTrackIndexFromPosition(2); 0227 int cid1 = timeline->getClipByStartPosition(tid1, 0); 0228 int cid2 = timeline->getClipByStartPosition(tid1, 100); 0229 REQUIRE(cid1 > -1); 0230 REQUIRE(cid2 > -1); 0231 // Check che clips are still grouped 0232 REQUIRE(timeline->m_groups->isInGroup(cid1)); 0233 REQUIRE(timeline->m_groups->isInGroup(cid2)); 0234 0235 auto state = [&]() { 0236 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0237 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0238 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0239 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0240 REQUIRE(timeline->getClipPosition(cid1) == 0); 0241 REQUIRE(timeline->getClipPosition(cid2) == 100); 0242 REQUIRE(timeline->getClipPlaytime(cid1) == 100); 0243 REQUIRE(timeline->getClipPlaytime(cid2) == 20); 0244 }; 0245 state(); 0246 QByteArray updatedHex = timeline->timelineHash().toHex(); 0247 const QString hash = openedDoc->getSequenceProperty(uuid, QStringLiteral("timelineHash")); 0248 REQUIRE(updatedHex == hash.toLatin1()); 0249 const QString binId1 = timeline->getClipBinId(cid1); 0250 0251 const QUuid secondSequence = allUuids.first(); 0252 timeline = openedDoc->getTimeline(secondSequence); 0253 pCore->projectManager()->setActiveTimeline(secondSequence); 0254 REQUIRE(timeline->getTracksCount() == 3); 0255 0256 // Now, add one clip, close sequence and reopen to ensure it is correctly stored as removed 0257 tid1 = timeline->getTrackIndexFromPosition(2); 0258 int cid5 = -1; 0259 int cid6 = -1; 0260 REQUIRE(timeline->requestClipInsertion(binId1, tid1, 330, cid5, true, true, false)); 0261 REQUIRE(timeline->requestClipInsertion(binId1, tid1, 120, cid6, true, true, false)); 0262 REQUIRE(timeline->getClipPosition(cid5) == 330); 0263 REQUIRE(timeline->getClipPosition(cid6) == 120); 0264 // Group the 2 clips 0265 REQUIRE(timeline->requestClipsGroup({cid5, cid6}, true)); 0266 0267 // Close sequence 0268 pCore->projectManager()->closeTimeline(secondSequence, false); 0269 timeline.reset(); 0270 0271 // Reopen sequence and check clip is still here 0272 pCore->projectManager()->openTimeline(allSequences.value(secondSequence), secondSequence); 0273 timeline = openedDoc->getTimeline(secondSequence); 0274 tid1 = timeline->getTrackIndexFromPosition(2); 0275 cid5 = timeline->getClipByStartPosition(tid1, 330); 0276 REQUIRE(cid5 > -1); 0277 cid6 = timeline->getClipByStartPosition(tid1, 120); 0278 REQUIRE(cid6 > -1); 0279 REQUIRE(timeline->m_groups->isInGroup(cid5)); 0280 REQUIRE(timeline->m_groups->isInGroup(cid6)); 0281 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0282 timeline.reset(); 0283 0284 // Save again 0285 QDir dir = QDir::temp(); 0286 pCore->projectManager()->testSaveFileAs(dir.absoluteFilePath(QStringLiteral("test-nest.kdenlive"))); 0287 pCore->projectManager()->closeCurrentDocument(false, false); 0288 } 0289 SECTION("Reopen and check in/out points") 0290 { 0291 // Create new document 0292 // We mock the project class so that the undoStack function returns our undoStack, and our mocked document 0293 KdenliveDoc::next_id = 0; 0294 Q_ASSERT(QFile::exists(QDir::temp().absoluteFilePath(QStringLiteral("test-nest.kdenlive")))); 0295 QString saveFile = QDir::temp().absoluteFilePath(QStringLiteral("test-nest.kdenlive")); 0296 QUrl openURL = QUrl::fromLocalFile(saveFile); 0297 0298 Mock<ProjectManager> pmMock; 0299 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack); 0300 When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))); 0301 QUndoGroup *undoGroup = new QUndoGroup(); 0302 undoGroup->addStack(undoStack.get()); 0303 DocOpenResult openResults = KdenliveDoc::Open(openURL, QDir::temp().path(), undoGroup, false, nullptr); 0304 REQUIRE(openResults.isSuccessful() == true); 0305 std::unique_ptr<KdenliveDoc> openedDoc = openResults.getDocument(); 0306 0307 pCore->projectManager()->m_project = openedDoc.get(); 0308 const QUuid uuid = openedDoc->uuid(); 0309 QDateTime documentDate = QFileInfo(openURL.toLocalFile()).lastModified(); 0310 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0311 0312 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0313 const QString firstSeqId = allSequences.value(uuid); 0314 pCore->projectManager()->openTimeline(firstSeqId, uuid); 0315 std::shared_ptr<TimelineItemModel> timeline = openedDoc->getTimeline(uuid); 0316 // Now reopen all timeline sequences 0317 QList<QUuid> allUuids = allSequences.keys(); 0318 allUuids.removeAll(uuid); 0319 pCore->projectManager()->testSetActiveDocument(openedDoc.get(), timeline); 0320 0321 REQUIRE(timeline->getTracksCount() == 4); 0322 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0323 int tid1 = timeline->getTrackIndexFromPosition(2); 0324 int cid1 = timeline->getClipByStartPosition(tid1, 0); 0325 int cid2 = timeline->getClipByStartPosition(tid1, 100); 0326 REQUIRE(cid1 > -1); 0327 REQUIRE(cid2 > -1); 0328 // Check the clips are still grouped 0329 REQUIRE(timeline->m_groups->isInGroup(cid1)); 0330 REQUIRE(timeline->m_groups->isInGroup(cid2)); 0331 0332 auto state = [&]() { 0333 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0334 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0335 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0336 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0337 REQUIRE(timeline->getClipPosition(cid1) == 0); 0338 REQUIRE(timeline->getClipPosition(cid2) == 100); 0339 REQUIRE(timeline->getClipPlaytime(cid1) == 100); 0340 REQUIRE(timeline->getClipPlaytime(cid2) == 20); 0341 }; 0342 state(); 0343 QByteArray updatedHex = timeline->timelineHash().toHex(); 0344 const QString hash = openedDoc->getSequenceProperty(uuid, QStringLiteral("timelineHash")); 0345 REQUIRE(updatedHex == hash.toLatin1()); 0346 const QString binId1 = timeline->getClipBinId(cid1); 0347 0348 const QUuid secondSequence = allUuids.first(); 0349 timeline = openedDoc->getTimeline(secondSequence); 0350 pCore->projectManager()->setActiveTimeline(secondSequence); 0351 REQUIRE(timeline->getTracksCount() == 3); 0352 0353 tid1 = timeline->getTrackIndexFromPosition(2); 0354 int cid5 = timeline->getClipByStartPosition(tid1, 330); 0355 REQUIRE(cid5 > -1); 0356 int cid6 = timeline->getClipByStartPosition(tid1, 120); 0357 REQUIRE(cid6 > -1); 0358 REQUIRE(timeline->m_groups->isInGroup(cid5)); 0359 REQUIRE(timeline->m_groups->isInGroup(cid6)); 0360 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0361 0362 QDir dir = QDir::temp(); 0363 QFile::remove(dir.absoluteFilePath(QStringLiteral("test-nest.kdenlive"))); 0364 // binModel->clean(); 0365 // pCore->m_projectManager = nullptr; 0366 timeline.reset(); 0367 pCore->projectManager()->closeCurrentDocument(false, false); 0368 } 0369 } 0370 0371 TEST_CASE("Save File, Reopen and check for corruption", "[SF3]") 0372 { 0373 auto binModel = pCore->projectItemModel(); 0374 binModel->clean(); 0375 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0376 SECTION("Open and simply save file") 0377 { 0378 QString path = sourcesPath + "/dataset/test-nesting-effects.kdenlive"; 0379 Q_ASSERT(QFile::exists(sourcesPath)); 0380 QUrl openURL = QUrl::fromLocalFile(path); 0381 0382 QUndoGroup *undoGroup = new QUndoGroup(); 0383 undoGroup->addStack(undoStack.get()); 0384 DocOpenResult openResults = KdenliveDoc::Open(openURL, QDir::temp().path(), undoGroup, false, nullptr); 0385 REQUIRE(openResults.isSuccessful() == true); 0386 std::unique_ptr<KdenliveDoc> openedDoc = openResults.getDocument(); 0387 0388 pCore->projectManager()->m_project = openedDoc.get(); 0389 const QUuid uuid = openedDoc->uuid(); 0390 QDateTime documentDate = QFileInfo(openURL.toLocalFile()).lastModified(); 0391 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0392 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0393 const QString firstSeqId = allSequences.take(uuid); 0394 pCore->projectManager()->openTimeline(firstSeqId, uuid); 0395 std::shared_ptr<TimelineItemModel> timeline = openedDoc->getTimeline(uuid); 0396 pCore->projectManager()->testSetActiveDocument(openedDoc.get(), timeline); 0397 // Now reopen all timeline sequences 0398 REQUIRE(openedDoc->checkConsistency()); 0399 // Save file 0400 QDir dir = QDir::temp(); 0401 pCore->projectManager()->testSaveFileAs(dir.absoluteFilePath(QStringLiteral("test-nest.kdenlive"))); 0402 timeline.reset(); 0403 pCore->projectManager()->closeCurrentDocument(false, false); 0404 } 0405 SECTION("Reopen and check in/out points") 0406 { 0407 // Create new document 0408 // We mock the project class so that the undoStack function returns our undoStack, and our mocked document 0409 KdenliveDoc::next_id = 0; 0410 QString saveFile = QDir::temp().absoluteFilePath(QStringLiteral("test-nest.kdenlive")); 0411 QUrl openURL = QUrl::fromLocalFile(saveFile); 0412 0413 Mock<ProjectManager> pmMock; 0414 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack); 0415 When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))); 0416 QUndoGroup *undoGroup = new QUndoGroup(); 0417 undoGroup->addStack(undoStack.get()); 0418 DocOpenResult openResults = KdenliveDoc::Open(openURL, QDir::temp().path(), undoGroup, false, nullptr); 0419 REQUIRE(openResults.isSuccessful() == true); 0420 std::unique_ptr<KdenliveDoc> openedDoc = openResults.getDocument(); 0421 0422 pCore->projectManager()->m_project = openedDoc.get(); 0423 const QUuid uuid = openedDoc->uuid(); 0424 QDateTime documentDate = QFileInfo(openURL.toLocalFile()).lastModified(); 0425 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0426 0427 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0428 const QString firstSeqId = allSequences.take(uuid); 0429 pCore->projectManager()->openTimeline(firstSeqId, uuid); 0430 std::shared_ptr<TimelineItemModel> timeline = openedDoc->getTimeline(uuid); 0431 pCore->projectManager()->testSetActiveDocument(openedDoc.get(), timeline); 0432 // Now reopen all timeline sequences 0433 QList<QUuid> allUuids = binModel->getAllSequenceClips().keys(); 0434 // Collect saved hashes 0435 QMap<QUuid, QString> timelineHashes; 0436 for (auto &u : allUuids) { 0437 timelineHashes.insert(u, openedDoc->getSequenceProperty(u, QStringLiteral("timelineHash"))); 0438 } 0439 REQUIRE(openedDoc->checkConsistency()); 0440 openedDoc->documentProperties(true); 0441 for (auto &u : allUuids) { 0442 QByteArray updatedHex = openedDoc->getTimeline(u)->timelineHash().toHex(); 0443 REQUIRE(updatedHex == timelineHashes.value(u)); 0444 } 0445 0446 QDir dir = QDir::temp(); 0447 QFile::remove(dir.absoluteFilePath(QStringLiteral("test-nest.kdenlive"))); 0448 // binModel->clean(); 0449 // pCore->m_projectManager = nullptr; 0450 pCore->projectManager()->closeCurrentDocument(false, false); 0451 } 0452 } 0453 0454 TEST_CASE("Save File And Check Sequence Effects", "[SF2]") 0455 { 0456 auto binModel = pCore->projectItemModel(); 0457 Q_ASSERT(binModel->clipsCount() == 0); 0458 binModel->clean(); 0459 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0460 0461 SECTION("Simple insert, add master effect and save") 0462 { 0463 // Create document 0464 KdenliveDoc document(undoStack); 0465 Mock<KdenliveDoc> docMock(document); 0466 KdenliveDoc &mockedDoc = docMock.get(); 0467 0468 pCore->projectManager()->m_project = &mockedDoc; 0469 QDateTime documentDate = QDateTime::currentDateTime(); 0470 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0471 auto timeline = mockedDoc.getTimeline(mockedDoc.uuid()); 0472 pCore->projectManager()->m_activeTimelineModel = timeline; 0473 0474 pCore->projectManager()->testSetActiveDocument(&mockedDoc, timeline); 0475 KdenliveDoc::next_id = 0; 0476 QDir dir = QDir::temp(); 0477 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0478 0479 // Add another sequence clip 0480 std::pair<int, int> tracks = {1, 2}; 0481 const QString seqId = ClipCreator::createPlaylistClip(QStringLiteral("Seq 2"), tracks, QStringLiteral("-1"), binModel); 0482 REQUIRE(seqId != QLatin1String("-1")); 0483 0484 QUuid uuid; 0485 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0486 QString firstSeqId = allSequences.value(mockedDoc.uuid()); 0487 QMapIterator<QUuid, QString> i(allSequences); 0488 while (i.hasNext()) { 0489 // Find clips with the tag 0490 i.next(); 0491 if (i.value() == seqId) { 0492 uuid = i.key(); 0493 } 0494 } 0495 REQUIRE(!uuid.isNull()); 0496 // Make last sequence active 0497 timeline.reset(); 0498 timeline = mockedDoc.getTimeline(uuid); 0499 // Add an effect to Bin sequence clip 0500 std::shared_ptr<EffectStackModel> binStack = binModel->getClipEffectStack(seqId.toInt()); 0501 REQUIRE(binStack->appendEffect(QStringLiteral("sepia"))); 0502 REQUIRE(binStack->checkConsistency()); 0503 0504 // Add an effect to master 0505 std::shared_ptr<EffectStackModel> masterStack = timeline->getMasterEffectStackModel(); 0506 REQUIRE(masterStack->rowCount() == 1); 0507 REQUIRE(masterStack->appendEffect(QStringLiteral("sepia"))); 0508 REQUIRE(masterStack->checkConsistency()); 0509 REQUIRE(masterStack->rowCount() == 2); 0510 0511 // Save and close 0512 REQUIRE(pCore->projectManager()->testSaveFileAs(dir.absoluteFilePath(QStringLiteral("test-nest2.kdenlive")))); 0513 pCore->projectManager()->closeCurrentDocument(false, false); 0514 } 0515 0516 SECTION("Open and check that effects are restored") 0517 { 0518 // Create new document 0519 // We mock the project class so that the undoStack function returns our undoStack, and our mocked document 0520 KdenliveDoc::next_id = 0; 0521 QString saveFile = QDir::temp().absoluteFilePath(QStringLiteral("test-nest2.kdenlive")); 0522 QUrl openURL = QUrl::fromLocalFile(saveFile); 0523 0524 Mock<ProjectManager> pmMock; 0525 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack); 0526 When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))); 0527 QUndoGroup *undoGroup = new QUndoGroup(); 0528 undoGroup->addStack(undoStack.get()); 0529 DocOpenResult openResults = KdenliveDoc::Open(openURL, QDir::temp().path(), undoGroup, false, nullptr); 0530 REQUIRE(openResults.isSuccessful() == true); 0531 std::unique_ptr<KdenliveDoc> openedDoc = openResults.getDocument(); 0532 0533 pCore->projectManager()->m_project = openedDoc.get(); 0534 const QUuid uuid = openedDoc->uuid(); 0535 QDateTime documentDate = QFileInfo(openURL.toLocalFile()).lastModified(); 0536 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 0537 0538 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 0539 const QString firstSeqId = allSequences.value(uuid); 0540 pCore->projectManager()->openTimeline(firstSeqId, uuid); 0541 std::shared_ptr<TimelineItemModel> timeline = openedDoc->getTimeline(uuid); 0542 // Now reopen all timeline sequences 0543 QList<QUuid> allUuids = allSequences.keys(); 0544 allUuids.removeAll(uuid); 0545 QString secondaryId; 0546 for (auto &u : allUuids) { 0547 secondaryId = allSequences.value(u); 0548 pCore->projectManager()->openTimeline(secondaryId, u); 0549 } 0550 0551 pCore->projectManager()->testSetActiveDocument(openedDoc.get(), timeline); 0552 0553 REQUIRE(timeline->getTracksCount() == 4); 0554 REQUIRE(timeline->checkConsistency(timeline->getGuideModel()->getSnapPoints())); 0555 0556 const QUuid secondSequence = allUuids.first(); 0557 timeline = openedDoc->getTimeline(secondSequence); 0558 pCore->projectManager()->setActiveTimeline(secondSequence); 0559 REQUIRE(timeline->getTracksCount() == 3); 0560 std::shared_ptr<EffectStackModel> stack = timeline->getMasterEffectStackModel(); 0561 REQUIRE(stack->checkConsistency()); 0562 REQUIRE(stack->rowCount() == 2); 0563 std::shared_ptr<EffectStackModel> binStack = binModel->getClipEffectStack(secondaryId.toInt()); 0564 REQUIRE(binStack->checkConsistency()); 0565 REQUIRE(binStack->rowCount() == 2); 0566 0567 pCore->projectManager()->closeCurrentDocument(false, false); 0568 QFile::remove(QDir::temp().absoluteFilePath(QStringLiteral("test-nest2.kdenlive"))); 0569 } 0570 }