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

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 }
0571 
0572 TEST_CASE("Check nested sequences on opening", "[NEST]")
0573 {
0574     auto binModel = pCore->projectItemModel();
0575     binModel->clean();
0576     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
0577     SECTION("Open and simply save file")
0578     {
0579         QString path = sourcesPath + "/dataset/test-nesting.kdenlive";
0580         Q_ASSERT(QFile::exists(sourcesPath));
0581         QUrl openURL = QUrl::fromLocalFile(path);
0582 
0583         QUndoGroup *undoGroup = new QUndoGroup();
0584         undoGroup->addStack(undoStack.get());
0585         DocOpenResult openResults = KdenliveDoc::Open(openURL, QDir::temp().path(), undoGroup, false, nullptr);
0586         REQUIRE(openResults.isSuccessful() == true);
0587         std::unique_ptr<KdenliveDoc> openedDoc = openResults.getDocument();
0588 
0589         pCore->projectManager()->m_project = openedDoc.get();
0590         const QUuid uuid = openedDoc->uuid();
0591         QDateTime documentDate = QFileInfo(openURL.toLocalFile()).lastModified();
0592         pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
0593         QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips();
0594         const QString firstSeqId = allSequences.take(uuid);
0595         pCore->projectManager()->openTimeline(firstSeqId, uuid);
0596         std::shared_ptr<TimelineItemModel> timeline1 = openedDoc->getTimeline(uuid);
0597         pCore->projectManager()->testSetActiveDocument(openedDoc.get(), timeline1);
0598         REQUIRE(openedDoc->checkConsistency());
0599         // Sequence 2 {9228b6f1-e900-4ae9-8591-3cd674dbae98}
0600         std::shared_ptr<TimelineItemModel> timeline2 = openedDoc->getTimeline(QUuid(QStringLiteral("{9228b6f1-e900-4ae9-8591-3cd674dbae98}")));
0601         // Sequence 3 {147c3db9-2050-4453-91bd-4e4ad0033fb5}
0602         std::shared_ptr<TimelineItemModel> timeline3 = openedDoc->getTimeline(QUuid(QStringLiteral("{147c3db9-2050-4453-91bd-4e4ad0033fb5}")));
0603         // Check that each timeline has the correct clipcount
0604         REQUIRE(timeline1->getClipsCount() == 1);
0605         REQUIRE(timeline2->getClipsCount() == 2);
0606         REQUIRE(timeline3->getClipsCount() == 2);
0607         timeline1.reset();
0608         timeline2.reset();
0609         timeline3.reset();
0610         pCore->projectManager()->closeCurrentDocument(false, false);
0611     }
0612 }