File indexing completed on 2024-12-01 07:35:31
0001 /* 0002 SPDX-FileCopyrightText: 2019-2022 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 SPDX-FileCopyrightText: 2017-2019 Nicolas Carion <french.ebook.lover@gmail.com> 0004 SPDX-FileCopyrightText: 2022 Eric Jiang 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 <QUndoGroup> 0011 0012 using namespace fakeit; 0013 std::default_random_engine g(42); 0014 0015 TEST_CASE("Basic creation/deletion of a track", "[TrackModel]") 0016 { 0017 auto binModel = pCore->projectItemModel(); 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); 0022 pCore->projectManager()->m_project = &document; 0023 TimelineItemModel tim(document.uuid(), undoStack); 0024 Mock<TimelineItemModel> timMock(tim); 0025 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 0026 TimelineItemModel::finishConstruct(timeline); 0027 pCore->projectManager()->testSetActiveDocument(&document, timeline); 0028 0029 Fake(Method(timMock, adjustAssetRange)); 0030 0031 // This is faked to allow to count calls 0032 0033 int id1, id2, id3; 0034 REQUIRE(timeline->requestTrackInsertion(-1, id1)); 0035 REQUIRE(timeline->checkConsistency()); 0036 REQUIRE(timeline->getTracksCount() == 1); 0037 REQUIRE(timeline->getTrackPosition(id1) == 0); 0038 RESET(timMock); 0039 0040 REQUIRE(timeline->requestTrackInsertion(-1, id2)); 0041 REQUIRE(timeline->checkConsistency()); 0042 REQUIRE(timeline->getTracksCount() == 2); 0043 REQUIRE(timeline->getTrackPosition(id2) == 1); 0044 RESET(timMock); 0045 0046 REQUIRE(timeline->requestTrackInsertion(-1, id3)); 0047 REQUIRE(timeline->checkConsistency()); 0048 REQUIRE(timeline->getTracksCount() == 3); 0049 REQUIRE(timeline->getTrackPosition(id3) == 2); 0050 RESET(timMock); 0051 0052 int id4; 0053 REQUIRE(timeline->requestTrackInsertion(1, id4)); 0054 REQUIRE(timeline->checkConsistency()); 0055 REQUIRE(timeline->getTracksCount() == 4); 0056 REQUIRE(timeline->getTrackPosition(id1) == 0); 0057 REQUIRE(timeline->getTrackPosition(id4) == 1); 0058 REQUIRE(timeline->getTrackPosition(id2) == 2); 0059 REQUIRE(timeline->getTrackPosition(id3) == 3); 0060 RESET(timMock); 0061 0062 // Test deletion 0063 REQUIRE(timeline->requestTrackDeletion(id3)); 0064 REQUIRE(timeline->checkConsistency()); 0065 REQUIRE(timeline->getTracksCount() == 3); 0066 RESET(timMock); 0067 0068 REQUIRE(timeline->requestTrackDeletion(id1)); 0069 REQUIRE(timeline->checkConsistency()); 0070 REQUIRE(timeline->getTracksCount() == 2); 0071 RESET(timMock); 0072 0073 REQUIRE(timeline->requestTrackDeletion(id4)); 0074 REQUIRE(timeline->checkConsistency()); 0075 REQUIRE(timeline->getTracksCount() == 1); 0076 RESET(timMock); 0077 0078 // We are not allowed to delete the last track 0079 REQUIRE_FALSE(timeline->requestTrackDeletion(id2)); 0080 REQUIRE(timeline->checkConsistency()); 0081 REQUIRE(timeline->getTracksCount() == 1); 0082 RESET(timMock); 0083 0084 SECTION("Delete a track with groups") 0085 { 0086 int tid1, tid2; 0087 REQUIRE(timeline->requestTrackInsertion(-1, tid1)); 0088 REQUIRE(timeline->requestTrackInsertion(-1, tid2)); 0089 REQUIRE(timeline->checkConsistency()); 0090 0091 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel); 0092 int length = 20; 0093 int cid1, cid2, cid3, cid4; 0094 REQUIRE(timeline->requestClipInsertion(binId, tid1, 2, cid1)); 0095 REQUIRE(timeline->requestClipInsertion(binId, tid2, 0, cid2)); 0096 REQUIRE(timeline->requestClipInsertion(binId, tid2, length, cid3)); 0097 REQUIRE(timeline->requestClipInsertion(binId, tid2, 2 * length, cid4)); 0098 REQUIRE(timeline->checkConsistency()); 0099 REQUIRE(timeline->getClipsCount() == 4); 0100 REQUIRE(timeline->getTracksCount() == 3); 0101 0102 auto g1 = std::unordered_set<int>({cid1, cid3}); 0103 auto g2 = std::unordered_set<int>({cid2, cid4}); 0104 auto g3 = std::unordered_set<int>({cid1, cid4}); 0105 REQUIRE(timeline->requestClipsGroup(g1)); 0106 REQUIRE(timeline->requestClipsGroup(g2)); 0107 REQUIRE(timeline->requestClipsGroup(g3)); 0108 REQUIRE(timeline->checkConsistency()); 0109 0110 REQUIRE(timeline->requestTrackDeletion(tid1)); 0111 REQUIRE(timeline->getClipsCount() == 3); 0112 REQUIRE(timeline->getTracksCount() == 2); 0113 REQUIRE(timeline->checkConsistency()); 0114 } 0115 pCore->projectManager()->closeCurrentDocument(false, false); 0116 } 0117 0118 TEST_CASE("Adding multiple A/V tracks", "[TrackModel]") 0119 { 0120 0121 auto binModel = pCore->projectItemModel(); 0122 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0123 0124 KdenliveDoc document(undoStack); 0125 0126 // We also mock timeline object to spy few functions and mock others 0127 pCore->projectManager()->m_project = &document; 0128 TimelineItemModel tim(document.uuid(), undoStack); 0129 Mock<TimelineItemModel> timMock(tim); 0130 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 0131 TimelineItemModel::finishConstruct(timeline); 0132 pCore->projectManager()->testSetActiveDocument(&document, timeline); 0133 0134 SECTION("Check AV track ordering") 0135 { 0136 // start state: 0137 // * V2 (position 3) 0138 // * V1 0139 // * A1 0140 // * A2 (position 0) 0141 int a1, a2, v1, v2; 0142 REQUIRE(timeline->requestTrackInsertion(0, a2, QString(), true)); 0143 REQUIRE(timeline->requestTrackInsertion(1, a1, QString(), true)); 0144 REQUIRE(timeline->requestTrackInsertion(2, v1, QString(), false)); 0145 REQUIRE(timeline->requestTrackInsertion(3, v2, QString(), false)); 0146 0147 // when we add 3 AV tracks above V1, we should have: 0148 // * V5 (position 9) 0149 // * V4 0150 // * V3 0151 // * V2 0152 // * V1 0153 // * A1 0154 // * A2 0155 // * A3 0156 // * A4 0157 // * A5 (position 0) 0158 QString trackName("New track"); 0159 REQUIRE(timeline->addTracksAtPosition(3, 3, trackName, false, true, false)); 0160 // but if the new tracks keep getting added at the same position 3, then we'll get 0161 // * V2 0162 // * V3 0163 // * V1 0164 // * V4 0165 // * A4 0166 // * V5 0167 // * A5 0168 // * A1 0169 // * A3 0170 // * A2 0171 // (numbering doesn't look like this in the GUI) 0172 0173 REQUIRE(timeline->getTracksCount() == 10); 0174 0175 // first 5 tracks should be audio, and last 5 tracks should be video 0176 auto it = timeline->m_allTracks.cbegin(); 0177 int position = 0; 0178 while (it != timeline->m_allTracks.cend()) { 0179 if (position < 5) { 0180 CHECK((*it)->isAudioTrack()); 0181 } else { 0182 CHECK(!(*it)->isAudioTrack()); 0183 } 0184 it++; 0185 position++; 0186 } 0187 // V1 track should be at index 5 (i.e. we shouldn't have inserted any video 0188 // tracks before it) 0189 REQUIRE(timeline->getTrackIndexFromPosition(5) == v1); 0190 } 0191 pCore->projectManager()->closeCurrentDocument(false, false); 0192 } 0193 0194 TEST_CASE("Basic creation/deletion of a clip", "[ClipModel]") 0195 { 0196 0197 auto binModel = pCore->projectItemModel(); 0198 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0199 0200 // Here we do some trickery to enable testing. 0201 // We mock the project class so that the undoStack function returns our undoStack 0202 KdenliveDoc document(undoStack); 0203 0204 // We also mock timeline object to spy few functions and mock others 0205 pCore->projectManager()->m_project = &document; 0206 TimelineItemModel tim(document.uuid(), undoStack); 0207 Mock<TimelineItemModel> timMock(tim); 0208 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 0209 TimelineItemModel::finishConstruct(timeline); 0210 pCore->projectManager()->testSetActiveDocument(&document, timeline); 0211 0212 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel); 0213 QString binId2 = createProducer(pCore->getProjectProfile(), "green", binModel); 0214 0215 REQUIRE(timeline->getClipsCount() == 0); 0216 int id1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 0217 REQUIRE(timeline->getClipsCount() == 1); 0218 REQUIRE(timeline->checkConsistency()); 0219 0220 int id2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly); 0221 REQUIRE(timeline->getClipsCount() == 2); 0222 REQUIRE(timeline->checkConsistency()); 0223 0224 int id3 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 0225 REQUIRE(timeline->getClipsCount() == 3); 0226 REQUIRE(timeline->checkConsistency()); 0227 0228 // Test deletion 0229 REQUIRE(timeline->requestItemDeletion(id2)); 0230 REQUIRE(timeline->checkConsistency()); 0231 REQUIRE(timeline->getClipsCount() == 2); 0232 REQUIRE(timeline->requestItemDeletion(id3)); 0233 REQUIRE(timeline->checkConsistency()); 0234 REQUIRE(timeline->getClipsCount() == 1); 0235 REQUIRE(timeline->requestItemDeletion(id1)); 0236 REQUIRE(timeline->checkConsistency()); 0237 REQUIRE(timeline->getClipsCount() == 0); 0238 pCore->projectManager()->closeCurrentDocument(false, false); 0239 } 0240 0241 TEST_CASE("Clip manipulation", "[ClipModel]") 0242 { 0243 auto binModel = pCore->projectItemModel(); 0244 binModel->clean(); 0245 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0246 0247 // Here we do some trickery to enable testing. 0248 // We mock the project class so that the undoStack function returns our undoStack 0249 KdenliveDoc document(undoStack); 0250 0251 // We also mock timeline object to spy few functions and mock others 0252 pCore->projectManager()->m_project = &document; 0253 TimelineItemModel tim(document.uuid(), undoStack); 0254 Mock<TimelineItemModel> timMock(tim); 0255 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 0256 TimelineItemModel::finishConstruct(timeline); 0257 0258 pCore->projectManager()->testSetActiveDocument(&document, timeline); 0259 0260 Fake(Method(timMock, adjustAssetRange)); 0261 0262 // This is faked to allow to count calls 0263 Fake(Method(timMock, _beginInsertRows)); 0264 Fake(Method(timMock, _beginRemoveRows)); 0265 Fake(Method(timMock, _endInsertRows)); 0266 Fake(Method(timMock, _endRemoveRows)); 0267 0268 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel); 0269 QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel); 0270 QString binId3 = createProducer(pCore->getProjectProfile(), "green", binModel); 0271 QString binId_unlimited = createProducer(pCore->getProjectProfile(), "green", binModel, 20, false); 0272 0273 int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 0274 int tid1, tid2, tid3; 0275 REQUIRE(timeline->requestTrackInsertion(-1, tid1)); 0276 REQUIRE(timeline->requestTrackInsertion(-1, tid2)); 0277 REQUIRE(timeline->requestTrackInsertion(-1, tid3)); 0278 int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly); 0279 int cid3 = ClipModel::construct(timeline, binId3, -1, PlaylistState::VideoOnly); 0280 int cid4 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly); 0281 int cid5 = ClipModel::construct(timeline, binId_unlimited, -1, PlaylistState::VideoOnly); 0282 0283 RESET(timMock); 0284 0285 SECTION("Endless clips can be resized both sides") 0286 { 0287 0288 REQUIRE(timeline->checkConsistency()); 0289 REQUIRE(timeline->getTrackClipsCount(tid1) == 0); 0290 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 0291 int l = timeline->getClipPlaytime(cid5); 0292 0293 // try resizing uninserted clip 0294 REQUIRE(timeline->requestItemResize(cid5, l + 2, false) == l + 2); 0295 REQUIRE(timeline->getClipPlaytime(cid5) == l + 2); 0296 undoStack->undo(); 0297 REQUIRE(timeline->getClipPlaytime(cid5) == l); 0298 undoStack->redo(); 0299 REQUIRE(timeline->getClipPlaytime(cid5) == l + 2); 0300 undoStack->undo(); 0301 REQUIRE(timeline->getClipPlaytime(cid5) == l); 0302 0303 REQUIRE(timeline->requestItemResize(cid5, 3 * l, true) == 3 * l); 0304 REQUIRE(timeline->getClipPlaytime(cid5) == 3 * l); 0305 undoStack->undo(); 0306 REQUIRE(timeline->getClipPlaytime(cid5) == l); 0307 undoStack->redo(); 0308 REQUIRE(timeline->getClipPlaytime(cid5) == 3 * l); 0309 undoStack->undo(); 0310 REQUIRE(timeline->getClipPlaytime(cid5) == l); 0311 0312 // try resizing inserted clip 0313 int pos = 10; 0314 REQUIRE(timeline->requestClipMove(cid5, tid1, pos)); 0315 0316 auto state = [&](int s, int p) { 0317 REQUIRE(timeline->checkConsistency()); 0318 REQUIRE(timeline->getClipTrackId(cid5) == tid1); 0319 REQUIRE(timeline->getClipPosition(cid5) == p); 0320 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0321 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 0322 REQUIRE(timeline->getClipPlaytime(cid5) == s); 0323 }; 0324 state(l, pos); 0325 0326 // too big 0327 REQUIRE(timeline->requestItemResize(cid5, l + pos + 2, false) == l + pos); 0328 undoStack->undo(); 0329 0330 REQUIRE(timeline->requestItemResize(cid5, l + 2, false) == l + 2); 0331 state(l + 2, pos - 2); 0332 undoStack->undo(); 0333 state(l, pos); 0334 undoStack->redo(); 0335 state(l + 2, pos - 2); 0336 undoStack->undo(); 0337 state(l, pos); 0338 0339 REQUIRE(timeline->requestItemResize(cid5, 3 * l, true) == 3 * l); 0340 state(3 * l, pos); 0341 undoStack->undo(); 0342 state(l, pos); 0343 undoStack->redo(); 0344 state(3 * l, pos); 0345 undoStack->undo(); 0346 state(l, pos); 0347 } 0348 0349 SECTION("Insert a clip in a track and change track") 0350 { 0351 REQUIRE(timeline->checkConsistency()); 0352 REQUIRE(timeline->getTrackClipsCount(tid1) == 0); 0353 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 0354 0355 REQUIRE(timeline->getClipTrackId(cid1) == -1); 0356 REQUIRE(timeline->getClipPosition(cid1) == -1); 0357 0358 int pos = 10; 0359 REQUIRE(timeline->requestClipMove(cid1, tid1, pos)); 0360 REQUIRE(timeline->checkConsistency()); 0361 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0362 REQUIRE(timeline->getClipPosition(cid1) == pos); 0363 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0364 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 0365 // Check that the model was correctly notified 0366 CHECK_INSERT(Once); 0367 0368 pos = 1; 0369 REQUIRE(timeline->requestClipMove(cid1, tid2, pos)); 0370 REQUIRE(timeline->checkConsistency()); 0371 REQUIRE(timeline->getClipTrackId(cid1) == tid2); 0372 REQUIRE(timeline->getClipPosition(cid1) == pos); 0373 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0374 REQUIRE(timeline->getTrackClipsCount(tid1) == 0); 0375 CHECK_MOVE(Once); 0376 0377 // Check conflicts 0378 int pos2 = binModel->getClipByBinID(binId)->frameDuration(); 0379 REQUIRE(timeline->requestClipMove(cid2, tid1, pos2)); 0380 REQUIRE(timeline->checkConsistency()); 0381 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0382 REQUIRE(timeline->getClipPosition(cid2) == pos2); 0383 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0384 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0385 CHECK_INSERT(Once); 0386 0387 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, pos2 + 2)); 0388 REQUIRE(timeline->checkConsistency()); 0389 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0390 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0391 REQUIRE(timeline->getClipTrackId(cid1) == tid2); 0392 REQUIRE(timeline->getClipPosition(cid1) == pos); 0393 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0394 REQUIRE(timeline->getClipPosition(cid2) == pos2); 0395 CHECK_MOVE(Once); 0396 0397 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, pos2 - 2)); 0398 REQUIRE(timeline->checkConsistency()); 0399 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0400 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0401 REQUIRE(timeline->getClipTrackId(cid1) == tid2); 0402 REQUIRE(timeline->getClipPosition(cid1) == pos); 0403 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0404 REQUIRE(timeline->getClipPosition(cid2) == pos2); 0405 CHECK_MOVE(Once); 0406 0407 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0408 REQUIRE(timeline->checkConsistency()); 0409 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 0410 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0411 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0412 REQUIRE(timeline->getClipPosition(cid1) == 0); 0413 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0414 REQUIRE(timeline->getClipPosition(cid2) == pos2); 0415 CHECK_MOVE(Once); 0416 } 0417 0418 int length = binModel->getClipByBinID(binId)->frameDuration(); 0419 SECTION("Insert consecutive clips") 0420 { 0421 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0422 REQUIRE(timeline->checkConsistency()); 0423 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0424 REQUIRE(timeline->getClipPosition(cid1) == 0); 0425 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0426 CHECK_INSERT(Once); 0427 0428 REQUIRE(timeline->requestClipMove(cid2, tid1, length)); 0429 REQUIRE(timeline->checkConsistency()); 0430 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0431 REQUIRE(timeline->getClipPosition(cid2) == length); 0432 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0433 CHECK_INSERT(Once); 0434 } 0435 0436 SECTION("Resize orphan clip") 0437 { 0438 REQUIRE(timeline->getClipPlaytime(cid2) == length); 0439 REQUIRE(timeline->requestItemResize(cid2, 5, true) == 5); 0440 REQUIRE(timeline->checkConsistency()); 0441 REQUIRE(binModel->getClipByBinID(binId)->getFramePlaytime() == length); 0442 auto inOut = std::pair<int, int>{0, 4}; 0443 REQUIRE(timeline->m_allClips[cid2]->getInOut() == inOut); 0444 REQUIRE(timeline->getClipPlaytime(cid2) == 5); 0445 REQUIRE(timeline->requestItemResize(cid2, 10, false) == -1); 0446 REQUIRE(timeline->requestItemResize(cid2, length + 1, true) == -1); 0447 REQUIRE(timeline->checkConsistency()); 0448 REQUIRE(timeline->getClipPlaytime(cid2) == 5); 0449 REQUIRE(timeline->getClipPlaytime(cid2) == 5); 0450 REQUIRE(timeline->requestItemResize(cid2, 2, false) == 2); 0451 REQUIRE(timeline->checkConsistency()); 0452 inOut = std::pair<int, int>{3, 4}; 0453 REQUIRE(timeline->m_allClips[cid2]->getInOut() == inOut); 0454 REQUIRE(timeline->getClipPlaytime(cid2) == 2); 0455 REQUIRE(timeline->requestItemResize(cid2, length, true) == -1); 0456 REQUIRE(timeline->checkConsistency()); 0457 REQUIRE(timeline->getClipPlaytime(cid2) == 2); 0458 CAPTURE(timeline->m_allClips[cid2]->m_producer->get_in()); 0459 REQUIRE(timeline->requestItemResize(cid2, length - 2, true) == -1); 0460 REQUIRE(timeline->checkConsistency()); 0461 REQUIRE(timeline->requestItemResize(cid2, length - 3, true) == length - 3); 0462 REQUIRE(timeline->checkConsistency()); 0463 REQUIRE(timeline->getClipPlaytime(cid2) == length - 3); 0464 } 0465 0466 SECTION("Resize inserted clips") 0467 { 0468 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0469 REQUIRE(timeline->checkConsistency()); 0470 CHECK_INSERT(Once); 0471 0472 REQUIRE(timeline->requestItemResize(cid1, 5, true) == 5); 0473 REQUIRE(timeline->checkConsistency()); 0474 REQUIRE(timeline->getClipPlaytime(cid1) == 5); 0475 REQUIRE(timeline->getClipPosition(cid1) == 0); 0476 CHECK_RESIZE(Once); 0477 0478 REQUIRE(timeline->requestClipMove(cid2, tid1, 5)); 0479 REQUIRE(timeline->checkConsistency()); 0480 REQUIRE(binModel->getClipByBinID(binId)->getFramePlaytime() == length); 0481 CHECK_INSERT(Once); 0482 0483 REQUIRE(timeline->requestItemResize(cid1, 6, true) == -1); 0484 REQUIRE(timeline->requestItemResize(cid1, 6, false) == -1); 0485 REQUIRE(timeline->checkConsistency()); 0486 NO_OTHERS(); 0487 0488 REQUIRE(timeline->requestItemResize(cid2, length - 5, false) == length - 5); 0489 REQUIRE(timeline->checkConsistency()); 0490 REQUIRE(timeline->getClipPosition(cid2) == 10); 0491 CHECK_RESIZE(Once); 0492 0493 REQUIRE(timeline->requestItemResize(cid1, 10, true) == 10); 0494 REQUIRE(timeline->checkConsistency()); 0495 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0496 CHECK_RESIZE(Once); 0497 } 0498 0499 SECTION("Change track of resized clips") 0500 { 0501 // // REQUIRE(timeline->allowClipMove(cid2, tid1, 5)); 0502 REQUIRE(timeline->requestClipMove(cid2, tid1, 5)); 0503 REQUIRE(timeline->checkConsistency()); 0504 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0505 0506 // // REQUIRE(timeline->allowClipMove(cid1, tid2, 10)); 0507 REQUIRE(timeline->requestClipMove(cid1, tid2, 10)); 0508 REQUIRE(timeline->checkConsistency()); 0509 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0510 0511 REQUIRE(timeline->requestItemResize(cid1, 5, false) == 5); 0512 REQUIRE(timeline->checkConsistency()); 0513 0514 // // REQUIRE(timeline->allowClipMove(cid1, tid1, 0)); 0515 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0516 REQUIRE(timeline->checkConsistency()); 0517 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0518 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 0519 } 0520 0521 SECTION("Clip Move") 0522 { 0523 REQUIRE(timeline->requestClipMove(cid2, tid1, 5)); 0524 REQUIRE(timeline->checkConsistency()); 0525 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0526 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0527 REQUIRE(timeline->getClipPosition(cid2) == 5); 0528 0529 REQUIRE(timeline->requestClipMove(cid1, tid1, 5 + length)); 0530 auto state = [&]() { 0531 REQUIRE(timeline->checkConsistency()); 0532 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0533 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0534 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0535 REQUIRE(timeline->getClipPosition(cid1) == 5 + length); 0536 REQUIRE(timeline->getClipPosition(cid2) == 5); 0537 }; 0538 state(); 0539 0540 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, 3 + length)); 0541 state(); 0542 0543 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, 0)); 0544 state(); 0545 0546 REQUIRE(timeline->requestClipMove(cid2, tid1, 0)); 0547 auto state2 = [&]() { 0548 REQUIRE(timeline->checkConsistency()); 0549 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0550 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0551 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0552 REQUIRE(timeline->getClipPosition(cid1) == 5 + length); 0553 REQUIRE(timeline->getClipPosition(cid2) == 0); 0554 }; 0555 state2(); 0556 0557 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, 0)); 0558 state2(); 0559 0560 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, length - 5)); 0561 state2(); 0562 0563 REQUIRE(timeline->requestClipMove(cid1, tid1, length)); 0564 REQUIRE(timeline->checkConsistency()); 0565 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0566 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0567 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0568 REQUIRE(timeline->getClipPosition(cid1) == length); 0569 REQUIRE(timeline->getClipPosition(cid2) == 0); 0570 0571 REQUIRE(timeline->requestItemResize(cid2, length - 5, true) == length - 5); 0572 REQUIRE(timeline->checkConsistency()); 0573 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0574 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0575 REQUIRE(timeline->getClipPosition(cid1) == length); 0576 REQUIRE(timeline->getClipPosition(cid2) == 0); 0577 0578 REQUIRE(timeline->requestClipMove(cid1, tid1, length - 5)); 0579 REQUIRE(timeline->checkConsistency()); 0580 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0581 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0582 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0583 REQUIRE(timeline->getClipPosition(cid1) == length - 5); 0584 REQUIRE(timeline->getClipPosition(cid2) == 0); 0585 0586 REQUIRE(timeline->requestItemResize(cid2, length - 10, false) == length - 10); 0587 REQUIRE(timeline->checkConsistency()); 0588 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0589 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0590 REQUIRE(timeline->getClipPosition(cid1) == length - 5); 0591 REQUIRE(timeline->getClipPosition(cid2) == 5); 0592 0593 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, 0)); 0594 REQUIRE(timeline->checkConsistency()); 0595 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0596 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0597 REQUIRE(timeline->getClipPosition(cid1) == length - 5); 0598 REQUIRE(timeline->getClipPosition(cid2) == 5); 0599 0600 REQUIRE(timeline->requestClipMove(cid2, tid1, 0)); 0601 REQUIRE(timeline->checkConsistency()); 0602 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0603 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0604 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0605 REQUIRE(timeline->getClipPosition(cid1) == length - 5); 0606 REQUIRE(timeline->getClipPosition(cid2) == 0); 0607 } 0608 0609 SECTION("Move and resize") 0610 { 0611 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0612 REQUIRE(timeline->requestItemResize(cid1, length - 2, false) == length - 2); 0613 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0614 auto state = [&]() { 0615 REQUIRE(timeline->checkConsistency()); 0616 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0617 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0618 REQUIRE(timeline->getClipPosition(cid1) == 0); 0619 REQUIRE(timeline->getClipPlaytime(cid1) == length - 2); 0620 }; 0621 state(); 0622 0623 // try to resize past the left end 0624 REQUIRE(timeline->requestItemResize(cid1, length, false) == -1); 0625 state(); 0626 0627 REQUIRE(timeline->requestItemResize(cid1, length - 4, true) == length - 4); 0628 REQUIRE(timeline->requestClipMove(cid2, tid1, length - 4 + 1)); 0629 REQUIRE(timeline->requestItemResize(cid2, length - 2, false) == length - 2); 0630 REQUIRE(timeline->requestClipMove(cid2, tid1, length - 4 + 1)); 0631 auto state2 = [&]() { 0632 REQUIRE(timeline->checkConsistency()); 0633 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0634 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0635 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0636 REQUIRE(timeline->getClipPosition(cid1) == 0); 0637 REQUIRE(timeline->getClipPlaytime(cid1) == length - 4); 0638 REQUIRE(timeline->getClipPosition(cid2) == length - 4 + 1); 0639 REQUIRE(timeline->getClipPlaytime(cid2) == length - 2); 0640 }; 0641 state2(); 0642 0643 // the gap between the two clips is 1 frame, we try to resize them by 2 frames 0644 // It will only be resized by one frame 0645 REQUIRE(timeline->requestItemResize(cid1, length - 2, true) == length - 3); 0646 undoStack->undo(); 0647 state2(); 0648 // Resize a clip over another clip will resize it to fill the gap 0649 REQUIRE(timeline->requestItemResize(cid2, length, false) == length - 1); 0650 undoStack->undo(); 0651 state2(); 0652 0653 REQUIRE(timeline->requestClipMove(cid2, tid1, length - 4)); 0654 auto state3 = [&]() { 0655 REQUIRE(timeline->checkConsistency()); 0656 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0657 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0658 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 0659 REQUIRE(timeline->getClipPosition(cid1) == 0); 0660 REQUIRE(timeline->getClipPlaytime(cid1) == length - 4); 0661 REQUIRE(timeline->getClipPosition(cid2) == length - 4); 0662 REQUIRE(timeline->getClipPlaytime(cid2) == length - 2); 0663 }; 0664 state3(); 0665 0666 // Now the gap is 0 frames, the resize should still fail 0667 REQUIRE(timeline->requestItemResize(cid1, length - 2, true) == -1); 0668 state3(); 0669 REQUIRE(timeline->requestItemResize(cid2, length, false) == -1); 0670 state3(); 0671 0672 // We move cid1 out of the way 0673 REQUIRE(timeline->requestClipMove(cid1, tid2, 0)); 0674 // now resize should work 0675 REQUIRE(timeline->requestItemResize(cid1, length - 2, true) == length - 2); 0676 REQUIRE(timeline->requestItemResize(cid2, length, false) == length); 0677 REQUIRE(timeline->checkConsistency()); 0678 } 0679 0680 SECTION("Group and selection") 0681 { 0682 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0683 REQUIRE(timeline->requestClipMove(cid2, tid1, length + 3)); 0684 REQUIRE(timeline->requestClipMove(cid3, tid1, 2 * length + 5)); 0685 auto pos_state = [&]() { 0686 REQUIRE(timeline->checkConsistency()); 0687 REQUIRE(timeline->getTrackClipsCount(tid1) == 3); 0688 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0689 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0690 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 0691 REQUIRE(timeline->getClipPosition(cid1) == 0); 0692 REQUIRE(timeline->getClipPosition(cid2) == length + 3); 0693 REQUIRE(timeline->getClipPosition(cid3) == 2 * length + 5); 0694 }; 0695 auto state0 = [&]() { 0696 pos_state(); 0697 REQUIRE_FALSE(timeline->m_groups->isInGroup(cid1)); 0698 REQUIRE_FALSE(timeline->m_groups->isInGroup(cid2)); 0699 REQUIRE_FALSE(timeline->m_groups->isInGroup(cid3)); 0700 }; 0701 state0(); 0702 0703 REQUIRE(timeline->requestClipsGroup({cid1, cid2})); 0704 auto state = [&]() { 0705 pos_state(); 0706 REQUIRE_FALSE(timeline->m_groups->isInGroup(cid3)); 0707 REQUIRE(timeline->m_groups->isInGroup(cid1)); 0708 int gid = timeline->m_groups->getRootId(cid1); 0709 REQUIRE(timeline->m_groups->getLeaves(gid) == std::unordered_set<int>{cid1, cid2}); 0710 }; 0711 state(); 0712 0713 // undo/redo should work fine 0714 undoStack->undo(); 0715 state0(); 0716 undoStack->redo(); 0717 state(); 0718 0719 // Tricky case, we do a non-trivial selection before undoing 0720 REQUIRE(timeline->requestSetSelection({cid1, cid3})); 0721 REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3}); 0722 undoStack->undo(); 0723 state0(); 0724 REQUIRE(timeline->requestSetSelection({cid1, cid3})); 0725 REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid3}); 0726 undoStack->redo(); 0727 state(); 0728 0729 // same thing, but when ungrouping manually 0730 REQUIRE(timeline->requestSetSelection({cid1, cid3})); 0731 REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3}); 0732 REQUIRE(timeline->requestClipUngroup(cid1)); 0733 state0(); 0734 0735 // normal undo/redo 0736 undoStack->undo(); 0737 state(); 0738 undoStack->redo(); 0739 state0(); 0740 0741 // undo/redo mixed with selections 0742 REQUIRE(timeline->requestSetSelection({cid1, cid3})); 0743 REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid3}); 0744 undoStack->undo(); 0745 state(); 0746 REQUIRE(timeline->requestSetSelection({cid1, cid3})); 0747 REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid1, cid2, cid3}); 0748 undoStack->redo(); 0749 state0(); 0750 } 0751 0752 SECTION("Group move") 0753 { 0754 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0755 REQUIRE(timeline->requestClipMove(cid2, tid1, length + 3)); 0756 REQUIRE(timeline->requestClipMove(cid3, tid1, 2 * length + 5)); 0757 REQUIRE(timeline->requestClipMove(cid4, tid2, 4)); 0758 0759 REQUIRE(timeline->checkConsistency()); 0760 REQUIRE(timeline->getTrackClipsCount(tid1) == 3); 0761 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0762 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0763 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 0764 REQUIRE(timeline->getClipTrackId(cid4) == tid2); 0765 REQUIRE(timeline->getClipPosition(cid1) == 0); 0766 REQUIRE(timeline->getClipPosition(cid2) == length + 3); 0767 REQUIRE(timeline->getClipPosition(cid3) == 2 * length + 5); 0768 REQUIRE(timeline->getClipPosition(cid4) == 4); 0769 0770 // check that move is possible without groups 0771 REQUIRE(timeline->requestClipMove(cid3, tid1, 2 * length + 3)); 0772 REQUIRE(timeline->checkConsistency()); 0773 undoStack->undo(); 0774 REQUIRE(timeline->checkConsistency()); 0775 // check that move is possible without groups 0776 REQUIRE(timeline->requestClipMove(cid4, tid2, 9)); 0777 REQUIRE(timeline->checkConsistency()); 0778 undoStack->undo(); 0779 REQUIRE(timeline->checkConsistency()); 0780 0781 auto state = [&]() { 0782 REQUIRE(timeline->checkConsistency()); 0783 REQUIRE(timeline->getTrackClipsCount(tid1) == 3); 0784 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0785 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0786 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 0787 REQUIRE(timeline->getClipTrackId(cid4) == tid2); 0788 REQUIRE(timeline->getClipPosition(cid1) == 0); 0789 REQUIRE(timeline->getClipPosition(cid2) == length + 3); 0790 REQUIRE(timeline->getClipPosition(cid3) == 2 * length + 5); 0791 REQUIRE(timeline->getClipPosition(cid4) == 4); 0792 }; 0793 state(); 0794 0795 // grouping 0796 REQUIRE(timeline->requestClipsGroup({cid1, cid3})); 0797 REQUIRE(timeline->requestClipsGroup({cid1, cid4})); 0798 0799 // move left is now forbidden, because clip1 is at position 0 0800 REQUIRE_FALSE(timeline->requestClipMove(cid3, tid1, 2 * length + 3)); 0801 state(); 0802 0803 // this move is impossible, because clip1 runs into clip2 0804 REQUIRE_FALSE(timeline->requestClipMove(cid4, tid2, 9)); 0805 state(); 0806 0807 // this move is possible 0808 REQUIRE(timeline->requestClipMove(cid3, tid1, 2 * length + 8)); 0809 auto state1 = [&]() { 0810 REQUIRE(timeline->checkConsistency()); 0811 REQUIRE(timeline->getTrackClipsCount(tid1) == 3); 0812 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0813 REQUIRE(timeline->getTrackClipsCount(tid3) == 0); 0814 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0815 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0816 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 0817 REQUIRE(timeline->getClipTrackId(cid4) == tid2); 0818 REQUIRE(timeline->getClipPosition(cid1) == 3); 0819 REQUIRE(timeline->getClipPosition(cid2) == length + 3); 0820 REQUIRE(timeline->getClipPosition(cid3) == 2 * length + 8); 0821 REQUIRE(timeline->getClipPosition(cid4) == 7); 0822 }; 0823 state1(); 0824 0825 // this move is possible 0826 REQUIRE(timeline->requestClipMove(cid1, tid2, 8)); 0827 auto state2 = [&]() { 0828 REQUIRE(timeline->checkConsistency()); 0829 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0830 REQUIRE(timeline->getTrackClipsCount(tid2) == 2); 0831 REQUIRE(timeline->getTrackClipsCount(tid3) == 1); 0832 REQUIRE(timeline->getClipTrackId(cid1) == tid2); 0833 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 0834 REQUIRE(timeline->getClipTrackId(cid3) == tid2); 0835 REQUIRE(timeline->getClipTrackId(cid4) == tid3); 0836 REQUIRE(timeline->getClipPosition(cid1) == 8); 0837 REQUIRE(timeline->getClipPosition(cid2) == length + 3); 0838 REQUIRE(timeline->getClipPosition(cid3) == 2 * length + 5 + 8); 0839 REQUIRE(timeline->getClipPosition(cid4) == 4 + 8); 0840 }; 0841 state2(); 0842 0843 undoStack->undo(); 0844 state1(); 0845 0846 undoStack->redo(); 0847 state2(); 0848 0849 REQUIRE(timeline->requestClipMove(cid1, tid1, 3)); 0850 state1(); 0851 } 0852 0853 SECTION("Group move consecutive clips") 0854 { 0855 REQUIRE(timeline->requestClipMove(cid1, tid1, 7)); 0856 REQUIRE(timeline->requestClipMove(cid2, tid1, 7 + length)); 0857 REQUIRE(timeline->requestClipMove(cid3, tid1, 7 + 2 * length)); 0858 REQUIRE(timeline->requestClipMove(cid4, tid1, 7 + 3 * length)); 0859 REQUIRE(timeline->requestClipsGroup({cid1, cid2, cid3, cid4})); 0860 0861 auto state = [&](int tid, int start) { 0862 REQUIRE(timeline->checkConsistency()); 0863 REQUIRE(timeline->getTrackClipsCount(tid) == 4); 0864 int i = 0; 0865 for (int cid : std::vector<int>({cid1, cid2, cid3, cid4})) { 0866 REQUIRE(timeline->getClipTrackId(cid) == tid); 0867 REQUIRE(timeline->getClipPosition(cid) == start + i * length); 0868 REQUIRE(timeline->getClipPlaytime(cid) == length); 0869 i++; 0870 } 0871 }; 0872 state(tid1, 7); 0873 0874 auto check_undo = [&](int target, int tid, int oldTid) { 0875 state(tid, target); 0876 undoStack->undo(); 0877 state(oldTid, 7); 0878 undoStack->redo(); 0879 state(tid, target); 0880 undoStack->undo(); 0881 state(oldTid, 7); 0882 }; 0883 0884 REQUIRE(timeline->requestClipMove(cid1, tid1, 6)); 0885 qDebug() << "state1"; 0886 state(tid1, 6); 0887 undoStack->undo(); 0888 state(tid1, 7); 0889 undoStack->redo(); 0890 state(tid1, 6); 0891 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 0892 qDebug() << "state2"; 0893 state(tid1, 0); 0894 undoStack->undo(); 0895 state(tid1, 6); 0896 undoStack->redo(); 0897 state(tid1, 0); 0898 undoStack->undo(); 0899 state(tid1, 6); 0900 undoStack->undo(); 0901 state(tid1, 7); 0902 0903 REQUIRE(timeline->requestClipMove(cid3, tid1, 1 + 2 * length)); 0904 qDebug() << "state3"; 0905 check_undo(1, tid1, tid1); 0906 0907 REQUIRE(timeline->requestClipMove(cid4, tid1, 4 + 3 * length)); 0908 qDebug() << "state4"; 0909 check_undo(4, tid1, tid1); 0910 0911 REQUIRE(timeline->requestClipMove(cid4, tid1, 11 + 3 * length)); 0912 qDebug() << "state5"; 0913 check_undo(11, tid1, tid1); 0914 0915 REQUIRE(timeline->requestClipMove(cid2, tid1, 13 + length)); 0916 qDebug() << "state6"; 0917 check_undo(13, tid1, tid1); 0918 0919 REQUIRE(timeline->requestClipMove(cid1, tid1, 20)); 0920 qDebug() << "state7"; 0921 check_undo(20, tid1, tid1); 0922 0923 REQUIRE(timeline->requestClipMove(cid4, tid1, 7 + 4 * length)); 0924 qDebug() << "state8"; 0925 check_undo(length + 7, tid1, tid1); 0926 0927 REQUIRE(timeline->requestClipMove(cid2, tid1, 7 + 2 * length)); 0928 qDebug() << "state9"; 0929 check_undo(length + 7, tid1, tid1); 0930 0931 REQUIRE(timeline->requestClipMove(cid1, tid1, 7 + length)); 0932 qDebug() << "state10"; 0933 check_undo(length + 7, tid1, tid1); 0934 0935 REQUIRE(timeline->requestClipMove(cid2, tid2, 8 + length)); 0936 qDebug() << "state11"; 0937 check_undo(8, tid2, tid1); 0938 } 0939 0940 SECTION("Group move to unavailable track") 0941 { 0942 REQUIRE(timeline->requestClipMove(cid1, tid1, 10)); 0943 REQUIRE(timeline->requestClipMove(cid2, tid2, 12)); 0944 REQUIRE(timeline->requestClipsGroup({cid1, cid2})); 0945 auto state = [&]() { 0946 REQUIRE(timeline->checkConsistency()); 0947 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 0948 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 0949 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 0950 REQUIRE(timeline->getClipTrackId(cid2) == tid2); 0951 }; 0952 state(); 0953 0954 // Moving clips on an unavailable track will do a same track move 0955 REQUIRE(timeline->requestClipMove(cid2, tid1, 10)); 0956 REQUIRE(timeline->getClipPosition(cid1) == 8); 0957 REQUIRE(timeline->getClipPosition(cid2) == 10); 0958 state(); 0959 REQUIRE(timeline->requestClipMove(cid2, tid1, 100)); 0960 REQUIRE(timeline->getClipPosition(cid1) == 98); 0961 REQUIRE(timeline->getClipPosition(cid2) == 100); 0962 state(); 0963 REQUIRE(timeline->requestClipMove(cid1, tid3, 100)); 0964 REQUIRE(timeline->getClipPosition(cid1) == 100); 0965 REQUIRE(timeline->getClipPosition(cid2) == 102); 0966 state(); 0967 } 0968 0969 SECTION("Group move with non-consecutive track ids") 0970 { 0971 int tid5 = TrackModel::construct(timeline); 0972 int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 0973 Q_UNUSED(cid6); 0974 int tid6 = TrackModel::construct(timeline); 0975 REQUIRE(tid5 + 1 != tid6); 0976 0977 REQUIRE(timeline->requestClipMove(cid1, tid5, 10)); 0978 REQUIRE(timeline->requestClipMove(cid2, tid5, length + 10)); 0979 REQUIRE(timeline->requestClipsGroup({cid1, cid2})); 0980 auto state = [&](int t) { 0981 REQUIRE(timeline->checkConsistency()); 0982 REQUIRE(timeline->getTrackClipsCount(t) == 2); 0983 REQUIRE(timeline->getClipTrackId(cid1) == t); 0984 REQUIRE(timeline->getClipTrackId(cid2) == t); 0985 REQUIRE(timeline->getClipPosition(cid1) == 10); 0986 REQUIRE(timeline->getClipPosition(cid2) == 10 + length); 0987 }; 0988 state(tid5); 0989 REQUIRE(timeline->requestClipMove(cid1, tid6, 10)); 0990 state(tid6); 0991 } 0992 0993 SECTION("Creation and movement of AV groups") 0994 { 0995 int tid6b = TrackModel::construct(timeline, -1, -1, QString(), true); 0996 int tid6 = TrackModel::construct(timeline, -1, -1, QString(), true); 0997 int tid5 = TrackModel::construct(timeline); 0998 int tid5b = TrackModel::construct(timeline); 0999 auto state0 = [&]() { 1000 REQUIRE(timeline->checkConsistency()); 1001 REQUIRE(timeline->getTrackClipsCount(tid5) == 0); 1002 REQUIRE(timeline->getTrackClipsCount(tid6) == 0); 1003 }; 1004 state0(); 1005 QString binId3 = createProducerWithSound(pCore->getProjectProfile(), binModel); 1006 1007 int cid6 = -1; 1008 // Setup insert stream data 1009 QMap<int, QString> audioInfo; 1010 audioInfo.insert(1, QStringLiteral("stream1")); 1011 timeline->m_binAudioTargets = audioInfo; 1012 REQUIRE(timeline->requestClipInsertion(binId3, tid5, 3, cid6, true, true, false)); 1013 int cid7 = timeline->m_groups->getSplitPartner(cid6); 1014 1015 auto check_group = [&]() { 1016 // we check that the av group was correctly created 1017 REQUIRE(timeline->getGroupElements(cid6) == std::unordered_set<int>({cid6, cid7})); 1018 int g1 = timeline->m_groups->getDirectAncestor(cid6); 1019 REQUIRE(timeline->m_groups->getDirectChildren(g1) == std::unordered_set<int>({cid6, cid7})); 1020 REQUIRE(timeline->m_groups->getType(g1) == GroupType::AVSplit); 1021 }; 1022 1023 auto state = [&](int pos) { 1024 REQUIRE(timeline->checkConsistency()); 1025 REQUIRE(timeline->getTrackClipsCount(tid5) == 1); 1026 REQUIRE(timeline->getTrackClipsCount(tid6) == 1); 1027 REQUIRE(timeline->getClipTrackId(cid6) == tid5); 1028 REQUIRE(timeline->getClipTrackId(cid7) == tid6); 1029 REQUIRE(timeline->getClipPosition(cid6) == pos); 1030 REQUIRE(timeline->getClipPosition(cid7) == pos); 1031 REQUIRE(timeline->getClipPtr(cid6)->clipState() == PlaylistState::VideoOnly); 1032 REQUIRE(timeline->getClipPtr(cid7)->clipState() == PlaylistState::AudioOnly); 1033 check_group(); 1034 }; 1035 state(3); 1036 undoStack->undo(); 1037 state0(); 1038 undoStack->redo(); 1039 state(3); 1040 1041 // test deletion + undo after selection 1042 REQUIRE(timeline->requestSetSelection({cid6})); 1043 REQUIRE(timeline->getCurrentSelection() == std::unordered_set<int>{cid6, cid7}); 1044 1045 REQUIRE(timeline->requestItemDeletion(cid6, true)); 1046 state0(); 1047 undoStack->undo(); 1048 state(3); 1049 undoStack->redo(); 1050 state0(); 1051 undoStack->undo(); 1052 state(3); 1053 1054 // simple translation on the right 1055 REQUIRE(timeline->requestClipMove(cid6, tid5, 10, true, true, true)); 1056 1057 state(10); 1058 undoStack->undo(); 1059 state(3); 1060 undoStack->redo(); 1061 state(10); 1062 1063 // simple translation on the left, moving the audio clip this time 1064 REQUIRE(timeline->requestClipMove(cid7, tid6, 1, true, true, true)); 1065 state(1); 1066 undoStack->undo(); 1067 state(10); 1068 undoStack->redo(); 1069 state(1); 1070 1071 // change track, moving video 1072 REQUIRE(timeline->requestClipMove(cid6, tid5b, 7, true, true, true)); 1073 auto state2 = [&](int pos) { 1074 REQUIRE(timeline->checkConsistency()); 1075 REQUIRE(timeline->getTrackClipsCount(tid5b) == 1); 1076 REQUIRE(timeline->getTrackClipsCount(tid6b) == 1); 1077 REQUIRE(timeline->getClipTrackId(cid6) == tid5b); 1078 REQUIRE(timeline->getClipTrackId(cid7) == tid6b); 1079 REQUIRE(timeline->getClipPosition(cid6) == pos); 1080 REQUIRE(timeline->getClipPosition(cid7) == pos); 1081 REQUIRE(timeline->getClipPtr(cid6)->clipState() == PlaylistState::VideoOnly); 1082 REQUIRE(timeline->getClipPtr(cid7)->clipState() == PlaylistState::AudioOnly); 1083 check_group(); 1084 }; 1085 state2(7); 1086 undoStack->undo(); 1087 state(1); 1088 undoStack->redo(); 1089 state2(7); 1090 1091 // change track, moving audio 1092 REQUIRE(timeline->requestClipMove(cid7, tid6b, 2, true, true, true)); 1093 state2(2); 1094 undoStack->undo(); 1095 state2(7); 1096 undoStack->redo(); 1097 state2(2); 1098 1099 undoStack->undo(); 1100 undoStack->undo(); 1101 state(1); 1102 } 1103 1104 SECTION("Clip clone") 1105 { 1106 int cid6 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 1107 int l = timeline->getClipPlaytime(cid6); 1108 REQUIRE(timeline->requestItemResize(cid6, l - 3, true, true, -1) == l - 3); 1109 REQUIRE(timeline->requestItemResize(cid6, l - 7, false, true, -1) == l - 7); 1110 1111 int newId; 1112 1113 std::function<bool(void)> undo = []() { return true; }; 1114 std::function<bool(void)> redo = []() { return true; }; 1115 REQUIRE(TimelineFunctions::cloneClip(timeline, cid6, newId, PlaylistState::VideoOnly, undo, redo)); 1116 REQUIRE(timeline->m_allClips[cid6]->binId() == timeline->m_allClips[newId]->binId()); 1117 // TODO check effects 1118 } 1119 pCore->projectManager()->closeCurrentDocument(false, false); 1120 } 1121 1122 TEST_CASE("Check id unicity", "[ClipModel]") 1123 { 1124 auto binModel = pCore->projectItemModel(); 1125 binModel->clean(); 1126 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 1127 1128 // Here we do some trickery to enable testing. 1129 // We mock the project class so that the undoStack function returns our undoStack 1130 KdenliveDoc document(undoStack); 1131 1132 // We also mock timeline object to spy few functions and mock others 1133 pCore->projectManager()->m_project = &document; 1134 TimelineItemModel tim(document.uuid(), undoStack); 1135 Mock<TimelineItemModel> timMock(tim); 1136 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 1137 TimelineItemModel::finishConstruct(timeline); 1138 1139 pCore->projectManager()->testSetActiveDocument(&document, timeline); 1140 1141 RESET(timMock); 1142 1143 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel); 1144 1145 std::vector<int> track_ids; 1146 std::unordered_set<int> all_ids; 1147 1148 std::bernoulli_distribution coin(0.5); 1149 1150 const int nbr = 20; 1151 1152 for (int i = 0; i < nbr; i++) { 1153 if (coin(g)) { 1154 int tid = TrackModel::construct(timeline); 1155 REQUIRE(all_ids.count(tid) == 0); 1156 all_ids.insert(tid); 1157 track_ids.push_back(tid); 1158 REQUIRE(timeline->getTracksCount() == int(track_ids.size())); 1159 } else { 1160 int cid = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 1161 REQUIRE(all_ids.count(cid) == 0); 1162 all_ids.insert(cid); 1163 REQUIRE(timeline->getClipsCount() == int(all_ids.size() - track_ids.size())); 1164 } 1165 } 1166 1167 REQUIRE(timeline->checkConsistency()); 1168 REQUIRE(all_ids.size() == nbr); 1169 REQUIRE(all_ids.size() != track_ids.size()); 1170 pCore->projectManager()->closeCurrentDocument(false, false); 1171 } 1172 1173 TEST_CASE("Undo and Redo", "[ClipModel]") 1174 { 1175 auto binModel = pCore->projectItemModel(); 1176 binModel->clean(); 1177 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 1178 1179 // Here we do some trickery to enable testing. 1180 // We mock the project class so that the undoStack function returns our undoStack 1181 KdenliveDoc document(undoStack); 1182 1183 // We also mock timeline object to spy few functions and mock others 1184 pCore->projectManager()->m_project = &document; 1185 TimelineItemModel tim(document.uuid(), undoStack); 1186 Mock<TimelineItemModel> timMock(tim); 1187 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 1188 TimelineItemModel::finishConstruct(timeline); 1189 pCore->projectManager()->testSetActiveDocument(&document, timeline); 1190 1191 RESET(timMock); 1192 1193 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel); 1194 QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel); 1195 1196 int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 1197 int tid1 = TrackModel::construct(timeline); 1198 int tid2 = TrackModel::construct(timeline); 1199 int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly); 1200 1201 int length = 20; 1202 unsigned long nclips = timeline->m_allClips.size(); 1203 1204 SECTION("requestCreateClip") 1205 { 1206 // an invalid clip id shouldn't get created 1207 { 1208 int temp; 1209 Fun undo = []() { return true; }; 1210 Fun redo = []() { return true; }; 1211 REQUIRE_FALSE(timeline->requestClipCreation("impossible bin id", temp, PlaylistState::VideoOnly, 1, 1., false, undo, redo)); 1212 } 1213 1214 auto state0 = [&]() { 1215 REQUIRE(timeline->checkConsistency()); 1216 REQUIRE(timeline->m_allClips.size() == nclips); 1217 }; 1218 state0(); 1219 1220 QString binId3 = createProducer(pCore->getProjectProfile(), "green", binModel); 1221 int cid3; 1222 { 1223 Fun undo = []() { return true; }; 1224 Fun redo = []() { return true; }; 1225 REQUIRE(timeline->requestClipCreation(binId3, cid3, PlaylistState::VideoOnly, 1, 1., false, undo, redo)); 1226 pCore->pushUndo(undo, redo, QString()); 1227 } 1228 1229 auto state1 = [&]() { 1230 REQUIRE(timeline->checkConsistency()); 1231 REQUIRE(timeline->m_allClips.size() == nclips + 1); 1232 REQUIRE(timeline->getClipPlaytime(cid3) == length); 1233 REQUIRE(timeline->getClipTrackId(cid3) == -1); 1234 }; 1235 state1(); 1236 1237 QString binId4 = binId3 + "/1/10"; 1238 int cid4; 1239 { 1240 Fun undo = []() { return true; }; 1241 Fun redo = []() { return true; }; 1242 REQUIRE(timeline->requestClipCreation(binId4, cid4, PlaylistState::VideoOnly, 1, 1., false, undo, redo)); 1243 pCore->pushUndo(undo, redo, QString()); 1244 } 1245 1246 auto state2 = [&]() { 1247 REQUIRE(timeline->checkConsistency()); 1248 REQUIRE(timeline->m_allClips.size() == nclips + 2); 1249 REQUIRE(timeline->getClipPlaytime(cid4) == 10); 1250 REQUIRE(timeline->getClipTrackId(cid4) == -1); 1251 auto inOut = std::pair<int, int>({1, 10}); 1252 REQUIRE(timeline->m_allClips.at(cid4)->getInOut() == inOut); 1253 REQUIRE(timeline->getClipPlaytime(cid3) == length); 1254 REQUIRE(timeline->getClipTrackId(cid3) == -1); 1255 }; 1256 state2(); 1257 undoStack->undo(); 1258 state1(); 1259 undoStack->undo(); 1260 state0(); 1261 undoStack->redo(); 1262 state1(); 1263 undoStack->redo(); 1264 state2(); 1265 } 1266 1267 SECTION("requestInsertClip") 1268 { 1269 auto state0 = [&]() { 1270 REQUIRE(timeline->checkConsistency()); 1271 REQUIRE(timeline->m_allClips.size() == nclips); 1272 }; 1273 state0(); 1274 1275 QString binId3 = createProducer(pCore->getProjectProfile(), "green", binModel); 1276 int cid3; 1277 REQUIRE(timeline->requestClipInsertion(binId3, tid1, 12, cid3, true)); 1278 1279 auto state1 = [&]() { 1280 REQUIRE(timeline->checkConsistency()); 1281 REQUIRE(timeline->m_allClips.size() == nclips + 1); 1282 REQUIRE(timeline->getClipPlaytime(cid3) == length); 1283 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 1284 REQUIRE(timeline->getClipPosition(cid3) == 12); 1285 }; 1286 state1(); 1287 1288 QString binId4 = binId3 + "/1/10"; 1289 int cid4; 1290 REQUIRE(timeline->requestClipInsertion(binId4, tid2, 17, cid4, true)); 1291 1292 auto state2 = [&]() { 1293 REQUIRE(timeline->checkConsistency()); 1294 REQUIRE(timeline->m_allClips.size() == nclips + 2); 1295 REQUIRE(timeline->getClipPlaytime(cid4) == 10); 1296 REQUIRE(timeline->getClipTrackId(cid4) == tid2); 1297 REQUIRE(timeline->getClipPosition(cid4) == 17); 1298 auto inOut = std::pair<int, int>({1, 10}); 1299 REQUIRE(timeline->m_allClips.at(cid4)->getInOut() == inOut); 1300 REQUIRE(timeline->getClipPlaytime(cid3) == length); 1301 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 1302 REQUIRE(timeline->getClipPosition(cid3) == 12); 1303 }; 1304 state2(); 1305 undoStack->undo(); 1306 state1(); 1307 undoStack->undo(); 1308 state0(); 1309 undoStack->redo(); 1310 state1(); 1311 undoStack->redo(); 1312 state2(); 1313 } 1314 int init_index = undoStack->index(); 1315 1316 SECTION("Basic move undo") 1317 { 1318 REQUIRE(timeline->requestClipMove(cid1, tid1, 5)); 1319 REQUIRE(timeline->checkConsistency()); 1320 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1321 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1322 REQUIRE(timeline->getClipPosition(cid1) == 5); 1323 REQUIRE(undoStack->index() == init_index + 1); 1324 CHECK_INSERT(Once); 1325 1326 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 1327 REQUIRE(timeline->checkConsistency()); 1328 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1329 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1330 REQUIRE(timeline->getClipPosition(cid1) == 0); 1331 REQUIRE(undoStack->index() == init_index + 2); 1332 // Move on same track does not trigger insert/remove row 1333 CHECK_MOVE(0); 1334 1335 undoStack->undo(); 1336 REQUIRE(timeline->checkConsistency()); 1337 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1338 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1339 REQUIRE(timeline->getClipPosition(cid1) == 5); 1340 REQUIRE(undoStack->index() == init_index + 1); 1341 CHECK_MOVE(0); 1342 1343 undoStack->redo(); 1344 REQUIRE(timeline->checkConsistency()); 1345 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1346 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1347 REQUIRE(timeline->getClipPosition(cid1) == 0); 1348 REQUIRE(undoStack->index() == init_index + 2); 1349 CHECK_MOVE(0); 1350 1351 undoStack->undo(); 1352 REQUIRE(timeline->checkConsistency()); 1353 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1354 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1355 REQUIRE(timeline->getClipPosition(cid1) == 5); 1356 REQUIRE(undoStack->index() == init_index + 1); 1357 CHECK_MOVE(0); 1358 1359 REQUIRE(timeline->requestClipMove(cid1, tid1, 2 * length)); 1360 REQUIRE(timeline->checkConsistency()); 1361 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1362 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1363 REQUIRE(timeline->getClipPosition(cid1) == 2 * length); 1364 REQUIRE(undoStack->index() == init_index + 2); 1365 CHECK_MOVE(0); 1366 1367 undoStack->undo(); 1368 REQUIRE(timeline->checkConsistency()); 1369 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1370 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1371 REQUIRE(timeline->getClipPosition(cid1) == 5); 1372 REQUIRE(undoStack->index() == init_index + 1); 1373 CHECK_MOVE(0); 1374 1375 undoStack->redo(); 1376 REQUIRE(timeline->checkConsistency()); 1377 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1378 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1379 REQUIRE(timeline->getClipPosition(cid1) == 2 * length); 1380 REQUIRE(undoStack->index() == init_index + 2); 1381 CHECK_MOVE(0); 1382 1383 undoStack->undo(); 1384 CHECK_MOVE(0); 1385 undoStack->undo(); 1386 REQUIRE(timeline->checkConsistency()); 1387 REQUIRE(timeline->getTrackClipsCount(tid1) == 0); 1388 REQUIRE(timeline->getClipTrackId(cid1) == -1); 1389 REQUIRE(undoStack->index() == init_index); 1390 CHECK_REMOVE(Once); 1391 } 1392 1393 SECTION("Basic resize orphan clip undo") 1394 { 1395 REQUIRE(timeline->getClipPlaytime(cid2) == length); 1396 1397 REQUIRE(timeline->requestItemResize(cid2, length - 5, true) == length - 5); 1398 REQUIRE(undoStack->index() == init_index + 1); 1399 REQUIRE(timeline->getClipPlaytime(cid2) == length - 5); 1400 1401 REQUIRE(timeline->requestItemResize(cid2, length - 10, false) == length - 10); 1402 REQUIRE(undoStack->index() == init_index + 2); 1403 REQUIRE(timeline->getClipPlaytime(cid2) == length - 10); 1404 1405 REQUIRE(timeline->requestItemResize(cid2, length, false) == -1); 1406 REQUIRE(undoStack->index() == init_index + 2); 1407 REQUIRE(timeline->getClipPlaytime(cid2) == length - 10); 1408 1409 undoStack->undo(); 1410 REQUIRE(undoStack->index() == init_index + 1); 1411 REQUIRE(timeline->getClipPlaytime(cid2) == length - 5); 1412 1413 undoStack->redo(); 1414 REQUIRE(undoStack->index() == init_index + 2); 1415 REQUIRE(timeline->getClipPlaytime(cid2) == length - 10); 1416 1417 undoStack->undo(); 1418 REQUIRE(undoStack->index() == init_index + 1); 1419 REQUIRE(timeline->getClipPlaytime(cid2) == length - 5); 1420 1421 undoStack->undo(); 1422 REQUIRE(undoStack->index() == init_index); 1423 REQUIRE(timeline->getClipPlaytime(cid2) == length); 1424 } 1425 SECTION("Basic resize inserted clip undo") 1426 { 1427 REQUIRE(timeline->getClipPlaytime(cid2) == length); 1428 1429 auto check = [&](int pos, int l) { 1430 REQUIRE(timeline->checkConsistency()); 1431 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1432 REQUIRE(timeline->getClipTrackId(cid2) == tid1); 1433 REQUIRE(timeline->getClipPlaytime(cid2) == l); 1434 REQUIRE(timeline->getClipPosition(cid2) == pos); 1435 }; 1436 REQUIRE(timeline->requestClipMove(cid2, tid1, 5)); 1437 INFO("Test 1"); 1438 check(5, length); 1439 REQUIRE(undoStack->index() == init_index + 1); 1440 1441 REQUIRE(timeline->requestItemResize(cid2, length - 5, true) == length - 5); 1442 INFO("Test 2"); 1443 check(5, length - 5); 1444 REQUIRE(undoStack->index() == init_index + 2); 1445 1446 REQUIRE(timeline->requestItemResize(cid2, length - 10, false) == length - 10); 1447 INFO("Test 3"); 1448 check(10, length - 10); 1449 REQUIRE(undoStack->index() == init_index + 3); 1450 1451 REQUIRE(timeline->requestItemResize(cid2, length, false) == -1); 1452 INFO("Test 4"); 1453 check(10, length - 10); 1454 REQUIRE(undoStack->index() == init_index + 3); 1455 1456 undoStack->undo(); 1457 INFO("Test 5"); 1458 check(5, length - 5); 1459 REQUIRE(undoStack->index() == init_index + 2); 1460 1461 undoStack->redo(); 1462 INFO("Test 6"); 1463 check(10, length - 10); 1464 REQUIRE(undoStack->index() == init_index + 3); 1465 1466 undoStack->undo(); 1467 INFO("Test 7"); 1468 check(5, length - 5); 1469 REQUIRE(undoStack->index() == init_index + 2); 1470 1471 undoStack->undo(); 1472 INFO("Test 8"); 1473 check(5, length); 1474 REQUIRE(undoStack->index() == init_index + 1); 1475 } 1476 SECTION("Clip Insertion Undo") 1477 { 1478 QString binId3 = createProducer(pCore->getProjectProfile(), "red", binModel); 1479 1480 REQUIRE(timeline->requestClipMove(cid1, tid1, 5)); 1481 auto state1 = [&]() { 1482 REQUIRE(timeline->checkConsistency()); 1483 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1484 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1485 REQUIRE(timeline->getClipPosition(cid1) == 5); 1486 REQUIRE(undoStack->index() == init_index + 1); 1487 }; 1488 state1(); 1489 1490 int cid3; 1491 REQUIRE_FALSE(timeline->requestClipInsertion(binId3, tid1, 5, cid3)); 1492 state1(); 1493 1494 REQUIRE_FALSE(timeline->requestClipInsertion(binId3, tid1, 6, cid3)); 1495 state1(); 1496 1497 REQUIRE(timeline->requestClipInsertion(binId3, tid1, 5 + length, cid3)); 1498 auto state2 = [&]() { 1499 REQUIRE(timeline->checkConsistency()); 1500 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 1501 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1502 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 1503 REQUIRE(timeline->getClipPosition(cid1) == 5); 1504 REQUIRE(timeline->getClipPosition(cid3) == 5 + length); 1505 REQUIRE(timeline->m_allClips[cid3]->isValid()); 1506 REQUIRE(undoStack->index() == init_index + 2); 1507 }; 1508 state2(); 1509 1510 REQUIRE(timeline->requestClipMove(cid3, tid1, 10 + length)); 1511 auto state3 = [&]() { 1512 REQUIRE(timeline->checkConsistency()); 1513 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 1514 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1515 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 1516 REQUIRE(timeline->getClipPosition(cid1) == 5); 1517 REQUIRE(timeline->getClipPosition(cid3) == 10 + length); 1518 REQUIRE(undoStack->index() == init_index + 3); 1519 }; 1520 state3(); 1521 1522 REQUIRE(timeline->requestItemResize(cid3, 1, true) == 1); 1523 auto state4 = [&]() { 1524 REQUIRE(timeline->checkConsistency()); 1525 REQUIRE(timeline->getTrackClipsCount(tid1) == 2); 1526 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1527 REQUIRE(timeline->getClipTrackId(cid3) == tid1); 1528 REQUIRE(timeline->getClipPosition(cid1) == 5); 1529 REQUIRE(timeline->getClipPlaytime(cid3) == 1); 1530 REQUIRE(timeline->getClipPosition(cid3) == 10 + length); 1531 REQUIRE(undoStack->index() == init_index + 4); 1532 }; 1533 state4(); 1534 1535 undoStack->undo(); 1536 state3(); 1537 1538 undoStack->undo(); 1539 state2(); 1540 1541 undoStack->undo(); 1542 state1(); 1543 1544 undoStack->redo(); 1545 state2(); 1546 1547 undoStack->redo(); 1548 state3(); 1549 1550 undoStack->redo(); 1551 state4(); 1552 1553 undoStack->undo(); 1554 state3(); 1555 1556 undoStack->undo(); 1557 state2(); 1558 1559 undoStack->undo(); 1560 state1(); 1561 } 1562 1563 SECTION("Clip Deletion undo") 1564 { 1565 REQUIRE(timeline->requestClipMove(cid1, tid1, 5)); 1566 auto state1 = [&]() { 1567 REQUIRE(timeline->checkConsistency()); 1568 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1569 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1570 REQUIRE(timeline->getClipPosition(cid1) == 5); 1571 REQUIRE(undoStack->index() == init_index + 1); 1572 }; 1573 state1(); 1574 1575 int nbClips = timeline->getClipsCount(); 1576 REQUIRE(timeline->requestItemDeletion(cid1)); 1577 auto state2 = [&]() { 1578 REQUIRE(timeline->checkConsistency()); 1579 REQUIRE(timeline->getTrackClipsCount(tid1) == 0); 1580 REQUIRE(timeline->getClipsCount() == nbClips - 1); 1581 REQUIRE(undoStack->index() == init_index + 2); 1582 }; 1583 state2(); 1584 1585 undoStack->undo(); 1586 state1(); 1587 1588 undoStack->redo(); 1589 state2(); 1590 1591 undoStack->undo(); 1592 state1(); 1593 } 1594 1595 SECTION("Select then delete") 1596 { 1597 REQUIRE(timeline->requestClipMove(cid1, tid1, 5)); 1598 REQUIRE(timeline->requestClipMove(cid2, tid2, 1)); 1599 auto state1 = [&]() { 1600 REQUIRE(timeline->checkConsistency()); 1601 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1602 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1603 REQUIRE(timeline->getClipPosition(cid1) == 5); 1604 REQUIRE(timeline->getTrackClipsCount(tid2) == 1); 1605 REQUIRE(timeline->getClipTrackId(cid2) == tid2); 1606 REQUIRE(timeline->getClipPosition(cid2) == 1); 1607 }; 1608 state1(); 1609 1610 REQUIRE(timeline->requestSetSelection({cid1, cid2})); 1611 int nbClips = timeline->getClipsCount(); 1612 REQUIRE(timeline->requestItemDeletion(cid1)); 1613 auto state2 = [&]() { 1614 REQUIRE(timeline->checkConsistency()); 1615 REQUIRE(timeline->getTrackClipsCount(tid1) == 0); 1616 REQUIRE(timeline->getTrackClipsCount(tid2) == 0); 1617 REQUIRE(timeline->getClipsCount() == nbClips - 2); 1618 }; 1619 state2(); 1620 1621 undoStack->undo(); 1622 state1(); 1623 1624 undoStack->redo(); 1625 state2(); 1626 1627 undoStack->undo(); 1628 state1(); 1629 } 1630 1631 SECTION("Track insertion undo") 1632 { 1633 std::map<int, int> orig_trackPositions, final_trackPositions; 1634 for (const auto &it : timeline->m_iteratorTable) { 1635 int track = it.first; 1636 int pos = timeline->getTrackPosition(track); 1637 orig_trackPositions[track] = pos; 1638 if (pos >= 1) pos++; 1639 final_trackPositions[track] = pos; 1640 } 1641 auto checkPositions = [&](const std::map<int, int> &pos) { 1642 for (const auto &p : pos) { 1643 REQUIRE(timeline->getTrackPosition(p.first) == p.second); 1644 } 1645 }; 1646 checkPositions(orig_trackPositions); 1647 int new_tid; 1648 REQUIRE(timeline->requestTrackInsertion(1, new_tid)); 1649 checkPositions(final_trackPositions); 1650 1651 undoStack->undo(); 1652 checkPositions(orig_trackPositions); 1653 1654 undoStack->redo(); 1655 checkPositions(final_trackPositions); 1656 1657 undoStack->undo(); 1658 checkPositions(orig_trackPositions); 1659 } 1660 1661 SECTION("Track deletion undo") 1662 { 1663 int nb_clips = timeline->getClipsCount(); 1664 int nb_tracks = timeline->getTracksCount(); 1665 REQUIRE(timeline->requestClipMove(cid1, tid1, 5)); 1666 auto state1 = [&]() { 1667 REQUIRE(timeline->checkConsistency()); 1668 REQUIRE(timeline->getTrackClipsCount(tid1) == 1); 1669 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1670 REQUIRE(timeline->getClipPosition(cid1) == 5); 1671 REQUIRE(undoStack->index() == init_index + 1); 1672 REQUIRE(timeline->getClipsCount() == nb_clips); 1673 REQUIRE(timeline->getTracksCount() == nb_tracks); 1674 }; 1675 state1(); 1676 1677 REQUIRE(timeline->requestTrackDeletion(tid1)); 1678 REQUIRE(timeline->getClipsCount() == nb_clips - 1); 1679 REQUIRE(timeline->getTracksCount() == nb_tracks - 1); 1680 1681 undoStack->undo(); 1682 state1(); 1683 1684 undoStack->redo(); 1685 REQUIRE(timeline->getClipsCount() == nb_clips - 1); 1686 REQUIRE(timeline->getTracksCount() == nb_tracks - 1); 1687 1688 undoStack->undo(); 1689 state1(); 1690 } 1691 1692 unsigned long clipCount = timeline->m_allClips.size(); 1693 SECTION("Clip creation and resize") 1694 { 1695 int cid6; 1696 auto state0 = [&]() { 1697 REQUIRE(timeline->m_allClips.size() == clipCount); 1698 REQUIRE(timeline->checkConsistency()); 1699 }; 1700 state0(); 1701 1702 { 1703 std::function<bool(void)> undo = []() { return true; }; 1704 std::function<bool(void)> redo = []() { return true; }; 1705 REQUIRE(timeline->requestClipCreation(binId, cid6, PlaylistState::VideoOnly, 1, 1., false, undo, redo)); 1706 pCore->pushUndo(undo, redo, QString()); 1707 } 1708 int l = timeline->getClipPlaytime(cid6); 1709 1710 auto state1 = [&]() { 1711 REQUIRE(timeline->m_allClips.size() == clipCount + 1); 1712 REQUIRE(timeline->isClip(cid6)); 1713 REQUIRE(timeline->getClipTrackId(cid6) == -1); 1714 REQUIRE(timeline->getClipPlaytime(cid6) == l); 1715 }; 1716 state1(); 1717 1718 { 1719 std::function<bool(void)> undo = []() { return true; }; 1720 std::function<bool(void)> redo = []() { return true; }; 1721 int size = l - 5; 1722 REQUIRE(timeline->requestItemResize(cid6, size, true, true, undo, redo, false)); 1723 pCore->pushUndo(undo, redo, QString()); 1724 } 1725 auto state2 = [&]() { 1726 REQUIRE(timeline->m_allClips.size() == clipCount + 1); 1727 REQUIRE(timeline->isClip(cid6)); 1728 REQUIRE(timeline->getClipTrackId(cid6) == -1); 1729 REQUIRE(timeline->getClipPlaytime(cid6) == l - 5); 1730 }; 1731 state2(); 1732 1733 { 1734 std::function<bool(void)> undo = []() { return true; }; 1735 std::function<bool(void)> redo = []() { return true; }; 1736 REQUIRE(timeline->requestClipMove(cid6, tid1, 7, true, true, true, true, undo, redo)); 1737 pCore->pushUndo(undo, redo, QString()); 1738 } 1739 auto state3 = [&]() { 1740 REQUIRE(timeline->m_allClips.size() == clipCount + 1); 1741 REQUIRE(timeline->isClip(cid6)); 1742 REQUIRE(timeline->getClipTrackId(cid6) == tid1); 1743 REQUIRE(timeline->getClipPosition(cid6) == 7); 1744 REQUIRE(timeline->getClipPlaytime(cid6) == l - 5); 1745 }; 1746 state3(); 1747 1748 { 1749 std::function<bool(void)> undo = []() { return true; }; 1750 std::function<bool(void)> redo = []() { return true; }; 1751 int size = l - 6; 1752 REQUIRE(timeline->requestItemResize(cid6, size, false, true, undo, redo, false)); 1753 pCore->pushUndo(undo, redo, QString()); 1754 } 1755 auto state4 = [&]() { 1756 REQUIRE(timeline->m_allClips.size() == clipCount + 1); 1757 REQUIRE(timeline->isClip(cid6)); 1758 REQUIRE(timeline->getClipTrackId(cid6) == tid1); 1759 REQUIRE(timeline->getClipPosition(cid6) == 8); 1760 REQUIRE(timeline->getClipPlaytime(cid6) == l - 6); 1761 }; 1762 state4(); 1763 1764 undoStack->undo(); 1765 state3(); 1766 undoStack->undo(); 1767 state2(); 1768 undoStack->undo(); 1769 state1(); 1770 undoStack->undo(); 1771 state0(); 1772 undoStack->redo(); 1773 state1(); 1774 undoStack->redo(); 1775 state2(); 1776 undoStack->redo(); 1777 state3(); 1778 undoStack->redo(); 1779 state4(); 1780 } 1781 pCore->projectManager()->closeCurrentDocument(false, false); 1782 } 1783 1784 TEST_CASE("Snapping", "[Snapping]") 1785 { 1786 auto binModel = pCore->projectItemModel(); 1787 binModel->clean(); 1788 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 1789 1790 // Here we do some trickery to enable testing. 1791 // We mock the project class so that the undoStack function returns our undoStack 1792 KdenliveDoc document(undoStack); 1793 Mock<KdenliveDoc> docMock(document); 1794 1795 // We also mock timeline object to spy few functions and mock others 1796 pCore->projectManager()->m_project = &document; 1797 TimelineItemModel tim(document.uuid(), undoStack); 1798 Mock<TimelineItemModel> timMock(tim); 1799 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 1800 TimelineItemModel::finishConstruct(timeline); 1801 pCore->projectManager()->testSetActiveDocument(&document, timeline); 1802 1803 RESET(timMock); 1804 1805 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel, 50); 1806 QString binId2 = createProducer(pCore->getProjectProfile(), "blue", binModel); 1807 1808 int tid1 = TrackModel::construct(timeline); 1809 int cid1 = ClipModel::construct(timeline, binId, -1, PlaylistState::VideoOnly); 1810 int tid2 = TrackModel::construct(timeline); 1811 int cid2 = ClipModel::construct(timeline, binId2, -1, PlaylistState::VideoOnly); 1812 1813 int length = timeline->getClipPlaytime(cid1); 1814 int length2 = timeline->getClipPlaytime(cid2); 1815 SECTION("getBlankSizeNearClip") 1816 { 1817 REQUIRE(timeline->requestClipMove(cid1, tid1, 0)); 1818 1819 // before 1820 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, false) == 0); 1821 // after 1822 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, true) == INT_MAX); 1823 REQUIRE(timeline->requestClipMove(cid1, tid1, 10)); 1824 // before 1825 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, false) == 10); 1826 // after 1827 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, true) == INT_MAX); 1828 REQUIRE(timeline->requestClipMove(cid2, tid1, 25 + length)); 1829 // before 1830 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, false) == 10); 1831 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid2, false) == 15); 1832 // after 1833 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, true) == 15); 1834 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid2, true) == INT_MAX); 1835 1836 REQUIRE(timeline->requestClipMove(cid2, tid1, 10 + length)); 1837 // before 1838 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, false) == 10); 1839 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid2, false) == 0); 1840 // after 1841 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid1, true) == 0); 1842 REQUIRE(timeline->getTrackById(tid1)->getBlankSizeNearClip(cid2, true) == INT_MAX); 1843 } 1844 SECTION("Snap move to a single clip") 1845 { 1846 int beg = 30; 1847 // in the absence of other clips, a valid move shouldn't be modified 1848 for (int snap = -1; snap <= 5; ++snap) { 1849 REQUIRE(timeline->suggestClipMove(cid2, tid2, beg, -1, snap).at(0) == beg); 1850 REQUIRE(timeline->suggestClipMove(cid2, tid2, beg + length, -1, snap).at(0) == beg + length); 1851 REQUIRE(timeline->checkConsistency()); 1852 } 1853 1854 // We add a clip in first track to create snap points 1855 REQUIRE(timeline->requestClipMove(cid1, tid1, beg)); 1856 1857 // Now a clip in second track should snap to beginning 1858 auto check_snap = [&](int pos, int perturb, int snap) { 1859 if (snap >= perturb) { 1860 REQUIRE(timeline->suggestClipMove(cid2, tid2, pos + perturb, -1, snap).at(0) == pos); 1861 REQUIRE(timeline->suggestClipMove(cid2, tid2, pos - perturb, -1, snap).at(0) == pos); 1862 } else { 1863 REQUIRE(timeline->suggestClipMove(cid2, tid2, pos + perturb, -1, snap).at(0) == pos + perturb); 1864 REQUIRE(timeline->suggestClipMove(cid2, tid2, pos - perturb, -1, snap).at(0) == pos - perturb); 1865 } 1866 }; 1867 for (int snap = -1; snap <= 5; ++snap) { 1868 for (int perturb = 0; perturb <= 6; ++perturb) { 1869 // snap to beginning 1870 check_snap(beg, perturb, snap); 1871 check_snap(beg + length, perturb, snap); 1872 // snap to end 1873 check_snap(beg - length2, perturb, snap); 1874 check_snap(beg + length - length2, perturb, snap); 1875 REQUIRE(timeline->checkConsistency()); 1876 } 1877 } 1878 1879 // Same test, but now clip is moved in position 0 first 1880 REQUIRE(timeline->requestClipMove(cid2, tid2, 0)); 1881 for (int snap = -1; snap <= 5; ++snap) { 1882 for (int perturb = 0; perturb <= 6; ++perturb) { 1883 // snap to beginning 1884 check_snap(beg, perturb, snap); 1885 check_snap(beg + length, perturb, snap); 1886 // snap to end 1887 check_snap(beg - length2, perturb, snap); 1888 check_snap(beg + length - length2, perturb, snap); 1889 REQUIRE(timeline->checkConsistency()); 1890 } 1891 } 1892 } 1893 pCore->projectManager()->closeCurrentDocument(false, false); 1894 } 1895 1896 TEST_CASE("Operations under locked tracks", "[Locked]") 1897 { 1898 1899 QString aCompo; 1900 // Look for a compo 1901 QVector<QPair<QString, QString>> transitions = TransitionsRepository::get()->getNames(); 1902 for (const auto &trans : qAsConst(transitions)) { 1903 if (TransitionsRepository::get()->isComposition(trans.first)) { 1904 aCompo = trans.first; 1905 break; 1906 } 1907 } 1908 REQUIRE(!aCompo.isEmpty()); 1909 1910 auto binModel = pCore->projectItemModel(); 1911 binModel->clean(); 1912 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 1913 1914 // Here we do some trickery to enable testing. 1915 // We mock the project class so that the undoStack function returns our undoStack 1916 KdenliveDoc document(undoStack); 1917 Mock<KdenliveDoc> docMock(document); 1918 1919 // We also mock timeline object to spy few functions and mock others 1920 pCore->projectManager()->m_project = &document; 1921 TimelineItemModel tim(document.uuid(), undoStack); 1922 Mock<TimelineItemModel> timMock(tim); 1923 auto timeline = std::shared_ptr<TimelineItemModel>(&timMock.get(), [](...) {}); 1924 TimelineItemModel::finishConstruct(timeline); 1925 pCore->projectManager()->testSetActiveDocument(&document, timeline); 1926 1927 Fake(Method(timMock, adjustAssetRange)); 1928 1929 // This is faked to allow to count calls 1930 Fake(Method(timMock, _beginInsertRows)); 1931 Fake(Method(timMock, _beginRemoveRows)); 1932 Fake(Method(timMock, _endInsertRows)); 1933 Fake(Method(timMock, _endRemoveRows)); 1934 1935 QString binId = createProducer(pCore->getProjectProfile(), "red", binModel); 1936 QString binId3 = createProducerWithSound(pCore->getProjectProfile(), binModel); 1937 1938 int tid1, tid2, tid3; 1939 REQUIRE(timeline->requestTrackInsertion(-1, tid1)); 1940 REQUIRE(timeline->requestTrackInsertion(-1, tid2)); 1941 REQUIRE(timeline->requestTrackInsertion(-1, tid3)); 1942 1943 RESET(timMock); 1944 1945 SECTION("Locked track can't receive insertion") 1946 { 1947 timeline->setTrackLockedState(tid1, true); 1948 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 1949 REQUIRE(timeline->getClipsCount() == 0); 1950 REQUIRE(timeline->checkConsistency()); 1951 int cid1 = -1; 1952 REQUIRE_FALSE(timeline->requestClipInsertion(binId, tid1, 2, cid1)); 1953 REQUIRE(timeline->getClipsCount() == 0); 1954 REQUIRE(timeline->checkConsistency()); 1955 REQUIRE(cid1 == -1); 1956 1957 // now unlock and check that insertion becomes possible again 1958 timeline->setTrackLockedState(tid1, false); 1959 REQUIRE_FALSE(timeline->getTrackById(tid1)->isLocked()); 1960 REQUIRE(timeline->getClipsCount() == 0); 1961 REQUIRE(timeline->checkConsistency()); 1962 REQUIRE(timeline->requestClipInsertion(binId, tid1, 2, cid1)); 1963 REQUIRE(timeline->getClipsCount() == 1); 1964 REQUIRE(timeline->checkConsistency()); 1965 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1966 REQUIRE(timeline->getClipPosition(cid1) == 2); 1967 } 1968 SECTION("Can't move clip on locked track") 1969 { 1970 int cid1 = -1; 1971 REQUIRE(timeline->requestClipInsertion(binId, tid1, 2, cid1)); 1972 REQUIRE(timeline->getClipsCount() == 1); 1973 REQUIRE(timeline->checkConsistency()); 1974 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1975 REQUIRE(timeline->getClipPosition(cid1) == 2); 1976 // not yet locked, move should work 1977 REQUIRE(timeline->requestClipMove(cid1, tid1, 4)); 1978 REQUIRE(timeline->getClipPosition(cid1) == 4); 1979 REQUIRE(timeline->checkConsistency()); 1980 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1981 1982 timeline->setTrackLockedState(tid1, true); 1983 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 1984 REQUIRE(timeline->checkConsistency()); 1985 REQUIRE(timeline->getClipsCount() == 1); 1986 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1987 REQUIRE(timeline->getClipPosition(cid1) == 4); 1988 1989 REQUIRE_FALSE(timeline->requestClipMove(cid1, tid1, 6)); 1990 1991 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 1992 REQUIRE(timeline->checkConsistency()); 1993 REQUIRE(timeline->getClipsCount() == 1); 1994 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 1995 REQUIRE(timeline->getClipPosition(cid1) == 4); 1996 1997 // unlock, move should work again 1998 timeline->setTrackLockedState(tid1, false); 1999 REQUIRE_FALSE(timeline->getTrackById(tid1)->isLocked()); 2000 REQUIRE(timeline->checkConsistency()); 2001 REQUIRE(timeline->requestClipMove(cid1, tid1, 6)); 2002 REQUIRE(timeline->getClipsCount() == 1); 2003 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 2004 REQUIRE(timeline->getClipPosition(cid1) == 6); 2005 REQUIRE(timeline->checkConsistency()); 2006 } 2007 SECTION("Can't move composition on locked track") 2008 { 2009 int compo = CompositionModel::construct(timeline, aCompo, QString()); 2010 timeline->setTrackLockedState(tid1, true); 2011 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 2012 REQUIRE(timeline->checkConsistency()); 2013 2014 REQUIRE(timeline->getCompositionTrackId(compo) == -1); 2015 REQUIRE(timeline->getTrackCompositionsCount(tid1) == 0); 2016 int pos = 10; 2017 REQUIRE_FALSE(timeline->requestCompositionMove(compo, tid1, pos)); 2018 REQUIRE(timeline->checkConsistency()); 2019 REQUIRE(timeline->getCompositionTrackId(compo) == -1); 2020 REQUIRE(timeline->getTrackCompositionsCount(tid1) == 0); 2021 2022 // unlock to be able to insert 2023 timeline->setTrackLockedState(tid1, false); 2024 REQUIRE_FALSE(timeline->getTrackById(tid1)->isLocked()); 2025 REQUIRE(timeline->checkConsistency()); 2026 REQUIRE(timeline->requestCompositionMove(compo, tid1, pos)); 2027 REQUIRE(timeline->checkConsistency()); 2028 REQUIRE(timeline->getCompositionTrackId(compo) == tid1); 2029 REQUIRE(timeline->getTrackCompositionsCount(tid1) == 1); 2030 REQUIRE(timeline->getCompositionPosition(compo) == pos); 2031 2032 // relock 2033 timeline->setTrackLockedState(tid1, true); 2034 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 2035 REQUIRE(timeline->checkConsistency()); 2036 REQUIRE_FALSE(timeline->requestCompositionMove(compo, tid1, pos + 10)); 2037 REQUIRE(timeline->checkConsistency()); 2038 REQUIRE(timeline->getCompositionTrackId(compo) == tid1); 2039 REQUIRE(timeline->getTrackCompositionsCount(tid1) == 1); 2040 REQUIRE(timeline->getCompositionPosition(compo) == pos); 2041 } 2042 SECTION("Can't resize clip on locked track") 2043 { 2044 int cid1 = -1; 2045 REQUIRE(timeline->requestClipInsertion(binId, tid1, 2, cid1)); 2046 REQUIRE(timeline->getClipsCount() == 1); 2047 2048 auto check = [&](int l) { 2049 REQUIRE(timeline->checkConsistency()); 2050 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 2051 REQUIRE(timeline->getClipPosition(cid1) == 2); 2052 REQUIRE(timeline->getClipPlaytime(cid1) == l); 2053 }; 2054 check(20); 2055 2056 // not yet locked, resize should work 2057 REQUIRE(timeline->requestItemResize(cid1, 18, true) == 18); 2058 check(18); 2059 2060 // lock 2061 timeline->setTrackLockedState(tid1, true); 2062 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 2063 check(18); 2064 REQUIRE(timeline->requestItemResize(cid1, 17, true) == -1); 2065 check(18); 2066 REQUIRE(timeline->requestItemResize(cid1, 17, false) == -1); 2067 check(18); 2068 REQUIRE(timeline->requestItemResize(cid1, 19, true) == -1); 2069 check(18); 2070 REQUIRE(timeline->requestItemResize(cid1, 19, false) == -1); 2071 check(18); 2072 2073 // unlock, resize should work again 2074 timeline->setTrackLockedState(tid1, false); 2075 REQUIRE_FALSE(timeline->getTrackById(tid1)->isLocked()); 2076 check(18); 2077 REQUIRE(timeline->requestItemResize(cid1, 17, true) == 17); 2078 check(17); 2079 } 2080 SECTION("Can't resize composition on locked track") 2081 { 2082 int compo = CompositionModel::construct(timeline, aCompo, QString()); 2083 REQUIRE(timeline->requestCompositionMove(compo, tid1, 2)); 2084 REQUIRE(timeline->requestItemResize(compo, 20, true) == 20); 2085 2086 auto check = [&](int l) { 2087 REQUIRE(timeline->checkConsistency()); 2088 REQUIRE(timeline->getCompositionsCount() == 1); 2089 REQUIRE(timeline->getCompositionTrackId(compo) == tid1); 2090 REQUIRE(timeline->getCompositionPosition(compo) == 2); 2091 REQUIRE(timeline->getCompositionPlaytime(compo) == l); 2092 }; 2093 check(20); 2094 2095 // not yet locked, resize should work 2096 REQUIRE(timeline->requestItemResize(compo, 18, true) == 18); 2097 check(18); 2098 2099 // lock 2100 timeline->setTrackLockedState(tid1, true); 2101 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 2102 check(18); 2103 REQUIRE(timeline->requestItemResize(compo, 17, true) == -1); 2104 check(18); 2105 REQUIRE(timeline->requestItemResize(compo, 17, false) == -1); 2106 check(18); 2107 REQUIRE(timeline->requestItemResize(compo, 19, true) == -1); 2108 check(18); 2109 REQUIRE(timeline->requestItemResize(compo, 19, false) == -1); 2110 check(18); 2111 2112 // unlock, resize should work again 2113 timeline->setTrackLockedState(tid1, false); 2114 REQUIRE_FALSE(timeline->getTrackById(tid1)->isLocked()); 2115 check(18); 2116 REQUIRE(timeline->requestItemResize(compo, 17, true) == 17); 2117 check(17); 2118 } 2119 SECTION("Can't remove clip contained in locked track") 2120 { 2121 std::function<bool(void)> undo = []() { return true; }; 2122 std::function<bool(void)> redo = []() { return true; }; 2123 2124 // insert a clip to the track 2125 int cid1 = -1; 2126 REQUIRE(timeline->requestClipInsertion(binId, tid1, 2, cid1)); 2127 REQUIRE(timeline->getClipsCount() == 1); 2128 REQUIRE(timeline->checkConsistency()); 2129 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 2130 REQUIRE(timeline->getClipPosition(cid1) == 2); 2131 2132 // lock the track 2133 timeline->setTrackLockedState(tid1, true); 2134 REQUIRE(timeline->getTrackById(tid1)->isLocked()); 2135 REQUIRE(timeline->checkConsistency()); 2136 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 2137 2138 // try to delete bin clip, this should not work 2139 REQUIRE_FALSE(binModel->requestBinClipDeletion(binModel->getClipByBinID(binId), undo, redo)); 2140 REQUIRE(timeline->checkConsistency()); 2141 REQUIRE(timeline->getClipsCount() == 1); 2142 REQUIRE(timeline->getClipTrackId(cid1) == tid1); 2143 REQUIRE(timeline->getClipPosition(cid1) == 2); 2144 2145 // unlock track, bin clip deletion should work now 2146 timeline->setTrackLockedState(tid1, false); 2147 REQUIRE_FALSE(timeline->getTrackById(tid1)->isLocked()); 2148 REQUIRE(binModel->requestBinClipDeletion(binModel->getClipByBinID(binId), undo, redo)); 2149 REQUIRE(timeline->checkConsistency()); 2150 REQUIRE(timeline->getClipsCount() == 0); 2151 REQUIRE(timeline->checkConsistency()); 2152 } 2153 2154 pCore->projectManager()->closeCurrentDocument(false, false); 2155 } 2156 2157 TEST_CASE("New KdenliveDoc activeTrack", "KdenliveDoc") 2158 { 2159 auto binModel = pCore->projectItemModel(); 2160 binModel->clean(); 2161 2162 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 2163 QUndoGroup *undoGroup = new QUndoGroup(); 2164 undoGroup->addStack(undoStack.get()); 2165 const QMap<QString, QString> emptyMap{}; 2166 2167 // Bug 442545: KdenliveDoc created with 0 video tracks causes a crash at 2168 // save time because the document's activeTrack was set to an out-of-range 2169 // position. 2170 2171 SECTION("0 video tracks") 2172 { 2173 // Create document 2174 KdenliveDoc doc(undoStack, {0, 2}); 2175 pCore->projectManager()->m_project = &doc; 2176 QDateTime documentDate = QDateTime::currentDateTime(); 2177 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 2178 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 2179 const QString firstSeqId = allSequences.value(doc.uuid()); 2180 pCore->projectManager()->openTimeline(firstSeqId, doc.uuid()); 2181 auto timeline = doc.getTimeline(doc.uuid()); 2182 pCore->projectManager()->m_activeTimelineModel = timeline; 2183 pCore->projectManager()->testSetActiveDocument(&doc, timeline); 2184 // since there are only 2 tracks, the activeTrack position should be 0 or 1 2185 CHECK(doc.getDocumentProperty("activeTrack").toInt() >= 0); 2186 CHECK(doc.getDocumentProperty("activeTrack").toInt() < 2); 2187 pCore->projectManager()->closeCurrentDocument(false, false); 2188 } 2189 2190 SECTION("both audio and video tracks") 2191 { 2192 KdenliveDoc doc(undoStack, {2, 2}); 2193 pCore->projectManager()->m_project = &doc; 2194 QDateTime documentDate = QDateTime::currentDateTime(); 2195 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 2196 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 2197 const QString firstSeqId = allSequences.value(doc.uuid()); 2198 pCore->projectManager()->openTimeline(firstSeqId, doc.uuid()); 2199 auto timeline = doc.getTimeline(doc.uuid()); 2200 pCore->projectManager()->m_activeTimelineModel = timeline; 2201 pCore->projectManager()->testSetActiveDocument(&doc, timeline); 2202 2203 CHECK(doc.getSequenceProperty(doc.uuid(), "activeTrack").toInt() >= 0); 2204 CHECK(doc.getSequenceProperty(doc.uuid(), "activeTrack").toInt() < 4); 2205 // because video tracks come after audio tracks, videoTarget position 2206 // should also be after the audio tracks 2207 CHECK(doc.getSequenceProperty(doc.uuid(), "videoTarget").toInt() > 1); 2208 CHECK(doc.getSequenceProperty(doc.uuid(), "videoTarget").toInt() < 4); 2209 2210 CHECK(doc.getSequenceProperty(doc.uuid(), "audioTarget").toInt() >= 0); 2211 CHECK(doc.getSequenceProperty(doc.uuid(), "audioTarget").toInt() < 2); 2212 pCore->projectManager()->closeCurrentDocument(false, false); 2213 } 2214 2215 SECTION("0 audio tracks") 2216 { 2217 KdenliveDoc doc(undoStack, {2, 0}); 2218 pCore->projectManager()->m_project = &doc; 2219 QDateTime documentDate = QDateTime::currentDateTime(); 2220 pCore->projectManager()->updateTimeline(false, QString(), QString(), documentDate, 0); 2221 QMap<QUuid, QString> allSequences = binModel->getAllSequenceClips(); 2222 const QString firstSeqId = allSequences.value(doc.uuid()); 2223 pCore->projectManager()->openTimeline(firstSeqId, doc.uuid()); 2224 auto timeline = doc.getTimeline(doc.uuid()); 2225 pCore->projectManager()->m_activeTimelineModel = timeline; 2226 pCore->projectManager()->testSetActiveDocument(&doc, timeline); 2227 2228 CHECK(doc.getSequenceProperty(doc.uuid(), "activeTrack").toInt() >= 0); 2229 CHECK(doc.getSequenceProperty(doc.uuid(), "activeTrack").toInt() < 2); 2230 CHECK(doc.getSequenceProperty(doc.uuid(), "videoTarget").toInt() >= 0); 2231 CHECK(doc.getSequenceProperty(doc.uuid(), "videoTarget").toInt() < 2); 2232 pCore->projectManager()->closeCurrentDocument(false, false); 2233 } 2234 }