/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. All rights reserved. * Copyright (C) 2013 Frederik Heber. All rights reserved. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * ActionSequenceUnitTest.cpp * * Created on: Dec 17, 2009 * Author: crueger */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "ActionSequenceUnitTest.hpp" #include "Actions/Action.hpp" #include "Actions/ActionQueue.hpp" #include "Actions/ActionSequence.hpp" #include "Actions/MakroAction.hpp" #include "stubs/DummyUI.hpp" using namespace MoleCuilder; #ifdef HAVE_TESTRUNNER #include "UnitTestMain.hpp" #endif /*HAVE_TESTRUNNER*/ /********************************************** Test classes **************************************/ // Registers the fixture into the 'registry' CPPUNIT_TEST_SUITE_REGISTRATION( ActionSequenceTest ); /* some neccessary stubs for tests */ class canUndoActionStub : public Action { public: canUndoActionStub(const ActionTrait &_trait): Action(_trait){} virtual ~canUndoActionStub(){} virtual Dialog* fillDialog(Dialog *dialog){ ASSERT(dialog,"No Dialog given when filling action dialog"); return dialog; } virtual ActionState::ptr performCall(){ return Action::success; } virtual ActionState::ptr performUndo(ActionState::ptr){ return Action::success; } virtual ActionState::ptr performRedo(ActionState::ptr){ return Action::success; } virtual bool canUndo(){ return true; } virtual bool shouldUndo(){ return true; } Action* clone(enum QueryOptions flag = Interactive) const { return new canUndoActionStub(Traits); } void prepare(enum QueryOptions flag = Interactive) {} void outputAsCLI(std::ostream &ost) const {} void outputAsPython(std::ostream &ost, const std::string &prefix) const {} void setOptionValue(const std::string &_token, const std::string &_value) {} }; class cannotUndoActionStub : public Action { public: cannotUndoActionStub(const ActionTrait &_trait) : Action(_trait){} virtual ~cannotUndoActionStub(){} virtual Dialog* fillDialog(Dialog *dialog){ ASSERT(dialog,"No Dialog given when filling action dialog"); return dialog; } virtual ActionState::ptr performCall(){ return Action::success; } virtual ActionState::ptr performUndo(ActionState::ptr){ return Action::success; } virtual ActionState::ptr performRedo(ActionState::ptr){ return Action::success; } virtual bool canUndo(){ return false; } virtual bool shouldUndo(){ return true; } Action* clone(enum QueryOptions flag = Interactive) const { return new cannotUndoActionStub(Traits); } void prepare(enum QueryOptions flag = Interactive) {} void outputAsCLI(std::ostream &ost) const {} void outputAsPython(std::ostream &ost, const std::string &prefix) const {} void setOptionValue(const std::string &_token, const std::string &_value) {} }; class wasCalledActionStub : public Action { public: wasCalledActionStub(const ActionTrait &_trait) : Action(_trait), called(false) {} virtual ~wasCalledActionStub(){} virtual Dialog* fillDialog(Dialog *dialog){ return dialog; } virtual ActionState::ptr performCall(){ called = true; return Action::success; } virtual ActionState::ptr performUndo(ActionState::ptr){ called = false; return Action::success; } virtual ActionState::ptr performRedo(ActionState::ptr){ called = true; return Action::success; } virtual bool canUndo(){ return true; } virtual bool shouldUndo(){ return true; } Action* clone(enum QueryOptions flag = Interactive) const { return new wasCalledActionStub(Traits); } void prepare(enum QueryOptions flag = Interactive) {} void outputAsCLI(std::ostream &ost) const {} void outputAsPython(std::ostream &ost, const std::string &prefix) const {} void setOptionValue(const std::string &_token, const std::string &_value) {} bool wasCalled(){ return called; } private: bool called; }; void ActionSequenceTest::setUp(){ hasDescriptor = false; // setup ActionHistory ActionQueue::getInstance(); // TODO: find a way to really reset the factory to a clean state in tear-down if(!hasDescriptor){ UIFactory::registerFactory(new DummyUIFactory::description()); hasDescriptor = true; } UIFactory::makeUserInterface("Dummy"); // create some necessary stubs used in this test ActionTrait canUndoTrait("canUndoActionStub"); ActionTrait cannotUndoTrait("cannotUndoActionStub"); positive1 = new canUndoActionStub(canUndoTrait); positive2 = new canUndoActionStub(canUndoTrait); negative1 = new cannotUndoActionStub(cannotUndoTrait); negative2 = new cannotUndoActionStub(cannotUndoTrait); ActionTrait wasCalledTrait("wasCalledActionStub"); shouldCall1 = new wasCalledActionStub(wasCalledTrait); shouldCall2 = new wasCalledActionStub(wasCalledTrait); shouldNotCall1 = new wasCalledActionStub(wasCalledTrait); shouldNotCall2 = new wasCalledActionStub(wasCalledTrait); } void ActionSequenceTest::tearDown(){ delete positive1; delete positive2; delete negative1; delete negative2; delete shouldCall1; delete shouldCall2; delete shouldNotCall1; delete shouldNotCall2; ActionQueue::purgeInstance(); { UIFactory::purgeInstance(); hasDescriptor = false; } } void ActionSequenceTest::canUndoTest(){ // first section: { // test some combinations { ActionSequence *sequence = new ActionSequence(); sequence->addAction(positive1->clone()); sequence->addAction(positive2->clone()); CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() ); delete sequence; } { ActionSequence *sequence = new ActionSequence(); sequence->addAction(positive1->clone()); sequence->addAction(negative2->clone()); CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() ); delete sequence; } { ActionSequence *sequence = new ActionSequence(); sequence->addAction(negative1->clone()); sequence->addAction(positive2->clone()); CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() ); delete sequence; } { ActionSequence *sequence = new ActionSequence(); sequence->addAction(negative1->clone()); sequence->addAction(negative2->clone()); CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() ); delete sequence; } } // second section: { // empty sequence can be undone ActionSequence *sequence = new ActionSequence(); CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() ); // if only a positive action is contained it can be undone sequence->addAction(positive1->clone()); CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() ); // the single negative action should block the process sequence->addAction(negative1->clone()); CPPUNIT_ASSERT_EQUAL( false, sequence->canUndo() ); // after removing the negative action all is well again delete sequence->removeLastAction(); CPPUNIT_ASSERT_EQUAL( true, sequence->canUndo() ); delete sequence; } } void ActionSequenceTest::doesCallTest(){ ActionSequence *sequence = new ActionSequence(); sequence->addAction(shouldCall1->clone()); sequence->addAction(shouldCall2->clone()); sequence->addAction(shouldNotCall1->clone()); sequence->addAction(shouldNotCall2->clone()); delete sequence->removeLastAction(); delete sequence->removeLastAction(); sequence->callAll(); ActionSequence::actionSet::const_iterator iter = sequence->actions.begin(); CPPUNIT_ASSERT_EQUAL(true,dynamic_cast(*iter++)->wasCalled()); CPPUNIT_ASSERT_EQUAL(true,dynamic_cast(*iter++)->wasCalled()); CPPUNIT_ASSERT( iter == sequence->actions.end() ); delete sequence; } void ActionSequenceTest::doesUndoTest(){ ActionSequence *sequence = new ActionSequence(); ActionTrait wasCalledTrait("wasCalledActionStub"); sequence->addAction(new wasCalledActionStub(wasCalledTrait)); sequence->addAction(new wasCalledActionStub(wasCalledTrait)); ActionTrait MakroTrait("Test MakroAction"); MakroAction act(MakroTrait,*sequence); // wasCalledActionStub *wasCalled1 = // static_cast(act.actions.actions.front()); // wasCalledActionStub *wasCalled2 = // static_cast(act.actions.actions.back()); act.call(); CPPUNIT_ASSERT_EQUAL(true, static_cast(act.actions.actions.front())->wasCalled()); CPPUNIT_ASSERT_EQUAL(true, static_cast(act.actions.actions.back())->wasCalled()); ActionQueue::getInstance().undoLast(); CPPUNIT_ASSERT_EQUAL(false, static_cast(act.actions.actions.front())->wasCalled()); CPPUNIT_ASSERT_EQUAL(false, static_cast(act.actions.actions.back())->wasCalled()); delete sequence; }