File indexing completed on 2024-04-28 16:08:41

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 }