1 #include <memory>
2
3 #include "test_utils.hpp"
4
5 using namespace fakeit;
6
test_model_equality(const std::shared_ptr<KeyframeModel> & m1,const std::shared_ptr<KeyframeModel> & m2)7 bool test_model_equality(const std::shared_ptr<KeyframeModel> &m1, const std::shared_ptr<KeyframeModel> &m2)
8 {
9 // we cheat a bit by simply comparing the underlying map
10 qDebug() << "Equality test" << m1->m_keyframeList.size() << m2->m_keyframeList.size();
11 QList<QVariant> model1;
12 QList<QVariant> model2;
13 for (const auto &m : m1->m_keyframeList) {
14 model1 << m.first.frames(25) << (int)m.second.first << m.second.second;
15 }
16 for (const auto &m : m2->m_keyframeList) {
17 model2 << m.first.frames(25) << (int)m.second.first << m.second.second;
18 }
19 return model1 == model2;
20 }
21
check_anim_identity(const std::shared_ptr<KeyframeModel> & m)22 bool check_anim_identity(const std::shared_ptr<KeyframeModel> &m)
23 {
24 auto m2 = std::make_shared<KeyframeModel>(m->m_model, m->m_index, m->m_undoStack);
25 m2->parseAnimProperty(m->getAnimProperty());
26 return test_model_equality(m, m2);
27 }
28
29 TEST_CASE("Keyframe model", "[KeyframeModel]")
30 {
31 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr);
32 std::shared_ptr<MarkerListModel> guideModel = std::make_shared<MarkerListModel>(undoStack);
33 // Here we do some trickery to enable testing.
34 // We mock the project class so that the undoStack function returns our undoStack
35
36 Mock<ProjectManager> pmMock;
37 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack);
38 When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
39
40 ProjectManager &mocked = pmMock.get();
41 pCore->m_projectManager = &mocked;
42
43 Mlt::Profile pr;
44 std::shared_ptr<Mlt::Producer> producer = std::make_shared<Mlt::Producer>(pr, "color", "red");
45 auto effectstack = EffectStackModel::construct(producer, {ObjectType::TimelineClip, 0}, undoStack);
46
47 effectstack->appendEffect(QStringLiteral("audiobalance"));
48 REQUIRE(effectstack->checkConsistency());
49 REQUIRE(effectstack->rowCount() == 1);
50 auto effect = std::dynamic_pointer_cast<EffectItemModel>(effectstack->getEffectStackRow(0));
51 effect->prepareKeyframes();
52 qDebug() << effect->getAssetId() << effect->getAllParameters();
53
54 REQUIRE(effect->rowCount() == 1);
55 QModelIndex index = effect->index(0, 0);
56
57 auto model = std::make_shared<KeyframeModel>(effect, index, undoStack);
58
59 SECTION("Add/remove + undo")
60 {
__anona1e3a5af0102() 61 auto state0 = [&]() {
62 REQUIRE(model->rowCount() == 1);
63 REQUIRE(check_anim_identity(model));
64 };
65 state0();
66
67 REQUIRE(model->addKeyframe(GenTime(1.1), KeyframeType::Linear, 42));
__anona1e3a5af0202() 68 auto state1 = [&]() {
69 REQUIRE(model->rowCount() == 2);
70 REQUIRE(check_anim_identity(model));
71 REQUIRE(model->hasKeyframe(GenTime(1.1)));
72 bool ok;
73 auto k = model->getKeyframe(GenTime(1.1), &ok);
74 REQUIRE(ok);
75 auto k0 = model->getKeyframe(GenTime(0), &ok);
76 REQUIRE(ok);
77 auto k1 = model->getClosestKeyframe(GenTime(0.655555), &ok);
78 REQUIRE(ok);
79 REQUIRE(k1 == k);
80 auto k2 = model->getNextKeyframe(GenTime(0.5), &ok);
81 REQUIRE(ok);
82 REQUIRE(k2 == k);
83 auto k3 = model->getPrevKeyframe(GenTime(0.5), &ok);
84 REQUIRE(ok);
85 REQUIRE(k3 == k0);
86 auto k4 = model->getPrevKeyframe(GenTime(10), &ok);
87 REQUIRE(ok);
88 REQUIRE(k4 == k);
89 model->getNextKeyframe(GenTime(10), &ok);
90 REQUIRE_FALSE(ok);
91 };
92 state1();
93
94 undoStack->undo();
95 state0();
96 undoStack->redo();
97 state1();
98
99 REQUIRE(model->addKeyframe(GenTime(12.6), KeyframeType::Discrete, 33));
__anona1e3a5af0302() 100 auto state2 = [&]() {
101 REQUIRE(model->rowCount() == 3);
102 REQUIRE(check_anim_identity(model));
103 REQUIRE(model->hasKeyframe(GenTime(1.1)));
104 REQUIRE(model->hasKeyframe(GenTime(12.6)));
105 bool ok;
106 auto k = model->getKeyframe(GenTime(1.1), &ok);
107 REQUIRE(ok);
108 auto k0 = model->getKeyframe(GenTime(0), &ok);
109 REQUIRE(ok);
110 auto kk = model->getKeyframe(GenTime(12.6), &ok);
111 REQUIRE(ok);
112 auto k1 = model->getClosestKeyframe(GenTime(0.655555), &ok);
113 REQUIRE(ok);
114 REQUIRE(k1 == k);
115 auto k2 = model->getNextKeyframe(GenTime(0.5), &ok);
116 REQUIRE(ok);
117 REQUIRE(k2 == k);
118 auto k3 = model->getPrevKeyframe(GenTime(0.5), &ok);
119 REQUIRE(ok);
120 REQUIRE(k3 == k0);
121 auto k4 = model->getPrevKeyframe(GenTime(10), &ok);
122 REQUIRE(ok);
123 REQUIRE(k4 == k);
124 auto k5 = model->getNextKeyframe(GenTime(10), &ok);
125 REQUIRE(ok);
126 REQUIRE(k5 == kk);
127 };
128 state2();
129
130 undoStack->undo();
131 state1();
132 undoStack->undo();
133 state0();
134 undoStack->redo();
135 state1();
136 undoStack->redo();
137 state2();
138
139 REQUIRE(model->removeKeyframe(GenTime(1.1)));
__anona1e3a5af0402() 140 auto state3 = [&]() {
141 REQUIRE(model->rowCount() == 2);
142 REQUIRE(check_anim_identity(model));
143 REQUIRE(model->hasKeyframe(GenTime(12.6)));
144 bool ok;
145 model->getKeyframe(GenTime(1.1), &ok);
146 REQUIRE_FALSE(ok);
147 auto k0 = model->getKeyframe(GenTime(0), &ok);
148 REQUIRE(ok);
149 auto kk = model->getKeyframe(GenTime(12.6), &ok);
150 REQUIRE(ok);
151 auto k1 = model->getClosestKeyframe(GenTime(0.655555), &ok);
152 REQUIRE(ok);
153 REQUIRE(k1 == k0);
154 auto k2 = model->getNextKeyframe(GenTime(0.5), &ok);
155 REQUIRE(ok);
156 REQUIRE(k2 == kk);
157 auto k3 = model->getPrevKeyframe(GenTime(0.5), &ok);
158 REQUIRE(ok);
159 REQUIRE(k3 == k0);
160 auto k4 = model->getPrevKeyframe(GenTime(10), &ok);
161 REQUIRE(ok);
162 REQUIRE(k4 == k0);
163 auto k5 = model->getNextKeyframe(GenTime(10), &ok);
164 REQUIRE(ok);
165 REQUIRE(k5 == kk);
166 };
167 state3();
168
169 undoStack->undo();
170 state2();
171 undoStack->undo();
172 state1();
173 undoStack->undo();
174 state0();
175 undoStack->redo();
176 state1();
177 undoStack->redo();
178 state2();
179 undoStack->redo();
180 state3();
181
182 REQUIRE(model->removeAllKeyframes());
183 state0();
184 REQUIRE(model->removeAllKeyframes());
185 state0();
186 undoStack->undo();
187 state0();
188 undoStack->undo();
189 state3();
190 undoStack->redo();
191 state0();
192 }
193
194 SECTION("Move keyframes + undo")
195 {
__anona1e3a5af0502() 196 auto state0 = [&]() {
197 REQUIRE(model->rowCount() == 1);
198 REQUIRE(check_anim_identity(model));
199 };
200 state0();
201
202 REQUIRE(model->addKeyframe(GenTime(1.1), KeyframeType::Linear, 42));
__anona1e3a5af0602(double pos) 203 auto state1 = [&](double pos) {
204 REQUIRE(model->rowCount() == 2);
205 REQUIRE(check_anim_identity(model));
206 REQUIRE(model->hasKeyframe(GenTime(pos)));
207 bool ok;
208 auto k = model->getKeyframe(GenTime(pos), &ok);
209 REQUIRE(ok);
210 auto k0 = model->getKeyframe(GenTime(0), &ok);
211 REQUIRE(ok);
212 auto k1 = model->getClosestKeyframe(GenTime(pos + 10), &ok);
213 REQUIRE(ok);
214 REQUIRE(k1 == k);
215 auto k2 = model->getNextKeyframe(GenTime(pos - 0.3), &ok);
216 REQUIRE(ok);
217 REQUIRE(k2 == k);
218 auto k3 = model->getPrevKeyframe(GenTime(pos - 0.3), &ok);
219 REQUIRE(ok);
220 REQUIRE(k3 == k0);
221 auto k4 = model->getPrevKeyframe(GenTime(pos + 0.3), &ok);
222 REQUIRE(ok);
223 REQUIRE(k4 == k);
224 model->getNextKeyframe(GenTime(pos + 0.3), &ok);
225 REQUIRE_FALSE(ok);
226 };
227 state1(1.1);
228
229 REQUIRE(model->moveKeyframe(GenTime(1.1), GenTime(2.6), -1, true));
230 state1(2.6);
231
232 undoStack->undo();
233 state1(1.1);
234 undoStack->redo();
235 state1(2.6);
236
237 REQUIRE(model->moveKeyframe(GenTime(2.6), GenTime(6.1), -1, true));
238 state1(6.1);
239
240 undoStack->undo();
241 state1(2.6);
242 undoStack->undo();
243 state1(1.1);
244 undoStack->redo();
245 state1(2.6);
246 undoStack->redo();
247 state1(6.1);
248
249 REQUIRE(model->addKeyframe(GenTime(12.6), KeyframeType::Discrete, 33));
250 // Moving a keyframe past another one another will move it 1 frame before or after.
251 REQUIRE(model->moveKeyframe(GenTime(6.1), GenTime(14), -1, true));
252 bool ok;
253 // There should be no keyframe after 12.6
254 model->getNextKeyframe(GenTime(12.6), &ok);
255 REQUIRE_FALSE(ok);
256 undoStack->undo();
257 undoStack->undo();
258 state1(6.1);
259 }
260 pCore->m_projectManager = nullptr;
261 }
262