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