Files
kdenlive/tests/trimmingtest.cpp
Kunda Ki e2b7ced389 Fix various typos
Fixes various user-facing and non-user-facing typos. Found via:
`codespell -S "*.po,*.svg,*.pgm" -L acount,afile,aline,atleast,buildin,build-in,indx,inout,nd,originaly,parth,seh,shotcut,splitted,wheight`
2025-09-03 17:58:12 +02:00

2043 lines
98 KiB
C++

/*
SPDX-FileCopyrightText: 2020-2022 Jean-Baptiste Mardelle <jb@kdenlive.org>
SPDX-FileCopyrightText: 2021 Julius Künzel <julius.kuenzel@kde.org>
SPDX-FileCopyrightText: 2017-2019 Nicolas Carion <french.ebook.lover@gmail.com>
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "test_utils.hpp"
// test specific headers
#include "doc/kdenlivedoc.h"
#include "timeline2/model/timelinefunctions.hpp"
using namespace fakeit;
TEST_CASE("Simple trimming operations", "[Trimming]")
{
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
// Here we do some trickery to enable testing.
pCore->setCurrentProfile(QStringLiteral("dv_pal"));
KdenliveDoc document(undoStack, {1, 3});
pCore->projectManager()->testSetDocument(&document);
QDateTime documentDate = QDateTime::currentDateTime();
KdenliveTests::updateTimeline(false, QString(), QString(), documentDate, 0);
auto timeline = document.getTimeline(document.uuid());
pCore->projectManager()->testSetActiveTimeline(timeline);
QString binId = KdenliveTests::createProducer(pCore->getProjectProfile(), "red", binModel);
QString binId2 = KdenliveTests::createProducer(pCore->getProjectProfile(), "blue", binModel);
QString binId3 = KdenliveTests::createProducerWithSound(pCore->getProjectProfile(), binModel);
int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int tid1 = timeline->getTrackIndexFromPosition(3);
int tid2 = timeline->getTrackIndexFromPosition(2);
int tid3 = timeline->getTrackIndexFromPosition(1);
// Add an audio track
int tid4 = timeline->getTrackIndexFromPosition(0);
int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid7 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int audio1 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
int audio2 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
int audio3 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
KdenliveTests::makeFiniteClipEnd(timeline, cid1);
KdenliveTests::makeFiniteClipEnd(timeline, cid2);
KdenliveTests::makeFiniteClipEnd(timeline, cid3);
KdenliveTests::makeFiniteClipEnd(timeline, cid4);
KdenliveTests::makeFiniteClipEnd(timeline, cid5);
KdenliveTests::makeFiniteClipEnd(timeline, cid6);
KdenliveTests::makeFiniteClipEnd(timeline, cid7);
SECTION("Clip cutting")
{
// Trivial split
REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
int l = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestItemResize(cid2, l - 3, true) == l - 3);
REQUIRE(timeline->requestItemResize(cid2, l - 5, false) == l - 5);
REQUIRE(timeline->requestClipMove(cid2, tid1, l));
REQUIRE(timeline->requestClipMove(cid3, tid1, l + l - 5));
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l - 5);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == l);
REQUIRE(timeline->getClipPosition(cid3) == l + l - 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 2);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 4);
};
state();
// require cut position outside the clip. Should return true and nothing is done
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 0));
state();
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 5 * l));
state();
// cut on edges doesn't do anything either
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l));
state();
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l + l - 5));
state();
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l + 4));
int splitted = timeline->getClipByPosition(tid1, l + 5);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == 4);
REQUIRE(timeline->getClipPlaytime(splitted) == l - 9);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == l);
REQUIRE(timeline->getClipPosition(splitted) == l + 4);
REQUIRE(timeline->getClipPosition(cid3) == l + l - 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 2);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, splitted)->getIn() == 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, splitted)->getOut() == l - 4);
};
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
}
SECTION("Cut and resize")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l = timeline->getClipPlaytime(cid1);
KdenliveTests::makeFiniteClipEnd(timeline, cid1);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPosition(cid1) == 5);
};
state();
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid1, 9));
int splitted = timeline->getClipByPosition(tid1, 10);
KdenliveTests::makeFiniteClipEnd(timeline, splitted);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(splitted) == tid1);
REQUIRE(timeline->getClipPlaytime(cid1) == 4);
REQUIRE(timeline->getClipPlaytime(splitted) == l - 4);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(timeline->getClipPosition(splitted) == 9);
};
state2();
REQUIRE(timeline->requestClipMove(splitted, tid2, 9, true, true));
REQUIRE(timeline->requestItemResize(splitted, l - 3, true, true) == -1);
REQUIRE(timeline->requestItemResize(splitted, l, false, true) == l);
REQUIRE(timeline->requestItemResize(cid1, 5, false, true) == -1);
REQUIRE(timeline->requestItemResize(cid1, l, true, true) == l);
auto state3 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(splitted) == tid2);
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(splitted) == l);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(timeline->getClipPosition(splitted) == 5);
};
state3();
undoStack->undo();
undoStack->undo();
undoStack->undo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->redo();
undoStack->redo();
undoStack->redo();
state3();
}
SECTION("Clip cutting 2")
{
// More complex group structure split split
int l = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
REQUIRE(timeline->requestClipMove(cid2, tid1, l));
REQUIRE(timeline->requestClipMove(cid3, tid1, 2 * l));
REQUIRE(timeline->requestClipMove(cid4, tid2, 0));
REQUIRE(timeline->requestClipMove(cid5, tid2, l));
REQUIRE(timeline->requestClipMove(cid6, tid2, 2 * l));
REQUIRE(timeline->requestClipMove(cid7, tid1, 200));
int gid1 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid4}), true, GroupType::Normal);
int gid2 = timeline->requestClipsGroup(std::unordered_set<int>({cid2, cid5}), true, GroupType::Normal);
int gid3 = timeline->requestClipsGroup(std::unordered_set<int>({cid3, cid6}), true, GroupType::Normal);
int gid4 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5, cid6, cid7}), true, GroupType::Normal);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
int p = 0;
for (int c : std::vector<int>({cid1, cid2, cid3})) {
REQUIRE(timeline->getClipPlaytime(c) == l);
REQUIRE(timeline->getClipTrackId(c) == tid1);
REQUIRE(timeline->getClipPosition(c) == p);
p += l;
}
p = 0;
for (int c : std::vector<int>({cid4, cid5, cid6})) {
REQUIRE(timeline->getClipPlaytime(c) == l);
REQUIRE(timeline->getClipTrackId(c) == tid2);
REQUIRE(timeline->getClipPosition(c) == p);
p += l;
}
REQUIRE(timeline->getClipPosition(cid7) == 200);
REQUIRE(timeline->getClipTrackId(cid7) == tid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid1) == std::unordered_set<int>({cid1, cid4}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid2) == std::unordered_set<int>({cid2, cid5}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid3) == std::unordered_set<int>({cid3, cid6}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid4) == std::unordered_set<int>({gid1, gid2, gid3, cid7}));
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5, cid6, cid7}));
};
state();
// These functions will return true but do nothing
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 0));
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 5 * l));
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l));
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, 2 * l));
state();
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid2, l + 4));
int splitted = timeline->getClipByPosition(tid1, l + 5);
int splitted2 = timeline->getClipByPosition(tid2, l + 5);
REQUIRE(splitted != splitted2);
auto check_groups = [&]() {
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid2) == std::unordered_set<int>({splitted, splitted2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid3) == std::unordered_set<int>({cid3, cid6}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(gid4) == std::unordered_set<int>({gid2, gid3, cid7}));
REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({splitted, splitted2, cid3, cid6, cid7}));
int g1b = KdenliveTests::groupUpLink(timeline)[cid1];
int g2b = KdenliveTests::groupUpLink(timeline)[cid2];
int g4b = KdenliveTests::groupsModel(timeline)->getRootId(cid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1b) == std::unordered_set<int>({cid1, cid4}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g2b) == std::unordered_set<int>({cid2, cid5}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g4b) == std::unordered_set<int>({g1b, g2b}));
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2, cid4, cid5}));
};
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
int p = 0;
for (int c : std::vector<int>({cid1, cid2, cid3})) {
REQUIRE(timeline->getClipPlaytime(c) == (c == cid2 ? 4 : l));
REQUIRE(timeline->getClipTrackId(c) == tid1);
REQUIRE(timeline->getClipPosition(c) == p);
p += l;
}
p = 0;
for (int c : std::vector<int>({cid4, cid5, cid6})) {
REQUIRE(timeline->getClipPlaytime(c) == (c == cid5 ? 4 : l));
REQUIRE(timeline->getClipTrackId(c) == tid2);
REQUIRE(timeline->getClipPosition(c) == p);
p += l;
}
REQUIRE(timeline->getClipPosition(cid7) == 200);
REQUIRE(timeline->getClipTrackId(cid7) == tid1);
REQUIRE(timeline->getClipPosition(splitted) == l + 4);
REQUIRE(timeline->getClipPlaytime(splitted) == l - 4);
REQUIRE(timeline->getClipTrackId(splitted) == tid1);
REQUIRE(timeline->getClipPosition(splitted2) == l + 4);
REQUIRE(timeline->getClipPlaytime(splitted2) == l - 4);
REQUIRE(timeline->getClipTrackId(splitted2) == tid2);
check_groups();
};
state2();
REQUIRE(timeline->requestClipMove(splitted, tid1, l + 4 + 10, true, true));
REQUIRE(timeline->requestClipMove(cid1, tid2, 10, true, true));
auto state3 = [&]() {
REQUIRE(timeline->checkConsistency());
int p = 0;
for (int c : std::vector<int>({cid1, cid2, cid3})) {
REQUIRE(timeline->getClipPlaytime(c) == (c == cid2 ? 4 : l));
REQUIRE(timeline->getClipTrackId(c) == (c == cid3 ? tid1 : tid2));
REQUIRE(timeline->getClipPosition(c) == p + 10);
p += l;
}
p = 0;
for (int c : std::vector<int>({cid4, cid5, cid6})) {
REQUIRE(timeline->getClipPlaytime(c) == (c == cid5 ? 4 : l));
REQUIRE(timeline->getClipTrackId(c) == (c == cid6 ? tid2 : tid3));
REQUIRE(timeline->getClipPosition(c) == p + 10);
p += l;
}
REQUIRE(timeline->getClipPosition(cid7) == 210);
REQUIRE(timeline->getClipTrackId(cid7) == tid1);
REQUIRE(timeline->getClipPosition(splitted) == l + 4 + 10);
REQUIRE(timeline->getClipPlaytime(splitted) == l - 4);
REQUIRE(timeline->getClipTrackId(splitted) == tid1);
REQUIRE(timeline->getClipPosition(splitted2) == l + 4 + 10);
REQUIRE(timeline->getClipPlaytime(splitted2) == l - 4);
REQUIRE(timeline->getClipTrackId(splitted2) == tid2);
check_groups();
};
state3();
undoStack->undo();
undoStack->undo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->redo();
undoStack->redo();
state3();
}
SECTION("Simple audio split")
{
int l = timeline->getClipPlaytime(audio1);
REQUIRE(timeline->requestClipMove(audio1, tid1, 3));
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(audio1) == l);
REQUIRE(timeline->getClipPosition(audio1) == 3);
REQUIRE(timeline->getClipTrackId(audio1) == tid1);
REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1}));
};
state();
REQUIRE(TimelineFunctions::requestSplitAudio(timeline, audio1, {tid4}));
int splitted1 = timeline->getClipByPosition(tid4, 3);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(audio1) == l);
REQUIRE(timeline->getClipPosition(audio1) == 3);
REQUIRE(timeline->getClipPlaytime(splitted1) == l);
REQUIRE(timeline->getClipPosition(splitted1) == 3);
REQUIRE(timeline->getClipTrackId(audio1) == tid1);
REQUIRE(timeline->getClipTrackId(splitted1) == tid4);
REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
REQUIRE(timeline->getTrackClipsCount(tid4) == 1);
REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1, splitted1}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(audio1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({audio1, splitted1}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
// We also make sure that clips that are audio only cannot be further splitted
REQUIRE(timeline->requestClipMove(cid1, tid1, l + 30));
// This is a color clip, shouldn't be splittable
REQUIRE_FALSE(TimelineFunctions::requestSplitAudio(timeline, cid1, {tid2}));
// Check we cannot split audio on a video track
REQUIRE_FALSE(TimelineFunctions::requestSplitAudio(timeline, audio1, {tid2}));
}
SECTION("Split audio on a selection")
{
int l = timeline->getClipPlaytime(audio2);
REQUIRE(timeline->requestClipMove(audio1, tid1, 0));
REQUIRE(timeline->requestClipMove(audio2, tid1, l));
REQUIRE(timeline->requestClipMove(audio3, tid1, 2 * l));
std::unordered_set<int> selection{audio1, audio3, audio2};
timeline->requestSetSelection(selection);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(audio1) == l);
REQUIRE(timeline->getClipPlaytime(audio2) == l);
REQUIRE(timeline->getClipPlaytime(audio3) == l);
REQUIRE(timeline->getClipPosition(audio1) == 0);
REQUIRE(timeline->getClipPosition(audio2) == l);
REQUIRE(timeline->getClipPosition(audio3) == l + l);
REQUIRE(timeline->getClipTrackId(audio1) == tid1);
REQUIRE(timeline->getClipTrackId(audio2) == tid1);
REQUIRE(timeline->getClipTrackId(audio3) == tid1);
REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1, audio2, audio3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>({audio1, audio3, audio2}));
};
state();
REQUIRE(TimelineFunctions::requestSplitAudio(timeline, audio1, {tid4}));
int splitted1 = timeline->getClipByPosition(tid4, 0);
int splitted2 = timeline->getClipByPosition(tid4, l);
int splitted3 = timeline->getClipByPosition(tid4, 2 * l);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(audio1) == l);
REQUIRE(timeline->getClipPlaytime(audio2) == l);
REQUIRE(timeline->getClipPlaytime(audio3) == l);
REQUIRE(timeline->getClipPosition(audio1) == 0);
REQUIRE(timeline->getClipPosition(audio2) == l);
REQUIRE(timeline->getClipPosition(audio3) == l + l);
REQUIRE(timeline->getClipPlaytime(splitted1) == l);
REQUIRE(timeline->getClipPlaytime(splitted2) == l);
REQUIRE(timeline->getClipPlaytime(splitted3) == l);
REQUIRE(timeline->getClipPosition(splitted1) == 0);
REQUIRE(timeline->getClipPosition(splitted2) == l);
REQUIRE(timeline->getClipPosition(splitted3) == l + l);
REQUIRE(timeline->getClipTrackId(audio1) == tid1);
REQUIRE(timeline->getClipTrackId(audio2) == tid1);
REQUIRE(timeline->getClipTrackId(audio3) == tid1);
REQUIRE(timeline->getClipTrackId(splitted1) == tid4);
REQUIRE(timeline->getClipTrackId(splitted2) == tid4);
REQUIRE(timeline->getClipTrackId(splitted3) == tid4);
REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
REQUIRE(timeline->getTrackClipsCount(tid4) == 3);
REQUIRE(timeline->getGroupElements(audio1) == std::unordered_set<int>({audio1, splitted1, audio2, audio3, splitted2, splitted3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>({audio1, splitted1, audio2, audio3, splitted2, splitted3}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(audio1);
int g2 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(audio2);
int g3 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(audio3);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({audio1, splitted1}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g2) == std::unordered_set<int>({audio2, splitted2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g3) == std::unordered_set<int>({audio3, splitted3}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g2) == GroupType::AVSplit);
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g3) == GroupType::AVSplit);
};
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
}
SECTION("Cut should preserve AV groups")
{
QString binId3 = KdenliveTests::createProducerWithSound(pCore->getProjectProfile(), binModel);
int tid6 = TrackModel::construct(timeline, -1, -1, QString(), true);
int tid5 = TrackModel::construct(timeline);
// Setup timeline audio drop info
QMap<int, QString> audioInfo;
audioInfo.insert(1, QStringLiteral("stream1"));
KdenliveTests::setAudioTargets(timeline, audioInfo);
KdenliveTests::setVideoTargets(timeline, tid5);
int cid6 = -1;
int pos = 3;
REQUIRE(timeline->requestClipInsertion(binId3, tid5, pos, cid6, true, true, false));
int cid7 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid6);
int l = timeline->getClipPlaytime(cid6);
REQUIRE(l >= 10);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid5) == 1);
REQUIRE(timeline->getTrackClipsCount(tid6) == 1);
REQUIRE(timeline->getClipTrackId(cid6) == tid5);
REQUIRE(timeline->getClipTrackId(cid7) == tid6);
REQUIRE(timeline->getClipPosition(cid6) == pos);
REQUIRE(timeline->getClipPosition(cid7) == pos);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid7)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid6) == std::unordered_set<int>({cid6, cid7}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid6);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid6, cid7}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state();
REQUIRE(TimelineFunctions::requestClipCut(timeline, cid6, pos + 4));
int cid8 = timeline->getClipByPosition(tid5, pos + 5);
int cid9 = timeline->getClipByPosition(tid6, pos + 5);
REQUIRE(cid8 >= 0);
REQUIRE(cid9 >= 0);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid5) == 2);
REQUIRE(timeline->getTrackClipsCount(tid6) == 2);
REQUIRE(timeline->getClipTrackId(cid6) == tid5);
REQUIRE(timeline->getClipTrackId(cid7) == tid6);
REQUIRE(timeline->getClipTrackId(cid8) == tid5);
REQUIRE(timeline->getClipTrackId(cid9) == tid6);
REQUIRE(timeline->getClipPosition(cid6) == pos);
REQUIRE(timeline->getClipPosition(cid7) == pos);
REQUIRE(timeline->getClipPosition(cid8) == pos + 4);
REQUIRE(timeline->getClipPosition(cid9) == pos + 4);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid7)->clipState() == PlaylistState::AudioOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid8)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid9)->clipState() == PlaylistState::AudioOnly);
// original AV group
REQUIRE(timeline->getGroupElements(cid6) == std::unordered_set<int>({cid6, cid7}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid6);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid6, cid7}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
// new AV group
REQUIRE(timeline->getGroupElements(cid8) == std::unordered_set<int>({cid8, cid9}));
int g2 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid8);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g2) == std::unordered_set<int>({cid8, cid9}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g2) == GroupType::AVSplit);
};
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
}
pCore->projectManager()->closeCurrentDocument(false, false);
}
TEST_CASE("Spacer operations", "[Spacer]")
{
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
pCore->setCurrentProfile(QStringLiteral("dv_pal"));
KdenliveDoc document(undoStack, {0, 1});
pCore->projectManager()->testSetDocument(&document);
QDateTime documentDate = QDateTime::currentDateTime();
KdenliveTests::updateTimeline(false, QString(), QString(), documentDate, 0);
auto timeline = document.getTimeline(document.uuid());
pCore->projectManager()->testSetActiveTimeline(timeline);
std::shared_ptr<MarkerListModel> guideModel = document.getGuideModel(document.uuid());
QString binId = KdenliveTests::createProducer(pCore->getProjectProfile(), "red", binModel);
QString binId2 = KdenliveTests::createProducer(pCore->getProjectProfile(), "blue", binModel);
QString binId3 = KdenliveTests::createProducerWithSound(pCore->getProjectProfile(), binModel);
int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int tid1 = timeline->getTrackIndexFromPosition(0);
// Add an audio track
// int tid4 = TrackModel::construct(timeline, -1, -1, QString(), true);
int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
// int audio1 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly);
KdenliveTests::makeFiniteClipEnd(timeline, cid1);
KdenliveTests::makeFiniteClipEnd(timeline, cid2);
KdenliveTests::makeFiniteClipEnd(timeline, cid3);
SECTION("Simple one track space insertion")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
int l = timeline->getClipPlaytime(cid1);
int p2 = l + 10;
REQUIRE(timeline->requestClipMove(cid2, tid1, p2));
int p3 = l + l + 20;
REQUIRE(timeline->requestClipMove(cid3, tid1, p3));
auto state = [&]() {
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2);
REQUIRE(timeline->getClipPosition(cid3) == p3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state();
int spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, l + 5).first;
REQUIRE(spacerIid > -1);
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
int itemPos = timeline->getItemPosition(spacerIid);
int space = 18;
REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space, tid1, -1, undo, redo));
auto state1 = [&]() {
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
REQUIRE(timeline->getClipPosition(cid3) == p3 + space);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state1();
int startPos = timeline->getClipPosition(cid3) + l / 2;
spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, startPos).first;
REQUIRE(spacerIid > -1);
undo = []() { return true; };
redo = []() { return true; };
itemPos = timeline->getItemPosition(spacerIid);
int space2 = 3;
REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space2, tid1, -1, undo, redo));
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
REQUIRE(timeline->getClipPosition(cid3) == p3 + space + space2);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state2();
undoStack->undo();
state1();
undoStack->undo();
state();
undoStack->redo();
state1();
undoStack->redo();
state2();
}
SECTION("Simple one track space insertion with guides")
{
pCore->projectManager()->testSetActiveTimeline(timeline);
REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
int l = timeline->getClipPlaytime(cid1);
int p2 = l + 10;
REQUIRE(timeline->requestClipMove(cid2, tid1, p2));
int p3 = l + l + 20;
REQUIRE(timeline->requestClipMove(cid3, tid1, p3));
double fps = pCore->getProjectProfile().fps();
guideModel->addMarker(GenTime(l / 2, fps), "guide1");
guideModel->addMarker(GenTime(l + 2, fps), "guide2");
guideModel->addMarker(GenTime(l + 7, fps), "guide3");
guideModel->addMarker(GenTime(p2 + l / 2, fps), "guide4");
guideModel->addMarker(GenTime(p3, fps), "guide5");
auto state = [&]() {
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2);
REQUIRE(timeline->getClipPosition(cid3) == p3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state();
int spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, l + 5).first;
REQUIRE(spacerIid > -1);
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
int itemPos = timeline->getItemPosition(spacerIid);
int space = 18;
REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space, tid1, l + 5, undo, redo));
auto state1 = [&]() {
REQUIRE(guideModel->getSnapPoints() == std::vector<int>{l / 2, l + 2, l + 7 + space, p2 + l / 2 + space, p3 + space});
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
REQUIRE(timeline->getClipPosition(cid3) == p3 + space);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state1();
int startPos = timeline->getClipPosition(cid3) + l / 2;
spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, startPos).first;
REQUIRE(spacerIid > -1);
undo = []() { return true; };
redo = []() { return true; };
itemPos = timeline->getItemPosition(spacerIid);
int space2 = 3;
REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos + space2, tid1, startPos, undo, redo));
auto state2 = [&]() {
REQUIRE(guideModel->getSnapPoints() == std::vector<int>{l / 2, l + 2, l + 7 + space, p2 + l / 2 + space, p3 + space + space2});
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2 + space);
REQUIRE(timeline->getClipPosition(cid3) == p3 + space + space2);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state2();
undoStack->undo();
state1();
undoStack->undo();
state();
undoStack->redo();
state1();
undoStack->redo();
state2();
}
SECTION("Simple one track space deletion")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 0));
int l = timeline->getClipPlaytime(cid1);
int p2 = l + 10;
REQUIRE(timeline->requestClipMove(cid2, tid1, p2));
int p3 = l + l + 10;
REQUIRE(timeline->requestClipMove(cid3, tid1, p3));
auto state = [&]() {
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2);
REQUIRE(timeline->getClipPosition(cid3) == p3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state();
int spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, l + 5).first;
REQUIRE(spacerIid > -1);
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
int itemPos = timeline->getItemPosition(spacerIid);
// space to remove is larger than possible (at the end only 10 frames should be removed)
int space = 18;
REQUIRE(TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos - space, tid1, -1, undo, redo));
auto state1 = [&]() {
REQUIRE(timeline->checkConsistency(guideModel->getSnapPoints()));
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPlaytime(cid2) == l);
REQUIRE(timeline->getClipPlaytime(cid3) == l);
REQUIRE(timeline->getClipPosition(cid1) == 0);
REQUIRE(timeline->getClipPosition(cid2) == p2 - 10);
REQUIRE(timeline->getClipPosition(cid3) == p3 - 10);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l - 1);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l - 1);
};
state1();
int startPos = timeline->getClipPosition(cid3) + l / 2;
spacerIid = TimelineFunctions::requestSpacerStartOperation(timeline, tid1, startPos).first;
REQUIRE(spacerIid > -1);
undo = []() { return true; };
redo = []() { return true; };
itemPos = timeline->getItemPosition(spacerIid);
int space2 = 3;
REQUIRE(!TimelineFunctions::requestSpacerEndOperation(timeline, spacerIid, itemPos, itemPos - space2, tid1, -1, undo, redo));
state1();
undoStack->undo();
state();
undoStack->redo();
state1();
}
pCore->projectManager()->closeCurrentDocument(false, false);
}
TEST_CASE("Insert/delete", "[Trimming2]")
{
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
// Here we do some trickery to enable testing.
pCore->setCurrentProfile(QStringLiteral("dv_pal"));
KdenliveDoc document(undoStack);
pCore->projectManager()->testSetDocument(&document);
QDateTime documentDate = QDateTime::currentDateTime();
KdenliveTests::updateTimeline(false, QString(), QString(), documentDate, 0);
auto timeline = document.getTimeline(document.uuid());
pCore->projectManager()->testSetActiveTimeline(timeline);
QString binId = KdenliveTests::createProducerWithSound(pCore->getProjectProfile(), binModel);
int tid2 = timeline->getTrackIndexFromPosition(1);
int tid1 = timeline->getTrackIndexFromPosition(2);
// Setup timeline audio drop info
QMap<int, QString> audioInfo;
audioInfo.insert(1, QStringLiteral("stream1"));
KdenliveTests::setAudioTargets(timeline, audioInfo);
KdenliveTests::setVideoTargets(timeline, tid1);
SECTION("Remove Space should preserve groups")
{
int cid1 = -1;
REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
int cid2 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid1);
auto state = [&](int pos) {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
REQUIRE(timeline->getTrackClipsCount(tid2) == 1);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid2);
REQUIRE(timeline->getClipPosition(cid1) == pos);
REQUIRE(timeline->getClipPosition(cid2) == pos);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state(3);
REQUIRE(TimelineFunctions::requestDeleteBlankAt(timeline, tid1, 1, true));
state(0);
undoStack->undo();
state(3);
undoStack->redo();
state(0);
}
SECTION("Insert zone should preserve groups")
{
int cid1 = -1;
REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
int cid2 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid1);
int l = timeline->getClipPlaytime(cid2);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
REQUIRE(timeline->getTrackClipsCount(tid2) == 1);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid2);
REQUIRE(timeline->getClipPosition(cid1) == 3);
REQUIRE(timeline->getClipPosition(cid2) == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state();
QMap<int, int> aTarget;
aTarget.insert(tid2, 0);
KdenliveTests::setAudioTimelineTargets(timeline, aTarget);
KdenliveTests::setVideoTargets(timeline, tid1);
// Make tracks active
timeline->setTrackProperty(tid1, QStringLiteral("kdenlive:timeline_active"), QStringLiteral("1"));
timeline->setTrackProperty(tid2, QStringLiteral("kdenlive:timeline_active"), QStringLiteral("1"));
REQUIRE(TimelineFunctions::insertZone(timeline, {tid1, tid2}, binId, 3 + 2, {l / 4, 3 * l / 4}, false));
aTarget.clear();
KdenliveTests::setAudioTimelineTargets(timeline, aTarget);
KdenliveTests::setVideoTargets(timeline, -1);
int small_length = 3 * l / 4 - l / 4;
int cid3 = timeline->getClipByPosition(tid1, 3 + 2);
int cid4 = timeline->getClipByPosition(tid2, 3 + 2);
int cid5 = timeline->getClipByPosition(tid1, 3 + 2 + small_length);
int cid6 = timeline->getClipByPosition(tid2, 3 + 2 + small_length);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
REQUIRE(timeline->getTrackClipsCount(tid2) == 3);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid2);
REQUIRE(timeline->getClipTrackId(cid3) == tid1);
REQUIRE(timeline->getClipTrackId(cid4) == tid2);
REQUIRE(timeline->getClipTrackId(cid5) == tid1);
REQUIRE(timeline->getClipTrackId(cid6) == tid2);
REQUIRE(timeline->getClipPosition(cid1) == 3);
REQUIRE(timeline->getClipPosition(cid2) == 3);
REQUIRE(timeline->getClipPosition(cid3) == 3 + 2);
REQUIRE(timeline->getClipPosition(cid4) == 3 + 2);
REQUIRE(timeline->getClipPosition(cid5) == 3 + 2 + small_length);
REQUIRE(timeline->getClipPosition(cid6) == 3 + 2 + small_length);
REQUIRE(timeline->getClipPlaytime(cid1) + timeline->getClipPlaytime(cid5) == l);
REQUIRE(timeline->getClipPlaytime(cid1) == 2);
REQUIRE(timeline->getClipPlaytime(cid3) == small_length);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->clipState() == PlaylistState::AudioOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->clipState() == PlaylistState::AudioOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({cid3, cid4}));
int g2 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid3);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g2) == std::unordered_set<int>({cid3, cid4}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g2) == GroupType::AVSplit);
int g3 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid5);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g3) == std::unordered_set<int>({cid5, cid6}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g3) == GroupType::AVSplit);
};
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
}
pCore->projectManager()->closeCurrentDocument(false, false);
}
TEST_CASE("Copy/paste", "[CP]")
{
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
pCore->setCurrentProfile(QStringLiteral("dv_pal"));
KdenliveDoc document(undoStack);
pCore->projectManager()->testSetDocument(&document);
QDateTime documentDate = QDateTime::currentDateTime();
KdenliveTests::updateTimeline(false, QString(), QString(), documentDate, 0);
auto timeline = document.getTimeline(document.uuid());
pCore->projectManager()->testSetActiveTimeline(timeline);
QString binId = KdenliveTests::createProducerWithSound(pCore->getProjectProfile(), binModel);
QString binId2 = KdenliveTests::createProducer(pCore->getProjectProfile(), "red", binModel);
int tid2b = timeline->getTrackIndexFromPosition(0);
int tid2 = timeline->getTrackIndexFromPosition(1);
int tid1 = timeline->getTrackIndexFromPosition(2);
int tid1b = timeline->getTrackIndexFromPosition(3);
// Setup timeline audio drop info
QMap<int, QString> audioInfo;
audioInfo.insert(1, QStringLiteral("stream1"));
KdenliveTests::setAudioTargets(timeline, audioInfo);
KdenliveTests::setVideoTargets(timeline, tid1);
SECTION("Simple copy paste of one clip")
{
int cid1 = -1;
REQUIRE(timeline->requestClipInsertion(binId2, tid1, 3, cid1, true, true, false));
int l = timeline->getClipPlaytime(cid1);
int cid2 = -1;
REQUIRE(timeline->requestClipInsertion(binId2, tid1, 3 + l, cid2, true, true, false));
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 2);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid1);
REQUIRE(timeline->getClipPosition(cid1) == 3);
REQUIRE(timeline->getClipPosition(cid2) == 3 + l);
};
state();
QString cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
// Try to paste in invalid positions
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
state();
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 4));
state();
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 4 + l));
state();
// Paste in audio track
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid2, 0));
state();
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid2b, 0));
state();
// Paste after the last clip
REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 3 + 2 * l));
int cid3 = timeline->getClipByPosition(tid1, 3 + 2 * l + 1);
REQUIRE(cid3 != -1);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 3);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid1);
REQUIRE(timeline->getClipTrackId(cid3) == tid1);
REQUIRE(timeline->getClipPosition(cid1) == 3);
REQUIRE(timeline->getClipPosition(cid2) == 3 + l);
REQUIRE(timeline->getClipPosition(cid3) == 3 + 2 * l);
};
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
// Paste in different track
REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1b, 0));
int cid4 = timeline->getClipByPosition(tid1b, 0);
REQUIRE(cid4 != -1);
auto state3 = [&]() {
state2();
REQUIRE(timeline->getTrackClipsCount(tid1b) == 1);
REQUIRE(timeline->getClipTrackId(cid4) == tid1b);
REQUIRE(timeline->getClipPosition(cid4) == 0);
};
state3();
undoStack->undo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->redo();
state3();
}
SECTION("Copy paste groups")
{
auto state0 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 0);
REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
REQUIRE(timeline->getTrackClipsCount(tid2b) == 0);
};
state0();
int cid1 = -1;
REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
int l = timeline->getClipPlaytime(cid1);
int cid2 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid1);
auto state = [&](int count1, int count2) {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == count1);
REQUIRE(timeline->getTrackClipsCount(tid2) == count1);
REQUIRE(timeline->getTrackClipsCount(tid1b) == count2);
REQUIRE(timeline->getTrackClipsCount(tid2b) == count2);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid2);
REQUIRE(timeline->getClipPosition(cid1) == 3);
REQUIRE(timeline->getClipPosition(cid2) == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state(1, 0);
QString cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
state(1, 0);
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 4));
state(1, 0);
// potentially annoying selection
REQUIRE(timeline->requestSetSelection({cid1}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
// paste on same track, after clip
REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 3 + 2 * l));
int cid3 = timeline->getClipByPosition(tid1, 3 + 2 * l + 1);
REQUIRE(cid3 != -1);
int cid4 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid3);
auto state2 = [&](int count1, int count2) {
state(count1, count2);
REQUIRE(timeline->getClipTrackId(cid3) == tid1);
REQUIRE(timeline->getClipTrackId(cid4) == tid2);
REQUIRE(timeline->getClipPosition(cid3) == 3 + 2 * l);
REQUIRE(timeline->getClipPosition(cid4) == 3 + 2 * l);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({cid3, cid4}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid3);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid3, cid4}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state2(2, 0);
// potentially annoying selection
REQUIRE(timeline->requestSetSelection({cid1}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
undoStack->undo();
REQUIRE(timeline->requestClearSelection());
state(1, 0);
// potentially annoying selection
REQUIRE(timeline->requestSetSelection({cid1}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
undoStack->redo();
REQUIRE(timeline->requestClearSelection());
state2(2, 0);
// another potentially annoying selection
REQUIRE(timeline->requestSetSelection({cid1, cid3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
undoStack->undo();
REQUIRE(timeline->requestClearSelection());
state(1, 0);
REQUIRE(timeline->requestSetSelection({cid1}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2});
undoStack->redo();
REQUIRE(timeline->requestClearSelection());
state2(2, 0);
// now, we copy the old clips as well as those we just pasted. Let's do it using a selection
REQUIRE(timeline->requestSetSelection({cid1, cid3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
// since everything is selected, everything should be copied here
cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
state2(2, 0);
// parasitic selection
REQUIRE(timeline->requestSetSelection({cid1, cid3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
// We paste on the other track
REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1b, 0));
qDebug() << "track tid1 count" << timeline->getTrackClipsCount(tid1);
qDebug() << "track tid2 count" << timeline->getTrackClipsCount(tid2);
qDebug() << "track tid1b count" << timeline->getTrackClipsCount(tid1b);
qDebug() << "track tid2b count" << timeline->getTrackClipsCount(tid2b);
int cid5 = timeline->getClipByPosition(tid1b, 0);
REQUIRE(cid5 != -1);
int cid6 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid5);
REQUIRE(cid6 != -1);
int cid7 = timeline->getClipByPosition(tid1b, 2 * l + 1);
REQUIRE(cid7 != -1);
int cid8 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid7);
REQUIRE(cid8 != -1);
auto state3 = [&](int count1, int count2) {
state2(count1, count2);
REQUIRE(timeline->getClipTrackId(cid5) == tid1b);
REQUIRE(timeline->getClipTrackId(cid7) == tid1b);
REQUIRE(timeline->getClipTrackId(cid6) == tid2b);
REQUIRE(timeline->getClipTrackId(cid8) == tid2b);
REQUIRE(timeline->getClipPosition(cid5) == 0);
REQUIRE(timeline->getClipPosition(cid6) == 0);
REQUIRE(timeline->getClipPosition(cid7) == 2 * l);
REQUIRE(timeline->getClipPosition(cid8) == 2 * l);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->clipState() == PlaylistState::AudioOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid7)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid8)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid5) == std::unordered_set<int>({cid5, cid6}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid5);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid5, cid6}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
REQUIRE(timeline->getGroupElements(cid7) == std::unordered_set<int>({cid7, cid8}));
int g2 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid7);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g2) == std::unordered_set<int>({cid7, cid8}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g2) == GroupType::AVSplit);
};
state3(2, 2);
// parasitic selection
REQUIRE(timeline->requestSetSelection({cid1, cid3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
undoStack->undo();
REQUIRE(timeline->requestClearSelection());
state2(2, 0);
REQUIRE(timeline->requestSetSelection({cid1, cid3}));
REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3, cid4});
undoStack->redo();
REQUIRE(timeline->requestClearSelection());
state3(2, 2);
}
SECTION("Paste when tracks get deleted")
{
auto state0 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 0);
REQUIRE(timeline->getTrackClipsCount(tid2) == 0);
REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
REQUIRE(timeline->getTrackClipsCount(tid2b) == 0);
};
state0();
int cid1 = -1;
REQUIRE(timeline->requestClipInsertion(binId, tid1, 3, cid1, true, true, false));
timeline->getClipPlaytime(cid1);
int cid2 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid1);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
REQUIRE(timeline->getTrackClipsCount(tid2) == 1);
REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
REQUIRE(timeline->getTrackClipsCount(tid2b) == 0);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipTrackId(cid2) == tid2);
REQUIRE(timeline->getClipPosition(cid1) == 3);
REQUIRE(timeline->getClipPosition(cid2) == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid1) == std::unordered_set<int>({cid1, cid2}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid1, cid2}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state();
QString cpy_str = TimelineFunctions::copyClips(timeline, {cid1});
// we delete origin of the copy, paste should still be possible
REQUIRE(timeline->requestItemDeletion(cid1));
state0();
REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
int cid3 = timeline->getClipByPosition(tid1, 0);
REQUIRE(cid3 != -1);
int cid4 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid3);
auto state2 = [&](int audio) {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getTrackClipsCount(tid1) == 1);
REQUIRE(timeline->getTrackClipsCount(audio) == 1);
REQUIRE(timeline->getTrackClipsCount(tid1b) == 0);
REQUIRE(timeline->getClipTrackId(cid3) == tid1);
REQUIRE(timeline->getClipTrackId(cid4) == audio);
REQUIRE(timeline->getClipPosition(cid3) == 0);
REQUIRE(timeline->getClipPosition(cid4) == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->clipState() == PlaylistState::VideoOnly);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->clipState() == PlaylistState::AudioOnly);
// we check that the av group was correctly created
REQUIRE(timeline->getGroupElements(cid3) == std::unordered_set<int>({cid3, cid4}));
int g1 = KdenliveTests::groupsModel(timeline)->getDirectAncestor(cid3);
REQUIRE(KdenliveTests::groupsModel(timeline)->getDirectChildren(g1) == std::unordered_set<int>({cid3, cid4}));
REQUIRE(KdenliveTests::groupsModel(timeline)->getType(g1) == GroupType::AVSplit);
};
state2(tid2);
undoStack->undo();
state0();
undoStack->redo();
state2(tid2);
undoStack->undo();
state0();
// now, we remove all audio tracks, making paste impossible
REQUIRE(timeline->requestTrackDeletion(tid2));
REQUIRE(timeline->requestTrackDeletion(tid2b));
REQUIRE_FALSE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
// undo one deletion
undoStack->undo();
// now, tid2b should be a valid audio track
REQUIRE(TimelineFunctions::pasteClips(timeline, cpy_str, tid1, 0));
cid3 = timeline->getClipByPosition(tid1, 0);
REQUIRE(cid3 != -1);
cid4 = KdenliveTests::groupsModel(timeline)->getSplitPartner(cid3);
state2(tid2b);
}
pCore->projectManager()->closeCurrentDocument(false, false);
}
TEST_CASE("Advanced trimming operations: Slip", "[TrimmingSlip]")
{
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
// Here we do some trickery to enable testing.
pCore->setCurrentProfile(QStringLiteral("dv_pal"));
KdenliveDoc document(undoStack, {0, 2});
pCore->projectManager()->testSetDocument(&document);
QDateTime documentDate = QDateTime::currentDateTime();
KdenliveTests::updateTimeline(false, QString(), QString(), documentDate, 0);
auto timeline = document.getTimeline(document.uuid());
pCore->projectManager()->testSetActiveTimeline(timeline);
QString binId = KdenliveTests::createProducer(pCore->getProjectProfile(), "red", binModel);
QString binId2 = KdenliveTests::createProducer(pCore->getProjectProfile(), "blue", binModel);
int tid1 = timeline->getTrackIndexFromPosition(0);
int tid2 = timeline->getTrackIndexFromPosition(1);
int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
KdenliveTests::makeFiniteClipEnd(timeline, cid1);
KdenliveTests::makeFiniteClipEnd(timeline, cid2);
KdenliveTests::makeFiniteClipEnd(timeline, cid3);
KdenliveTests::makeFiniteClipEnd(timeline, cid4);
KdenliveTests::makeFiniteClipEnd(timeline, cid5);
// sliping a fullsized clips should not to anything
SECTION("Slip single fullsized clip")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l = timeline->getClipPlaytime(cid1);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
};
state();
REQUIRE(timeline->requestClipSlip(cid1, 3) == 3);
state();
REQUIRE(timeline->requestClipSlip(cid1, -3) == -3);
state();
undoStack->undo();
state();
undoStack->undo();
state();
undoStack->redo();
state();
undoStack->redo();
state();
}
// slipping a downsized clip should only change the in and out point
SECTION("Slip single cut clip")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->requestItemResize(cid1, l - 5, true) == l - 5);
REQUIRE(timeline->requestItemResize(cid1, l - 11, false) == l - 11);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 6);
};
state();
REQUIRE(timeline->requestClipSlip(cid1, 3) == 3);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 9);
};
state2();
REQUIRE(timeline->requestClipSlip(cid1, -3) == -3);
state();
undoStack->undo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->redo();
state();
}
// if offset is bigger than the borders use the biggest possible offset without going beyond the borders
SECTION("Slip beyond borders")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->getClipPlaytime(cid1) == l);
REQUIRE(timeline->requestItemResize(cid1, l - 5, true) == l - 5);
REQUIRE(timeline->requestItemResize(cid1, l - 11, false) == l - 11);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 6);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 6);
};
state();
// left border
REQUIRE(timeline->requestClipSlip(cid1, 30) == 30);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 12);
};
state2();
// right border
REQUIRE(timeline->requestClipSlip(cid1, -30) == -30);
auto state3 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 11);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l - 1);
};
state3();
undoStack->undo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->redo();
state3();
}
// slipping one clip of a group should slip all members
SECTION("Slip group")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l1 = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->requestItemResize(cid1, l1 - 5, true) == l1 - 5);
REQUIRE(timeline->requestItemResize(cid1, l1 - 11, false) == l1 - 11);
REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
int l2 = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestItemResize(cid2, l2 - 11, false) == l2 - 11);
REQUIRE(timeline->requestClipMove(cid3, tid2, 25));
int l3 = timeline->getClipPlaytime(cid3);
REQUIRE(timeline->requestItemResize(cid3, l3 - 5, true) == l3 - 5);
REQUIRE(timeline->requestClipMove(cid4, tid2, 0));
int l4 = timeline->getClipPlaytime(cid4);
REQUIRE(timeline->requestItemResize(cid4, l4 - 9, true) == l4 - 9);
REQUIRE(timeline->requestItemResize(cid4, l4 - 17, false) == l4 - 17);
REQUIRE(timeline->requestClipMove(cid5, tid2, 60));
int l5 = timeline->getClipPlaytime(cid5);
int gid1 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}), true, GroupType::Normal);
REQUIRE(gid1 > -1);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1 - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 6);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 11);
REQUIRE(timeline->getClipPosition(cid2) == 50 + 11);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 11);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 1);
REQUIRE(timeline->getClipTrackId(cid2) == tid1);
REQUIRE(timeline->getClipPlaytime(cid3) == l3 - 5);
REQUIRE(timeline->getClipPosition(cid3) == 25);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 6);
REQUIRE(timeline->getClipTrackId(cid3) == tid2);
REQUIRE(timeline->getClipPlaytime(cid4) == l4 - 17);
REQUIRE(timeline->getClipPosition(cid4) == 8);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getIn() == 8);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getOut() == l4 - 10);
REQUIRE(timeline->getClipTrackId(cid4) == tid2);
REQUIRE(timeline->getClipPlaytime(cid5) == l5);
REQUIRE(timeline->getClipPosition(cid5) == 60);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getOut() == l5 - 1);
REQUIRE(timeline->getClipTrackId(cid5) == tid2);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}));
};
state();
REQUIRE(timeline->requestClipSlip(cid1, 3) == 3);
auto state2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1 - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 9);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 11);
REQUIRE(timeline->getClipPosition(cid2) == 50 + 11);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 8);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 4);
REQUIRE(timeline->getClipTrackId(cid2) == tid1);
REQUIRE(timeline->getClipPlaytime(cid3) == l3 - 5);
REQUIRE(timeline->getClipPosition(cid3) == 25);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 6);
REQUIRE(timeline->getClipTrackId(cid3) == tid2);
REQUIRE(timeline->getClipPlaytime(cid4) == l4 - 17);
REQUIRE(timeline->getClipPosition(cid4) == 8);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getIn() == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getOut() == l4 - 13);
REQUIRE(timeline->getClipTrackId(cid4) == tid2);
REQUIRE(timeline->getClipPlaytime(cid5) == l5);
REQUIRE(timeline->getClipPosition(cid5) == 60);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getOut() == l5 - 1);
REQUIRE(timeline->getClipTrackId(cid5) == tid2);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}));
};
state2();
REQUIRE(timeline->requestClipSlip(cid4, 30) == 30);
auto state3 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1 - 11);
REQUIRE(timeline->getClipPosition(cid1) == 5 + 6);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 12);
REQUIRE(timeline->getClipTrackId(cid1) == tid1);
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 11);
REQUIRE(timeline->getClipPosition(cid2) == 50 + 11);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 12);
REQUIRE(timeline->getClipTrackId(cid2) == tid1);
REQUIRE(timeline->getClipPlaytime(cid3) == l3 - 5);
REQUIRE(timeline->getClipPosition(cid3) == 25);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 6);
REQUIRE(timeline->getClipTrackId(cid3) == tid2);
REQUIRE(timeline->getClipPlaytime(cid4) == l4 - 17);
REQUIRE(timeline->getClipPosition(cid4) == 8);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getOut() == l4 - 18);
REQUIRE(timeline->getClipTrackId(cid4) == tid2);
REQUIRE(timeline->getClipPlaytime(cid5) == l5);
REQUIRE(timeline->getClipPosition(cid5) == 60);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getOut() == l5 - 1);
REQUIRE(timeline->getClipTrackId(cid5) == tid2);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3, cid4, cid5}));
};
state3();
undoStack->undo();
state2();
undoStack->undo();
state();
undoStack->redo();
state2();
undoStack->redo();
state3();
}
pCore->projectManager()->closeCurrentDocument(false, false);
}
TEST_CASE("Advanced trimming operations: Ripple", "[TrimmingRipple]")
{
auto binModel = pCore->projectItemModel();
binModel->clean();
std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
// Here we do some trickery to enable testing.
pCore->setCurrentProfile(QStringLiteral("dv_pal"));
KdenliveDoc document(undoStack, {0, 2});
pCore->projectManager()->testSetDocument(&document);
QDateTime documentDate = QDateTime::currentDateTime();
KdenliveTests::updateTimeline(false, QString(), QString(), documentDate, 0);
auto timeline = document.getTimeline(document.uuid());
pCore->projectManager()->testSetActiveTimeline(timeline);
QString binId = KdenliveTests::createProducer(pCore->getProjectProfile(), "red", binModel);
QString binId2 = KdenliveTests::createProducer(pCore->getProjectProfile(), "blue", binModel);
int tid1 = timeline->getTrackIndexFromPosition(0);
int tid2 = timeline->getTrackIndexFromPosition(1);
int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly);
int cid3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid4 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid5 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly);
KdenliveTests::makeFiniteClipEnd(timeline, cid1);
KdenliveTests::makeFiniteClipEnd(timeline, cid2);
KdenliveTests::makeFiniteClipEnd(timeline, cid3);
KdenliveTests::makeFiniteClipEnd(timeline, cid4);
KdenliveTests::makeFiniteClipEnd(timeline, cid5);
// ripple resize a fullsized clip longer should not to anything
SECTION("Ripple resize single fullsized clip (longer)")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l1 = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
int l2 = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
int l3 = timeline->getClipPlaytime(cid3);
auto state = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 1);
REQUIRE(timeline->getClipPlaytime(cid2) == l2);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 1);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 80);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
};
state();
REQUIRE(timeline->requestItemRippleResize(timeline, cid1, l1 + 5, true));
state();
REQUIRE(timeline->requestItemRippleResize(timeline, cid1, l1 + 5, false));
state();
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 + 5, true));
REQUIRE(timeline->requestItemRippleResize(timeline, cid1, l2 + 5, false));
state();
REQUIRE(timeline->requestItemRippleResize(timeline, cid3, l3 + 5, true));
REQUIRE(timeline->requestItemRippleResize(timeline, cid3, l3 + 5, false));
state();
undoStack->undo();
state();
undoStack->undo();
state();
undoStack->redo();
state();
undoStack->redo();
state();
}
// ripple resize a fullsized clip shorter should resize the clip and move following clips
SECTION("Ripple resize single fullsized clip (shorter)")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l1 = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
int l2 = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
int l3 = timeline->getClipPlaytime(cid3);
auto stateA1 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 1);
};
auto stateA2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 80);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
};
stateA1();
stateA2();
auto stateB = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 5);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 6);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 75);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
};
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 5, true) == l2 - 5);
stateA1();
stateB();
auto stateC = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 8);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 6);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 72);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
};
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 8, false));
stateA1();
stateC();
undoStack->undo();
stateA1();
stateB();
undoStack->undo();
stateA1();
stateA2();
undoStack->redo();
stateA1();
stateB();
undoStack->redo();
stateA1();
stateC();
}
// ripple resize a grouped clip should move the affect all group partners on other tracks with same position as well
SECTION("Ripple resize fullsized multitrack group (shorter)")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l1 = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
int l2 = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
int l3 = timeline->getClipPlaytime(cid3);
REQUIRE(timeline->requestClipMove(cid4, tid2, 5));
int l4 = timeline->getClipPlaytime(cid4);
REQUIRE(l4 == l1);
timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid4}), true, GroupType::Normal);
REQUIRE(timeline->requestClipMove(cid5, tid2, 50));
int l5 = timeline->getClipPlaytime(cid5);
REQUIRE(l5 == l2);
timeline->requestClipsGroup(std::unordered_set<int>({cid2, cid5}), true, GroupType::Normal);
REQUIRE(timeline->requestClipMove(cid6, tid2, 80));
int l6 = timeline->getClipPlaytime(cid6);
REQUIRE(l6 == l3);
timeline->requestClipsGroup(std::unordered_set<int>({cid3, cid6}), true, GroupType::Normal);
auto stateA1 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid4) == timeline->getClipPlaytime(cid1));
REQUIRE(timeline->getClipPosition(cid4) == timeline->getClipPosition(cid1));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getIn() == KdenliveTests::getClipPtr(timeline, cid1)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid4)->getOut() == KdenliveTests::getClipPtr(timeline, cid1)->getOut());
};
auto stateA2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 80);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid5) == timeline->getClipPlaytime(cid2));
REQUIRE(timeline->getClipPosition(cid5) == timeline->getClipPosition(cid2));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getIn() == KdenliveTests::getClipPtr(timeline, cid2)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getOut() == KdenliveTests::getClipPtr(timeline, cid2)->getOut());
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid6) == timeline->getClipPlaytime(cid3));
REQUIRE(timeline->getClipPosition(cid6) == timeline->getClipPosition(cid3));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->getIn() == KdenliveTests::getClipPtr(timeline, cid3)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->getOut() == KdenliveTests::getClipPtr(timeline, cid3)->getOut());
};
stateA1();
stateA2();
auto stateB = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 5);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 6);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 75);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid5) == timeline->getClipPlaytime(cid2));
REQUIRE(timeline->getClipPosition(cid5) == timeline->getClipPosition(cid2));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getIn() == KdenliveTests::getClipPtr(timeline, cid2)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getOut() == KdenliveTests::getClipPtr(timeline, cid2)->getOut());
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid6) == timeline->getClipPlaytime(cid3));
REQUIRE(timeline->getClipPosition(cid6) == timeline->getClipPosition(cid3));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->getIn() == KdenliveTests::getClipPtr(timeline, cid3)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->getOut() == KdenliveTests::getClipPtr(timeline, cid3)->getOut());
};
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 5, true) == l2 - 5);
stateA1();
stateB();
auto stateC = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 8);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 6);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 72);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid5) == timeline->getClipPlaytime(cid2));
REQUIRE(timeline->getClipPosition(cid5) == timeline->getClipPosition(cid2));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getIn() == KdenliveTests::getClipPtr(timeline, cid2)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid5)->getOut() == KdenliveTests::getClipPtr(timeline, cid2)->getOut());
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid6) == timeline->getClipPlaytime(cid3));
REQUIRE(timeline->getClipPosition(cid6) == timeline->getClipPosition(cid3));
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->getIn() == KdenliveTests::getClipPtr(timeline, cid3)->getIn());
REQUIRE(KdenliveTests::getClipPtr(timeline, cid6)->getOut() == KdenliveTests::getClipPtr(timeline, cid3)->getOut());
};
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 8, false));
stateA1();
stateC();
undoStack->undo();
stateA1();
stateB();
undoStack->undo();
stateA1();
stateA2();
undoStack->redo();
stateA1();
stateB();
undoStack->redo();
stateA1();
stateC();
}
SECTION("Ripple resize fullsized single track group (shorter)")
{
REQUIRE(timeline->requestClipMove(cid1, tid1, 5));
int l1 = timeline->getClipPlaytime(cid1);
REQUIRE(timeline->requestClipMove(cid2, tid1, 50));
int l2 = timeline->getClipPlaytime(cid2);
REQUIRE(timeline->requestClipMove(cid3, tid1, 80));
int l3 = timeline->getClipPlaytime(cid3);
int gid1 = timeline->requestClipsGroup(std::unordered_set<int>({cid1, cid2, cid3}), true, GroupType::Normal);
auto stateA1 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid1) == l1);
REQUIRE(timeline->getClipPosition(cid1) == 5);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid1)->getOut() == l1 - 1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
};
auto stateA2 = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 1);
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 80);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
};
stateA1();
stateA2();
auto stateB = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 5);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 6);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 75);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
};
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 5, true) == l2 - 5);
stateA1();
stateB();
auto stateC = [&]() {
REQUIRE(timeline->checkConsistency());
REQUIRE(timeline->getClipPlaytime(cid2) == l2 - 8);
REQUIRE(timeline->getClipPosition(cid2) == 50);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getIn() == 3);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid2)->getOut() == l2 - 6);
REQUIRE(timeline->getClipPlaytime(cid3) == l3);
REQUIRE(timeline->getClipPosition(cid3) == 72);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getIn() == 0);
REQUIRE(KdenliveTests::getClipPtr(timeline, cid3)->getOut() == l3 - 1);
REQUIRE(KdenliveTests::groupsModel(timeline)->getLeaves(gid1) == std::unordered_set<int>({cid1, cid2, cid3}));
};
REQUIRE(timeline->requestItemRippleResize(timeline, cid2, l2 - 8, false));
stateA1();
stateC();
undoStack->undo();
stateA1();
stateB();
undoStack->undo();
stateA1();
stateA2();
undoStack->redo();
stateA1();
stateB();
undoStack->redo();
stateA1();
stateC();
}
pCore->projectManager()->closeCurrentDocument(false, false);
}