1 #include "test_utils.hpp"
2 
3 #include "kdenlivesettings.h"
4 #define private public
5 #define protected public
6 #include "bin/model/markerlistmodel.hpp"
7 #include "timeline2/model/snapmodel.hpp"
8 
9 using Marker = std::tuple<GenTime, QString, int>;
10 double fps;
11 
checkMarkerList(const std::shared_ptr<MarkerListModel> & model,const std::vector<Marker> & l,const std::shared_ptr<SnapModel> & snaps)12 void checkMarkerList(const std::shared_ptr<MarkerListModel> &model, const std::vector<Marker> &l, const std::shared_ptr<SnapModel> &snaps)
13 {
14     auto list = l;
15     //std::sort(list.begin(), list.end(), [](const Marker &a, const Marker &b) { return std::get<0>(a) < std::get<0>(b); });
16 
17     REQUIRE(model->rowCount() == (int)list.size());
18     if (model->rowCount() == 0) {
19         REQUIRE(snaps->getClosestPoint(0) == -1);
20     }
21     for (int i = 0; i < model->rowCount(); ++i) {
22         Marker m;
23         // Model markers and List do not necessarily use the same order
24         for (size_t j = 0; j < list.size(); j++) {
25             if (qAbs(std::get<0>(list[j]).seconds() - model->data(model->index(i), MarkerListModel::PosRole).toDouble()) < 0.9 / fps) {
26                 m = list[j];
27                 list.erase(std::remove(list.begin(), list.end(), m), list.end());
28                 break;
29             }
30         }
31         REQUIRE(qAbs(std::get<0>(m).seconds() - model->data(model->index(i), MarkerListModel::PosRole).toDouble()) < 0.9 / fps);
32         REQUIRE(std::get<1>(m) == model->data(model->index(i), MarkerListModel::CommentRole).toString());
33         REQUIRE(std::get<2>(m) == model->data(model->index(i), MarkerListModel::TypeRole).toInt());
34         REQUIRE(MarkerListModel::markerTypes[std::get<2>(m)] == model->data(model->index(i), MarkerListModel::ColorRole).value<QColor>());
35 
36         // check for marker existence
37         int frame = std::get<0>(m).frames(fps);
38         REQUIRE(model->hasMarker(frame));
39 
40         // cheap way to check for snap
41         REQUIRE(snaps->getClosestPoint(frame) == frame);
42     }
43 }
44 
checkStates(const std::shared_ptr<DocUndoStack> & undoStack,const std::shared_ptr<MarkerListModel> & model,const std::vector<std::vector<Marker>> & states,const std::shared_ptr<SnapModel> & snaps)45 void checkStates(const std::shared_ptr<DocUndoStack> &undoStack, const std::shared_ptr<MarkerListModel> &model, const std::vector<std::vector<Marker>> &states,
46                  const std::shared_ptr<SnapModel> &snaps)
47 {
48     for (size_t i = 0; i < states.size(); ++i) {
49         checkMarkerList(model, states[states.size() - 1 - i], snaps);
50         if (i != states.size() - 1) {
51             undoStack->undo();
52         }
53     }
54     for (size_t i = 1; i < states.size(); ++i) {
55         undoStack->redo();
56         checkMarkerList(model, states[i], snaps);
57     }
58 }
59 
60 TEST_CASE("Marker model", "[MarkerListModel]")
61 {
62     fps = pCore->getCurrentFps();
63     GenTime::setFps(fps);
64     std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
65 
66     std::shared_ptr<MarkerListModel> model = std::make_shared<MarkerListModel>(undoStack, nullptr);
67 
68     std::shared_ptr<SnapModel> snaps = std::make_shared<SnapModel>();
69     model->registerSnapModel(snaps);
70 
71     // Here we do some trickery to enable testing.
72     // We mock the project class so that the getGuideModel function returns this model
73 
74     Mock<ProjectManager> pmMock;
75     When(Method(pmMock, getGuideModel)).AlwaysReturn(model);
76     When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
77 
78     ProjectManager &mocked = pmMock.get();
79     pCore->m_projectManager = &mocked;
80 
81     SECTION("Basic Manipulation")
82     {
83         std::vector<Marker> list;
84         checkMarkerList(model, list, snaps);
85 
86         // add markers
87         list.emplace_back(GenTime(1.3), QLatin1String("test marker"), 3);
88         REQUIRE(model->addMarker(GenTime(1.3), QLatin1String("test marker"), 3));
89         checkMarkerList(model, list, snaps);
90         auto state1 = list;
91 
92         checkStates(undoStack, model, {{}, state1}, snaps);
93 
94         list.emplace_back(GenTime(0.3), QLatin1String("test marker2"), 0);
95         REQUIRE(model->addMarker(GenTime(0.3), QLatin1String("test marker2"), 0));
96         checkMarkerList(model, list, snaps);
97         auto state2 = list;
98 
99         checkStates(undoStack, model, {{}, state1, state2}, snaps);
100 
101         // delete unexisting marker shouldn't work
102         REQUIRE_FALSE(model->removeMarker(GenTime(42.)));
103         checkMarkerList(model, list, snaps);
104         checkStates(undoStack, model, {{}, state1, state2}, snaps);
105 
106         // rename markers
107         std::get<1>(list[0]) = QLatin1String("new comment");
108         std::get<2>(list[0]) = 1;
109         REQUIRE(model->addMarker(GenTime(1.3), QLatin1String("new comment"), 1));
110         checkMarkerList(model, list, snaps);
111         auto state3 = list;
112         checkStates(undoStack, model, {{}, state1, state2, state3}, snaps);
113 
114         // edit marker
115         GenTime oldPos = std::get<0>(list[1]);
116         std::get<0>(list[1]) = GenTime(42.8);
117         std::get<1>(list[1]) = QLatin1String("edited comment");
118         std::get<2>(list[1]) = 3;
119         REQUIRE(model->editMarker(oldPos, GenTime(42.8), QLatin1String("edited comment"), 3));
120         checkMarkerList(model, list, snaps);
121         auto state4 = list;
122         checkStates(undoStack, model, {{}, state1, state2, state3, state4}, snaps);
123 
124         // delete markers
125         std::swap(list[0], list[1]);
126         list.pop_back();
127         REQUIRE(model->removeMarker(GenTime(1.3)));
128         checkMarkerList(model, list, snaps);
129         auto state5 = list;
130         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5}, snaps);
131         GenTime old = std::get<0>(list.back());
132         list.pop_back();
133         REQUIRE(model->removeMarker(old));
134         checkMarkerList(model, list, snaps);
135         auto state6 = list;
136         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6}, snaps);
137 
138         // add some back
139         list.emplace_back(GenTime(1.7), QLatin1String("test marker6"), KdenliveSettings::default_marker_type());
140         REQUIRE(model->addMarker(GenTime(1.7), QLatin1String("test marker6"), -1));
141         auto state7 = list;
142         list.emplace_back(GenTime(2), QLatin1String("auieuansr"), 3);
143         REQUIRE(model->addMarker(GenTime(2), QLatin1String("auieuansr"), 3));
144         auto state8 = list;
145         list.emplace_back(GenTime(0), QLatin1String("sasenust"), 1);
146         REQUIRE(model->addMarker(GenTime(0), QLatin1String("sasenust"), 1));
147         checkMarkerList(model, list, snaps);
148         auto state9 = list;
149         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6, state7, state8, state9}, snaps);
150 
151         // try spurious model registration
152         // std::shared_ptr<SnapInterface> spurious;
153         // REQUIRE(ABORTS(&MarkerListModel::registerSnapModel, model, spurious));
154 
155         // try real model registration
156         std::shared_ptr<SnapModel> other_snaps = std::make_shared<SnapModel>();
157         model->registerSnapModel(other_snaps);
158         checkMarkerList(model, list, other_snaps);
159 
160         // remove all
161         REQUIRE(model->removeAllMarkers());
162         checkMarkerList(model, {}, snaps);
163         checkStates(undoStack, model, {{}, state1, state2, state3, state4, state5, state6, state7, state8, state9, {}}, snaps);
164     }
165 
166     SECTION("Json identity test")
167     {
168         std::vector<Marker> list;
169         checkMarkerList(model, list, snaps);
170 
171         // add markers
172         list.emplace_back(GenTime(1.3), QLatin1String("test marker"), 3);
173         model->addMarker(GenTime(1.3), QLatin1String("test marker"), 3);
174         list.emplace_back(GenTime(0.3), QLatin1String("test marker2"), 0);
175         model->addMarker(GenTime(0.3), QLatin1String("test marker2"), 0);
176         list.emplace_back(GenTime(3), QLatin1String("test marker3"), 0);
177         model->addMarker(GenTime(3), QLatin1String("test marker3"), 0);
178         checkMarkerList(model, list, snaps);
179 
180         // export
181         QString json = model->toJson();
182 
183         // clean
184         model->removeMarker(GenTime(0.3));
185         model->removeMarker(GenTime(3));
186         model->removeMarker(GenTime(1.3));
187         checkMarkerList(model, {}, snaps);
188 
189         // Reimport
190         REQUIRE(model->importFromJson(json, false));
191         checkMarkerList(model, list, snaps);
192 
193         // undo/redo
194         undoStack->undo();
195         checkMarkerList(model, {}, snaps);
196         undoStack->redo();
197         checkMarkerList(model, list, snaps);
198 
199         // now we try the same thing with non-empty model
200         undoStack->undo();
201         checkMarkerList(model, {}, snaps);
202         // non - conflicting marker
203         list.emplace_back(GenTime(5), QLatin1String("non conflicting"), 0);
204         std::vector<Marker> otherMarkers;
205         otherMarkers.emplace_back(GenTime(5), QLatin1String("non conflicting"), 0);
206         model->addMarker(GenTime(5), QLatin1String("non conflicting"), 0);
207         REQUIRE(model->importFromJson(json, false));
208         checkMarkerList(model, list, snaps);
209         undoStack->undo();
210         checkMarkerList(model, otherMarkers, snaps);
211         undoStack->redo();
212         checkMarkerList(model, list, snaps);
213         undoStack->undo();
214 
215         // conflicting marker
216         otherMarkers.emplace_back(GenTime(1.3), QLatin1String("conflicting"), 1);
217         model->addMarker(GenTime(1.3), QLatin1String("conflicting"), 1);
218         checkMarkerList(model, otherMarkers, snaps);
219         REQUIRE_FALSE(model->importFromJson(json, false));
220         checkMarkerList(model, otherMarkers, snaps);
221         REQUIRE(model->importFromJson(json, true));
222         checkMarkerList(model, list, snaps);
223         undoStack->undo();
224         checkMarkerList(model, otherMarkers, snaps);
225         undoStack->redo();
226         checkMarkerList(model, list, snaps);
227     }
228     pCore->m_projectManager = nullptr;
229 }
230