File indexing completed on 2024-05-19 08:47:54
0001 /*************************************************************************** 0002 * Copyright (C) 2013-2014 by Linuxstopmotion contributors; * 0003 * see the AUTHORS file for details. * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 0019 ***************************************************************************/ 0020 0021 #include "tstopmotionundo.h" 0022 0023 #include "testundo.h" 0024 #include "testhome.h" 0025 #include "fakefiles.h" 0026 #include "oomtestutil.h" 0027 #include "hash.h" 0028 #include "src/domain/undo/addallcommands.h" 0029 #include "src/domain/undo/executor.h" 0030 #include "src/domain/animation/animation.h" 0031 #include "src/domain/animation/scenevector.h" 0032 #include "src/domain/animation/workspacefile.h" 0033 #include "src/domain/filenamevisitor.h" 0034 #include "src/presentation/frontends/frontend.h" 0035 #include "src/technical/stringiterator.h" 0036 #include "src/foundation/stringwriter.h" 0037 0038 #include <QtTest/QtTest> 0039 0040 0041 #include <algorithm> 0042 #include <vector> 0043 #include <string> 0044 #include <stdio.h> 0045 #include <string.h> 0046 #include <unistd.h> 0047 0048 class UiException; 0049 0050 void getHashes(std::vector<Hash>& out, const char* filenameTemplate) { 0051 std::string filenameStr(filenameTemplate); 0052 char* filename = &filenameStr[0]; 0053 char* digitPtr = strchr(filename, '?'); 0054 for (int i = 0; i != 10; ++i) { 0055 *digitPtr = '0' + i; 0056 if (0 != access(filename, F_OK)) 0057 return; 0058 out.resize(i + 1); 0059 Hash h; 0060 FILE* f = fopen(filename, "r"); 0061 h.add(f); 0062 fclose(f); 0063 out[i] = h; 0064 } 0065 } 0066 0067 class MockFrontend : public Frontend { 0068 public: 0069 ~MockFrontend() { 0070 } 0071 int run(int, char **) { 0072 return 0; 0073 } 0074 void showProgress(ProgressMessage, int) { 0075 } 0076 void hideProgress() { 0077 } 0078 void updateProgress(int) { 0079 } 0080 bool isOperationAborted() { 0081 return false; 0082 } 0083 void processEvents() { 0084 } 0085 void reportError(const char *, int) { 0086 } 0087 bool askQuestion(Question) { 0088 return true; 0089 } 0090 int runExternalCommand(const char *) { 0091 return 0; 0092 } 0093 void reportWarning(const char *) { 0094 } 0095 void handleException(UiException&) { 0096 } 0097 }; 0098 0099 TestStopmotionUndo::TestStopmotionUndo() : anim(0), mockFrontend(0), 0100 sv(0), ex(0), mfs(0), animTester(0) { 0101 sv = new SceneVector(); 0102 ex = makeAnimationCommandExecutor(*sv); 0103 mfs = new RealOggEmptyJpg(); 0104 testEnvFs = new TestHome(); 0105 mockFrontend = new MockFrontend(); 0106 loadOomTestUtil(); 0107 } 0108 0109 TestStopmotionUndo::~TestStopmotionUndo() { 0110 wrapFileSystem(0); 0111 delete mockFrontend; 0112 delete testEnvFs; 0113 delete mfs; 0114 delete ex; 0115 delete sv; 0116 } 0117 0118 class SceneVectorTestHelper : public ModelTestHelper { 0119 SceneVector& sv; 0120 class HashingFileNameVisitor : public FileNameVisitor { 0121 Hash h; 0122 public: 0123 HashingFileNameVisitor(SceneVector& sv) { 0124 sv.accept(*this); 0125 } 0126 Hash get() const { 0127 return h; 0128 } 0129 void visitImage(const char* s) { 0130 h.add(""); 0131 h.add(s); 0132 } 0133 void visitSound(const char* s) { 0134 h.add(s); 0135 } 0136 void reportNewScene() { 0137 h.add(""); 0138 } 0139 }; 0140 class DumpingFileNameVisitor : public FileNameVisitor { 0141 StringWriter dump; 0142 public: 0143 DumpingFileNameVisitor(SceneVector& sv) { 0144 sv.accept(*this); 0145 } 0146 const char* get() const { 0147 return dump.result(); 0148 } 0149 void visitImage(const char* s) { 0150 dump.writeIdentifier("frame:"); 0151 dump.writeChar('\n'); 0152 dump.writeString(s); 0153 dump.writeChar('\n'); 0154 } 0155 void visitSound(const char* s) { 0156 dump.writeString(s); 0157 dump.writeChar('\n'); 0158 } 0159 void reportNewScene() { 0160 dump.writeIdentifier("scene:"); 0161 dump.writeChar('\n'); 0162 } 0163 }; 0164 public: 0165 SceneVectorTestHelper(SceneVector& s) : sv(s) { 0166 } 0167 ~SceneVectorTestHelper() { 0168 } 0169 void resetModel(Executor&) { 0170 sv.clear(); 0171 } 0172 Hash hashModel(const Executor&) { 0173 HashingFileNameVisitor v(sv); 0174 return v.get(); 0175 } 0176 void dumpModel(std::string& out, const Executor&) { 0177 DumpingFileNameVisitor v(sv); 0178 out = v.get(); 0179 } 0180 }; 0181 0182 void TestStopmotionUndo::stopmotionCommandsInvertCorrectly() { 0183 SceneVectorTestHelper helper(*sv); 0184 wrapFileSystem(0); 0185 wrapFileSystem(testEnvFs); 0186 wrapFileSystem(mfs); 0187 testUndo(*ex, helper); 0188 wrapFileSystem(0); 0189 } 0190 0191 class AnimTester { 0192 const Animation* anim; 0193 std::vector<Hash> imageHashes; 0194 std::vector<Hash> soundHashes; 0195 int scene; 0196 int frame; 0197 int sound; 0198 int soundCount; 0199 bool seenFrame; 0200 void frameEnd() { 0201 QCOMPARE(sound, anim->soundCount(scene, frame)); 0202 sound = 0; 0203 ++frame; 0204 } 0205 void sceneEnd() { 0206 if (seenFrame) { 0207 frameEnd(); 0208 seenFrame = false; 0209 } 0210 QCOMPARE(frame, anim->frameCount(scene)); 0211 frame = 0; 0212 ++scene; 0213 } 0214 void testFrame(int n) { 0215 seenFrame = true; 0216 const char* path = anim->getImagePath(scene, frame); 0217 FILE* fh = fopen(path, "r"); 0218 if (!fh) { 0219 char msg[500]; 0220 snprintf(msg, sizeof(msg), "Failed to open file %s", path); 0221 QFAIL(msg); 0222 } 0223 Hash h; 0224 h.add(fh); 0225 fclose(fh); 0226 QCOMPARE(imageHashes[n], h); 0227 } 0228 void testSound(int n) { 0229 const char* path = anim->getSoundPath(scene, frame, sound - 1); 0230 FILE* fh = fopen(path, "r"); 0231 Hash h; 0232 h.add(fh); 0233 fclose(fh); 0234 QCOMPARE(soundHashes[n], h); 0235 } 0236 public: 0237 AnimTester(const Animation* a) : anim(a), scene(0), 0238 frame(0), sound(0), soundCount(0), seenFrame(false) { 0239 getHashes(imageHashes, "resources/frame?.jpg"); 0240 getHashes(soundHashes, "resources/sound?.ogg"); 0241 } 0242 ~AnimTester() { 0243 } 0244 void test(const char* expected) { 0245 scene = 0; 0246 frame = 0; 0247 sound = 0; 0248 soundCount = 0; 0249 seenFrame = false; 0250 for (; *expected; ++expected) { 0251 switch (*expected) { 0252 case ';': 0253 sceneEnd(); 0254 break; 0255 case ',': 0256 frameEnd(); 0257 break; 0258 case '/': 0259 ++sound; 0260 break; 0261 default: 0262 int n = *expected - '0'; 0263 QVERIFY(0 <= n && n <= 9); 0264 if (sound == 0) { 0265 testFrame(n); 0266 } else { 0267 testSound(n); 0268 ++soundCount; 0269 } 0270 break; 0271 } 0272 } 0273 sceneEnd(); 0274 QCOMPARE(soundCount, anim->soundCount()); 0275 } 0276 }; 0277 0278 class StringContainer : public StringIterator { 0279 typedef std::vector<const char*> vect_t; 0280 vect_t v; 0281 vect_t::size_type i; 0282 public: 0283 StringContainer() : i(0) { 0284 } 0285 ~StringContainer() { 0286 } 0287 int count() { 0288 return static_cast<int>(v.size()) - i; 0289 } 0290 bool atEnd() const { 0291 return v.size() == i; 0292 } 0293 const char* get() const { 0294 return v[i]; 0295 } 0296 void next() { 0297 ++i; 0298 } 0299 void add(const char* s) { 0300 v.insert(v.end(), s); 0301 } 0302 }; 0303 0304 void TestStopmotionUndo::setUpAnim() { 0305 wrapFileSystem(0); 0306 wrapFileSystem(testEnvFs); 0307 WorkspaceFile::clear(); 0308 delete anim; 0309 anim = 0; 0310 delete animTester; 0311 animTester = 0; 0312 anim = new Animation(); 0313 anim->registerFrontend(mockFrontend); 0314 animTester = new AnimTester(anim); 0315 anim->newScene(0); 0316 anim->newScene(1); 0317 anim->newScene(2); 0318 StringContainer frames0; 0319 frames0.add("resources/frame0.jpg"); 0320 frames0.add("resources/frame1.jpg"); 0321 anim->addFrames(0, 0, frames0); 0322 StringContainer frames1; 0323 frames1.add("resources/frame2.jpg"); 0324 anim->addFrames(1, 0, frames1); 0325 StringContainer frames2; 0326 frames2.add("resources/frame3.jpg"); 0327 frames2.add("resources/frame4.jpg"); 0328 anim->addFrames(2, 0, frames2); 0329 anim->addSound(2, 0, "resources/sound0.ogg"); 0330 anim->addSound(2, 1, "resources/sound1.ogg"); 0331 } 0332 0333 void TestStopmotionUndo::addFrames() { 0334 setUpAnim(); 0335 StringContainer newFrames; 0336 newFrames.add("resources/frame5.jpg"); 0337 newFrames.add("resources/frame6.jpg"); 0338 anim->addFrames(0, 1, newFrames); 0339 animTester->test("0,5,6,1;2;3/0,4/1"); 0340 } 0341 0342 void TestStopmotionUndo::removeFrames() { 0343 setUpAnim(); 0344 anim->removeFrames(0, 0, 1); 0345 animTester->test("1;2;3/0,4/1"); 0346 anim->removeFrames(2, 1, 1); 0347 animTester->test("1;2;3/0"); 0348 } 0349 0350 void TestStopmotionUndo::moveFrames() { 0351 setUpAnim(); 0352 anim->moveFrames(2, 0, 2, 0, 1); 0353 animTester->test("0,3/0,4/1,1;2;"); 0354 } 0355 0356 void TestStopmotionUndo::setImagePath() { 0357 setUpAnim(); 0358 std::string oldPath = anim->getImagePath(2, 1); 0359 anim->setImagePath(2, 1, "resources/frame5.jpg"); 0360 QVERIFY(oldPath != anim->getImagePath(2, 1)); 0361 animTester->test("0,1;2;3/0,5/1"); 0362 } 0363 0364 void TestStopmotionUndo::duplicateImage() { 0365 setUpAnim(); 0366 std::string oldPath = anim->getImagePath(2, 1); 0367 anim->duplicateImage(2, 1); 0368 QVERIFY(oldPath != anim->getImagePath(2, 1)); 0369 animTester->test("0,1;2;3/0,4/1"); 0370 } 0371 0372 void TestStopmotionUndo::addSound() { 0373 setUpAnim(); 0374 anim->addSound(2, 0, "resources/sound2.ogg"); 0375 animTester->test("0,1;2;3/0/2,4/1"); 0376 } 0377 0378 void TestStopmotionUndo::removeSound() { 0379 setUpAnim(); 0380 anim->addSound(2, 0, "resources/sound2.ogg"); 0381 anim->removeSound(2, 0, 0); 0382 animTester->test("0,1;2;3/2,4/1"); 0383 anim->removeSound(2, 0, 0); 0384 animTester->test("0,1;2;3,4/1"); 0385 } 0386 0387 void TestStopmotionUndo::setSoundName() { 0388 setUpAnim(); 0389 anim->setSoundName(2, 0, 0, "tommy"); 0390 animTester->test("0,1;2;3/0,4/1"); 0391 QCOMPARE(anim->getSoundName(2, 0, 0), "tommy"); 0392 } 0393 0394 void TestStopmotionUndo::newScene() { 0395 setUpAnim(); 0396 anim->newScene(1); 0397 animTester->test("0,1;;2;3/0,4/1"); 0398 } 0399 0400 void TestStopmotionUndo::removeScene() { 0401 setUpAnim(); 0402 anim->removeScene(1); 0403 animTester->test("0,1;3/0,4/1"); 0404 anim->removeScene(1); 0405 animTester->test("0,1"); 0406 } 0407 0408 void TestStopmotionUndo::moveScene() { 0409 setUpAnim(); 0410 anim->moveScene(2, 0); 0411 animTester->test("3/0,4/1;0,1;2"); 0412 }