/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "qstatemachine.h" #include "qstate.h" #include "qhistorystate.h" #include "qkeyeventtransition.h" #include "qmouseeventtransition.h" #include "private/qstatemachine_p.h" // Will try to wait for the condition while allowing event processing #define QTRY_COMPARE(__expr, __expected) \ do { \ const int __step = 50; \ const int __timeout = 5000; \ if ((__expr) != (__expected)) { \ QTest::qWait(0); \ } \ for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ QTest::qWait(__step); \ } \ QCOMPARE(__expr, __expected); \ } while(0) //TESTED_CLASS= //TESTED_FILES= static int globalTick; // Run exec for a maximum of TIMEOUT msecs #define QCOREAPPLICATION_EXEC(TIMEOUT) \ { \ QTimer timer; \ timer.setSingleShot(true); \ timer.setInterval(TIMEOUT); \ timer.start(); \ connect(&timer, SIGNAL(timeout()), QCoreApplication::instance(), SLOT(quit())); \ QCoreApplication::exec(); \ } class tst_QStateMachine : public QObject { Q_OBJECT public: tst_QStateMachine(); virtual ~tst_QStateMachine(); private slots: void init(); void cleanup(); void rootState(); void addAndRemoveState(); void stateEntryAndExit(); void postEvent(); void stateFinished(); void parallelStates(); void allSourceToTargetConfigurations(); void signalTransitions(); void eventTransitions(); void historyStates(); void transitionToRootState(); void transitionEntersParent(); void defaultErrorState(); void customGlobalErrorState(); void customLocalErrorStateInBrokenState(); void customLocalErrorStateInOtherState(); void customLocalErrorStateInParentOfBrokenState(); void customLocalErrorStateOverridesParent(); void errorStateHasChildren(); void errorStateHasErrors(); void errorStateIsRootState(); void errorStateEntersParentFirst(); void customErrorStateIsNull(); void clearError(); void historyStateHasNowhereToGo(); void historyStateAsInitialState(); void brokenStateIsNeverEntered(); void customErrorStateNotInGraph(); void transitionToStateNotInGraph(); void restoreProperties(); void defaultGlobalRestorePolicy(); void globalRestorePolicySetToRestore(); void globalRestorePolicySetToDoNotRestore(); //void restorePolicyNotInherited(); //void mixedRestoreProperties(); //void setRestorePolicyToDoNotRestore(); //void setGlobalRestorePolicyToGlobalRestore(); //void restorePolicyOnChildState(); void transitionWithParent(); void parallelStateTransition(); void simpleAnimation(); void twoAnimations(); void twoAnimatedTransitions(); void playAnimationTwice(); void nestedTargetStateForAnimation(); void animatedGlobalRestoreProperty(); void specificTargetValueOfAnimation(); void addDefaultAnimation(); void addDefaultAnimationWithUnusedAnimation(); void addDefaultAnimationForSource(); void addDefaultAnimationForTarget(); void removeDefaultAnimation(); void removeDefaultAnimationForSource(); void removeDefaultAnimationForTarget(); void overrideDefaultAnimationWithSource(); void overrideDefaultAnimationWithTarget(); void overrideDefaultAnimationWithSpecific(); void overrideDefaultSourceAnimationWithSpecific(); void overrideDefaultTargetAnimationWithSpecific(); void overrideDefaultTargetAnimationWithSource(); }; tst_QStateMachine::tst_QStateMachine() { } tst_QStateMachine::~tst_QStateMachine() { } class TestState : public QState { public: enum Event { Entry, Exit }; TestState(QState *parent) : QState(parent) {} QList > events; protected: virtual void onEntry() { events.append(qMakePair(globalTick++, Entry)); } virtual void onExit() { events.append(qMakePair(globalTick++, Exit)); } }; class TestTransition : public QAbstractTransition { public: TestTransition(QAbstractState *target) : QAbstractTransition(QList() << target) {} QList triggers; protected: virtual bool eventTest(QEvent *) const { return true; } virtual void onTransition() { triggers.append(globalTick++); } }; static QtMsgType s_msgType; static QByteArray s_msg; static bool s_countWarnings; static QtMsgHandler s_oldHandler; static void defaultErrorStateTestMessageHandler(QtMsgType type, const char *msg) { s_msgType = type; s_msg = msg; if (s_countWarnings) s_oldHandler(type, msg); } void tst_QStateMachine::init() { s_msg = QByteArray(); s_msgType = QtDebugMsg; s_countWarnings = true; s_oldHandler = qInstallMsgHandler(defaultErrorStateTestMessageHandler); } void tst_QStateMachine::cleanup() { qInstallMsgHandler(s_oldHandler); } class EventTransition : public QAbstractTransition { public: EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0) : QAbstractTransition(QList() << target, parent), m_type(type) {} protected: virtual bool eventTest(QEvent *e) const { return (e->type() == m_type); } virtual void onTransition() {} private: QEvent::Type m_type; }; void tst_QStateMachine::transitionToRootState() { QStateMachine machine; QState *initialState = new QState(); machine.addState(initialState); machine.setInitialState(initialState); initialState->addTransition(new EventTransition(QEvent::User, machine.rootState())); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 2); QVERIFY(machine.configuration().contains(initialState)); QVERIFY(machine.configuration().contains(machine.rootState())); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 2); QVERIFY(machine.configuration().contains(initialState)); QVERIFY(machine.configuration().contains(machine.rootState())); } void tst_QStateMachine::transitionEntersParent() { QStateMachine machine; QObject *entryController = new QObject(&machine); entryController->setObjectName("entryController"); entryController->setProperty("greatGrandParentEntered", false); entryController->setProperty("grandParentEntered", false); entryController->setProperty("parentEntered", false); entryController->setProperty("stateEntered", false); QState *greatGrandParent = new QState(); greatGrandParent->setObjectName("grandParent"); greatGrandParent->assignProperty(entryController, "greatGrandParentEntered", true); machine.addState(greatGrandParent); machine.setInitialState(greatGrandParent); QState *grandParent = new QState(greatGrandParent); grandParent->setObjectName("grandParent"); grandParent->assignProperty(entryController, "grandParentEntered", true); QState *parent = new QState(grandParent); parent->setObjectName("parent"); parent->assignProperty(entryController, "parentEntered", true); QState *state = new QState(parent); state->setObjectName("state"); state->assignProperty(entryController, "stateEntered", true); QState *initialStateOfGreatGrandParent = new QState(greatGrandParent); initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent"); greatGrandParent->setInitialState(initialStateOfGreatGrandParent); initialStateOfGreatGrandParent->addTransition(new EventTransition(QEvent::User, state)); machine.start(); QCoreApplication::processEvents(); QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), true); QCOMPARE(entryController->property("grandParentEntered").toBool(), false); QCOMPARE(entryController->property("parentEntered").toBool(), false); QCOMPARE(entryController->property("stateEntered").toBool(), false); QCOMPARE(machine.configuration().count(), 2); QVERIFY(machine.configuration().contains(greatGrandParent)); QVERIFY(machine.configuration().contains(initialStateOfGreatGrandParent)); entryController->setProperty("greatGrandParentEntered", false); entryController->setProperty("grandParentEntered", false); entryController->setProperty("parentEntered", false); entryController->setProperty("stateEntered", false); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), false); QCOMPARE(entryController->property("grandParentEntered").toBool(), true); QCOMPARE(entryController->property("parentEntered").toBool(), true); QCOMPARE(entryController->property("stateEntered").toBool(), true); QCOMPARE(machine.configuration().count(), 4); QVERIFY(machine.configuration().contains(greatGrandParent)); QVERIFY(machine.configuration().contains(grandParent)); QVERIFY(machine.configuration().contains(parent)); QVERIFY(machine.configuration().contains(state)); } void tst_QStateMachine::defaultErrorState() { s_countWarnings = false; // we expect warnings here QStateMachine machine; QVERIFY(machine.errorState() != 0); QState *brokenState = new QState(); brokenState->setObjectName("MyInitialState"); machine.addState(brokenState); machine.setInitialState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); // initialState has no initial state machine.start(); QCoreApplication::processEvents(); QCOMPARE(s_msgType, QtWarningMsg); QCOMPARE(QString::fromLatin1(s_msg.data()), QString::fromLatin1("Unrecoverable error detected in running state machine: Missing initial state in compound state 'MyInitialState'")); QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'MyInitialState'")); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(machine.errorState())); } class CustomErrorState: public QState { public: CustomErrorState(QStateMachine *machine, QState *parent = 0) : QState(parent), error(QStateMachine::NoError), m_machine(machine) { } void onEntry() { error = m_machine->error(); errorString = m_machine->errorString(); } QStateMachine::Error error; QString errorString; private: QStateMachine *m_machine; }; void tst_QStateMachine::customGlobalErrorState() { QStateMachine machine; CustomErrorState *customErrorState = new CustomErrorState(&machine); customErrorState->setObjectName("customErrorState"); machine.addState(customErrorState); machine.setErrorState(customErrorState); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); brokenState->setObjectName("brokenState"); machine.addState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.errorState(), customErrorState); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(initialState)); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(initialState)); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(customErrorState)); QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError); QCOMPARE(customErrorState->errorString, QString::fromLatin1("Missing initial state in compound state 'brokenState'")); QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'")); QVERIFY(s_msg.isEmpty()); QCOMPARE(s_msgType, QtDebugMsg); } void tst_QStateMachine::customLocalErrorStateInBrokenState() { QStateMachine machine; CustomErrorState *customErrorState = new CustomErrorState(&machine); machine.addState(customErrorState); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); brokenState->setObjectName("brokenState"); machine.addState(brokenState); brokenState->setErrorState(customErrorState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(customErrorState)); QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError); QVERIFY(s_msg.isEmpty()); } void tst_QStateMachine::customLocalErrorStateInOtherState() { s_countWarnings = false; QStateMachine machine; CustomErrorState *customErrorState = new CustomErrorState(&machine); machine.addState(customErrorState); QState *initialState = new QState(); initialState->setObjectName("initialState"); initialState->setErrorState(customErrorState); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); brokenState->setObjectName("brokenState"); machine.addState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(machine.errorState())); QCOMPARE(s_msgType, QtWarningMsg); } void tst_QStateMachine::customLocalErrorStateInParentOfBrokenState() { QStateMachine machine; CustomErrorState *customErrorState = new CustomErrorState(&machine); machine.addState(customErrorState); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *parentOfBrokenState = new QState(); machine.addState(parentOfBrokenState); parentOfBrokenState->setObjectName("parentOfBrokenState"); parentOfBrokenState->setErrorState(customErrorState); QState *brokenState = new QState(parentOfBrokenState); brokenState->setObjectName("brokenState"); parentOfBrokenState->setInitialState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(customErrorState)); QVERIFY(s_msg.isEmpty()); } void tst_QStateMachine::customLocalErrorStateOverridesParent() { QStateMachine machine; CustomErrorState *customErrorStateForParent = new CustomErrorState(&machine); machine.addState(customErrorStateForParent); CustomErrorState *customErrorStateForBrokenState = new CustomErrorState(&machine); machine.addState(customErrorStateForBrokenState); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *parentOfBrokenState = new QState(); machine.addState(parentOfBrokenState); parentOfBrokenState->setObjectName("parentOfBrokenState"); parentOfBrokenState->setErrorState(customErrorStateForParent); QState *brokenState = new QState(parentOfBrokenState); brokenState->setObjectName("brokenState"); brokenState->setErrorState(customErrorStateForBrokenState); parentOfBrokenState->setInitialState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(customErrorStateForBrokenState)); QCOMPARE(customErrorStateForBrokenState->error, QStateMachine::NoInitialStateError); QCOMPARE(customErrorStateForParent->error, QStateMachine::NoError); QVERIFY(s_msg.isEmpty()); } void tst_QStateMachine::errorStateHasChildren() { QStateMachine machine; CustomErrorState *customErrorState = new CustomErrorState(&machine); customErrorState->setObjectName("customErrorState"); machine.addState(customErrorState); machine.setErrorState(customErrorState); QState *childOfErrorState = new QState(customErrorState); childOfErrorState->setObjectName("childOfErrorState"); customErrorState->setInitialState(childOfErrorState); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); brokenState->setObjectName("brokenState"); machine.addState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 2); QVERIFY(machine.configuration().contains(customErrorState)); QVERIFY(machine.configuration().contains(childOfErrorState)); QVERIFY(s_msg.isEmpty()); } void tst_QStateMachine::errorStateHasErrors() { s_countWarnings = false; QStateMachine machine; CustomErrorState *customErrorState = new CustomErrorState(&machine); customErrorState->setObjectName("customErrorState"); machine.addState(customErrorState); QAbstractState *oldErrorState = machine.errorState(); machine.setErrorState(customErrorState); QState *childOfErrorState = new QState(customErrorState); childOfErrorState->setObjectName("childOfErrorState"); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); brokenState->setObjectName("brokenState"); machine.addState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(oldErrorState)); // Fall back to default QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'customErrorState'")); QCOMPARE(s_msgType, QtWarningMsg); } void tst_QStateMachine::errorStateIsRootState() { QStateMachine machine; machine.setErrorState(machine.rootState()); QState *initialState = new QState(); initialState->setObjectName("initialState"); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); brokenState->setObjectName("brokenState"); machine.addState(brokenState); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); QEXPECT_FAIL("", "Covered by transitionToRootState test. Remove this when that test passes", Continue); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(initialState)); } void tst_QStateMachine::errorStateEntersParentFirst() { QStateMachine machine; QObject *entryController = new QObject(&machine); entryController->setObjectName("entryController"); entryController->setProperty("greatGrandParentEntered", false); entryController->setProperty("grandParentEntered", false); entryController->setProperty("parentEntered", false); entryController->setProperty("errorStateEntered", false); QState *greatGrandParent = new QState(); greatGrandParent->setObjectName("greatGrandParent"); greatGrandParent->assignProperty(entryController, "greatGrandParentEntered", true); machine.addState(greatGrandParent); machine.setInitialState(greatGrandParent); QState *grandParent = new QState(greatGrandParent); grandParent->setObjectName("grandParent"); grandParent->assignProperty(entryController, "grandParentEntered", true); QState *parent = new QState(grandParent); parent->setObjectName("parent"); parent->assignProperty(entryController, "parentEntered", true); QState *errorState = new QState(parent); errorState->setObjectName("errorState"); errorState->assignProperty(entryController, "errorStateEntered", true); machine.setErrorState(errorState); QState *initialStateOfGreatGrandParent = new QState(greatGrandParent); initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent"); greatGrandParent->setInitialState(initialStateOfGreatGrandParent); QState *brokenState = new QState(greatGrandParent); brokenState->setObjectName("brokenState"); QState *childState = new QState(brokenState); childState->setObjectName("childState"); initialStateOfGreatGrandParent->addTransition(new EventTransition(QEvent::User, brokenState)); machine.start(); QCoreApplication::processEvents(); QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), true); QCOMPARE(entryController->property("grandParentEntered").toBool(), false); QCOMPARE(entryController->property("parentEntered").toBool(), false); QCOMPARE(entryController->property("errorStateEntered").toBool(), false); QCOMPARE(machine.configuration().count(), 2); QVERIFY(machine.configuration().contains(greatGrandParent)); QVERIFY(machine.configuration().contains(initialStateOfGreatGrandParent)); entryController->setProperty("greatGrandParentEntered", false); entryController->setProperty("grandParentEntered", false); entryController->setProperty("parentEntered", false); entryController->setProperty("errorStateEntered", false); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), false); QCOMPARE(entryController->property("grandParentEntered").toBool(), true); QCOMPARE(entryController->property("parentEntered").toBool(), true); QCOMPARE(entryController->property("errorStateEntered").toBool(), true); QCOMPARE(machine.configuration().count(), 4); QVERIFY(machine.configuration().contains(greatGrandParent)); QVERIFY(machine.configuration().contains(grandParent)); QVERIFY(machine.configuration().contains(parent)); QVERIFY(machine.configuration().contains(errorState)); } void tst_QStateMachine::customErrorStateIsNull() { s_countWarnings = false; QStateMachine machine; QAbstractState *oldErrorState = machine.errorState(); machine.rootState()->setErrorState(0); QState *initialState = new QState(); machine.addState(initialState); machine.setInitialState(initialState); QState *brokenState = new QState(); machine.addState(brokenState); new QState(brokenState); initialState->addTransition(new EventTransition(QEvent::User, brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(machine.errorState(), reinterpret_cast(0)); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(oldErrorState)); QCOMPARE(s_msgType, QtWarningMsg); } void tst_QStateMachine::clearError() { QStateMachine machine; machine.setErrorState(new QState(machine.rootState())); // avoid warnings QState *brokenState = new QState(machine.rootState()); brokenState->setObjectName("brokenState"); machine.setInitialState(brokenState); new QState(brokenState); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.error(), QStateMachine::NoInitialStateError); QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'")); machine.clearError(); QCOMPARE(machine.error(), QStateMachine::NoError); QVERIFY(machine.errorString().isEmpty()); } void tst_QStateMachine::historyStateAsInitialState() { QStateMachine machine; QHistoryState *hs = new QHistoryState(machine.rootState()); machine.setInitialState(hs); QState *s1 = new QState(machine.rootState()); hs->setDefaultState(s1); QState *s2 = new QState(machine.rootState()); QHistoryState *s2h = new QHistoryState(s2); s2->setInitialState(s2h); QState *s21 = new QState(s2); s2h->setDefaultState(s21); s1->addTransition(new EventTransition(QEvent::User, s2)); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 1); QVERIFY(machine.configuration().contains(s1)); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 2); QVERIFY(machine.configuration().contains(s2)); QVERIFY(machine.configuration().contains(s21)); } void tst_QStateMachine::historyStateHasNowhereToGo() { QStateMachine machine; QState *initialState = new QState(machine.rootState()); machine.setInitialState(initialState); machine.setErrorState(new QState(machine.rootState())); // avoid warnings QState *brokenState = new QState(machine.rootState()); brokenState->setObjectName("brokenState"); brokenState->setInitialState(new QState(brokenState)); QHistoryState *historyState = new QHistoryState(brokenState); historyState->setObjectName("historyState"); initialState->addTransition(new EventTransition(QEvent::User, historyState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(machine.errorState())); QCOMPARE(machine.error(), QStateMachine::NoDefaultStateInHistoryState); QCOMPARE(machine.errorString(), QString::fromLatin1("Missing transition from history state 'historyState'")); } void tst_QStateMachine::brokenStateIsNeverEntered() { QStateMachine machine; QObject *entryController = new QObject(); entryController->setProperty("brokenStateEntered", false); entryController->setProperty("childStateEntered", false); entryController->setProperty("errorStateEntered", false); QState *initialState = new QState(machine.rootState()); machine.setInitialState(initialState); QState *errorState = new QState(machine.rootState()); errorState->assignProperty(entryController, "errorStateEntered", true); machine.setErrorState(errorState); QState *brokenState = new QState(machine.rootState()); brokenState->assignProperty(entryController, "brokenStateEntered", true); brokenState->setObjectName("brokenState"); QState *childState = new QState(brokenState); childState->assignProperty(entryController, "childStateEntered", true); initialState->addTransition(new EventTransition(QEvent::User, brokenState)); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(entryController->property("errorStateEntered").toBool(), true); QCOMPARE(entryController->property("brokenStateEntered").toBool(), false); QCOMPARE(entryController->property("childStateEntered").toBool(), false); } void tst_QStateMachine::transitionToStateNotInGraph() { QSKIP("Hangs", SkipAll); s_countWarnings = false; QStateMachine machine; QState *initialState = new QState(machine.rootState()); initialState->setObjectName("initialState"); machine.setInitialState(initialState); QState *independentState = new QState(); initialState->addTransition(independentState); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(initialState)); } void tst_QStateMachine::customErrorStateNotInGraph() { s_countWarnings = false; QStateMachine machine; QState *errorState = new QState(); errorState->setObjectName("errorState"); machine.setErrorState(errorState); QVERIFY(errorState != machine.errorState()); QState *initialBrokenState = new QState(machine.rootState()); initialBrokenState->setObjectName("initialBrokenState"); machine.setInitialState(initialBrokenState); new QState(initialBrokenState); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(machine.errorState())); } void tst_QStateMachine::restoreProperties() { QObject *object = new QObject(); object->setProperty("a", 1); object->setProperty("b", 2); QStateMachine machine; machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); QState *S1 = new QState(); S1->setObjectName("S1"); S1->assignProperty(object, "a", 3); machine.addState(S1); QState *S2 = new QState(); S2->setObjectName("S2"); S2->assignProperty(object, "b", 5); machine.addState(S2); QState *S3 = new QState(); S3->setObjectName("S3"); machine.addState(S3); QFinalState *S4 = new QFinalState(); machine.addState(S4); S1->addTransition(new EventTransition(QEvent::User, S2)); S2->addTransition(new EventTransition(QEvent::User, S3)); S3->addTransition(S4); machine.setInitialState(S1); machine.start(); QCoreApplication::processEvents(); QCOMPARE(object->property("a").toInt(), 3); QCOMPARE(object->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(object->property("a").toInt(), 1); QCOMPARE(object->property("b").toInt(), 5); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(object->property("a").toInt(), 1); QCOMPARE(object->property("b").toInt(), 2); } void tst_QStateMachine::rootState() { QStateMachine machine; QVERIFY(machine.rootState() != 0); QVERIFY(qobject_cast(machine.rootState()) != 0); QCOMPARE(qobject_cast(machine.rootState())->parentState(), (QState*)0); QCOMPARE(machine.rootState()->parent(), (QObject*)&machine); QState *s1 = new QState(machine.rootState()); QCOMPARE(s1->parentState(), machine.rootState()); QState *s2 = new QState(); s2->setParent(&machine); QCOMPARE(s2->parentState(), machine.rootState()); } void tst_QStateMachine::addAndRemoveState() { QStateMachine machine; QCOMPARE(machine.states().size(), 1); // the error state QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: cannot add null state"); machine.addState(0); QState *s1 = new QState(); QCOMPARE(s1->parentState(), (QState*)0); machine.addState(s1); QCOMPARE(s1->parentState(), machine.rootState()); QCOMPARE(machine.states().size(), 2); QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); QCOMPARE(machine.states().at(1), (QAbstractState*)s1); QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine"); machine.addState(s1); QState *s2 = new QState(); QCOMPARE(s2->parentState(), (QState*)0); machine.addState(s2); QCOMPARE(s2->parentState(), machine.rootState()); QCOMPARE(machine.states().size(), 3); QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); QCOMPARE(machine.states().at(1), (QAbstractState*)s1); QCOMPARE(machine.states().at(2), (QAbstractState*)s2); QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine"); machine.addState(s2); machine.removeState(s1); QCOMPARE(s1->parentState(), (QState*)0); QCOMPARE(machine.states().size(), 2); QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); QCOMPARE(machine.states().at(1), (QAbstractState*)s2); machine.removeState(s2); QCOMPARE(s2->parentState(), (QState*)0); QCOMPARE(machine.states().size(), 1); QCOMPARE(machine.states().at(0), (QAbstractState*)machine.errorState()); QTest::ignoreMessage(QtWarningMsg, "QStateMachine::removeState: cannot remove null state"); machine.removeState(0); delete s1; delete s2; // ### how to deal with this? // machine.removeState(machine.errorState()); } void tst_QStateMachine::stateEntryAndExit() { // Two top-level states { QStateMachine machine; TestState *s1 = new TestState(machine.rootState()); TestState *s2 = new TestState(machine.rootState()); QFinalState *s3 = new QFinalState(machine.rootState()); TestTransition *t = new TestTransition(s2); s1->addTransition(t); s2->addTransition(s3); QSignalSpy startedSpy(&machine, SIGNAL(started())); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s1); QVERIFY(machine.configuration().isEmpty()); globalTick = 0; machine.start(); QTRY_COMPARE(startedSpy.count(), 1); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(s3)); // s1 is entered QCOMPARE(s1->events.count(), 2); QCOMPARE(s1->events.at(0).first, 0); QCOMPARE(s1->events.at(0).second, TestState::Entry); // s1 is exited QCOMPARE(s1->events.at(1).first, 1); QCOMPARE(s1->events.at(1).second, TestState::Exit); // t is triggered QCOMPARE(t->triggers.count(), 1); QCOMPARE(t->triggers.at(0), 2); // s2 is entered QCOMPARE(s2->events.count(), 2); QCOMPARE(s2->events.at(0).first, 3); QCOMPARE(s2->events.at(0).second, TestState::Entry); // s2 is exited QCOMPARE(s2->events.at(1).first, 4); QCOMPARE(s2->events.at(1).second, TestState::Exit); } // Two top-level states, one has two child states { QStateMachine machine; TestState *s1 = new TestState(machine.rootState()); TestState *s11 = new TestState(s1); TestState *s12 = new TestState(s1); TestState *s2 = new TestState(machine.rootState()); QFinalState *s3 = new QFinalState(machine.rootState()); s1->setInitialState(s11); TestTransition *t1 = new TestTransition(s12); s11->addTransition(t1); TestTransition *t2 = new TestTransition(s2); s12->addTransition(t2); s2->addTransition(s3); QSignalSpy startedSpy(&machine, SIGNAL(started())); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s1); globalTick = 0; machine.start(); QTRY_COMPARE(startedSpy.count(), 1); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(machine.configuration().count(), 1); QVERIFY(machine.configuration().contains(s3)); // s1 is entered QCOMPARE(s1->events.count(), 2); QCOMPARE(s1->events.at(0).first, 0); QCOMPARE(s1->events.at(0).second, TestState::Entry); // s11 is entered QCOMPARE(s11->events.count(), 2); QCOMPARE(s11->events.at(0).first, 1); QCOMPARE(s11->events.at(0).second, TestState::Entry); // s11 is exited QCOMPARE(s11->events.at(1).first, 2); QCOMPARE(s11->events.at(1).second, TestState::Exit); // t1 is triggered QCOMPARE(t1->triggers.count(), 1); QCOMPARE(t1->triggers.at(0), 3); // s12 is entered QCOMPARE(s12->events.count(), 2); QCOMPARE(s12->events.at(0).first, 4); QCOMPARE(s12->events.at(0).second, TestState::Entry); // s12 is exited QCOMPARE(s12->events.at(1).first, 5); QCOMPARE(s12->events.at(1).second, TestState::Exit); // s1 is exited QCOMPARE(s1->events.at(1).first, 6); QCOMPARE(s1->events.at(1).second, TestState::Exit); // t2 is triggered QCOMPARE(t2->triggers.count(), 1); QCOMPARE(t2->triggers.at(0), 7); // s2 is entered QCOMPARE(s2->events.count(), 2); QCOMPARE(s2->events.at(0).first, 8); QCOMPARE(s2->events.at(0).second, TestState::Entry); // s2 is exited QCOMPARE(s2->events.at(1).first, 9); QCOMPARE(s2->events.at(1).second, TestState::Exit); } } struct StringEvent : public QEvent { public: StringEvent(const QString &val) : QEvent(QEvent::Type(QEvent::User+2)), value(val) {} QString value; }; class StringTransition : public QAbstractTransition { public: StringTransition(const QString &value, QAbstractState *target) : QAbstractTransition(QList() << target), m_value(value) {} protected: virtual bool eventTest(QEvent *e) const { if (e->type() != QEvent::Type(QEvent::User+2)) return false; StringEvent *se = static_cast(e); return (m_value == se->value) && (!m_cond.isValid() || (m_cond.indexIn(m_value) != -1)); } virtual void onTransition() {} private: QString m_value; QRegExp m_cond; }; class StringEventPoster : public QState { public: StringEventPoster(QStateMachine *machine, const QString &value, QState *parent = 0) : QState(parent), m_machine(machine), m_value(value) {} void setString(const QString &value) { m_value = value; } protected: virtual void onEntry() { m_machine->postEvent(new StringEvent(m_value)); } virtual void onExit() {} private: QStateMachine *m_machine; QString m_value; }; void tst_QStateMachine::postEvent() { QStateMachine machine; StringEventPoster *s1 = new StringEventPoster(&machine, "a"); QFinalState *s2 = new QFinalState; s1->addTransition(new StringTransition("a", s2)); machine.addState(s1); machine.addState(s2); machine.setInitialState(s1); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.start(); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(machine.configuration().size(), 1); QVERIFY(machine.configuration().contains(s2)); s1->setString("b"); QFinalState *s3 = new QFinalState(); machine.addState(s3); s1->addTransition(new StringTransition("b", s3)); finishedSpy.clear(); machine.start(); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(machine.configuration().size(), 1); QVERIFY(machine.configuration().contains(s3)); } void tst_QStateMachine::stateFinished() { QStateMachine machine; QState *s1 = new QState(machine.rootState()); QState *s1_1 = new QState(s1); QFinalState *s1_2 = new QFinalState(s1); s1_1->addTransition(s1_2); s1->setInitialState(s1_1); QFinalState *s2 = new QFinalState(machine.rootState()); s1->addTransition(s1, SIGNAL(finished()), s2); machine.setInitialState(s1); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.start(); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(machine.configuration().size(), 1); QVERIFY(machine.configuration().contains(s2)); } void tst_QStateMachine::parallelStates() { QStateMachine machine; QState *s1 = new QState(QState::ParallelGroup); QState *s1_1 = new QState(s1); QState *s1_1_1 = new QState(s1_1); QFinalState *s1_1_f = new QFinalState(s1_1); s1_1_1->addTransition(s1_1_f); s1_1->setInitialState(s1_1_1); QState *s1_2 = new QState(s1); QState *s1_2_1 = new QState(s1_2); QFinalState *s1_2_f = new QFinalState(s1_2); s1_2_1->addTransition(s1_2_f); s1_2->setInitialState(s1_2_1); machine.addState(s1); QFinalState *s2 = new QFinalState(); machine.addState(s2); s1->addTransition(s1, SIGNAL(finished()), s2); machine.setInitialState(s1); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.start(); QTRY_COMPARE(finishedSpy.count(), 1); } void tst_QStateMachine::allSourceToTargetConfigurations() { QStateMachine machine; QState *s0 = new QState(machine.rootState()); s0->setObjectName("s0"); QState *s1 = new QState(s0); s1->setObjectName("s1"); QState *s11 = new QState(s1); s11->setObjectName("s11"); QState *s2 = new QState(s0); s2->setObjectName("s2"); QState *s21 = new QState(s2); s21->setObjectName("s21"); QState *s211 = new QState(s21); s211->setObjectName("s211"); QFinalState *f = new QFinalState(machine.rootState()); f->setObjectName("f"); s0->setInitialState(s1); s1->setInitialState(s11); s2->setInitialState(s21); s21->setInitialState(s211); s11->addTransition(new StringTransition("g", s211)); s1->addTransition(new StringTransition("a", s1)); s1->addTransition(new StringTransition("b", s11)); s1->addTransition(new StringTransition("c", s2)); s1->addTransition(new StringTransition("d", s0)); s1->addTransition(new StringTransition("f", s211)); s211->addTransition(new StringTransition("d", s21)); s211->addTransition(new StringTransition("g", s0)); s211->addTransition(new StringTransition("h", f)); s21->addTransition(new StringTransition("b", s211)); s2->addTransition(new StringTransition("c", s1)); s2->addTransition(new StringTransition("f", s11)); s0->addTransition(new StringTransition("e", s211)); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("a")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("b")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("c")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("d")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("e")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("f")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("g")); QCoreApplication::processEvents(); machine.postEvent(new StringEvent("h")); QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); } class SignalEmitter : public QObject { Q_OBJECT public: SignalEmitter(QObject *parent = 0) : QObject(parent) {} void emitSignalWithNoArg() { emit signalWithNoArg(); } void emitSignalWithIntArg(int arg) { emit signalWithIntArg(arg); } void emitSignalWithStringArg(const QString &arg) { emit signalWithStringArg(arg); } Q_SIGNALS: void signalWithNoArg(); void signalWithIntArg(int); void signalWithStringArg(const QString &); }; class TestSignalTransition : public QSignalTransition { public: TestSignalTransition() : QSignalTransition() {} TestSignalTransition(QObject *sender, const char *signal, QAbstractState *target) : QSignalTransition(sender, signal, QList() << target) {} QVariantList argumentsReceived() const { return m_args; } protected: bool eventTest(QEvent *e) const { if (!QSignalTransition::eventTest(e)) return false; QSignalEvent *se = static_cast(e); const_cast(this)->m_args = se->arguments(); return true; } private: QVariantList m_args; }; void tst_QStateMachine::signalTransitions() { { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); SignalEmitter emitter; s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); emitter.emitSignalWithNoArg(); QTRY_COMPARE(finishedSpy.count(), 1); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); SignalEmitter emitter; TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithIntArg(int)), s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); emitter.emitSignalWithIntArg(123); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(trans->argumentsReceived().size(), 1); QCOMPARE(trans->argumentsReceived().at(0).toInt(), 123); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); SignalEmitter emitter; TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); QString testString = QString::fromLatin1("hello"); emitter.emitSignalWithStringArg(testString); QTRY_COMPARE(finishedSpy.count(), 1); QCOMPARE(trans->argumentsReceived().size(), 1); QCOMPARE(trans->argumentsReceived().at(0).toString(), testString); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); TestSignalTransition *trans = new TestSignalTransition(); QCOMPARE(trans->senderObject(), (QObject*)0); QCOMPARE(trans->signal(), QByteArray()); SignalEmitter emitter; trans->setSenderObject(&emitter); QCOMPARE(trans->senderObject(), (QObject*)&emitter); trans->setSignal(SIGNAL(signalWithNoArg())); QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg()))); trans->setTargetState(s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); emitter.emitSignalWithNoArg(); QTRY_COMPARE(finishedSpy.count(), 1); } } void tst_QStateMachine::eventTransitions() { QPushButton button; { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); QMouseEventTransition *trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton); QCOMPARE(trans->eventType(), QEvent::MouseButtonPress); QCOMPARE(trans->button(), Qt::LeftButton); trans->setTargetState(s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); QTest::mousePress(&button, Qt::LeftButton); QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); QEventTransition *trans = new QEventTransition(); QCOMPARE(trans->eventObject(), (QObject*)0); QCOMPARE(trans->eventType(), QEvent::None); trans->setEventObject(&button); QCOMPARE(trans->eventObject(), (QObject*)&button); trans->setEventType(QEvent::MouseButtonPress); QCOMPARE(trans->eventType(), QEvent::MouseButtonPress); trans->setTargetState(s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); QTest::mousePress(&button, Qt::LeftButton); QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); QMouseEventTransition *trans = new QMouseEventTransition(); QCOMPARE(trans->eventObject(), (QObject*)0); QCOMPARE(trans->eventType(), QEvent::None); QCOMPARE(trans->button(), Qt::NoButton); trans->setEventObject(&button); trans->setEventType(QEvent::MouseButtonPress); trans->setButton(Qt::LeftButton); trans->setTargetState(s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); QTest::mousePress(&button, Qt::LeftButton); QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); QKeyEventTransition *trans = new QKeyEventTransition(&button, QEvent::KeyPress, Qt::Key_A); QCOMPARE(trans->eventType(), QEvent::KeyPress); QCOMPARE(trans->key(), (int)Qt::Key_A); trans->setTargetState(s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); QTest::keyPress(&button, Qt::Key_A); QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); } { QStateMachine machine; QState *s0 = new QState(machine.rootState()); QFinalState *s1 = new QFinalState(machine.rootState()); QKeyEventTransition *trans = new QKeyEventTransition(); QCOMPARE(trans->eventObject(), (QObject*)0); QCOMPARE(trans->eventType(), QEvent::None); QCOMPARE(trans->key(), 0); trans->setEventObject(&button); trans->setEventType(QEvent::KeyPress); trans->setKey(Qt::Key_A); trans->setTargetState(s1); s0->addTransition(trans); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); machine.start(); QCoreApplication::processEvents(); QTest::keyPress(&button, Qt::Key_A); QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); } } void tst_QStateMachine::historyStates() { QStateMachine machine; QState *root = machine.rootState(); QState *s0 = new QState(root); QState *s00 = new QState(s0); QState *s01 = new QState(s0); QHistoryState *s0h = new QHistoryState(s0); QState *s1 = new QState(root); QFinalState *s2 = new QFinalState(root); s00->addTransition(new StringTransition("a", s01)); s0->addTransition(new StringTransition("b", s1)); s1->addTransition(new StringTransition("c", s0h)); s0->addTransition(new StringTransition("d", s2)); root->setInitialState(s0); s0->setInitialState(s00); QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.start(); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 2); QVERIFY(machine.configuration().contains(s0)); QVERIFY(machine.configuration().contains(s00)); machine.postEvent(new StringEvent("a")); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 2); QVERIFY(machine.configuration().contains(s0)); QVERIFY(machine.configuration().contains(s01)); machine.postEvent(new StringEvent("b")); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 1); QVERIFY(machine.configuration().contains(s1)); machine.postEvent(new StringEvent("c")); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 2); QVERIFY(machine.configuration().contains(s0)); QVERIFY(machine.configuration().contains(s01)); machine.postEvent(new StringEvent("d")); QCoreApplication::processEvents(); QCOMPARE(machine.configuration().size(), 1); QVERIFY(machine.configuration().contains(s2)); QTRY_COMPARE(finishedSpy.count(), 1); } void tst_QStateMachine::defaultGlobalRestorePolicy() { QStateMachine machine; QObject *propertyHolder = new QObject(); propertyHolder->setProperty("a", 1); propertyHolder->setProperty("b", 2); QState *s1 = new QState(machine.rootState()); s1->assignProperty(propertyHolder, "a", 3); QState *s2 = new QState(machine.rootState()); s2->assignProperty(propertyHolder, "b", 4); QState *s3 = new QState(machine.rootState()); s1->addTransition(new EventTransition(QEvent::User, s2)); s2->addTransition(new EventTransition(QEvent::User, s3)); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 4); } /* void tst_QStateMachine::restorePolicyNotInherited() { QStateMachine machine; QObject *propertyHolder = new QObject(); propertyHolder->setProperty("a", 1); propertyHolder->setProperty("b", 2); QState *parentState = new QState(machine.rootState()); parentState->setObjectName("parentState"); parentState->setRestorePolicy(QState::RestoreProperties); QState *s1 = new QState(parentState); s1->setObjectName("s1"); s1->assignProperty(propertyHolder, "a", 3); parentState->setInitialState(s1); QState *s2 = new QState(parentState); s2->setObjectName("s2"); s2->assignProperty(propertyHolder, "b", 4); QState *s3 = new QState(parentState); s3->setObjectName("s3"); s1->addTransition(new EventTransition(QEvent::User, s2)); s2->addTransition(new EventTransition(QEvent::User, s3)); machine.setInitialState(parentState); machine.start(); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 4); }*/ void tst_QStateMachine::globalRestorePolicySetToDoNotRestore() { QStateMachine machine; machine.setGlobalRestorePolicy(QStateMachine::DoNotRestoreProperties); QObject *propertyHolder = new QObject(); propertyHolder->setProperty("a", 1); propertyHolder->setProperty("b", 2); QState *s1 = new QState(machine.rootState()); s1->assignProperty(propertyHolder, "a", 3); QState *s2 = new QState(machine.rootState()); s2->assignProperty(propertyHolder, "b", 4); QState *s3 = new QState(machine.rootState()); s1->addTransition(new EventTransition(QEvent::User, s2)); s2->addTransition(new EventTransition(QEvent::User, s3)); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 4); } /* void tst_QStateMachine::setRestorePolicyToDoNotRestore() { QObject *object = new QObject(); object->setProperty("a", 1); object->setProperty("b", 2); QStateMachine machine; QState *S1 = new QState(); S1->setObjectName("S1"); S1->assignProperty(object, "a", 3); S1->setRestorePolicy(QState::DoNotRestoreProperties); machine.addState(S1); QState *S2 = new QState(); S2->setObjectName("S2"); S2->assignProperty(object, "b", 5); S2->setRestorePolicy(QState::DoNotRestoreProperties); machine.addState(S2); QState *S3 = new QState(); S3->setObjectName("S3"); S3->setRestorePolicy(QState::DoNotRestoreProperties); machine.addState(S3); QFinalState *S4 = new QFinalState(); machine.addState(S4); S1->addTransition(new EventTransition(QEvent::User, S2)); S2->addTransition(new EventTransition(QEvent::User, S3)); S3->addTransition(S4); machine.setInitialState(S1); machine.start(); QCoreApplication::processEvents(); QCOMPARE(object->property("a").toInt(), 3); QCOMPARE(object->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(object->property("a").toInt(), 3); QCOMPARE(object->property("b").toInt(), 5); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(object->property("a").toInt(), 3); QCOMPARE(object->property("b").toInt(), 5); } void tst_QStateMachine::setGlobalRestorePolicyToGlobalRestore() { s_countWarnings = false; QStateMachine machine; machine.setGlobalRestorePolicy(QStateMachine::GlobalRestorePolicy); QCOMPARE(machine.globalRestorePolicy(), QStateMachine::DoNotRestoreProperties); QCOMPARE(s_msgType, QtWarningMsg); s_msgType = QtDebugMsg; machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); machine.setGlobalRestorePolicy(QStateMachine::GlobalRestorePolicy); QCOMPARE(machine.globalRestorePolicy(), QStateMachine::RestoreProperties); QCOMPARE(s_msgType, QtWarningMsg); } void tst_QStateMachine::restorePolicyOnChildState() { QStateMachine machine; QObject *propertyHolder = new QObject(); propertyHolder->setProperty("a", 1); propertyHolder->setProperty("b", 2); QState *parentState = new QState(machine.rootState()); parentState->setObjectName("parentState"); QState *s1 = new QState(parentState); s1->setRestorePolicy(QState::RestoreProperties); s1->setObjectName("s1"); s1->assignProperty(propertyHolder, "a", 3); parentState->setInitialState(s1); QState *s2 = new QState(parentState); s2->setRestorePolicy(QState::RestoreProperties); s2->setObjectName("s2"); s2->assignProperty(propertyHolder, "b", 4); QState *s3 = new QState(parentState); s3->setRestorePolicy(QState::RestoreProperties); s3->setObjectName("s3"); s1->addTransition(new EventTransition(QEvent::User, s2)); s2->addTransition(new EventTransition(QEvent::User, s3)); machine.setInitialState(parentState); machine.start(); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 1); QCOMPARE(propertyHolder->property("b").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 1); QCOMPARE(propertyHolder->property("b").toInt(), 2); } */ void tst_QStateMachine::globalRestorePolicySetToRestore() { QStateMachine machine; machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); QObject *propertyHolder = new QObject(); propertyHolder->setProperty("a", 1); propertyHolder->setProperty("b", 2); QState *s1 = new QState(machine.rootState()); s1->assignProperty(propertyHolder, "a", 3); QState *s2 = new QState(machine.rootState()); s2->assignProperty(propertyHolder, "b", 4); QState *s3 = new QState(machine.rootState()); s1->addTransition(new EventTransition(QEvent::User, s2)); s2->addTransition(new EventTransition(QEvent::User, s3)); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 3); QCOMPARE(propertyHolder->property("b").toInt(), 2); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 1); QCOMPARE(propertyHolder->property("b").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QCOMPARE(propertyHolder->property("a").toInt(), 1); QCOMPARE(propertyHolder->property("b").toInt(), 2); } /* void tst_QStateMachine::mixedRestoreProperties() { QStateMachine machine; QObject *propertyHolder = new QObject(); propertyHolder->setProperty("a", 1); QState *s1 = new QState(machine.rootState()); s1->setRestorePolicy(QState::RestoreProperties); s1->assignProperty(propertyHolder, "a", 3); QState *s2 = new QState(machine.rootState()); s2->assignProperty(propertyHolder, "a", 4); QState *s3 = new QState(machine.rootState()); QState *s4 = new QState(machine.rootState()); s4->assignProperty(propertyHolder, "a", 5); QState *s5 = new QState(machine.rootState()); s5->setRestorePolicy(QState::RestoreProperties); s5->assignProperty(propertyHolder, "a", 6); s1->addTransition(new EventTransition(QEvent::User, s2)); s2->addTransition(new EventTransition(QEvent::User, s3)); s3->addTransition(new EventTransition(QEvent::User, s4)); s4->addTransition(new EventTransition(QEvent::User, s5)); s5->addTransition(new EventTransition(QEvent::User, s3)); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); // Enter s1, save current QCOMPARE(propertyHolder->property("a").toInt(), 3); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); // Enter s2, restorePolicy == DoNotRestore, so restore all properties QCOMPARE(propertyHolder->property("a").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); // Enter s3 QCOMPARE(propertyHolder->property("a").toInt(), 4); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); // Enter s4 QCOMPARE(propertyHolder->property("a").toInt(), 5); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); // Enter s5, save current QCOMPARE(propertyHolder->property("a").toInt(), 6); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); // Enter s3, restore QCOMPARE(propertyHolder->property("a").toInt(), 5); } */ void tst_QStateMachine::transitionWithParent() { QStateMachine machine; QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); EventTransition *trans = new EventTransition(QEvent::User, s2, s1); QCOMPARE(trans->sourceState(), s1); QCOMPARE(trans->targetState(), (QAbstractState*)s2); QCOMPARE(trans->targetStates().size(), 1); QCOMPARE(trans->targetStates().at(0), (QAbstractState*)s2); } void tst_QStateMachine::simpleAnimation() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("fooBar", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "fooBar", 2.0); EventTransition *et = new EventTransition(QEvent::User, s2); QPropertyAnimation *animation = new QPropertyAnimation(object, "fooBar", s2); et->addAnimation(animation); s1->addTransition(et); QState *s3 = new QState(machine.rootState()); s2->addTransition(animation, SIGNAL(finished()), s3); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("fooBar").toDouble(), 2.0); } class SlotCalledCounter: public QObject { Q_OBJECT public: SlotCalledCounter() : counter(0) {} int counter; public slots: void slot() { counter++; } }; void tst_QStateMachine::twoAnimations() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); object->setProperty("bar", 3.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); s2->assignProperty(object, "bar", 10.0); QPropertyAnimation *animationFoo = new QPropertyAnimation(object, "foo", s2); QPropertyAnimation *animationBar = new QPropertyAnimation(object, "bar", s2); animationBar->setDuration(900); SlotCalledCounter counter; connect(animationFoo, SIGNAL(finished()), &counter, SLOT(slot())); connect(animationBar, SIGNAL(finished()), &counter, SLOT(slot())); EventTransition *et = new EventTransition(QEvent::User, s2); et->addAnimation(animationFoo); et->addAnimation(animationBar); s1->addTransition(et); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s2->addTransition(&machine, SIGNAL(animationsFinished()), s3); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); QCOMPARE(object->property("bar").toDouble(), 10.0); QCOMPARE(counter.counter, 2); } void tst_QStateMachine::twoAnimatedTransitions() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 5.0); QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2); s1->addTransition(new EventTransition(QEvent::User, s2))->addAnimation(fooAnimation); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s2->addTransition(fooAnimation, SIGNAL(finished()), s3); QState *s4 = new QState(machine.rootState()); s4->assignProperty(object, "foo", 2.0); QPropertyAnimation *fooAnimation2 = new QPropertyAnimation(object, "foo", s4); s3->addTransition(new EventTransition(QEvent::User, s4))->addAnimation(fooAnimation2); QState *s5 = new QState(machine.rootState()); QObject::connect(s5, SIGNAL(entered()), QApplication::instance(), SLOT(quit())); s4->addTransition(fooAnimation2, SIGNAL(finished()), s5); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 5.0); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s5)); QCOMPARE(object->property("foo").toDouble(), 2.0); } void tst_QStateMachine::playAnimationTwice() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 5.0); QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2); s1->addTransition(new EventTransition(QEvent::User, s2))->addAnimation(fooAnimation); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s2->addTransition(fooAnimation, SIGNAL(finished()), s3); QState *s4 = new QState(machine.rootState()); s4->assignProperty(object, "foo", 2.0); s3->addTransition(new EventTransition(QEvent::User, s4))->addAnimation(fooAnimation); QState *s5 = new QState(machine.rootState()); QObject::connect(s5, SIGNAL(entered()), QApplication::instance(), SLOT(quit())); s4->addTransition(fooAnimation, SIGNAL(finished()), s5); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 5.0); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s5)); QCOMPARE(object->property("foo").toDouble(), 2.0); } void tst_QStateMachine::nestedTargetStateForAnimation() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); object->setProperty("bar", 3.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s2Child = new QState(s2); s2Child->assignProperty(object, "bar", 10.0); s2->setInitialState(s2Child); QState *s2Child2 = new QState(s2); s2Child2->assignProperty(object, "bar", 11.0); QAbstractTransition *at = s2Child->addTransition(new EventTransition(QEvent::User, s2Child2)); QPropertyAnimation *animation = new QPropertyAnimation(object, "bar", s2); connect(animation, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(animation); at = s1->addTransition(new EventTransition(QEvent::User, s2)); animation = new QPropertyAnimation(object, "foo", s2); connect(animation, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(animation); animation = new QPropertyAnimation(object, "bar", s2); connect(animation, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(animation); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s2->addTransition(&machine, SIGNAL(animationsFinished()), s3); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); QCOMPARE(object->property("bar").toDouble(), 10.0); QCOMPARE(counter.counter, 2); } void tst_QStateMachine::animatedGlobalRestoreProperty() { QStateMachine machine; machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QState *s4 = new QState(machine.rootState()); QObject::connect(s4, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); QAbstractTransition *at = s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", s2); connect(pa, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(pa); at = s2->addTransition(pa, SIGNAL(finished()), s3); pa = new QPropertyAnimation(object, "foo", s3); connect(pa, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(pa); at = s3->addTransition(pa, SIGNAL(finished()), s4); pa = new QPropertyAnimation(object, "foo", s4); connect(pa, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(pa); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s4)); QCOMPARE(object->property("foo").toDouble(), 1.0); QCOMPARE(counter.counter, 2); } void tst_QStateMachine::specificTargetValueOfAnimation() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QPropertyAnimation *anim = new QPropertyAnimation(object, "foo"); anim->setEndValue(10.0); s1->addTransition(new EventTransition(QEvent::User, s2))->addAnimation(anim); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s2->addTransition(anim, SIGNAL(finished()), s3); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); QCOMPARE(anim->endValue().toDouble(), 10.0); } void tst_QStateMachine::addDefaultAnimation() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine); machine.addDefaultAnimation(pa); s2->addTransition(pa, SIGNAL(finished()), s3); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); } void tst_QStateMachine::addDefaultAnimationWithUnusedAnimation() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); object->setProperty("bar", 2.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine); connect(pa, SIGNAL(finished()), &counter, SLOT(slot())); machine.addDefaultAnimation(pa); s2->addTransition(pa, SIGNAL(finished()), s3); pa = new QPropertyAnimation(object, "bar", &machine); connect(pa, SIGNAL(finished()), &counter, SLOT(slot())); machine.addDefaultAnimation(pa); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); QCOMPARE(counter.counter, 1); } void tst_QStateMachine::addDefaultAnimationForSource() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine); machine.addDefaultAnimationForSourceState(s1, pa); s2->addTransition(pa, SIGNAL(finished()), s3); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); } void tst_QStateMachine::addDefaultAnimationForTarget() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); QState *s1 = new QState(machine.rootState()); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine); machine.addDefaultAnimationForTargetState(s2, pa); s2->addTransition(pa, SIGNAL(finished()), s3); machine.setInitialState(s1); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(object->property("foo").toDouble(), 2.0); } void tst_QStateMachine::removeDefaultAnimation() { QStateMachine machine; QCOMPARE(machine.defaultAnimations().size(), 0); QPropertyAnimation *anim = new QPropertyAnimation(this, "foo"); machine.addDefaultAnimation(anim); QCOMPARE(machine.defaultAnimations().size(), 1); QVERIFY(machine.defaultAnimations().contains(anim)); machine.removeDefaultAnimation(anim); QCOMPARE(machine.defaultAnimations().size(), 0); machine.addDefaultAnimation(anim); QPropertyAnimation *anim2 = new QPropertyAnimation(this, "foo"); machine.addDefaultAnimation(anim2); QCOMPARE(machine.defaultAnimations().size(), 2); QVERIFY(machine.defaultAnimations().contains(anim)); QVERIFY(machine.defaultAnimations().contains(anim2)); machine.removeDefaultAnimation(anim); QCOMPARE(machine.defaultAnimations().size(), 1); QVERIFY(machine.defaultAnimations().contains(anim2)); machine.removeDefaultAnimation(anim2); QCOMPARE(machine.defaultAnimations().size(), 0); } void tst_QStateMachine::removeDefaultAnimationForSource() { QStateMachine machine; QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 0); QPropertyAnimation *anim = new QPropertyAnimation(this, "foo"); machine.addDefaultAnimationForSourceState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimations().size(), 0); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 0); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 1); QVERIFY(machine.defaultAnimationsForSourceState(machine.rootState()).contains(anim)); machine.removeDefaultAnimationForTargetState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimations().size(), 0); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 0); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 1); QVERIFY(machine.defaultAnimationsForSourceState(machine.rootState()).contains(anim)); machine.removeDefaultAnimationForSourceState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 0); machine.addDefaultAnimationForSourceState(machine.rootState(), anim); QPropertyAnimation *anim2 = new QPropertyAnimation(this, "foo"); machine.addDefaultAnimationForSourceState(machine.rootState(), anim2); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 2); QVERIFY(machine.defaultAnimationsForSourceState(machine.rootState()).contains(anim)); QVERIFY(machine.defaultAnimationsForSourceState(machine.rootState()).contains(anim2)); machine.removeDefaultAnimationForSourceState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 1); QVERIFY(machine.defaultAnimationsForSourceState(machine.rootState()).contains(anim2)); machine.removeDefaultAnimationForSourceState(machine.rootState(), anim2); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 0); } void tst_QStateMachine::removeDefaultAnimationForTarget() { QStateMachine machine; QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 0); QPropertyAnimation *anim = new QPropertyAnimation(this, "foo"); machine.addDefaultAnimationForTargetState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimations().size(), 0); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 0); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 1); QVERIFY(machine.defaultAnimationsForTargetState(machine.rootState()).contains(anim)); machine.removeDefaultAnimationForSourceState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimations().size(), 0); QCOMPARE(machine.defaultAnimationsForSourceState(machine.rootState()).size(), 0); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 1); QVERIFY(machine.defaultAnimationsForTargetState(machine.rootState()).contains(anim)); machine.removeDefaultAnimationForTargetState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 0); machine.addDefaultAnimationForTargetState(machine.rootState(), anim); QPropertyAnimation *anim2 = new QPropertyAnimation(this, "foo"); machine.addDefaultAnimationForTargetState(machine.rootState(), anim2); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 2); QVERIFY(machine.defaultAnimationsForTargetState(machine.rootState()).contains(anim)); QVERIFY(machine.defaultAnimationsForTargetState(machine.rootState()).contains(anim2)); machine.removeDefaultAnimationForTargetState(machine.rootState(), anim); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 1); QVERIFY(machine.defaultAnimationsForTargetState(machine.rootState()).contains(anim2)); machine.removeDefaultAnimationForTargetState(machine.rootState(), anim2); QCOMPARE(machine.defaultAnimationsForTargetState(machine.rootState()).size(), 0); } void tst_QStateMachine::overrideDefaultAnimationWithSource() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); machine.setInitialState(s1); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo"); connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo"); s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3); connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); machine.addDefaultAnimation(defaultAnimation); machine.addDefaultAnimationForSourceState(s1, moreSpecificAnimation); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(counter.counter, 2); // specific animation started and stopped } void tst_QStateMachine::overrideDefaultAnimationWithTarget() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); machine.setInitialState(s1); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo"); connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo"); s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3); connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); machine.addDefaultAnimation(defaultAnimation); machine.addDefaultAnimationForTargetState(s2, moreSpecificAnimation); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(counter.counter, 2); // specific animation started and stopped } void tst_QStateMachine::overrideDefaultAnimationWithSpecific() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); machine.setInitialState(s1); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); QAbstractTransition *at = s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo"); connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo"); s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3); connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); machine.addDefaultAnimation(defaultAnimation); at->addAnimation(moreSpecificAnimation); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(counter.counter, 2); // specific animation started and stopped } void tst_QStateMachine::overrideDefaultSourceAnimationWithSpecific() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); machine.setInitialState(s1); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); QAbstractTransition *at = s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo"); connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo"); s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3); connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); machine.addDefaultAnimationForSourceState(s1, defaultAnimation); at->addAnimation(moreSpecificAnimation); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(counter.counter, 2); // specific animation started and stopped } void tst_QStateMachine::overrideDefaultTargetAnimationWithSpecific() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); machine.setInitialState(s1); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); QAbstractTransition *at = s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo"); connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo"); s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3); connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); machine.addDefaultAnimationForTargetState(s2, defaultAnimation); at->addAnimation(moreSpecificAnimation); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(counter.counter, 2); // specific animation started and stopped } void tst_QStateMachine::overrideDefaultTargetAnimationWithSource() { QStateMachine machine; QObject *object = new QObject(); object->setProperty("foo", 1.0); SlotCalledCounter counter; QState *s1 = new QState(machine.rootState()); machine.setInitialState(s1); QState *s2 = new QState(machine.rootState()); s2->assignProperty(object, "foo", 2.0); QState *s3 = new QState(machine.rootState()); QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); QAbstractTransition *at = s1->addTransition(new EventTransition(QEvent::User, s2)); QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo"); connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo"); s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3); connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), &counter, SLOT(slot())); machine.addDefaultAnimationForTargetState(s2, defaultAnimation); machine.addDefaultAnimationForSourceState(s1, moreSpecificAnimation); machine.start(); QCoreApplication::processEvents(); machine.postEvent(new QEvent(QEvent::User)); QCOREAPPLICATION_EXEC(5000); QVERIFY(machine.configuration().contains(s3)); QCOMPARE(counter.counter, 2); // specific animation started and stopped } void tst_QStateMachine::parallelStateTransition() { QStateMachine machine; QState *parallelState = new QState(QState::ParallelGroup, machine.rootState()); machine.setInitialState(parallelState); QState *s1 = new QState(parallelState); QState *s2 = new QState(parallelState); QState *s1InitialChild = new QState(s1); s1->setInitialState(s1InitialChild); QState *s2InitialChild = new QState(s2); s2->setInitialState(s2InitialChild); QState *s1OtherChild = new QState(s1); QState *s2OtherChild = new QState(s2); s1->addTransition(new EventTransition(QEvent::User, s1OtherChild)); machine.start(); QCoreApplication::processEvents(); QVERIFY(machine.configuration().contains(parallelState)); QVERIFY(machine.configuration().contains(s1)); QVERIFY(machine.configuration().contains(s2)); QVERIFY(machine.configuration().contains(s1InitialChild)); QVERIFY(machine.configuration().contains(s2InitialChild)); QCOMPARE(machine.configuration().size(), 5); machine.postEvent(new QEvent(QEvent::User)); QCoreApplication::processEvents(); QVERIFY(machine.configuration().contains(parallelState)); QVERIFY(machine.configuration().contains(s1)); QEXPECT_FAIL("", "This failure is defined in the algorithm. We need to find out what behavior is correct.", Abort); QVERIFY(machine.configuration().contains(s2)); QVERIFY(machine.configuration().contains(s1OtherChild)); QVERIFY(machine.configuration().contains(s2InitialChild)); QCOMPARE(machine.configuration().size(), 5); } QTEST_MAIN(tst_QStateMachine) #include "tst_qstatemachine.moc"