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

0001 /*
0002     SPDX-FileCopyrightText: 2020-2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003     SPDX-FileCopyrightText: 2021 Julius K├╝nzel <jk.kdedev@smartlab.uber.space>
0004     SPDX-FileCopyrightText: 2017-2019 Nicolas Carion <french.ebook.lover@gmail.com>
0005     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 #include "test_utils.hpp"
0008 // test specific headers
0009 #include "doc/kdenlivedoc.h"
0010 #include "timeline2/model/timelinefunctions.hpp"
0011 
0012 using namespace fakeit;
0013 
0014 TEST_CASE("Simple trimming operations", "[Trimming]")
0015 {
0016     auto binModel = pCore->projectItemModel();
0017     binModel->clean();
0018     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
0019 
0020     // Here we do some trickery to enable testing.
0021     KdenliveDoc document(undoStack, {1, 3});
0022     pCore->projectManager()->m_project = &document;
0023     QDateTime documentDate = QDateTime::currentDateTime();
0024     pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
0025     auto timeline = document.getTimeline(document.uuid());
0026     pCore->projectManager()->m_activeTimelineModel = timeline;
0027     pCore->projectManager()->testSetActiveDocument(&document, timeline);
0028 
0029     QString binId = createProducer(pCore->getProjectProfile(), "red", binModel);
0030     QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel);
0031     QString binId3 = createProducerWithSound(pCore->getProjectProfile(), binModel);
0032 
0033     int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0034     int tid1 = timeline->getTrackIndexFromPosition(3);
0035     int tid2 = timeline->getTrackIndexFromPosition(2);
0036     int tid3 = timeline->getTrackIndexFromPosition(1);
0037 
0038     // Add an audio track
0039     int tid4 = timeline->getTrackIndexFromPosition(0);
0040     int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
0041     int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0042     int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0043     int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0044     int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0045     int cid7 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0046 
0047     int audio1 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
0048     int audio2 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
0049     int audio3 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
0050 
0051     timeline->m_allClips[cid1]->m_endlessResize = false;
0052     timeline->m_allClips[cid2]->m_endlessResize = false;
0053     timeline->m_allClips[cid3]->m_endlessResize = false;
0054     timeline->m_allClips[cid4]->m_endlessResize = false;
0055     timeline->m_allClips[cid5]->m_endlessResize = false;
0056     timeline->m_allClips[cid6]->m_endlessResize = false;
0057     timeline->m_allClips[cid7]->m_endlessResize = false;
0058 
0059     SECTION("Clip cutting")
0060     {
0061         // Trivial split
0062         REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
0063         int l = timeline->getClipPlaytime(cid2);
0064         REQUIRE(timeline->requestItemResize(cid2, l - 3, true) == l - 3);
0065         REQUIRE(timeline->requestItemResize(cid2, l - 5, false) == l - 5);
0066         REQUIRE(timeline->requestClipMove(cid2, tid1, l));
0067         REQUIRE(timeline->requestClipMove(cid3, tid1, l + l - 5));
0068         auto state = [&]() {
0069             REQUIRE(timeline->checkConsistency());
0070             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0071             REQUIRE(timeline->getClipPlaytime(cid2) == l - 5);
0072             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0073             REQUIRE(timeline->getClipPosition(cid1) == 0);
0074             REQUIRE(timeline->getClipPosition(cid2) == l);
0075             REQUIRE(timeline->getClipPosition(cid3) == l + l - 5);
0076             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 2);
0077             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 4);
0078         };
0079         state();
0080 
0081         // require cut position outside the clip. Should return true and nothing is done
0082         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 0));
0083         state();
0084         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 5 * l));
0085         state();
0086         // cut on edges doesn't do anything either
0087         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l));
0088         state();
0089         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l + l - 5));
0090         state();
0091 
0092         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l + 4));
0093         int splitted = timeline->getClipByPosition(tid1, l + 5);
0094         auto state2 = [&]() {
0095             REQUIRE(timeline->checkConsistency());
0096             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0097             REQUIRE(timeline->getClipPlaytime(cid2) == 4);
0098             REQUIRE(timeline->getClipPlaytime(splitted) == l - 9);
0099             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0100             REQUIRE(timeline->getClipPosition(cid1) == 0);
0101             REQUIRE(timeline->getClipPosition(cid2) == l);
0102             REQUIRE(timeline->getClipPosition(splitted) == l + 4);
0103             REQUIRE(timeline->getClipPosition(cid3) == l + l - 5);
0104             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 2);
0105             REQUIRE(timeline->getClipPtr(cid2)->getOut() == 5);
0106             REQUIRE(timeline->getClipPtr(splitted)->getIn() == 6);
0107             REQUIRE(timeline->getClipPtr(splitted)->getOut() == l - 4);
0108         };
0109         state2();
0110 
0111         undoStack->undo();
0112         state();
0113 
0114         undoStack->redo();
0115         state2();
0116     }
0117 
0118     SECTION("Cut and resize")
0119     {
0120         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
0121         int l = timeline->getClipPlaytime(cid1);
0122         timeline->m_allClips[cid1]->m_endlessResize = false;
0123 
0124         auto state = [&]() {
0125             REQUIRE(timeline->checkConsistency());
0126             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
0127             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0128             REQUIRE(timeline->getClipPosition(cid1) == 5);
0129         };
0130         state();
0131 
0132         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid1, 9));
0133         int splitted = timeline->getClipByPosition(tid1, 10);
0134         timeline->m_allClips[splitted]->m_endlessResize = false;
0135         auto state2 = [&]() {
0136             REQUIRE(timeline->checkConsistency());
0137             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
0138             REQUIRE(timeline->getClipTrackId(splitted) == tid1);
0139             REQUIRE(timeline->getClipPlaytime(cid1) == 4);
0140             REQUIRE(timeline->getClipPlaytime(splitted) == l - 4);
0141             REQUIRE(timeline->getClipPosition(cid1) == 5);
0142             REQUIRE(timeline->getClipPosition(splitted) == 9);
0143         };
0144         state2();
0145 
0146         REQUIRE(timeline->requestClipMove(splitted, tid2, 9, true, true));
0147         REQUIRE(timeline->requestItemResize(splitted, l - 3, true, true) == -1);
0148         REQUIRE(timeline->requestItemResize(splitted, l, false, true) == l);
0149         REQUIRE(timeline->requestItemResize(cid1, 5, false, true) == -1);
0150         REQUIRE(timeline->requestItemResize(cid1, l, true, true) == l);
0151         auto state3 = [&]() {
0152             REQUIRE(timeline->checkConsistency());
0153             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
0154             REQUIRE(timeline->getClipTrackId(splitted) == tid2);
0155             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0156             REQUIRE(timeline->getClipPlaytime(splitted) == l);
0157             REQUIRE(timeline->getClipPosition(cid1) == 5);
0158             REQUIRE(timeline->getClipPosition(splitted) == 5);
0159         };
0160         state3();
0161 
0162         undoStack->undo();
0163         undoStack->undo();
0164         undoStack->undo();
0165         state2();
0166         undoStack->undo();
0167         state();
0168         undoStack->redo();
0169         state2();
0170         undoStack->redo();
0171         undoStack->redo();
0172         undoStack->redo();
0173         state3();
0174     }
0175 
0176     SECTION("Clip cutting 2")
0177     {
0178         // More complex group structure split split
0179         int l = timeline->getClipPlaytime(cid2);
0180         REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
0181         REQUIRE(timeline->requestClipMove(cid2, tid1, l));
0182         REQUIRE(timeline->requestClipMove(cid3, tid1, 2 * l));
0183         REQUIRE(timeline->requestClipMove(cid4, tid2, 0));
0184         REQUIRE(timeline->requestClipMove(cid5, tid2, l));
0185         REQUIRE(timeline->requestClipMove(cid6, tid2, 2 * l));
0186         REQUIRE(timeline->requestClipMove(cid7, tid1, 200));
0187         int gid1 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid4}), true, GroupType::Normal);
0188         int gid2 = timeline->requestClipsGroup(std::unordered_set<int>({cid2, cid5}), true, GroupType::Normal);
0189         int gid3 = timeline->requestClipsGroup(std::unordered_set<int>({cid3, cid6}), true, GroupType::Normal);
0190         int gid4 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5, cid6, cid7}), true, GroupType::Normal);
0191         auto state = [&]() {
0192             REQUIRE(timeline->checkConsistency());
0193             int p = 0;
0194             for (int c : std::vector<int>({cid1, cid2, cid3})) {
0195                 REQUIRE(timeline->getClipPlaytime(c) == l);
0196                 REQUIRE(timeline->getClipTrackId(c) == tid1);
0197                 REQUIRE(timeline->getClipPosition(c) == p);
0198                 p += l;
0199             }
0200             p = 0;
0201             for (int c : std::vector<int>({cid4, cid5, cid6})) {
0202                 REQUIRE(timeline->getClipPlaytime(c) == l);
0203                 REQUIRE(timeline->getClipTrackId(c) == tid2);
0204                 REQUIRE(timeline->getClipPosition(c) == p);
0205                 p += l;
0206             }
0207             REQUIRE(timeline->getClipPosition(cid7) == 200);
0208             REQUIRE(timeline->getClipTrackId(cid7) == tid1);
0209             REQUIRE(timeline->m_groups->getDirectChildren(gid1) == std::unordered_set<int>({cid1, cid4}));
0210             REQUIRE(timeline->m_groups->getDirectChildren(gid2) == std::unordered_set<int>({cid2, cid5}));
0211             REQUIRE(timeline->m_groups->getDirectChildren(gid3) == std::unordered_set<int>({cid3, cid6}));
0212             REQUIRE(timeline->m_groups->getDirectChildren(gid4) == std::unordered_set<int>({gid1, gid2, gid3, cid7}));
0213             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5, cid6, cid7}));
0214         };
0215         state();
0216 
0217         // These functions will return true but do nothing
0218         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 0));
0219         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 5 * l));
0220         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l));
0221         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 2 * l));
0222         state();
0223 
0224         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l + 4));
0225         int splitted = timeline->getClipByPosition(tid1, l + 5);
0226         int splitted2 = timeline->getClipByPosition(tid2, l + 5);
0227         REQUIRE(splitted != splitted2);
0228         auto check_groups = [&]() {
0229             REQUIRE(timeline->m_groups->getDirectChildren(gid2) == std::unordered_set<int>({splitted, splitted2}));
0230             REQUIRE(timeline->m_groups->getDirectChildren(gid3) == std::unordered_set<int>({cid3, cid6}));
0231             REQUIRE(timeline->m_groups->getDirectChildren(gid4) == std::unordered_set<int>({gid2, gid3, cid7}));
0232             REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({splitted, splitted2, cid3, cid6, cid7}));
0233 
0234             int g1b = timeline->m_groups->m_upLink[cid1];
0235             int g2b = timeline->m_groups->m_upLink[cid2];
0236             int g4b = timeline->m_groups->getRootId(cid1);
0237             REQUIRE(timeline->m_groups->getDirectChildren(g1b) == std::unordered_set<int>({cid1, cid4}));
0238             REQUIRE(timeline->m_groups->getDirectChildren(g2b) == std::unordered_set<int>({cid2, cid5}));
0239             REQUIRE(timeline->m_groups->getDirectChildren(g4b) == std::unordered_set<int>({g1b, g2b}));
0240             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2, cid4, cid5}));
0241         };
0242         auto state2 = [&]() {
0243             REQUIRE(timeline->checkConsistency());
0244             int p = 0;
0245             for (int c : std::vector<int>({cid1, cid2, cid3})) {
0246                 REQUIRE(timeline->getClipPlaytime(c) == (c == cid2 ? 4 : l));
0247                 REQUIRE(timeline->getClipTrackId(c) == tid1);
0248                 REQUIRE(timeline->getClipPosition(c) == p);
0249                 p += l;
0250             }
0251             p = 0;
0252             for (int c : std::vector<int>({cid4, cid5, cid6})) {
0253                 REQUIRE(timeline->getClipPlaytime(c) == (c == cid5 ? 4 : l));
0254                 REQUIRE(timeline->getClipTrackId(c) == tid2);
0255                 REQUIRE(timeline->getClipPosition(c) == p);
0256                 p += l;
0257             }
0258             REQUIRE(timeline->getClipPosition(cid7) == 200);
0259             REQUIRE(timeline->getClipTrackId(cid7) == tid1);
0260             REQUIRE(timeline->getClipPosition(splitted) == l + 4);
0261             REQUIRE(timeline->getClipPlaytime(splitted) == l - 4);
0262             REQUIRE(timeline->getClipTrackId(splitted) == tid1);
0263             REQUIRE(timeline->getClipPosition(splitted2) == l + 4);
0264             REQUIRE(timeline->getClipPlaytime(splitted2) == l - 4);
0265             REQUIRE(timeline->getClipTrackId(splitted2) == tid2);
0266             check_groups();
0267         };
0268         state2();
0269 
0270         REQUIRE(timeline->requestClipMove(splitted, tid1, l + 4 + 10, true, true));
0271         REQUIRE(timeline->requestClipMove(cid1, tid2, 10, true, true));
0272         auto state3 = [&]() {
0273             REQUIRE(timeline->checkConsistency());
0274             int p = 0;
0275             for (int c : std::vector<int>({cid1, cid2, cid3})) {
0276                 REQUIRE(timeline->getClipPlaytime(c) == (c == cid2 ? 4 : l));
0277                 REQUIRE(timeline->getClipTrackId(c) == (c == cid3 ? tid1 : tid2));
0278                 REQUIRE(timeline->getClipPosition(c) == p + 10);
0279                 p += l;
0280             }
0281             p = 0;
0282             for (int c : std::vector<int>({cid4, cid5, cid6})) {
0283                 REQUIRE(timeline->getClipPlaytime(c) == (c == cid5 ? 4 : l));
0284                 REQUIRE(timeline->getClipTrackId(c) == (c == cid6 ? tid2 : tid3));
0285                 REQUIRE(timeline->getClipPosition(c) == p + 10);
0286                 p += l;
0287             }
0288             REQUIRE(timeline->getClipPosition(cid7) == 210);
0289             REQUIRE(timeline->getClipTrackId(cid7) == tid1);
0290             REQUIRE(timeline->getClipPosition(splitted) == l + 4 + 10);
0291             REQUIRE(timeline->getClipPlaytime(splitted) == l - 4);
0292             REQUIRE(timeline->getClipTrackId(splitted) == tid1);
0293             REQUIRE(timeline->getClipPosition(splitted2) == l + 4 + 10);
0294             REQUIRE(timeline->getClipPlaytime(splitted2) == l - 4);
0295             REQUIRE(timeline->getClipTrackId(splitted2) == tid2);
0296             check_groups();
0297         };
0298         state3();
0299 
0300         undoStack->undo();
0301         undoStack->undo();
0302         state2();
0303         undoStack->undo();
0304         state();
0305         undoStack->redo();
0306         state2();
0307         undoStack->redo();
0308         undoStack->redo();
0309         state3();
0310     }
0311 
0312     SECTION("Simple audio split")
0313     {
0314         int l = timeline->getClipPlaytime(audio1);
0315         REQUIRE(timeline->requestClipMove(audio1, tid1, 3));
0316 
0317         auto state = [&]() {
0318             REQUIRE(timeline->checkConsistency());
0319             REQUIRE(timeline->getClipPlaytime(audio1) == l);
0320             REQUIRE(timeline->getClipPosition(audio1) == 3);
0321             REQUIRE(timeline->getClipTrackId(audio1) == tid1);
0322             REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
0323             REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
0324 
0325             REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1}));
0326         };
0327         state();
0328 
0329         REQUIRE(TimelineFunctions::requestSplitAudio(timeline, audio1, tid4));
0330         int splitted1 = timeline->getClipByPosition(tid4, 3);
0331         auto state2 = [&]() {
0332             REQUIRE(timeline->checkConsistency());
0333             REQUIRE(timeline->getClipPlaytime(audio1) == l);
0334             REQUIRE(timeline->getClipPosition(audio1) == 3);
0335             REQUIRE(timeline->getClipPlaytime(splitted1) == l);
0336             REQUIRE(timeline->getClipPosition(splitted1) == 3);
0337             REQUIRE(timeline->getClipTrackId(audio1) == tid1);
0338             REQUIRE(timeline->getClipTrackId(splitted1) == tid4);
0339             REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
0340             REQUIRE(timeline->getTrackClipsCount(tid4) == 1);
0341 
0342             REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1, splitted1}));
0343 
0344             int g1 = timeline->m_groups->getDirectAncestor(audio1);
0345             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({audio1, splitted1}));
0346             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0347         };
0348         state2();
0349 
0350         undoStack->undo();
0351         state();
0352         undoStack->redo();
0353         state2();
0354         undoStack->undo();
0355         state();
0356         undoStack->redo();
0357         state2();
0358 
0359         // We also make sure that clips that are audio only cannot be further splitted
0360         REQUIRE(timeline->requestClipMove(cid1, tid1, l + 30));
0361         // This is a color clip, shouldn't be splittable
0362         REQUIRE_FALSE(TimelineFunctions::requestSplitAudio(timeline, cid1, tid2));
0363         // Check we cannot split audio on a video track
0364         REQUIRE_FALSE(TimelineFunctions::requestSplitAudio(timeline, audio1, tid2));
0365     }
0366     SECTION("Split audio on a selection")
0367     {
0368 
0369         int l = timeline->getClipPlaytime(audio2);
0370         REQUIRE(timeline->requestClipMove(audio1, tid1, 0));
0371         REQUIRE(timeline->requestClipMove(audio2, tid1, l));
0372         REQUIRE(timeline->requestClipMove(audio3, tid1, 2 * l));
0373 
0374         std::unordered_set<int> selection{audio1, audio3, audio2};
0375         timeline->requestSetSelection(selection);
0376 
0377         auto state = [&]() {
0378             REQUIRE(timeline->checkConsistency());
0379             REQUIRE(timeline->getClipPlaytime(audio1) == l);
0380             REQUIRE(timeline->getClipPlaytime(audio2) == l);
0381             REQUIRE(timeline->getClipPlaytime(audio3) == l);
0382             REQUIRE(timeline->getClipPosition(audio1) == 0);
0383             REQUIRE(timeline->getClipPosition(audio2) == l);
0384             REQUIRE(timeline->getClipPosition(audio3) == l + l);
0385             REQUIRE(timeline->getClipTrackId(audio1) == tid1);
0386             REQUIRE(timeline->getClipTrackId(audio2) == tid1);
0387             REQUIRE(timeline->getClipTrackId(audio3) == tid1);
0388             REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
0389             REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
0390 
0391             REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1, audio2, audio3}));
0392 
0393             REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>({audio1, audio3, audio2}));
0394         };
0395         state();
0396 
0397         REQUIRE(TimelineFunctions::requestSplitAudio(timeline, audio1, tid4));
0398         int splitted1 = timeline->getClipByPosition(tid4, 0);
0399         int splitted2 = timeline->getClipByPosition(tid4, l);
0400         int splitted3 = timeline->getClipByPosition(tid4, 2 * l);
0401         auto state2 = [&]() {
0402             REQUIRE(timeline->checkConsistency());
0403             REQUIRE(timeline->getClipPlaytime(audio1) == l);
0404             REQUIRE(timeline->getClipPlaytime(audio2) == l);
0405             REQUIRE(timeline->getClipPlaytime(audio3) == l);
0406             REQUIRE(timeline->getClipPosition(audio1) == 0);
0407             REQUIRE(timeline->getClipPosition(audio2) == l);
0408             REQUIRE(timeline->getClipPosition(audio3) == l + l);
0409             REQUIRE(timeline->getClipPlaytime(splitted1) == l);
0410             REQUIRE(timeline->getClipPlaytime(splitted2) == l);
0411             REQUIRE(timeline->getClipPlaytime(splitted3) == l);
0412             REQUIRE(timeline->getClipPosition(splitted1) == 0);
0413             REQUIRE(timeline->getClipPosition(splitted2) == l);
0414             REQUIRE(timeline->getClipPosition(splitted3) == l + l);
0415             REQUIRE(timeline->getClipTrackId(audio1) == tid1);
0416             REQUIRE(timeline->getClipTrackId(audio2) == tid1);
0417             REQUIRE(timeline->getClipTrackId(audio3) == tid1);
0418             REQUIRE(timeline->getClipTrackId(splitted1) == tid4);
0419             REQUIRE(timeline->getClipTrackId(splitted2) == tid4);
0420             REQUIRE(timeline->getClipTrackId(splitted3) == tid4);
0421             REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
0422             REQUIRE(timeline->getTrackClipsCount(tid4) == 3);
0423 
0424             REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1, splitted1, audio2, audio3, splitted2, splitted3}));
0425             REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>({audio1, splitted1, audio2, audio3, splitted2, splitted3}));
0426 
0427             int g1 = timeline->m_groups->getDirectAncestor(audio1);
0428             int g2 = timeline->m_groups->getDirectAncestor(audio2);
0429             int g3 = timeline->m_groups->getDirectAncestor(audio3);
0430             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({audio1, splitted1}));
0431             REQUIRE(timeline->m_groups->getDirectChildren(g2) == std::unordered_set<int>({audio2, splitted2}));
0432             REQUIRE(timeline->m_groups->getDirectChildren(g3) == std::unordered_set<int>({audio3, splitted3}));
0433             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0434             REQUIRE(timeline->m_groups->getType(g2) == GroupType::AVSplit);
0435             REQUIRE(timeline->m_groups->getType(g3) == GroupType::AVSplit);
0436         };
0437         state2();
0438 
0439         undoStack->undo();
0440         state();
0441         undoStack->redo();
0442         state2();
0443     }
0444     SECTION("Cut should preserve AV groups")
0445     {
0446         QString binId3 = createProducerWithSound(pCore->getProjectProfile(), binModel);
0447 
0448         int tid6 = TrackModel::construct(timeline, -1, -1, QString(), true);
0449         int tid5 = TrackModel::construct(timeline);
0450 
0451         // Setup timeline audio drop info
0452         QMap<int, QString> audioInfo;
0453         audioInfo.insert(1, QStringLiteral("stream1"));
0454         timeline->m_binAudioTargets = audioInfo;
0455         timeline->m_videoTarget = tid5;
0456 
0457         int cid6 = -1;
0458         int pos = 3;
0459         REQUIRE(timeline->requestClipInsertion(binId3, tid5, pos, cid6, true, true, false));
0460         int cid7 = timeline->m_groups->getSplitPartner(cid6);
0461         int l = timeline->getClipPlaytime(cid6);
0462         REQUIRE(l >= 10);
0463 
0464         auto state = [&]() {
0465             REQUIRE(timeline->checkConsistency());
0466             REQUIRE(timeline->getTrackClipsCount(tid5) == 1);
0467             REQUIRE(timeline->getTrackClipsCount(tid6) == 1);
0468             REQUIRE(timeline->getClipTrackId(cid6) == tid5);
0469             REQUIRE(timeline->getClipTrackId(cid7) == tid6);
0470             REQUIRE(timeline->getClipPosition(cid6) == pos);
0471             REQUIRE(timeline->getClipPosition(cid7) == pos);
0472             REQUIRE(timeline->getClipPtr(cid6)->clipState() == PlaylistState::VideoOnly);
0473             REQUIRE(timeline->getClipPtr(cid7)->clipState() == PlaylistState::AudioOnly);
0474 
0475             // we check that the av group was correctly created
0476             REQUIRE(timeline->getGroupElements(cid6) == std::unordered_set<int>({cid6, cid7}));
0477             int g1 = timeline->m_groups->getDirectAncestor(cid6);
0478             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid6, cid7}));
0479             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0480         };
0481         state();
0482 
0483         REQUIRE(TimelineFunctions::requestClipCut(timeline, cid6, pos + 4));
0484 
0485         int cid8 = timeline->getClipByPosition(tid5, pos + 5);
0486         int cid9 = timeline->getClipByPosition(tid6, pos + 5);
0487         REQUIRE(cid8 >= 0);
0488         REQUIRE(cid9 >= 0);
0489 
0490         auto state2 = [&]() {
0491             REQUIRE(timeline->checkConsistency());
0492             REQUIRE(timeline->getTrackClipsCount(tid5) == 2);
0493             REQUIRE(timeline->getTrackClipsCount(tid6) == 2);
0494             REQUIRE(timeline->getClipTrackId(cid6) == tid5);
0495             REQUIRE(timeline->getClipTrackId(cid7) == tid6);
0496             REQUIRE(timeline->getClipTrackId(cid8) == tid5);
0497             REQUIRE(timeline->getClipTrackId(cid9) == tid6);
0498 
0499             REQUIRE(timeline->getClipPosition(cid6) == pos);
0500             REQUIRE(timeline->getClipPosition(cid7) == pos);
0501             REQUIRE(timeline->getClipPosition(cid8) == pos + 4);
0502             REQUIRE(timeline->getClipPosition(cid9) == pos + 4);
0503 
0504             REQUIRE(timeline->getClipPtr(cid6)->clipState() == PlaylistState::VideoOnly);
0505             REQUIRE(timeline->getClipPtr(cid7)->clipState() == PlaylistState::AudioOnly);
0506             REQUIRE(timeline->getClipPtr(cid8)->clipState() == PlaylistState::VideoOnly);
0507             REQUIRE(timeline->getClipPtr(cid9)->clipState() == PlaylistState::AudioOnly);
0508 
0509             // original AV group
0510             REQUIRE(timeline->getGroupElements(cid6) == std::unordered_set<int>({cid6, cid7}));
0511             int g1 = timeline->m_groups->getDirectAncestor(cid6);
0512             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid6, cid7}));
0513             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0514 
0515             // new AV group
0516             REQUIRE(timeline->getGroupElements(cid8) == std::unordered_set<int>({cid8, cid9}));
0517             int g2 = timeline->m_groups->getDirectAncestor(cid8);
0518             REQUIRE(timeline->m_groups->getDirectChildren(g2) == std::unordered_set<int>({cid8, cid9}));
0519             REQUIRE(timeline->m_groups->getType(g2) == GroupType::AVSplit);
0520         };
0521         state2();
0522 
0523         undoStack->undo();
0524         state();
0525         undoStack->redo();
0526         state2();
0527     }
0528 
0529     pCore->projectManager()->closeCurrentDocument(false, false);
0530 }
0531 
0532 TEST_CASE("Spacer operations", "[Spacer]")
0533 {
0534     auto binModel = pCore->projectItemModel();
0535     binModel->clean();
0536     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
0537     KdenliveDoc document(undoStack, {0, 1});
0538     pCore->projectManager()->m_project = &document;
0539     QDateTime documentDate = QDateTime::currentDateTime();
0540     pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
0541     auto timeline = document.getTimeline(document.uuid());
0542     pCore->projectManager()->m_activeTimelineModel = timeline;
0543     pCore->projectManager()->testSetActiveDocument(&document, timeline);
0544 
0545     std::shared_ptr<MarkerListModel> guideModel = document.getGuideModel(document.uuid());
0546 
0547     QString binId = createProducer(pCore->getProjectProfile(), "red", binModel);
0548     QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel);
0549     QString binId3 = createProducerWithSound(pCore->getProjectProfile(), binModel);
0550 
0551     int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0552     int tid1 = timeline->getTrackIndexFromPosition(0);
0553 
0554     // Add an audio track
0555     // int tid4 = TrackModel::construct(timeline, -1, -1, QString(), true);
0556     int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
0557     int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
0558 
0559     // int audio1 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
0560 
0561     timeline->m_allClips[cid1]->m_endlessResize = false;
0562     timeline->m_allClips[cid2]->m_endlessResize = false;
0563     timeline->m_allClips[cid3]->m_endlessResize = false;
0564 
0565     SECTION("Simple one track space insertion")
0566     {
0567         REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
0568         int l = timeline->getClipPlaytime(cid1);
0569         int p2 = l + 10;
0570         REQUIRE(timeline->requestClipMove(cid2, tid1, p2));
0571         int p3 = l + l + 20;
0572         REQUIRE(timeline->requestClipMove(cid3, tid1, p3));
0573         auto state = [&]() {
0574             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0575             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0576             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0577             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0578             REQUIRE(timeline->getClipPosition(cid1) == 0);
0579             REQUIRE(timeline->getClipPosition(cid2) == p2);
0580             REQUIRE(timeline->getClipPosition(cid3) == p3);
0581 
0582             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0583             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0584             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0585             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0586             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0587             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0588         };
0589         state();
0590 
0591         int spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, l + 5).first;
0592         REQUIRE(spacerIid > -1);
0593         std::function<bool(void)> undo = []() { return true; };
0594         std::function<bool(void)> redo = []() { return true; };
0595         int itemPos = timeline->getItemPosition(spacerIid);
0596         int space = 18;
0597         REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space, tid1, -1, undo, redo));
0598         auto state1 = [&]() {
0599             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0600             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0601             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0602             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0603             REQUIRE(timeline->getClipPosition(cid1) == 0);
0604             REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
0605             REQUIRE(timeline->getClipPosition(cid3) == p3 + space);
0606             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0607             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0608             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0609             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0610             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0611             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0612         };
0613         state1();
0614 
0615         int startPos = timeline->getClipPosition(cid3) + l / 2;
0616         spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, startPos).first;
0617         REQUIRE(spacerIid > -1);
0618         undo = []() { return true; };
0619         redo = []() { return true; };
0620         itemPos = timeline->getItemPosition(spacerIid);
0621         int space2 = 3;
0622         REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space2, tid1, -1, undo, redo));
0623         auto state2 = [&]() {
0624             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0625             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0626             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0627             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0628             REQUIRE(timeline->getClipPosition(cid1) == 0);
0629             REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
0630             REQUIRE(timeline->getClipPosition(cid3) == p3 + space + space2);
0631             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0632             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0633             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0634             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0635             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0636             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0637         };
0638         state2();
0639 
0640         undoStack->undo();
0641         state1();
0642         undoStack->undo();
0643         state();
0644 
0645         undoStack->redo();
0646         state1();
0647         undoStack->redo();
0648         state2();
0649     }
0650 
0651     SECTION("Simple one track space insertion with guides")
0652     {
0653         pCore->projectManager()->m_activeTimelineModel = timeline;
0654         REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
0655         int l = timeline->getClipPlaytime(cid1);
0656         int p2 = l + 10;
0657         REQUIRE(timeline->requestClipMove(cid2, tid1, p2));
0658         int p3 = l + l + 20;
0659         REQUIRE(timeline->requestClipMove(cid3, tid1, p3));
0660         double fps = pCore->getProjectProfile().fps();
0661         guideModel->addMarker(GenTime(l / 2, fps), "guide1");
0662         guideModel->addMarker(GenTime(l + 2, fps), "guide2");
0663         guideModel->addMarker(GenTime(l + 7, fps), "guide3");
0664         guideModel->addMarker(GenTime(p2 + l / 2, fps), "guide4");
0665         guideModel->addMarker(GenTime(p3, fps), "guide5");
0666         auto state = [&]() {
0667             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0668             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0669             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0670             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0671             REQUIRE(timeline->getClipPosition(cid1) == 0);
0672             REQUIRE(timeline->getClipPosition(cid2) == p2);
0673             REQUIRE(timeline->getClipPosition(cid3) == p3);
0674 
0675             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0676             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0677             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0678             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0679             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0680             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0681         };
0682         state();
0683 
0684         int spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, l + 5).first;
0685         REQUIRE(spacerIid > -1);
0686         std::function<bool(void)> undo = []() { return true; };
0687         std::function<bool(void)> redo = []() { return true; };
0688         int itemPos = timeline->getItemPosition(spacerIid);
0689         int space = 18;
0690         REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space, tid1, l + 5, undo, redo));
0691         auto state1 = [&]() {
0692             REQUIRE(guideModel->getSnapPoints() == std::vector<int>{l / 2, l + 2, l + 7 + space, p2 + l / 2 + space, p3 + space});
0693             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0694             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0695             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0696             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0697             REQUIRE(timeline->getClipPosition(cid1) == 0);
0698             REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
0699             REQUIRE(timeline->getClipPosition(cid3) == p3 + space);
0700             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0701             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0702             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0703             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0704             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0705             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0706         };
0707         state1();
0708 
0709         int startPos = timeline->getClipPosition(cid3) + l / 2;
0710         spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, startPos).first;
0711         REQUIRE(spacerIid > -1);
0712         undo = []() { return true; };
0713         redo = []() { return true; };
0714         itemPos = timeline->getItemPosition(spacerIid);
0715         int space2 = 3;
0716         REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space2, tid1, startPos, undo, redo));
0717         auto state2 = [&]() {
0718             REQUIRE(guideModel->getSnapPoints() == std::vector<int>{l / 2, l + 2, l + 7 + space, p2 + l / 2 + space, p3 + space + space2});
0719             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0720             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0721             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0722             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0723             REQUIRE(timeline->getClipPosition(cid1) == 0);
0724             REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
0725             REQUIRE(timeline->getClipPosition(cid3) == p3 + space + space2);
0726             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0727             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0728             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0729             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0730             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0731             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0732         };
0733         state2();
0734 
0735         undoStack->undo();
0736         state1();
0737         undoStack->undo();
0738         state();
0739 
0740         undoStack->redo();
0741         state1();
0742         undoStack->redo();
0743         state2();
0744     }
0745 
0746     SECTION("Simple one track space deletion")
0747     {
0748         REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
0749         int l = timeline->getClipPlaytime(cid1);
0750         int p2 = l + 10;
0751         REQUIRE(timeline->requestClipMove(cid2, tid1, p2));
0752         int p3 = l + l + 10;
0753         REQUIRE(timeline->requestClipMove(cid3, tid1, p3));
0754         auto state = [&]() {
0755             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0756             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0757             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0758             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0759             REQUIRE(timeline->getClipPosition(cid1) == 0);
0760             REQUIRE(timeline->getClipPosition(cid2) == p2);
0761             REQUIRE(timeline->getClipPosition(cid3) == p3);
0762 
0763             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0764             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0765             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0766             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0767             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0768             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0769         };
0770         state();
0771 
0772         int spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, l + 5).first;
0773         REQUIRE(spacerIid > -1);
0774         std::function<bool(void)> undo = []() { return true; };
0775         std::function<bool(void)> redo = []() { return true; };
0776         int itemPos = timeline->getItemPosition(spacerIid);
0777         // space to remove is larger than possible (at the end only 10 frames should be removed)
0778         int space = 18;
0779         REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos - space, tid1, -1, undo, redo));
0780         auto state1 = [&]() {
0781             REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
0782             REQUIRE(timeline->getClipPlaytime(cid1) == l);
0783             REQUIRE(timeline->getClipPlaytime(cid2) == l);
0784             REQUIRE(timeline->getClipPlaytime(cid3) == l);
0785             REQUIRE(timeline->getClipPosition(cid1) == 0);
0786             REQUIRE(timeline->getClipPosition(cid2) == p2 - 10);
0787             REQUIRE(timeline->getClipPosition(cid3) == p3 - 10);
0788             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
0789             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
0790             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
0791             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l - 1);
0792             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
0793             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l - 1);
0794         };
0795         state1();
0796 
0797         int startPos = timeline->getClipPosition(cid3) + l / 2;
0798         spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, startPos).first;
0799         REQUIRE(spacerIid > -1);
0800         undo = []() { return true; };
0801         redo = []() { return true; };
0802         itemPos = timeline->getItemPosition(spacerIid);
0803         int space2 = 3;
0804         REQUIRE(!TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos - space2, tid1, -1, undo, redo));
0805         state1();
0806 
0807         undoStack->undo();
0808         state();
0809 
0810         undoStack->redo();
0811         state1();
0812     }
0813 
0814     pCore->projectManager()->closeCurrentDocument(false, false);
0815 }
0816 
0817 TEST_CASE("Insert/delete", "[Trimming2]")
0818 {
0819     auto binModel = pCore->projectItemModel();
0820     binModel->clean();
0821     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
0822 
0823     // Here we do some trickery to enable testing.
0824     KdenliveDoc document(undoStack);
0825     pCore->projectManager()->m_project = &document;
0826     QDateTime documentDate = QDateTime::currentDateTime();
0827     pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
0828     auto timeline = document.getTimeline(document.uuid());
0829     pCore->projectManager()->m_activeTimelineModel = timeline;
0830     pCore->projectManager()->testSetActiveDocument(&document, timeline);
0831 
0832     QString binId = createProducerWithSound(pCore->getProjectProfile(), binModel);
0833     int tid2 = timeline->getTrackIndexFromPosition(1);
0834     int tid1 = timeline->getTrackIndexFromPosition(2);
0835 
0836     // Setup timeline audio drop info
0837     QMap<int, QString> audioInfo;
0838     audioInfo.insert(1, QStringLiteral("stream1"));
0839     timeline->m_binAudioTargets = audioInfo;
0840     timeline->m_videoTarget = tid1;
0841 
0842     SECTION("Remove Space should preserve groups")
0843     {
0844 
0845         int cid1 = -1;
0846         REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
0847         int cid2 = timeline->m_groups->getSplitPartner(cid1);
0848 
0849         auto state = [&](int pos) {
0850             REQUIRE(timeline->checkConsistency());
0851             REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
0852             REQUIRE(timeline->getTrackClipsCount(tid2) == 1);
0853             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
0854             REQUIRE(timeline->getClipTrackId(cid2) == tid2);
0855             REQUIRE(timeline->getClipPosition(cid1) == pos);
0856             REQUIRE(timeline->getClipPosition(cid2) == pos);
0857             REQUIRE(timeline->getClipPtr(cid1)->clipState() == PlaylistState::VideoOnly);
0858             REQUIRE(timeline->getClipPtr(cid2)->clipState() == PlaylistState::AudioOnly);
0859             // we check that the av group was correctly created
0860             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
0861             int g1 = timeline->m_groups->getDirectAncestor(cid1);
0862             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
0863             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0864         };
0865         state(3);
0866 
0867         REQUIRE(TimelineFunctions::requestDeleteBlankAt(timeline, tid1, 1, true));
0868         state(0);
0869 
0870         undoStack->undo();
0871         state(3);
0872         undoStack->redo();
0873         state(0);
0874     }
0875 
0876     SECTION("Insert zone should preserve groups")
0877     {
0878         int cid1 = -1;
0879         REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
0880         int cid2 = timeline->m_groups->getSplitPartner(cid1);
0881 
0882         int l = timeline->getClipPlaytime(cid2);
0883         auto state = [&]() {
0884             REQUIRE(timeline->checkConsistency());
0885             REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
0886             REQUIRE(timeline->getTrackClipsCount(tid2) == 1);
0887             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
0888             REQUIRE(timeline->getClipTrackId(cid2) == tid2);
0889             REQUIRE(timeline->getClipPosition(cid1) == 3);
0890             REQUIRE(timeline->getClipPosition(cid2) == 3);
0891             REQUIRE(timeline->getClipPtr(cid1)->clipState() == PlaylistState::VideoOnly);
0892             REQUIRE(timeline->getClipPtr(cid2)->clipState() == PlaylistState::AudioOnly);
0893             // we check that the av group was correctly created
0894             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
0895             int g1 = timeline->m_groups->getDirectAncestor(cid1);
0896             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
0897             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0898         };
0899         state();
0900 
0901         timeline->m_audioTarget.insert(tid2, 0);
0902         timeline->m_videoTarget = tid1;
0903         // Make tracks active
0904         timeline->setTrackProperty(tid1, QStringLiteral("kdenlive:timeline_active"), QStringLiteral("1"));
0905         timeline->setTrackProperty(tid2, QStringLiteral("kdenlive:timeline_active"), QStringLiteral("1"));
0906         REQUIRE(TimelineFunctions::insertZone(timeline, {tid1, tid2}, binId, 3 + 2, {l / 4, 3 * l / 4}, false));
0907         timeline->m_audioTarget.clear();
0908         timeline->m_videoTarget = -1;
0909         int small_length = 3 * l / 4 - l / 4;
0910         int cid3 = timeline->getClipByPosition(tid1, 3 + 2);
0911         int cid4 = timeline->getClipByPosition(tid2, 3 + 2);
0912         int cid5 = timeline->getClipByPosition(tid1, 3 + 2 + small_length);
0913         int cid6 = timeline->getClipByPosition(tid2, 3 + 2 + small_length);
0914 
0915         auto state2 = [&]() {
0916             REQUIRE(timeline->checkConsistency());
0917             REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
0918             REQUIRE(timeline->getTrackClipsCount(tid2) == 3);
0919             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
0920             REQUIRE(timeline->getClipTrackId(cid2) == tid2);
0921             REQUIRE(timeline->getClipTrackId(cid3) == tid1);
0922             REQUIRE(timeline->getClipTrackId(cid4) == tid2);
0923             REQUIRE(timeline->getClipTrackId(cid5) == tid1);
0924             REQUIRE(timeline->getClipTrackId(cid6) == tid2);
0925             REQUIRE(timeline->getClipPosition(cid1) == 3);
0926             REQUIRE(timeline->getClipPosition(cid2) == 3);
0927             REQUIRE(timeline->getClipPosition(cid3) == 3 + 2);
0928             REQUIRE(timeline->getClipPosition(cid4) == 3 + 2);
0929             REQUIRE(timeline->getClipPosition(cid5) == 3 + 2 + small_length);
0930             REQUIRE(timeline->getClipPosition(cid6) == 3 + 2 + small_length);
0931             REQUIRE(timeline->getClipPlaytime(cid1) + timeline->getClipPlaytime(cid5) == l);
0932             REQUIRE(timeline->getClipPlaytime(cid1) == 2);
0933             REQUIRE(timeline->getClipPlaytime(cid3) == small_length);
0934             REQUIRE(timeline->getClipPtr(cid1)->clipState() == PlaylistState::VideoOnly);
0935             REQUIRE(timeline->getClipPtr(cid2)->clipState() == PlaylistState::AudioOnly);
0936             REQUIRE(timeline->getClipPtr(cid3)->clipState() == PlaylistState::VideoOnly);
0937             REQUIRE(timeline->getClipPtr(cid4)->clipState() == PlaylistState::AudioOnly);
0938             REQUIRE(timeline->getClipPtr(cid5)->clipState() == PlaylistState::VideoOnly);
0939             REQUIRE(timeline->getClipPtr(cid6)->clipState() == PlaylistState::AudioOnly);
0940             // we check that the av group was correctly created
0941             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
0942             int g1 = timeline->m_groups->getDirectAncestor(cid1);
0943             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
0944             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
0945 
0946             REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({cid3, cid4}));
0947             int g2 = timeline->m_groups->getDirectAncestor(cid3);
0948             REQUIRE(timeline->m_groups->getDirectChildren(g2) == std::unordered_set<int>({cid3, cid4}));
0949             REQUIRE(timeline->m_groups->getType(g2) == GroupType::AVSplit);
0950 
0951             int g3 = timeline->m_groups->getDirectAncestor(cid5);
0952             REQUIRE(timeline->m_groups->getDirectChildren(g3) == std::unordered_set<int>({cid5, cid6}));
0953             REQUIRE(timeline->m_groups->getType(g3) == GroupType::AVSplit);
0954         };
0955         state2();
0956         undoStack->undo();
0957         state();
0958         undoStack->redo();
0959         state2();
0960     }
0961 
0962     pCore->projectManager()->closeCurrentDocument(false, false);
0963 }
0964 
0965 TEST_CASE("Copy/paste", "[CP]")
0966 {
0967     auto binModel = pCore->projectItemModel();
0968     binModel->clean();
0969     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
0970 
0971     KdenliveDoc document(undoStack);
0972     pCore->projectManager()->m_project = &document;
0973     QDateTime documentDate = QDateTime::currentDateTime();
0974     pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
0975     auto timeline = document.getTimeline(document.uuid());
0976     pCore->projectManager()->m_activeTimelineModel = timeline;
0977     pCore->projectManager()->testSetActiveDocument(&document, timeline);
0978 
0979     QString binId = createProducerWithSound(pCore->getProjectProfile(), binModel);
0980     QString binId2 = createProducer(pCore->getProjectProfile(), "red", binModel);
0981 
0982     int tid2b = timeline->getTrackIndexFromPosition(0);
0983     int tid2 = timeline->getTrackIndexFromPosition(1);
0984     int tid1 = timeline->getTrackIndexFromPosition(2);
0985     int tid1b = timeline->getTrackIndexFromPosition(3);
0986 
0987     // Setup timeline audio drop info
0988     QMap<int, QString> audioInfo;
0989     audioInfo.insert(1, QStringLiteral("stream1"));
0990     timeline->m_binAudioTargets = audioInfo;
0991     timeline->m_videoTarget = tid1;
0992 
0993     SECTION("Simple copy paste of one clip")
0994     {
0995 
0996         int cid1 = -1;
0997         REQUIRE(timeline->requestClipInsertion(binId2, tid1, 3, cid1, true, true, false));
0998         int l = timeline->getClipPlaytime(cid1);
0999         int cid2 = -1;
1000         REQUIRE(timeline->requestClipInsertion(binId2, tid1, 3 + l, cid2, true, true, false));
1001 
1002         auto state = [&]() {
1003             REQUIRE(timeline->checkConsistency());
1004             REQUIRE(timeline->getTrackClipsCount(tid1) == 2);
1005             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1006             REQUIRE(timeline->getClipTrackId(cid2) == tid1);
1007             REQUIRE(timeline->getClipPosition(cid1) == 3);
1008             REQUIRE(timeline->getClipPosition(cid2) == 3 + l);
1009         };
1010         state();
1011 
1012         QString cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
1013 
1014         // Try to paste in invalid positions
1015         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
1016         state();
1017         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 4));
1018         state();
1019         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 4 + l));
1020         state();
1021         // Paste in audio track
1022         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid2, 0));
1023         state();
1024         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid2b, 0));
1025         state();
1026 
1027         // Paste after the last clip
1028         REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 3 + 2 * l));
1029         int cid3 = timeline->getTrackById(tid1)->getClipByPosition(3 + 2 * l + 1);
1030         REQUIRE(cid3 != -1);
1031         auto state2 = [&]() {
1032             REQUIRE(timeline->checkConsistency());
1033             REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
1034             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1035             REQUIRE(timeline->getClipTrackId(cid2) == tid1);
1036             REQUIRE(timeline->getClipTrackId(cid3) == tid1);
1037             REQUIRE(timeline->getClipPosition(cid1) == 3);
1038             REQUIRE(timeline->getClipPosition(cid2) == 3 + l);
1039             REQUIRE(timeline->getClipPosition(cid3) == 3 + 2 * l);
1040         };
1041         state2();
1042 
1043         undoStack->undo();
1044         state();
1045         undoStack->redo();
1046         state2();
1047 
1048         // Paste in different track
1049         REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1b, 0));
1050         int cid4 = timeline->getTrackById(tid1b)->getClipByPosition(0);
1051         REQUIRE(cid4 != -1);
1052         auto state3 = [&]() {
1053             state2();
1054             REQUIRE(timeline->getTrackClipsCount(tid1b) == 1);
1055             REQUIRE(timeline->getClipTrackId(cid4) == tid1b);
1056             REQUIRE(timeline->getClipPosition(cid4) == 0);
1057         };
1058         state3();
1059 
1060         undoStack->undo();
1061         state2();
1062         undoStack->undo();
1063         state();
1064         undoStack->redo();
1065         state2();
1066         undoStack->redo();
1067         state3();
1068     }
1069 
1070     SECTION("Copy paste groups")
1071     {
1072 
1073         auto state0 = [&]() {
1074             REQUIRE(timeline->checkConsistency());
1075             REQUIRE(timeline->getTrackClipsCount(tid1) == 0);
1076             REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
1077             REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
1078             REQUIRE(timeline->getTrackClipsCount(tid2b) == 0);
1079         };
1080         state0();
1081         int cid1 = -1;
1082         REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
1083         int l = timeline->getClipPlaytime(cid1);
1084         int cid2 = timeline->m_groups->getSplitPartner(cid1);
1085 
1086         auto state = [&](int count1, int count2) {
1087             REQUIRE(timeline->checkConsistency());
1088             REQUIRE(timeline->getTrackClipsCount(tid1) == count1);
1089             REQUIRE(timeline->getTrackClipsCount(tid2) == count1);
1090             REQUIRE(timeline->getTrackClipsCount(tid1b) == count2);
1091             REQUIRE(timeline->getTrackClipsCount(tid2b) == count2);
1092             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1093             REQUIRE(timeline->getClipTrackId(cid2) == tid2);
1094             REQUIRE(timeline->getClipPosition(cid1) == 3);
1095             REQUIRE(timeline->getClipPosition(cid2) == 3);
1096             REQUIRE(timeline->getClipPtr(cid1)->clipState() == PlaylistState::VideoOnly);
1097             REQUIRE(timeline->getClipPtr(cid2)->clipState() == PlaylistState::AudioOnly);
1098             // we check that the av group was correctly created
1099             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
1100             int g1 = timeline->m_groups->getDirectAncestor(cid1);
1101             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
1102             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
1103         };
1104         state(1, 0);
1105 
1106         QString cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
1107 
1108         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
1109         state(1, 0);
1110         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 4));
1111         state(1, 0);
1112 
1113         // potentially annoying selection
1114         REQUIRE(timeline->requestSetSelection({cid1}));
1115         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
1116 
1117         // paste on same track, after clip
1118         REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 3 + 2 * l));
1119         int cid3 = timeline->getTrackById(tid1)->getClipByPosition(3 + 2 * l + 1);
1120         REQUIRE(cid3 != -1);
1121         int cid4 = timeline->m_groups->getSplitPartner(cid3);
1122         auto state2 = [&](int count1, int count2) {
1123             state(count1, count2);
1124             REQUIRE(timeline->getClipTrackId(cid3) == tid1);
1125             REQUIRE(timeline->getClipTrackId(cid4) == tid2);
1126             REQUIRE(timeline->getClipPosition(cid3) == 3 + 2 * l);
1127             REQUIRE(timeline->getClipPosition(cid4) == 3 + 2 * l);
1128             REQUIRE(timeline->getClipPtr(cid3)->clipState() == PlaylistState::VideoOnly);
1129             REQUIRE(timeline->getClipPtr(cid4)->clipState() == PlaylistState::AudioOnly);
1130             // we check that the av group was correctly created
1131             REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({cid3, cid4}));
1132             int g1 = timeline->m_groups->getDirectAncestor(cid3);
1133             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid3, cid4}));
1134             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
1135         };
1136         state2(2, 0);
1137 
1138         // potentially annoying selection
1139         REQUIRE(timeline->requestSetSelection({cid1}));
1140         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
1141         undoStack->undo();
1142         REQUIRE(timeline->requestClearSelection());
1143         state(1, 0);
1144 
1145         // potentially annoying selection
1146         REQUIRE(timeline->requestSetSelection({cid1}));
1147         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
1148         undoStack->redo();
1149         REQUIRE(timeline->requestClearSelection());
1150         state2(2, 0);
1151 
1152         // another potentially annoying selection
1153         REQUIRE(timeline->requestSetSelection({cid1, cid3}));
1154         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
1155         undoStack->undo();
1156         REQUIRE(timeline->requestClearSelection());
1157         state(1, 0);
1158         REQUIRE(timeline->requestSetSelection({cid1}));
1159         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
1160         undoStack->redo();
1161         REQUIRE(timeline->requestClearSelection());
1162         state2(2, 0);
1163 
1164         // now, we copy the old clips as well as those we just pasted. Let's do it using a selection
1165         REQUIRE(timeline->requestSetSelection({cid1, cid3}));
1166         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
1167         // since everything is selected, everything should be copied here
1168         cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
1169 
1170         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
1171         state2(2, 0);
1172 
1173         // parasitic selection
1174         REQUIRE(timeline->requestSetSelection({cid1, cid3}));
1175         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
1176 
1177         // We paste on the other track
1178         REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1b, 0));
1179         qDebug() << "track tid1 count" << timeline->getTrackClipsCount(tid1);
1180         qDebug() << "track tid2 count" << timeline->getTrackClipsCount(tid2);
1181         qDebug() << "track tid1b count" << timeline->getTrackClipsCount(tid1b);
1182         qDebug() << "track tid2b count" << timeline->getTrackClipsCount(tid2b);
1183         int cid5 = timeline->getTrackById(tid1b)->getClipByPosition(0);
1184         REQUIRE(cid5 != -1);
1185         int cid6 = timeline->m_groups->getSplitPartner(cid5);
1186         REQUIRE(cid6 != -1);
1187         int cid7 = timeline->getTrackById(tid1b)->getClipByPosition(2 * l + 1);
1188         REQUIRE(cid7 != -1);
1189         int cid8 = timeline->m_groups->getSplitPartner(cid7);
1190         REQUIRE(cid8 != -1);
1191         auto state3 = [&](int count1, int count2) {
1192             state2(count1, count2);
1193             REQUIRE(timeline->getClipTrackId(cid5) == tid1b);
1194             REQUIRE(timeline->getClipTrackId(cid7) == tid1b);
1195             REQUIRE(timeline->getClipTrackId(cid6) == tid2b);
1196             REQUIRE(timeline->getClipTrackId(cid8) == tid2b);
1197             REQUIRE(timeline->getClipPosition(cid5) == 0);
1198             REQUIRE(timeline->getClipPosition(cid6) == 0);
1199             REQUIRE(timeline->getClipPosition(cid7) == 2 * l);
1200             REQUIRE(timeline->getClipPosition(cid8) == 2 * l);
1201             REQUIRE(timeline->getClipPtr(cid5)->clipState() == PlaylistState::VideoOnly);
1202             REQUIRE(timeline->getClipPtr(cid6)->clipState() == PlaylistState::AudioOnly);
1203             REQUIRE(timeline->getClipPtr(cid7)->clipState() == PlaylistState::VideoOnly);
1204             REQUIRE(timeline->getClipPtr(cid8)->clipState() == PlaylistState::AudioOnly);
1205             // we check that the av group was correctly created
1206             REQUIRE(timeline->getGroupElements(cid5) == std::unordered_set<int>({cid5, cid6}));
1207             int g1 = timeline->m_groups->getDirectAncestor(cid5);
1208             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid5, cid6}));
1209             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
1210 
1211             REQUIRE(timeline->getGroupElements(cid7) == std::unordered_set<int>({cid7, cid8}));
1212             int g2 = timeline->m_groups->getDirectAncestor(cid7);
1213             REQUIRE(timeline->m_groups->getDirectChildren(g2) == std::unordered_set<int>({cid7, cid8}));
1214             REQUIRE(timeline->m_groups->getType(g2) == GroupType::AVSplit);
1215         };
1216         state3(2, 2);
1217 
1218         // parasitic selection
1219         REQUIRE(timeline->requestSetSelection({cid1, cid3}));
1220         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
1221         undoStack->undo();
1222         REQUIRE(timeline->requestClearSelection());
1223         state2(2, 0);
1224         REQUIRE(timeline->requestSetSelection({cid1, cid3}));
1225         REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
1226         undoStack->redo();
1227         REQUIRE(timeline->requestClearSelection());
1228         state3(2, 2);
1229     }
1230 
1231     SECTION("Paste when tracks get deleted")
1232     {
1233         auto state0 = [&]() {
1234             REQUIRE(timeline->checkConsistency());
1235             REQUIRE(timeline->getTrackClipsCount(tid1) == 0);
1236             REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
1237             REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
1238             REQUIRE(timeline->getTrackClipsCount(tid2b) == 0);
1239         };
1240         state0();
1241         int cid1 = -1;
1242         REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
1243         timeline->getClipPlaytime(cid1);
1244         int cid2 = timeline->m_groups->getSplitPartner(cid1);
1245 
1246         auto state = [&]() {
1247             REQUIRE(timeline->checkConsistency());
1248             REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
1249             REQUIRE(timeline->getTrackClipsCount(tid2) == 1);
1250             REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
1251             REQUIRE(timeline->getTrackClipsCount(tid2b) == 0);
1252             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1253             REQUIRE(timeline->getClipTrackId(cid2) == tid2);
1254             REQUIRE(timeline->getClipPosition(cid1) == 3);
1255             REQUIRE(timeline->getClipPosition(cid2) == 3);
1256             REQUIRE(timeline->getClipPtr(cid1)->clipState() == PlaylistState::VideoOnly);
1257             REQUIRE(timeline->getClipPtr(cid2)->clipState() == PlaylistState::AudioOnly);
1258             // we check that the av group was correctly created
1259             REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
1260             int g1 = timeline->m_groups->getDirectAncestor(cid1);
1261             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
1262             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
1263         };
1264         state();
1265 
1266         QString cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
1267 
1268         // we delete origin of the copy, paste should still be possible
1269         REQUIRE(timeline->requestItemDeletion(cid1));
1270         state0();
1271 
1272         REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
1273         int cid3 = timeline->getTrackById(tid1)->getClipByPosition(0);
1274         REQUIRE(cid3 != -1);
1275         int cid4 = timeline->m_groups->getSplitPartner(cid3);
1276         auto state2 = [&](int audio) {
1277             REQUIRE(timeline->checkConsistency());
1278             REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
1279             REQUIRE(timeline->getTrackClipsCount(audio) == 1);
1280             REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
1281             REQUIRE(timeline->getClipTrackId(cid3) == tid1);
1282             REQUIRE(timeline->getClipTrackId(cid4) == audio);
1283             REQUIRE(timeline->getClipPosition(cid3) == 0);
1284             REQUIRE(timeline->getClipPosition(cid4) == 0);
1285             REQUIRE(timeline->getClipPtr(cid3)->clipState() == PlaylistState::VideoOnly);
1286             REQUIRE(timeline->getClipPtr(cid4)->clipState() == PlaylistState::AudioOnly);
1287             // we check that the av group was correctly created
1288             REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({cid3, cid4}));
1289             int g1 = timeline->m_groups->getDirectAncestor(cid3);
1290             REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid3, cid4}));
1291             REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit);
1292         };
1293         state2(tid2);
1294 
1295         undoStack->undo();
1296         state0();
1297         undoStack->redo();
1298         state2(tid2);
1299         undoStack->undo();
1300         state0();
1301 
1302         // now, we remove all audio tracks, making paste impossible
1303         REQUIRE(timeline->requestTrackDeletion(tid2));
1304         REQUIRE(timeline->requestTrackDeletion(tid2b));
1305         REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
1306 
1307         // undo one deletion
1308         undoStack->undo();
1309         // now, tid2b should be a valid audio track
1310         REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
1311         cid3 = timeline->getTrackById(tid1)->getClipByPosition(0);
1312         REQUIRE(cid3 != -1);
1313         cid4 = timeline->m_groups->getSplitPartner(cid3);
1314         state2(tid2b);
1315     }
1316     pCore->projectManager()->closeCurrentDocument(false, false);
1317 }
1318 
1319 TEST_CASE("Advanced trimming operations: Slip", "[TrimmingSlip]")
1320 {
1321     auto binModel = pCore->projectItemModel();
1322     binModel->clean();
1323     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
1324 
1325     // Here we do some trickery to enable testing.
1326     KdenliveDoc document(undoStack, {0, 2});
1327     pCore->projectManager()->m_project = &document;
1328     QDateTime documentDate = QDateTime::currentDateTime();
1329     pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
1330     auto timeline = document.getTimeline(document.uuid());
1331     pCore->projectManager()->m_activeTimelineModel = timeline;
1332     pCore->projectManager()->testSetActiveDocument(&document, timeline);
1333 
1334     QString binId = createProducer(pCore->getProjectProfile(), "red", binModel);
1335     QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel);
1336 
1337     int tid1 = timeline->getTrackIndexFromPosition(0);
1338     int tid2 = timeline->getTrackIndexFromPosition(1);
1339 
1340     int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1341     int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
1342     int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1343     int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1344     int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1345 
1346     timeline->m_allClips[cid1]->m_endlessResize = false;
1347     timeline->m_allClips[cid2]->m_endlessResize = false;
1348     timeline->m_allClips[cid3]->m_endlessResize = false;
1349     timeline->m_allClips[cid4]->m_endlessResize = false;
1350     timeline->m_allClips[cid5]->m_endlessResize = false;
1351 
1352     // sliping a fullsized clips should not to anything
1353     SECTION("Slip single fullsized clip")
1354     {
1355         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1356         int l = timeline->getClipPlaytime(cid1);
1357         auto state = [&]() {
1358             REQUIRE(timeline->checkConsistency());
1359             REQUIRE(timeline->getClipPlaytime(cid1) == l);
1360             REQUIRE(timeline->getClipPosition(cid1) == 5);
1361             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1362             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
1363         };
1364         state();
1365 
1366         REQUIRE(timeline->requestClipSlip(cid1, 3) == 3);
1367         state();
1368 
1369         REQUIRE(timeline->requestClipSlip(cid1, -3) == -3);
1370         state();
1371 
1372         undoStack->undo();
1373         state();
1374         undoStack->undo();
1375         state();
1376 
1377         undoStack->redo();
1378         state();
1379         undoStack->redo();
1380         state();
1381     }
1382 
1383     // slipping a downsized clip should only change the in and out point
1384     SECTION("Slip single cutted clip")
1385     {
1386         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1387         int l = timeline->getClipPlaytime(cid1);
1388         REQUIRE(timeline->requestItemResize(cid1, l - 5, true) == l - 5);
1389         REQUIRE(timeline->requestItemResize(cid1, l - 11, false) == l - 11);
1390 
1391         auto state = [&]() {
1392             REQUIRE(timeline->checkConsistency());
1393             REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
1394             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1395             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 6);
1396             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 6);
1397         };
1398         state();
1399 
1400         REQUIRE(timeline->requestClipSlip(cid1, 3) == 3);
1401         auto state2 = [&]() {
1402             REQUIRE(timeline->checkConsistency());
1403             REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
1404             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1405             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 3);
1406             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 9);
1407         };
1408         state2();
1409 
1410         REQUIRE(timeline->requestClipSlip(cid1, -3) == -3);
1411         state();
1412 
1413         undoStack->undo();
1414         state2();
1415         undoStack->undo();
1416         state();
1417 
1418         undoStack->redo();
1419         state2();
1420         undoStack->redo();
1421         state();
1422     }
1423 
1424     // if offset is bigger than the borders use the biggest possible offset without going beyond the borders
1425     SECTION("Slip beyond borders")
1426     {
1427         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1428         int l = timeline->getClipPlaytime(cid1);
1429 
1430         REQUIRE(timeline->getClipPlaytime(cid1) == l);
1431         REQUIRE(timeline->requestItemResize(cid1, l - 5, true) == l - 5);
1432         REQUIRE(timeline->requestItemResize(cid1, l - 11, false) == l - 11);
1433         REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 6);
1434 
1435         auto state = [&]() {
1436             REQUIRE(timeline->checkConsistency());
1437             REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
1438             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1439             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 6);
1440             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 6);
1441         };
1442         state();
1443 
1444         // left border
1445         REQUIRE(timeline->requestClipSlip(cid1, 30) == 30);
1446         auto state2 = [&]() {
1447             REQUIRE(timeline->checkConsistency());
1448             REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
1449             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1450             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1451             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 12);
1452         };
1453         state2();
1454 
1455         // right border
1456         REQUIRE(timeline->requestClipSlip(cid1, -30) == -30);
1457         auto state3 = [&]() {
1458             REQUIRE(timeline->checkConsistency());
1459             REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
1460             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1461             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 11);
1462             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l - 1);
1463         };
1464         state3();
1465 
1466         undoStack->undo();
1467         state2();
1468         undoStack->undo();
1469         state();
1470 
1471         undoStack->redo();
1472         state2();
1473         undoStack->redo();
1474         state3();
1475     }
1476 
1477     // slipping one clip of a group should slip all members
1478     SECTION("Slip group")
1479     {
1480         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1481         int l1 = timeline->getClipPlaytime(cid1);
1482         REQUIRE(timeline->requestItemResize(cid1, l1 - 5, true) == l1 - 5);
1483         REQUIRE(timeline->requestItemResize(cid1, l1 - 11, false) == l1 - 11);
1484 
1485         REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
1486         int l2 = timeline->getClipPlaytime(cid2);
1487         REQUIRE(timeline->requestItemResize(cid2, l2 - 11, false) == l2 - 11);
1488 
1489         REQUIRE(timeline->requestClipMove(cid3, tid2, 25));
1490         int l3 = timeline->getClipPlaytime(cid3);
1491         REQUIRE(timeline->requestItemResize(cid3, l3 - 5, true) == l3 - 5);
1492 
1493         REQUIRE(timeline->requestClipMove(cid4, tid2, 0));
1494         int l4 = timeline->getClipPlaytime(cid4);
1495         REQUIRE(timeline->requestItemResize(cid4, l4 - 9, true) == l4 - 9);
1496         REQUIRE(timeline->requestItemResize(cid4, l4 - 17, false) == l4 - 17);
1497 
1498         REQUIRE(timeline->requestClipMove(cid5, tid2, 60));
1499         int l5 = timeline->getClipPlaytime(cid5);
1500         int gid1 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}), true, GroupType::Normal);
1501         REQUIRE(gid1 > -1);
1502 
1503         auto state = [&]() {
1504             REQUIRE(timeline->checkConsistency());
1505             REQUIRE(timeline->getClipPlaytime(cid1) == l1 - 11);
1506             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1507             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 6);
1508             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 6);
1509             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1510 
1511             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 11);
1512             REQUIRE(timeline->getClipPosition(cid2) == 50 + 11);
1513             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 11);
1514             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 1);
1515             REQUIRE(timeline->getClipTrackId(cid2) == tid1);
1516 
1517             REQUIRE(timeline->getClipPlaytime(cid3) == l3 - 5);
1518             REQUIRE(timeline->getClipPosition(cid3) == 25);
1519             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1520             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 6);
1521             REQUIRE(timeline->getClipTrackId(cid3) == tid2);
1522 
1523             REQUIRE(timeline->getClipPlaytime(cid4) == l4 - 17);
1524             REQUIRE(timeline->getClipPosition(cid4) == 8);
1525             REQUIRE(timeline->getClipPtr(cid4)->getIn() == 8);
1526             REQUIRE(timeline->getClipPtr(cid4)->getOut() == l4 - 10);
1527             REQUIRE(timeline->getClipTrackId(cid4) == tid2);
1528 
1529             REQUIRE(timeline->getClipPlaytime(cid5) == l5);
1530             REQUIRE(timeline->getClipPosition(cid5) == 60);
1531             REQUIRE(timeline->getClipPtr(cid5)->getIn() == 0);
1532             REQUIRE(timeline->getClipPtr(cid5)->getOut() == l5 - 1);
1533             REQUIRE(timeline->getClipTrackId(cid5) == tid2);
1534 
1535             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}));
1536         };
1537         state();
1538 
1539         REQUIRE(timeline->requestClipSlip(cid1, 3) == 3);
1540 
1541         auto state2 = [&]() {
1542             REQUIRE(timeline->checkConsistency());
1543             REQUIRE(timeline->getClipPlaytime(cid1) == l1 - 11);
1544             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1545             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 3);
1546             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 9);
1547             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1548 
1549             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 11);
1550             REQUIRE(timeline->getClipPosition(cid2) == 50 + 11);
1551             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 8);
1552             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 4);
1553             REQUIRE(timeline->getClipTrackId(cid2) == tid1);
1554 
1555             REQUIRE(timeline->getClipPlaytime(cid3) == l3 - 5);
1556             REQUIRE(timeline->getClipPosition(cid3) == 25);
1557             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1558             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 6);
1559             REQUIRE(timeline->getClipTrackId(cid3) == tid2);
1560 
1561             REQUIRE(timeline->getClipPlaytime(cid4) == l4 - 17);
1562             REQUIRE(timeline->getClipPosition(cid4) == 8);
1563             REQUIRE(timeline->getClipPtr(cid4)->getIn() == 5);
1564             REQUIRE(timeline->getClipPtr(cid4)->getOut() == l4 - 13);
1565             REQUIRE(timeline->getClipTrackId(cid4) == tid2);
1566 
1567             REQUIRE(timeline->getClipPlaytime(cid5) == l5);
1568             REQUIRE(timeline->getClipPosition(cid5) == 60);
1569             REQUIRE(timeline->getClipPtr(cid5)->getIn() == 0);
1570             REQUIRE(timeline->getClipPtr(cid5)->getOut() == l5 - 1);
1571             REQUIRE(timeline->getClipTrackId(cid5) == tid2);
1572 
1573             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}));
1574         };
1575         state2();
1576 
1577         REQUIRE(timeline->requestClipSlip(cid4, 30) == 30);
1578 
1579         auto state3 = [&]() {
1580             REQUIRE(timeline->checkConsistency());
1581             REQUIRE(timeline->getClipPlaytime(cid1) == l1 - 11);
1582             REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
1583             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1584             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 12);
1585             REQUIRE(timeline->getClipTrackId(cid1) == tid1);
1586 
1587             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 11);
1588             REQUIRE(timeline->getClipPosition(cid2) == 50 + 11);
1589             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1590             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 12);
1591             REQUIRE(timeline->getClipTrackId(cid2) == tid1);
1592 
1593             REQUIRE(timeline->getClipPlaytime(cid3) == l3 - 5);
1594             REQUIRE(timeline->getClipPosition(cid3) == 25);
1595             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1596             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 6);
1597             REQUIRE(timeline->getClipTrackId(cid3) == tid2);
1598 
1599             REQUIRE(timeline->getClipPlaytime(cid4) == l4 - 17);
1600             REQUIRE(timeline->getClipPosition(cid4) == 8);
1601             REQUIRE(timeline->getClipPtr(cid4)->getIn() == 0);
1602             REQUIRE(timeline->getClipPtr(cid4)->getOut() == l4 - 18);
1603             REQUIRE(timeline->getClipTrackId(cid4) == tid2);
1604 
1605             REQUIRE(timeline->getClipPlaytime(cid5) == l5);
1606             REQUIRE(timeline->getClipPosition(cid5) == 60);
1607             REQUIRE(timeline->getClipPtr(cid5)->getIn() == 0);
1608             REQUIRE(timeline->getClipPtr(cid5)->getOut() == l5 - 1);
1609             REQUIRE(timeline->getClipTrackId(cid5) == tid2);
1610 
1611             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}));
1612         };
1613         state3();
1614 
1615         undoStack->undo();
1616         state2();
1617         undoStack->undo();
1618         state();
1619 
1620         undoStack->redo();
1621         state2();
1622         undoStack->redo();
1623         state3();
1624     }
1625 
1626     pCore->projectManager()->closeCurrentDocument(false, false);
1627 }
1628 
1629 TEST_CASE("Advanced trimming operations: Ripple", "[TrimmingRipple]")
1630 {
1631     auto binModel = pCore->projectItemModel();
1632     binModel->clean();
1633     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
1634 
1635     // Here we do some trickery to enable testing.
1636     KdenliveDoc document(undoStack, {0, 2});
1637     pCore->projectManager()->m_project = &document;
1638     QDateTime documentDate = QDateTime::currentDateTime();
1639     pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0);
1640     auto timeline = document.getTimeline(document.uuid());
1641     pCore->projectManager()->m_activeTimelineModel = timeline;
1642     pCore->projectManager()->testSetActiveDocument(&document, timeline);
1643 
1644     QString binId = createProducer(pCore->getProjectProfile(), "red", binModel);
1645     QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel);
1646 
1647     int tid1 = timeline->getTrackIndexFromPosition(0);
1648     int tid2 = timeline->getTrackIndexFromPosition(1);
1649 
1650     int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1651     int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
1652     int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1653     int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1654     int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1655     int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
1656 
1657     timeline->m_allClips[cid1]->m_endlessResize = false;
1658     timeline->m_allClips[cid2]->m_endlessResize = false;
1659     timeline->m_allClips[cid3]->m_endlessResize = false;
1660     timeline->m_allClips[cid4]->m_endlessResize = false;
1661     timeline->m_allClips[cid5]->m_endlessResize = false;
1662 
1663     // ripple resize a fullsized clip longer should not to anything
1664     SECTION("Ripple resize single fullsized clip (longer)")
1665     {
1666         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1667         int l1 = timeline->getClipPlaytime(cid1);
1668 
1669         REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
1670         int l2 = timeline->getClipPlaytime(cid2);
1671 
1672         REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
1673         int l3 = timeline->getClipPlaytime(cid3);
1674 
1675         auto state = [&]() {
1676             REQUIRE(timeline->checkConsistency());
1677             REQUIRE(timeline->getClipPlaytime(cid1) == l1);
1678             REQUIRE(timeline->getClipPosition(cid1) == 5);
1679             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1680             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 1);
1681             REQUIRE(timeline->getClipPlaytime(cid2) == l2);
1682             REQUIRE(timeline->getClipPosition(cid2) == 50);
1683             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1684             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 1);
1685             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1686             REQUIRE(timeline->getClipPosition(cid3) == 80);
1687             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1688             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1689         };
1690         state();
1691 
1692         REQUIRE(timeline->requestItemRippleResize(timeline, cid1, l1 + 5, true));
1693         state();
1694 
1695         REQUIRE(timeline->requestItemRippleResize(timeline, cid1, l1 + 5, false));
1696         state();
1697 
1698         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 + 5, true));
1699         REQUIRE(timeline->requestItemRippleResize(timeline, cid1, l2 + 5, false));
1700         state();
1701 
1702         REQUIRE(timeline->requestItemRippleResize(timeline, cid3, l3 + 5, true));
1703         REQUIRE(timeline->requestItemRippleResize(timeline, cid3, l3 + 5, false));
1704         state();
1705 
1706         undoStack->undo();
1707         state();
1708         undoStack->undo();
1709         state();
1710 
1711         undoStack->redo();
1712         state();
1713         undoStack->redo();
1714         state();
1715     }
1716 
1717     // ripple resize a fullsized clip shorter should resize the clip and move following clips
1718     SECTION("Ripple resize single fullsized clip (shorter)")
1719     {
1720         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1721         int l1 = timeline->getClipPlaytime(cid1);
1722 
1723         REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
1724         int l2 = timeline->getClipPlaytime(cid2);
1725 
1726         REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
1727         int l3 = timeline->getClipPlaytime(cid3);
1728 
1729         auto stateA1 = [&]() {
1730             REQUIRE(timeline->checkConsistency());
1731             REQUIRE(timeline->getClipPlaytime(cid1) == l1);
1732             REQUIRE(timeline->getClipPosition(cid1) == 5);
1733             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1734             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 1);
1735         };
1736         auto stateA2 = [&]() {
1737             REQUIRE(timeline->checkConsistency());
1738             REQUIRE(timeline->getClipPlaytime(cid2) == l2);
1739             REQUIRE(timeline->getClipPosition(cid2) == 50);
1740             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1741             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 1);
1742 
1743             REQUIRE(timeline->checkConsistency());
1744             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1745             REQUIRE(timeline->getClipPosition(cid3) == 80);
1746             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1747             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1748         };
1749         stateA1();
1750         stateA2();
1751 
1752         auto stateB = [&]() {
1753             REQUIRE(timeline->checkConsistency());
1754             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 5);
1755             REQUIRE(timeline->getClipPosition(cid2) == 50);
1756             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1757             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 6);
1758 
1759             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1760             REQUIRE(timeline->getClipPosition(cid3) == 75);
1761             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1762             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1763         };
1764         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 5, true) == l2 - 5);
1765         stateA1();
1766         stateB();
1767 
1768         auto stateC = [&]() {
1769             REQUIRE(timeline->checkConsistency());
1770             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 8);
1771             REQUIRE(timeline->getClipPosition(cid2) == 50);
1772             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 3);
1773             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 6);
1774 
1775             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1776             REQUIRE(timeline->getClipPosition(cid3) == 72);
1777             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1778             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1779         };
1780 
1781         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 8, false));
1782         stateA1();
1783         stateC();
1784 
1785         undoStack->undo();
1786         stateA1();
1787         stateB();
1788 
1789         undoStack->undo();
1790         stateA1();
1791         stateA2();
1792 
1793         undoStack->redo();
1794         stateA1();
1795         stateB();
1796 
1797         undoStack->redo();
1798         stateA1();
1799         stateC();
1800     }
1801 
1802     // ripple resize a grouped clip should move the affect all group partners on other tracks with same position as well
1803     SECTION("Ripple resize fullsized multitrack group (shorter)")
1804     {
1805         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1806         int l1 = timeline->getClipPlaytime(cid1);
1807 
1808         REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
1809         int l2 = timeline->getClipPlaytime(cid2);
1810 
1811         REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
1812         int l3 = timeline->getClipPlaytime(cid3);
1813 
1814         REQUIRE(timeline->requestClipMove(cid4, tid2, 5));
1815         int l4 = timeline->getClipPlaytime(cid4);
1816         REQUIRE(l4 == l1);
1817         timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid4}), true, GroupType::Normal);
1818 
1819         REQUIRE(timeline->requestClipMove(cid5, tid2, 50));
1820         int l5 = timeline->getClipPlaytime(cid5);
1821         REQUIRE(l5 == l2);
1822         timeline->requestClipsGroup(std::unordered_set<int>({cid2, cid5}), true, GroupType::Normal);
1823 
1824         REQUIRE(timeline->requestClipMove(cid6, tid2, 80));
1825         int l6 = timeline->getClipPlaytime(cid6);
1826         REQUIRE(l6 == l3);
1827         timeline->requestClipsGroup(std::unordered_set<int>({cid3, cid6}), true, GroupType::Normal);
1828 
1829         auto stateA1 = [&]() {
1830             REQUIRE(timeline->checkConsistency());
1831             REQUIRE(timeline->getClipPlaytime(cid1) == l1);
1832             REQUIRE(timeline->getClipPosition(cid1) == 5);
1833             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1834             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 1);
1835 
1836             REQUIRE(timeline->checkConsistency());
1837             REQUIRE(timeline->getClipPlaytime(cid4) == timeline->getClipPlaytime(cid1));
1838             REQUIRE(timeline->getClipPosition(cid4) == timeline->getClipPosition(cid1));
1839             REQUIRE(timeline->getClipPtr(cid4)->getIn() == timeline->getClipPtr(cid1)->getIn());
1840             REQUIRE(timeline->getClipPtr(cid4)->getOut() == timeline->getClipPtr(cid1)->getOut());
1841         };
1842         auto stateA2 = [&]() {
1843             REQUIRE(timeline->checkConsistency());
1844             REQUIRE(timeline->getClipPlaytime(cid2) == l2);
1845             REQUIRE(timeline->getClipPosition(cid2) == 50);
1846             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1847             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 1);
1848 
1849             REQUIRE(timeline->checkConsistency());
1850             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1851             REQUIRE(timeline->getClipPosition(cid3) == 80);
1852             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1853             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1854 
1855             REQUIRE(timeline->checkConsistency());
1856             REQUIRE(timeline->getClipPlaytime(cid5) == timeline->getClipPlaytime(cid2));
1857             REQUIRE(timeline->getClipPosition(cid5) == timeline->getClipPosition(cid2));
1858             REQUIRE(timeline->getClipPtr(cid5)->getIn() == timeline->getClipPtr(cid2)->getIn());
1859             REQUIRE(timeline->getClipPtr(cid5)->getOut() == timeline->getClipPtr(cid2)->getOut());
1860 
1861             REQUIRE(timeline->checkConsistency());
1862             REQUIRE(timeline->getClipPlaytime(cid6) == timeline->getClipPlaytime(cid3));
1863             REQUIRE(timeline->getClipPosition(cid6) == timeline->getClipPosition(cid3));
1864             REQUIRE(timeline->getClipPtr(cid6)->getIn() == timeline->getClipPtr(cid3)->getIn());
1865             REQUIRE(timeline->getClipPtr(cid6)->getOut() == timeline->getClipPtr(cid3)->getOut());
1866         };
1867         stateA1();
1868         stateA2();
1869 
1870         auto stateB = [&]() {
1871             REQUIRE(timeline->checkConsistency());
1872             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 5);
1873             REQUIRE(timeline->getClipPosition(cid2) == 50);
1874             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1875             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 6);
1876 
1877             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1878             REQUIRE(timeline->getClipPosition(cid3) == 75);
1879             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1880             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1881 
1882             REQUIRE(timeline->checkConsistency());
1883             REQUIRE(timeline->getClipPlaytime(cid5) == timeline->getClipPlaytime(cid2));
1884             REQUIRE(timeline->getClipPosition(cid5) == timeline->getClipPosition(cid2));
1885             REQUIRE(timeline->getClipPtr(cid5)->getIn() == timeline->getClipPtr(cid2)->getIn());
1886             REQUIRE(timeline->getClipPtr(cid5)->getOut() == timeline->getClipPtr(cid2)->getOut());
1887 
1888             REQUIRE(timeline->checkConsistency());
1889             REQUIRE(timeline->getClipPlaytime(cid6) == timeline->getClipPlaytime(cid3));
1890             REQUIRE(timeline->getClipPosition(cid6) == timeline->getClipPosition(cid3));
1891             REQUIRE(timeline->getClipPtr(cid6)->getIn() == timeline->getClipPtr(cid3)->getIn());
1892             REQUIRE(timeline->getClipPtr(cid6)->getOut() == timeline->getClipPtr(cid3)->getOut());
1893         };
1894         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 5, true) == l2 - 5);
1895         stateA1();
1896         stateB();
1897 
1898         auto stateC = [&]() {
1899             REQUIRE(timeline->checkConsistency());
1900             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 8);
1901             REQUIRE(timeline->getClipPosition(cid2) == 50);
1902             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 3);
1903             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 6);
1904 
1905             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1906             REQUIRE(timeline->getClipPosition(cid3) == 72);
1907             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1908             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1909 
1910             REQUIRE(timeline->checkConsistency());
1911             REQUIRE(timeline->getClipPlaytime(cid5) == timeline->getClipPlaytime(cid2));
1912             REQUIRE(timeline->getClipPosition(cid5) == timeline->getClipPosition(cid2));
1913             REQUIRE(timeline->getClipPtr(cid5)->getIn() == timeline->getClipPtr(cid2)->getIn());
1914             REQUIRE(timeline->getClipPtr(cid5)->getOut() == timeline->getClipPtr(cid2)->getOut());
1915 
1916             REQUIRE(timeline->checkConsistency());
1917             REQUIRE(timeline->getClipPlaytime(cid6) == timeline->getClipPlaytime(cid3));
1918             REQUIRE(timeline->getClipPosition(cid6) == timeline->getClipPosition(cid3));
1919             REQUIRE(timeline->getClipPtr(cid6)->getIn() == timeline->getClipPtr(cid3)->getIn());
1920             REQUIRE(timeline->getClipPtr(cid6)->getOut() == timeline->getClipPtr(cid3)->getOut());
1921         };
1922 
1923         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 8, false));
1924         stateA1();
1925         stateC();
1926 
1927         undoStack->undo();
1928         stateA1();
1929         stateB();
1930 
1931         undoStack->undo();
1932         stateA1();
1933         stateA2();
1934 
1935         undoStack->redo();
1936         stateA1();
1937         stateB();
1938 
1939         undoStack->redo();
1940         stateA1();
1941         stateC();
1942     }
1943 
1944     SECTION("Ripple resize fullsized single track group (shorter)")
1945     {
1946         REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
1947         int l1 = timeline->getClipPlaytime(cid1);
1948 
1949         REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
1950         int l2 = timeline->getClipPlaytime(cid2);
1951 
1952         REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
1953         int l3 = timeline->getClipPlaytime(cid3);
1954 
1955         int gid1 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid2, cid3}), true, GroupType::Normal);
1956 
1957         auto stateA1 = [&]() {
1958             REQUIRE(timeline->checkConsistency());
1959             REQUIRE(timeline->getClipPlaytime(cid1) == l1);
1960             REQUIRE(timeline->getClipPosition(cid1) == 5);
1961             REQUIRE(timeline->getClipPtr(cid1)->getIn() == 0);
1962             REQUIRE(timeline->getClipPtr(cid1)->getOut() == l1 - 1);
1963 
1964             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
1965         };
1966         auto stateA2 = [&]() {
1967             REQUIRE(timeline->checkConsistency());
1968             REQUIRE(timeline->getClipPlaytime(cid2) == l2);
1969             REQUIRE(timeline->getClipPosition(cid2) == 50);
1970             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1971             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 1);
1972 
1973             REQUIRE(timeline->checkConsistency());
1974             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1975             REQUIRE(timeline->getClipPosition(cid3) == 80);
1976             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1977             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1978 
1979             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
1980         };
1981         stateA1();
1982         stateA2();
1983 
1984         auto stateB = [&]() {
1985             REQUIRE(timeline->checkConsistency());
1986             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 5);
1987             REQUIRE(timeline->getClipPosition(cid2) == 50);
1988             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 0);
1989             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 6);
1990 
1991             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
1992             REQUIRE(timeline->getClipPosition(cid3) == 75);
1993             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
1994             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
1995 
1996             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
1997         };
1998         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 5, true) == l2 - 5);
1999         stateA1();
2000         stateB();
2001 
2002         auto stateC = [&]() {
2003             REQUIRE(timeline->checkConsistency());
2004             REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 8);
2005             REQUIRE(timeline->getClipPosition(cid2) == 50);
2006             REQUIRE(timeline->getClipPtr(cid2)->getIn() == 3);
2007             REQUIRE(timeline->getClipPtr(cid2)->getOut() == l2 - 6);
2008 
2009             REQUIRE(timeline->getClipPlaytime(cid3) == l3);
2010             REQUIRE(timeline->getClipPosition(cid3) == 72);
2011             REQUIRE(timeline->getClipPtr(cid3)->getIn() == 0);
2012             REQUIRE(timeline->getClipPtr(cid3)->getOut() == l3 - 1);
2013 
2014             REQUIRE(timeline->m_groups->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
2015         };
2016 
2017         REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 8, false));
2018         stateA1();
2019         stateC();
2020 
2021         undoStack->undo();
2022         stateA1();
2023         stateB();
2024 
2025         undoStack->undo();
2026         stateA1();
2027         stateA2();
2028 
2029         undoStack->redo();
2030         stateA1();
2031         stateB();
2032 
2033         undoStack->redo();
2034         stateA1();
2035         stateC();
2036     }
2037 
2038     pCore->projectManager()->closeCurrentDocument(false, false);
2039 }