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