/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
** this package.
**
** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtCore/QCoreApplication>
#include <QtGui/QPushButton>
#include <QtGui/QGraphicsScene>
#include <QtGui/QGraphicsSceneEvent>
#include <QtGui/QGraphicsTextItem>
#include "qstatemachine.h"
#include "qstate.h"
#include "qhistorystate.h"
#include "qkeyeventtransition.h"
#include "qmouseeventtransition.h"
#include "private/qstate_p.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 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); }
void emitSignalWithDefaultArg()
{ emit signalWithDefaultArg(); }
Q_SIGNALS:
void signalWithNoArg();
void signalWithIntArg(int);
void signalWithStringArg(const QString &);
void signalWithDefaultArg(int i = 42);
};
class tst_QStateMachine : public QObject
{
Q_OBJECT
public:
tst_QStateMachine();
virtual ~tst_QStateMachine();
private slots:
void init();
void cleanup();
void rootState();
void machineWithParent();
void addAndRemoveState();
void stateEntryAndExit();
void assignProperty();
void assignPropertyWithAnimation();
void postEvent();
void cancelDelayedEvent();
void postDelayedEventAndStop();
void stopAndPostEvent();
void stateFinished();
void parallelStates();
void parallelRootState();
void allSourceToTargetConfigurations();
void signalTransitions();
void eventTransitions();
void graphicsSceneEventTransitions();
void historyStates();
void startAndStop();
void targetStateWithNoParent();
void targetStateDeleted();
void transitionToRootState();
void transitionFromRootState();
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 historyStateAfterRestart();
void brokenStateIsNeverEntered();
void customErrorStateNotInGraph();
void transitionToStateNotInGraph();
void restoreProperties();
void defaultGlobalRestorePolicy();
void globalRestorePolicySetToRestore();
void globalRestorePolicySetToDontRestore();
void noInitialStateForInitialState();
//void restorePolicyNotInherited();
//void mixedRestoreProperties();
//void setRestorePolicyToDoNotRestore();
//void setGlobalRestorePolicyToGlobalRestore();
//void restorePolicyOnChildState();
void transitionWithParent();
void transitionsFromParallelStateWithNoChildren();
void parallelStateTransition();
void parallelStateAssignmentsDone();
void nestedRestoreProperties();
void nestedRestoreProperties2();
void simpleAnimation();
void twoAnimations();
void twoAnimatedTransitions();
void playAnimationTwice();
void nestedTargetStateForAnimation();
void propertiesAssignedSignalTransitionsReuseAnimationGroup();
void animatedGlobalRestoreProperty();
void specificTargetValueOfAnimation();
void addDefaultAnimation();
void addDefaultAnimationWithUnusedAnimation();
void removeDefaultAnimation();
void overrideDefaultAnimationWithSpecific();
// void addDefaultAnimationForSource();
// void addDefaultAnimationForTarget();
// void removeDefaultAnimationForSource();
// void removeDefaultAnimationForTarget();
// void overrideDefaultAnimationWithSource();
// void overrideDefaultAnimationWithTarget();
// void overrideDefaultSourceAnimationWithSpecific();
// void overrideDefaultTargetAnimationWithSpecific();
// void overrideDefaultTargetAnimationWithSource();
void nestedStateMachines();
void goToState();
void task260403_clonedSignals();
void postEventFromOtherThread();
void eventFilterForApplication();
void eventClassesExported();
};
tst_QStateMachine::tst_QStateMachine()
{
}
tst_QStateMachine::~tst_QStateMachine()
{
}
class TestState : public QState
{
public:
enum Event {
Entry,
Exit
};
TestState(QState *parent)
: QState(parent) {}
QList<QPair<int, Event> > events;
protected:
virtual void onEntry(QEvent *) {
events.append(qMakePair(globalTick++, Entry));
}
virtual void onExit(QEvent *) {
events.append(qMakePair(globalTick++, Exit));
}
};
class TestTransition : public QAbstractTransition
{
public:
TestTransition(QAbstractState *target)
: QAbstractTransition()
{ setTargetState(target); }
QList<int> triggers;
protected:
virtual bool eventTest(QEvent *) {
return true;
}
virtual void onTransition(QEvent *) {
triggers.append(globalTick++);
}
};
void tst_QStateMachine::init()
{
}
void tst_QStateMachine::cleanup()
{
}
class EventTransition : public QAbstractTransition
{
public:
EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0)
: QAbstractTransition(parent), m_type(type)
{ setTargetState(target); }
protected:
virtual bool eventTest(QEvent *e) {
return (e->type() == m_type);
}
virtual void onTransition(QEvent *) {}
private:
QEvent::Type m_type;
};
void tst_QStateMachine::transitionToRootState()
{
QStateMachine machine;
machine.setObjectName("machine");
QState *initialState = new QState();
initialState->setObjectName("initial");
machine.addState(initialState);
machine.setInitialState(initialState);
QAbstractTransition *trans = new EventTransition(QEvent::User, &machine);
initialState->addTransition(trans);
QCOMPARE(trans->sourceState(), initialState);
QCOMPARE(trans->targetState(), static_cast<QAbstractState *>(&machine));
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(initialState));
machine.postEvent(new QEvent(QEvent::User));
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: No common ancestor for targets and source of transition from state 'initial'");
QCoreApplication::processEvents();
QVERIFY(machine.configuration().isEmpty());
QVERIFY(!machine.isRunning());
}
void tst_QStateMachine::transitionFromRootState()
{
QStateMachine machine;
QState *root = &machine;
QState *s1 = new QState(root);
EventTransition *trans = new EventTransition(QEvent::User, s1);
root->addTransition(trans);
QCOMPARE(trans->sourceState(), root);
QCOMPARE(trans->targetState(), static_cast<QAbstractState *>(s1));
}
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()
{
QStateMachine machine;
QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
QState *brokenState = new QState();
brokenState->setObjectName("MyInitialState");
machine.addState(brokenState);
machine.setInitialState(brokenState);
QState *childState = new QState(brokenState);
childState->setObjectName("childState");
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'MyInitialState'");
// initialState has no initial state
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'MyInitialState'"));
QCOMPARE(machine.isRunning(), false);
}
class CustomErrorState: public QState
{
public:
CustomErrorState(QStateMachine *machine, QState *parent = 0)
: QState(parent), error(QStateMachine::NoError), m_machine(machine)
{
}
void onEntry(QEvent *)
{
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(), static_cast<QAbstractState*>(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.isRunning(), true);
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'"));
}
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.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(customErrorState));
QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
}
void tst_QStateMachine::customLocalErrorStateInOtherState()
{
QStateMachine machine;
CustomErrorState *customErrorState = new CustomErrorState(&machine);
machine.addState(customErrorState);
QState *initialState = new QState();
initialState->setObjectName("initialState");
QTest::ignoreMessage(QtWarningMsg, "QState::setErrorState: error state cannot belong to a different state machine");
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));
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
machine.start();
QCoreApplication::processEvents();
machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
}
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.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(customErrorState));
}
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);
}
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.isRunning(), true);
QCOMPARE(machine.configuration().count(), 2);
QVERIFY(machine.configuration().contains(customErrorState));
QVERIFY(machine.configuration().contains(childOfErrorState));
}
void tst_QStateMachine::errorStateHasErrors()
{
QStateMachine machine;
CustomErrorState *customErrorState = new CustomErrorState(&machine);
customErrorState->setObjectName("customErrorState");
machine.addState(customErrorState);
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)));
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'customErrorState'");
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'customErrorState'"));
}
void tst_QStateMachine::errorStateIsRootState()
{
QStateMachine machine;
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::setErrorState: root state cannot be error state");
machine.setErrorState(&machine);
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)));
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
}
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()
{
QStateMachine machine;
machine.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));
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state ''");
QCoreApplication::processEvents();
QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::clearError()
{
QStateMachine machine;
machine.setErrorState(new QState(&machine)); // avoid warnings
QState *brokenState = new QState(&machine);
brokenState->setObjectName("brokenState");
machine.setInitialState(brokenState);
new QState(brokenState);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), true);
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);
machine.setInitialState(hs);
QState *s1 = new QState(&machine);
hs->setDefaultState(s1);
QState *s2 = new QState(&machine);
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);
machine.setInitialState(initialState);
machine.setErrorState(new QState(&machine)); // avoid warnings
QState *brokenState = new QState(&machine);
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.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(machine.errorState()));
QCOMPARE(machine.error(), QStateMachine::NoDefaultStateInHistoryStateError);
QCOMPARE(machine.errorString(), QString::fromLatin1("Missing default state in history state 'historyState'"));
}
void tst_QStateMachine::historyStateAfterRestart()
{
// QTBUG-8842
QStateMachine machine;
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
QState *s21 = new QState(s2);
QState *s22 = new QState(s2);
QHistoryState *s2h = new QHistoryState(s2);
s2h->setDefaultState(s21);
s1->addTransition(new EventTransition(QEvent::User, s2h));
s21->addTransition(new EventTransition(QEvent::User, s22));
s2->addTransition(new EventTransition(QEvent::User, s1));
for (int x = 0; x < 2; ++x) {
QSignalSpy startedSpy(&machine, SIGNAL(started()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(s1));
// s1 -> s2h -> s21 (default state)
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().count(), 2);
QVERIFY(machine.configuration().contains(s2));
// This used to fail on the 2nd run because the
// history had not been cleared.
QVERIFY(machine.configuration().contains(s21));
// s21 -> s22
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().count(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s22));
// s2 -> s1 (s22 saved in s2h)
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(s1));
// s1 -> s2h -> s22 (saved state)
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().count(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s22));
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
machine.stop();
QTRY_COMPARE(stoppedSpy.count(), 1);
}
}
void tst_QStateMachine::brokenStateIsNeverEntered()
{
QStateMachine machine;
QObject *entryController = new QObject(&machine);
entryController->setProperty("brokenStateEntered", false);
entryController->setProperty("childStateEntered", false);
entryController->setProperty("errorStateEntered", false);
QState *initialState = new QState(&machine);
machine.setInitialState(initialState);
QState *errorState = new QState(&machine);
errorState->assignProperty(entryController, "errorStateEntered", true);
machine.setErrorState(errorState);
QState *brokenState = new QState(&machine);
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()
{
QStateMachine machine;
QState *initialState = new QState(&machine);
initialState->setObjectName("initialState");
machine.setInitialState(initialState);
QState independentState;
independentState.setObjectName("independentState");
initialState->addTransition(&independentState);
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: No common ancestor for targets and source of transition from state 'initialState'");
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::customErrorStateNotInGraph()
{
QStateMachine machine;
QState errorState;
errorState.setObjectName("errorState");
QTest::ignoreMessage(QtWarningMsg, "QState::setErrorState: error state cannot belong to a different state machine");
machine.setErrorState(&errorState);
QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
QState *initialBrokenState = new QState(&machine);
initialBrokenState->setObjectName("initialBrokenState");
machine.setInitialState(initialBrokenState);
new QState(initialBrokenState);
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'initialBrokenState'");
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::restoreProperties()
{
QStateMachine machine;
QCOMPARE(machine.globalRestorePolicy(), QStateMachine::DontRestoreProperties);
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
QObject *object = new QObject(&machine);
object->setProperty("a", 1);
object->setProperty("b", 2);
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;
QCOMPARE(qobject_cast<QState*>(machine.parentState()), (QState*)0);
QCOMPARE(machine.machine(), (QStateMachine*)0);
QState *s1 = new QState(&machine);
QCOMPARE(s1->parentState(), static_cast<QState*>(&machine));
QState *s2 = new QState();
s2->setParent(&machine);
QCOMPARE(s2->parentState(), static_cast<QState*>(&machine));
}
void tst_QStateMachine::machineWithParent()
{
QObject object;
QStateMachine *machine = new QStateMachine(&object);
QCOMPARE(machine->parent(), &object);
QCOMPARE(machine->parentState(), (QObject*)0);
}
void tst_QStateMachine::addAndRemoveState()
{
#ifdef QT_BUILD_INTERNAL
QStateMachine machine;
QStatePrivate *root_d = QStatePrivate::get(&machine);
QCOMPARE(root_d->childStates().size(), 0);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: cannot add null state");
machine.addState(0);
QState *s1 = new QState();
QCOMPARE(s1->parentState(), (QState*)0);
QCOMPARE(s1->machine(), (QStateMachine*)0);
machine.addState(s1);
QCOMPARE(s1->machine(), static_cast<QStateMachine*>(&machine));
QCOMPARE(s1->parentState(), static_cast<QState*>(&machine));
QCOMPARE(root_d->childStates().size(), 1);
QCOMPARE(root_d->childStates().at(0), (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(), static_cast<QState*>(&machine));
QCOMPARE(root_d->childStates().size(), 2);
QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
QCOMPARE(root_d->childStates().at(1), (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(root_d->childStates().size(), 1);
QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s2);
machine.removeState(s2);
QCOMPARE(s2->parentState(), (QState*)0);
QCOMPARE(root_d->childStates().size(), 0);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::removeState: cannot remove null state");
machine.removeState(0);
{
QStateMachine machine2;
{
QString warning;
warning.sprintf("QStateMachine::removeState: state %p's machine (%p) is different from this machine (%p)",
&machine2, (void*)0, &machine);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
machine.removeState(&machine2);
}
// ### check this behavior
machine.addState(&machine2);
QCOMPARE(machine2.parent(), (QObject*)&machine);
}
delete s1;
delete s2;
// ### how to deal with this?
// machine.removeState(machine.errorState());
#endif
}
void tst_QStateMachine::stateEntryAndExit()
{
// Two top-level states
{
QStateMachine machine;
TestState *s1 = new TestState(&machine);
QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state");
s1->addTransition((QAbstractState*)0);
QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add null transition");
s1->addTransition((QAbstractTransition*)0);
QTest::ignoreMessage(QtWarningMsg, "QState::removeTransition: cannot remove null transition");
s1->removeTransition((QAbstractTransition*)0);
TestState *s2 = new TestState(&machine);
QFinalState *s3 = new QFinalState(&machine);
TestTransition *t = new TestTransition(s2);
QCOMPARE(t->machine(), (QStateMachine*)0);
QCOMPARE(t->sourceState(), (QState*)0);
QCOMPARE(t->targetState(), (QAbstractState*)s2);
QCOMPARE(t->targetStates().size(), 1);
QCOMPARE(t->targetStates().at(0), (QAbstractState*)s2);
t->setTargetState(0);
QCOMPARE(t->targetState(), (QAbstractState*)0);
QVERIFY(t->targetStates().isEmpty());
t->setTargetState(s2);
QCOMPARE(t->targetState(), (QAbstractState*)s2);
QTest::ignoreMessage(QtWarningMsg, "QAbstractTransition::setTargetStates: target state(s) cannot be null");
t->setTargetStates(QList<QAbstractState*>() << 0);
QCOMPARE(t->targetState(), (QAbstractState*)s2);
t->setTargetStates(QList<QAbstractState*>() << s2);
QCOMPARE(t->targetState(), (QAbstractState*)s2);
QCOMPARE(t->targetStates().size(), 1);
QCOMPARE(t->targetStates().at(0), (QAbstractState*)s2);
s1->addTransition(t);
QCOMPARE(t->sourceState(), (QState*)s1);
QCOMPARE(t->machine(), &machine);
{
QAbstractTransition *trans = s2->addTransition(s3);
QVERIFY(trans != 0);
QCOMPARE(trans->sourceState(), (QState*)s2);
QCOMPARE(trans->targetState(), (QAbstractState*)s3);
{
QString warning;
warning.sprintf("QState::removeTransition: transition %p's source state (%p) is different from this state (%p)", trans, s2, s1);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
s1->removeTransition(trans);
}
s2->removeTransition(trans);
QCOMPARE(trans->sourceState(), (QState*)0);
QCOMPARE(trans->targetState(), (QAbstractState*)s3);
s2->addTransition(trans);
QCOMPARE(trans->sourceState(), (QState*)s2);
}
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s1);
QCOMPARE(machine.initialState(), (QAbstractState*)s1);
{
QString warning;
warning.sprintf("QState::setInitialState: state %p is not a child of this state (%p)", &machine, &machine);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
machine.setInitialState(&machine);
QCOMPARE(machine.initialState(), (QAbstractState*)s1);
}
QVERIFY(machine.configuration().isEmpty());
globalTick = 0;
QVERIFY(!machine.isRunning());
QSignalSpy s1EnteredSpy(s1, SIGNAL(entered()));
QSignalSpy s1ExitedSpy(s1, SIGNAL(exited()));
QSignalSpy tTriggeredSpy(t, SIGNAL(triggered()));
QSignalSpy s2EnteredSpy(s2, SIGNAL(entered()));
QSignalSpy s2ExitedSpy(s2, SIGNAL(exited()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QTRY_COMPARE(finishedSpy.count(), 1);
QTRY_COMPARE(stoppedSpy.count(), 0);
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);
QCOMPARE(s1EnteredSpy.count(), 1);
QCOMPARE(s1ExitedSpy.count(), 1);
QCOMPARE(tTriggeredSpy.count(), 1);
QCOMPARE(s2EnteredSpy.count(), 1);
QCOMPARE(s2ExitedSpy.count(), 1);
}
// Two top-level states, one has two child states
{
QStateMachine machine;
TestState *s1 = new TestState(&machine);
TestState *s11 = new TestState(s1);
TestState *s12 = new TestState(s1);
TestState *s2 = new TestState(&machine);
QFinalState *s3 = new QFinalState(&machine);
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);
}
}
void tst_QStateMachine::assignProperty()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
QTest::ignoreMessage(QtWarningMsg, "QState::assignProperty: cannot assign property 'foo' of null object");
s1->assignProperty(0, "foo", QVariant());
s1->assignProperty(s1, "objectName", "s1");
QFinalState *s2 = new QFinalState(&machine);
s1->addTransition(s2);
machine.setInitialState(s1);
machine.start();
QTRY_COMPARE(s1->objectName(), QString::fromLatin1("s1"));
s1->assignProperty(s1, "objectName", "foo");
machine.start();
QTRY_COMPARE(s1->objectName(), QString::fromLatin1("foo"));
s1->assignProperty(s1, "noSuchProperty", 123);
machine.start();
QTRY_COMPARE(s1->dynamicPropertyNames().size(), 1);
QCOMPARE(s1->dynamicPropertyNames().at(0), QByteArray("noSuchProperty"));
QCOMPARE(s1->objectName(), QString::fromLatin1("foo"));
{
QSignalSpy propertiesAssignedSpy(s1, SIGNAL(propertiesAssigned()));
machine.start();
QTRY_COMPARE(propertiesAssignedSpy.count(), 1);
}
// nested states
{
QState *s11 = new QState(s1);
QString str = QString::fromLatin1("set by nested state");
s11->assignProperty(s11, "objectName", str);
s1->setInitialState(s11);
machine.start();
QTRY_COMPARE(s11->objectName(), str);
}
}
void tst_QStateMachine::assignPropertyWithAnimation()
{
// Single animation
{
QStateMachine machine;
QVERIFY(machine.isAnimated());
machine.setAnimated(false);
QVERIFY(!machine.isAnimated());
machine.setAnimated(true);
QVERIFY(machine.isAnimated());
QObject obj;
obj.setProperty("foo", 321);
obj.setProperty("bar", 654);
QState *s1 = new QState(&machine);
s1->assignProperty(&obj, "foo", 123);
QState *s2 = new QState(&machine);
s2->assignProperty(&obj, "foo", 456);
s2->assignProperty(&obj, "bar", 789);
QAbstractTransition *trans = s1->addTransition(s2);
QVERIFY(trans->animations().isEmpty());
QTest::ignoreMessage(QtWarningMsg, "QAbstractTransition::addAnimation: cannot add null animation");
trans->addAnimation(0);
QPropertyAnimation anim(&obj, "foo");
anim.setDuration(250);
trans->addAnimation(&anim);
QCOMPARE(trans->animations().size(), 1);
QCOMPARE(trans->animations().at(0), (QAbstractAnimation*)&anim);
QCOMPARE(anim.parent(), (QObject*)0);
QTest::ignoreMessage(QtWarningMsg, "QAbstractTransition::removeAnimation: cannot remove null animation");
trans->removeAnimation(0);
trans->removeAnimation(&anim);
QVERIFY(trans->animations().isEmpty());
trans->addAnimation(&anim);
QCOMPARE(trans->animations().size(), 1);
QCOMPARE(trans->animations().at(0), (QAbstractAnimation*)&anim);
QFinalState *s3 = new QFinalState(&machine);
s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
machine.setInitialState(s1);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(obj.property("foo").toInt(), 456);
QCOMPARE(obj.property("bar").toInt(), 789);
}
// Two animations
{
QStateMachine machine;
QObject obj;
obj.setProperty("foo", 321);
obj.setProperty("bar", 654);
QState *s1 = new QState(&machine);
s1->assignProperty(&obj, "foo", 123);
QState *s2 = new QState(&machine);
s2->assignProperty(&obj, "foo", 456);
s2->assignProperty(&obj, "bar", 789);
QAbstractTransition *trans = s1->addTransition(s2);
QPropertyAnimation anim(&obj, "foo");
anim.setDuration(150);
trans->addAnimation(&anim);
QPropertyAnimation anim2(&obj, "bar");
anim2.setDuration(150);
trans->addAnimation(&anim2);
QFinalState *s3 = new QFinalState(&machine);
s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
machine.setInitialState(s1);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(obj.property("foo").toInt(), 456);
QCOMPARE(obj.property("bar").toInt(), 789);
}
// Animation group
{
QStateMachine machine;
QObject obj;
obj.setProperty("foo", 321);
obj.setProperty("bar", 654);
QState *s1 = new QState(&machine);
s1->assignProperty(&obj, "foo", 123);
s1->assignProperty(&obj, "bar", 321);
QState *s2 = new QState(&machine);
s2->assignProperty(&obj, "foo", 456);
s2->assignProperty(&obj, "bar", 654);
s2->assignProperty(&obj, "baz", 789);
QAbstractTransition *trans = s1->addTransition(s2);
QSequentialAnimationGroup group;
group.addAnimation(new QPropertyAnimation(&obj, "foo"));
group.addAnimation(new QPropertyAnimation(&obj, "bar"));
trans->addAnimation(&group);
QFinalState *s3 = new QFinalState(&machine);
s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
machine.setInitialState(s1);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(obj.property("foo").toInt(), 456);
QCOMPARE(obj.property("bar").toInt(), 654);
QCOMPARE(obj.property("baz").toInt(), 789);
}
// Nested states
{
QStateMachine machine;
QObject obj;
obj.setProperty("foo", 321);
obj.setProperty("bar", 654);
QState *s1 = new QState(&machine);
QCOMPARE(s1->childMode(), QState::ExclusiveStates);
s1->setChildMode(QState::ParallelStates);
QCOMPARE(s1->childMode(), QState::ParallelStates);
s1->setChildMode(QState::ExclusiveStates);
QCOMPARE(s1->childMode(), QState::ExclusiveStates);
QCOMPARE(s1->initialState(), (QAbstractState*)0);
s1->setObjectName("s1");
s1->assignProperty(&obj, "foo", 123);
s1->assignProperty(&obj, "bar", 456);
QState *s2 = new QState(&machine);
s2->setObjectName("s2");
s2->assignProperty(&obj, "foo", 321);
QState *s21 = new QState(s2);
s21->setObjectName("s21");
s21->assignProperty(&obj, "bar", 654);
QState *s22 = new QState(s2);
s22->setObjectName("s22");
s22->assignProperty(&obj, "bar", 789);
s2->setInitialState(s21);
QCOMPARE(s2->initialState(), (QAbstractState*)s21);
QAbstractTransition *trans = s1->addTransition(s2);
QPropertyAnimation anim(&obj, "foo");
anim.setDuration(500);
trans->addAnimation(&anim);
QPropertyAnimation anim2(&obj, "bar");
anim2.setDuration(250);
trans->addAnimation(&anim2);
s21->addTransition(s21, SIGNAL(propertiesAssigned()), s22);
QFinalState *s3 = new QFinalState(&machine);
s22->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
machine.setInitialState(s1);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(obj.property("foo").toInt(), 321);
QCOMPARE(obj.property("bar").toInt(), 789);
}
// Aborted animation
{
QStateMachine machine;
SignalEmitter emitter;
QObject obj;
obj.setProperty("foo", 321);
obj.setProperty("bar", 654);
QState *group = new QState(&machine);
QState *s1 = new QState(group);
group->setInitialState(s1);
s1->assignProperty(&obj, "foo", 123);
QState *s2 = new QState(group);
s2->assignProperty(&obj, "foo", 456);
s2->assignProperty(&obj, "bar", 789);
QAbstractTransition *trans = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
QPropertyAnimation anim(&obj, "foo");
anim.setDuration(8000);
trans->addAnimation(&anim);
QPropertyAnimation anim2(&obj, "bar");
anim2.setDuration(8000);
trans->addAnimation(&anim2);
QState *s3 = new QState(group);
s3->assignProperty(&obj, "foo", 911);
s2->addTransition(&emitter, SIGNAL(signalWithNoArg()), s3);
machine.setInitialState(group);
machine.start();
QTRY_COMPARE(machine.configuration().contains(s1), true);
QSignalSpy propertiesAssignedSpy(s2, SIGNAL(propertiesAssigned()));
emitter.emitSignalWithNoArg();
QTRY_COMPARE(machine.configuration().contains(s2), true);
QVERIFY(propertiesAssignedSpy.isEmpty());
emitter.emitSignalWithNoArg(); // will cause animations from s1-->s2 to abort
QTRY_COMPARE(machine.configuration().contains(s3), true);
QVERIFY(propertiesAssignedSpy.isEmpty());
QCOMPARE(obj.property("foo").toInt(), 911);
QCOMPARE(obj.property("bar").toInt(), 789);
}
}
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(), m_value(value)
{ setTargetState(target); }
protected:
virtual bool eventTest(QEvent *e)
{
if (e->type() != QEvent::Type(QEvent::User+2))
return false;
StringEvent *se = static_cast<StringEvent*>(e);
return (m_value == se->value) && (!m_cond.isValid() || (m_cond.indexIn(m_value) != -1));
}
virtual void onTransition(QEvent *) {}
private:
QString m_value;
QRegExp m_cond;
};
class StringEventPoster : public QState
{
public:
StringEventPoster(const QString &value, QState *parent = 0)
: QState(parent), m_value(value), m_delay(-1) {}
void setString(const QString &value)
{ m_value = value; }
void setDelay(int delay)
{ m_delay = delay; }
protected:
virtual void onEntry(QEvent *)
{
if (m_delay == -1)
machine()->postEvent(new StringEvent(m_value));
else
machine()->postDelayedEvent(new StringEvent(m_value), m_delay);
}
virtual void onExit(QEvent *) {}
private:
QString m_value;
int m_delay;
};
void tst_QStateMachine::postEvent()
{
for (int x = 0; x < 2; ++x) {
QStateMachine machine;
{
QEvent e(QEvent::None);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::postEvent: cannot post event when the state machine is not running");
machine.postEvent(&e);
}
StringEventPoster *s1 = new StringEventPoster("a");
if (x == 1)
s1->setDelay(100);
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::cancelDelayedEvent()
{
QStateMachine machine;
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::cancelDelayedEvent: the machine is not running");
QVERIFY(!machine.cancelDelayedEvent(-1));
QState *s1 = new QState(&machine);
QFinalState *s2 = new QFinalState(&machine);
s1->addTransition(new StringTransition("a", s2));
machine.setInitialState(s1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
int id1 = machine.postDelayedEvent(new StringEvent("c"), 50000);
QVERIFY(id1 != -1);
int id2 = machine.postDelayedEvent(new StringEvent("b"), 25000);
QVERIFY(id2 != -1);
QVERIFY(id2 != id1);
int id3 = machine.postDelayedEvent(new StringEvent("a"), 100);
QVERIFY(id3 != -1);
QVERIFY(id3 != id2);
QVERIFY(machine.cancelDelayedEvent(id1));
QVERIFY(!machine.cancelDelayedEvent(id1));
QVERIFY(machine.cancelDelayedEvent(id2));
QVERIFY(!machine.cancelDelayedEvent(id2));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
}
void tst_QStateMachine::postDelayedEventAndStop()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
QFinalState *s2 = new QFinalState(&machine);
s1->addTransition(new StringTransition("a", s2));
machine.setInitialState(s1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
int id1 = machine.postDelayedEvent(new StringEvent("a"), 0);
QVERIFY(id1 != -1);
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
machine.stop();
QTRY_COMPARE(stoppedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.start();
QTRY_COMPARE(startedSpy.count(), 2);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
int id2 = machine.postDelayedEvent(new StringEvent("a"), 1000);
QVERIFY(id2 != -1);
machine.stop();
QTRY_COMPARE(stoppedSpy.count(), 2);
machine.start();
QTRY_COMPARE(startedSpy.count(), 3);
QTestEventLoop::instance().enterLoop(2);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
}
void tst_QStateMachine::stopAndPostEvent()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
machine.stop();
QCOMPARE(stoppedSpy.count(), 0);
machine.postEvent(new QEvent(QEvent::User));
QTRY_COMPARE(stoppedSpy.count(), 1);
QCoreApplication::processEvents();
}
void tst_QStateMachine::stateFinished()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
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);
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::ParallelStates);
QCOMPARE(s1->childMode(), QState::ParallelStates);
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);
{
QString warning;
warning.sprintf("QState::setInitialState: ignoring attempt to set initial state of parallel state group %p", s1);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
s1->setInitialState(0);
}
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);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
}
void tst_QStateMachine::parallelRootState()
{
QStateMachine machine;
QState *root = &machine;
QCOMPARE(root->childMode(), QState::ExclusiveStates);
root->setChildMode(QState::ParallelStates);
QCOMPARE(root->childMode(), QState::ParallelStates);
QState *s1 = new QState(root);
QFinalState *s1_f = new QFinalState(s1);
s1->setInitialState(s1_f);
QState *s2 = new QState(root);
QFinalState *s2_f = new QFinalState(s2);
s2->setInitialState(s2_f);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start: No initial state set for machine. Refusing to start.");
machine.start();
QCoreApplication::processEvents();
QEXPECT_FAIL("", "parallel root state is not supported (task 256587)", Continue);
QCOMPARE(startedSpy.count(), 1);
}
void tst_QStateMachine::allSourceToTargetConfigurations()
{
QStateMachine machine;
QState *s0 = new QState(&machine);
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);
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 TestSignalTransition : public QSignalTransition
{
public:
TestSignalTransition(QState *sourceState = 0)
: QSignalTransition(sourceState), m_sender(0)
{}
TestSignalTransition(QObject *sender, const char *signal,
QAbstractState *target)
: QSignalTransition(sender, signal), m_sender(0)
{ setTargetState(target); }
QObject *senderReceived() const {
return m_sender;
}
int signalIndexReceived() const {
return m_signalIndex;
}
QVariantList argumentsReceived() const {
return m_args;
}
protected:
bool eventTest(QEvent *e) {
if (!QSignalTransition::eventTest(e))
return false;
QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
m_sender = se->sender();
m_signalIndex = se->signalIndex();
m_args = se->arguments();
return true;
}
private:
QObject *m_sender;
int m_signalIndex;
QVariantList m_args;
};
void tst_QStateMachine::signalTransitions()
{
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: sender cannot be null");
QCOMPARE(s0->addTransition(0, SIGNAL(noSuchSignal()), 0), (QSignalTransition*)0);
SignalEmitter emitter;
QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: signal cannot be null");
QCOMPARE(s0->addTransition(&emitter, 0, 0), (QSignalTransition*)0);
QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state");
QCOMPARE(s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), 0), (QSignalTransition*)0);
QFinalState *s1 = new QFinalState(&machine);
QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: no such signal SignalEmitter::noSuchSignal()");
QCOMPARE(s0->addTransition(&emitter, SIGNAL(noSuchSignal()), s1), (QSignalTransition*)0);
QSignalTransition *trans = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
QVERIFY(trans != 0);
QCOMPARE(trans->sourceState(), s0);
QCOMPARE(trans->targetState(), (QAbstractState*)s1);
QCOMPARE(trans->senderObject(), (QObject*)&emitter);
QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QCoreApplication::processEvents();
emitter.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 1);
emitter.emitSignalWithNoArg();
trans->setSignal(SIGNAL(signalWithIntArg(int)));
QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithIntArg(int))));
machine.start();
QCoreApplication::processEvents();
emitter.emitSignalWithIntArg(123);
QTRY_COMPARE(finishedSpy.count(), 2);
machine.start();
QCoreApplication::processEvents();
trans->setSignal(SIGNAL(signalWithNoArg()));
QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
emitter.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 3);
SignalEmitter emitter2;
machine.start();
QCoreApplication::processEvents();
trans->setSenderObject(&emitter2);
emitter2.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 4);
machine.start();
QCoreApplication::processEvents();
QTest::ignoreMessage(QtWarningMsg, "QSignalTransition: no such signal: SignalEmitter::noSuchSignal()");
trans->setSignal(SIGNAL(noSuchSignal()));
QCOMPARE(trans->signal(), QByteArray(SIGNAL(noSuchSignal())));
}
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
SignalEmitter emitter;
QSignalTransition *trans = s0->addTransition(&emitter, "signalWithNoArg()", s1);
QVERIFY(trans != 0);
QCOMPARE(trans->sourceState(), s0);
QCOMPARE(trans->targetState(), (QAbstractState*)s1);
QCOMPARE(trans->senderObject(), (QObject*)&emitter);
QCOMPARE(trans->signal(), QByteArray("signalWithNoArg()"));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QCoreApplication::processEvents();
emitter.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 1);
trans->setSignal("signalWithIntArg(int)");
QCOMPARE(trans->signal(), QByteArray("signalWithIntArg(int)"));
machine.start();
QCoreApplication::processEvents();
emitter.emitSignalWithIntArg(123);
QTRY_COMPARE(finishedSpy.count(), 2);
}
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
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->senderReceived(), (QObject*)&emitter);
QCOMPARE(trans->signalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithIntArg(int)"));
QCOMPARE(trans->argumentsReceived().size(), 1);
QCOMPARE(trans->argumentsReceived().at(0).toInt(), 123);
}
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
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->senderReceived(), (QObject*)&emitter);
QCOMPARE(trans->signalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithStringArg(QString)"));
QCOMPARE(trans->argumentsReceived().size(), 1);
QCOMPARE(trans->argumentsReceived().at(0).toString(), testString);
}
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
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);
}
// Multiple transitions for same (object,signal)
{
QStateMachine machine;
SignalEmitter emitter;
QState *s0 = new QState(&machine);
QState *s1 = new QState(&machine);
QSignalTransition *t0 = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
QSignalTransition *t1 = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s0);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
emitter.emitSignalWithNoArg();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
s0->removeTransition(t0);
emitter.emitSignalWithNoArg();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
emitter.emitSignalWithNoArg();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
s1->removeTransition(t1);
emitter.emitSignalWithNoArg();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
s0->addTransition(t0);
s1->addTransition(t1);
emitter.emitSignalWithNoArg();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
}
// multiple signal transitions from same source
{
QStateMachine machine;
SignalEmitter emitter;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
QFinalState *s2 = new QFinalState(&machine);
s0->addTransition(&emitter, SIGNAL(signalWithIntArg(int)), s2);
QFinalState *s3 = new QFinalState(&machine);
s0->addTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s3);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
emitter.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.start();
QTRY_COMPARE(startedSpy.count(), 2);
emitter.emitSignalWithIntArg(123);
QTRY_COMPARE(finishedSpy.count(), 2);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
machine.start();
QTRY_COMPARE(startedSpy.count(), 3);
emitter.emitSignalWithStringArg("hello");
QTRY_COMPARE(finishedSpy.count(), 3);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s3));
}
// signature normalization
{
QStateMachine machine;
SignalEmitter emitter;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
QSignalTransition *t0 = s0->addTransition(&emitter, SIGNAL( signalWithNoArg( ) ), s1);
QVERIFY(t0 != 0);
QCOMPARE(t0->signal(), QByteArray(SIGNAL( signalWithNoArg( ) )));
QSignalTransition *t1 = s0->addTransition(&emitter, SIGNAL( signalWithStringArg( const QString & ) ), s1);
QVERIFY(t1 != 0);
QCOMPARE(t1->signal(), QByteArray(SIGNAL( signalWithStringArg( const QString & ) )));
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(finishedSpy.count(), 0);
emitter.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 1);
}
}
class TestEventTransition : public QEventTransition
{
public:
TestEventTransition(QState *sourceState = 0)
: QEventTransition(sourceState),
m_eventSource(0), m_eventType(QEvent::None)
{}
TestEventTransition(QObject *object, QEvent::Type type,
QAbstractState *target)
: QEventTransition(object, type),
m_eventSource(0), m_eventType(QEvent::None)
{ setTargetState(target); }
QObject *eventSourceReceived() const {
return m_eventSource;
}
QEvent::Type eventTypeReceived() const {
return m_eventType;
}
protected:
bool eventTest(QEvent *e) {
if (!QEventTransition::eventTest(e))
return false;
QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(e);
m_eventSource = we->object();
m_eventType = we->event()->type();
return true;
}
private:
QObject *m_eventSource;
QEvent::Type m_eventType;
};
void tst_QStateMachine::eventTransitions()
{
QPushButton button;
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
QMouseEventTransition *trans;
trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton);
QCOMPARE(trans->targetState(), (QAbstractState*)0);
trans->setTargetState(s1);
QCOMPARE(trans->eventType(), QEvent::MouseButtonPress);
QCOMPARE(trans->button(), Qt::LeftButton);
QCOMPARE(trans->targetState(), (QAbstractState*)s1);
s0->addTransition(trans);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QCoreApplication::processEvents();
QTest::mousePress(&button, Qt::LeftButton);
QTRY_COMPARE(finishedSpy.count(), 1);
QTest::mousePress(&button, Qt::LeftButton);
trans->setEventType(QEvent::MouseButtonRelease);
QCOMPARE(trans->eventType(), QEvent::MouseButtonRelease);
machine.start();
QCoreApplication::processEvents();
QTest::mouseRelease(&button, Qt::LeftButton);
QTRY_COMPARE(finishedSpy.count(), 2);
machine.start();
QCoreApplication::processEvents();
trans->setEventType(QEvent::MouseButtonPress);
QTest::mousePress(&button, Qt::LeftButton);
QTRY_COMPARE(finishedSpy.count(), 3);
QPushButton button2;
machine.start();
QCoreApplication::processEvents();
trans->setEventSource(&button2);
QTest::mousePress(&button2, Qt::LeftButton);
QTRY_COMPARE(finishedSpy.count(), 4);
}
for (int x = 0; x < 2; ++x) {
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
QEventTransition *trans;
if (x == 0) {
trans = new QEventTransition();
QCOMPARE(trans->eventSource(), (QObject*)0);
QCOMPARE(trans->eventType(), QEvent::None);
trans->setEventSource(&button);
trans->setEventType(QEvent::MouseButtonPress);
trans->setTargetState(s1);
} else if (x == 1) {
trans = new QEventTransition(&button, QEvent::MouseButtonPress);
trans->setTargetState(s1);
}
QCOMPARE(trans->eventSource(), (QObject*)&button);
QCOMPARE(trans->eventType(), QEvent::MouseButtonPress);
QCOMPARE(trans->targetState(), (QAbstractState*)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);
QFinalState *s1 = new QFinalState(&machine);
QMouseEventTransition *trans = new QMouseEventTransition();
QCOMPARE(trans->eventSource(), (QObject*)0);
QCOMPARE(trans->eventType(), QEvent::None);
QCOMPARE(trans->button(), Qt::NoButton);
trans->setEventSource(&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);
QFinalState *s1 = new QFinalState(&machine);
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);
QFinalState *s1 = new QFinalState(&machine);
QKeyEventTransition *trans = new QKeyEventTransition();
QCOMPARE(trans->eventSource(), (QObject*)0);
QCOMPARE(trans->eventType(), QEvent::None);
QCOMPARE(trans->key(), 0);
trans->setEventSource(&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);
}
// Multiple transitions for same (object,event)
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QState *s1 = new QState(&machine);
QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress);
t0->setTargetState(s1);
s0->addTransition(t0);
QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonPress);
t1->setTargetState(s0);
s1->addTransition(t1);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
QTest::mousePress(&button, Qt::LeftButton);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
s0->removeTransition(t0);
QTest::mousePress(&button, Qt::LeftButton);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
QTest::mousePress(&button, Qt::LeftButton);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
s1->removeTransition(t1);
QTest::mousePress(&button, Qt::LeftButton);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s0));
s0->addTransition(t0);
s1->addTransition(t1);
QTest::mousePress(&button, Qt::LeftButton);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
}
// multiple event transitions from same source
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
QFinalState *s2 = new QFinalState(&machine);
QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress);
t0->setTargetState(s1);
s0->addTransition(t0);
QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonRelease);
t1->setTargetState(s2);
s0->addTransition(t1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QTest::mousePress(&button, Qt::LeftButton);
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.start();
QTRY_COMPARE(startedSpy.count(), 2);
QTest::mouseRelease(&button, Qt::LeftButton);
QTRY_COMPARE(finishedSpy.count(), 2);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
}
// custom event
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
QEventTransition *trans = new QEventTransition(&button, QEvent::Type(QEvent::User+1));
trans->setTargetState(s1);
s0->addTransition(trans);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
machine.setInitialState(s0);
machine.start();
QTest::ignoreMessage(QtWarningMsg, "QObject event transitions are not supported for custom types");
QTRY_COMPARE(startedSpy.count(), 1);
}
// custom transition
{
QStateMachine machine;
QState *s0 = new QState(&machine);
QFinalState *s1 = new QFinalState(&machine);
TestEventTransition *trans = new TestEventTransition(&button, QEvent::MouseButtonPress, s1);
s0->addTransition(trans);
QCOMPARE(trans->eventSourceReceived(), (QObject*)0);
QCOMPARE(trans->eventTypeReceived(), QEvent::None);
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.setInitialState(s0);
machine.start();
QCoreApplication::processEvents();
QTest::mousePress(&button, Qt::LeftButton);
QCoreApplication::processEvents();
QTRY_COMPARE(finishedSpy.count(), 1);
QCOMPARE(trans->eventSourceReceived(), (QObject*)&button);
QCOMPARE(trans->eventTypeReceived(), QEvent::MouseButtonPress);
}
}
void tst_QStateMachine::graphicsSceneEventTransitions()
{
QGraphicsScene scene;
QGraphicsTextItem *textItem = scene.addText("foo");
QStateMachine machine;
QState *s1 = new QState(&machine);
QFinalState *s2 = new QFinalState(&machine);
QEventTransition *t = new QEventTransition(textItem, QEvent::GraphicsSceneMouseMove);
t->setTargetState(s2);
s1->addTransition(t);
machine.setInitialState(s1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QVERIFY(finishedSpy.count() == 0);
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
scene.sendEvent(textItem, &mouseEvent);
QTRY_COMPARE(finishedSpy.count(), 1);
}
void tst_QStateMachine::historyStates()
{
for (int x = 0; x < 2; ++x) {
QStateMachine machine;
QState *root = &machine;
QState *s0 = new QState(root);
QState *s00 = new QState(s0);
QState *s01 = new QState(s0);
QHistoryState *s0h;
if (x == 0) {
s0h = new QHistoryState(s0);
QCOMPARE(s0h->historyType(), QHistoryState::ShallowHistory);
s0h->setHistoryType(QHistoryState::DeepHistory);
} else {
s0h = new QHistoryState(QHistoryState::DeepHistory, s0);
}
QCOMPARE(s0h->historyType(), QHistoryState::DeepHistory);
s0h->setHistoryType(QHistoryState::ShallowHistory);
QCOMPARE(s0h->historyType(), QHistoryState::ShallowHistory);
QCOMPARE(s0h->defaultState(), (QAbstractState*)0);
s0h->setDefaultState(s00);
QCOMPARE(s0h->defaultState(), (QAbstractState*)s00);
QString warning;
warning.sprintf("QHistoryState::setDefaultState: state %p does not belong to this history state's group (%p)", s0, s0);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
s0h->setDefaultState(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::startAndStop()
{
QStateMachine machine;
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
QVERIFY(!machine.isRunning());
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start: No initial state set for machine. Refusing to start.");
machine.start();
QCOMPARE(startedSpy.count(), 0);
QCOMPARE(stoppedSpy.count(), 0);
QCOMPARE(finishedSpy.count(), 0);
QVERIFY(!machine.isRunning());
machine.stop();
QCOMPARE(startedSpy.count(), 0);
QCOMPARE(stoppedSpy.count(), 0);
QCOMPARE(finishedSpy.count(), 0);
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
machine.start();
QTRY_COMPARE(machine.isRunning(), true);
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(stoppedSpy.count(), 0);
QCOMPARE(finishedSpy.count(), 0);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(s1));
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start(): already running");
machine.start();
machine.stop();
QTRY_COMPARE(machine.isRunning(), false);
QTRY_COMPARE(stoppedSpy.count(), 1);
QCOMPARE(startedSpy.count(), 1);
QCOMPARE(finishedSpy.count(), 0);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.start();
machine.stop();
QTRY_COMPARE(startedSpy.count(), 2);
QCOMPARE(stoppedSpy.count(), 2);
}
void tst_QStateMachine::targetStateWithNoParent()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
s1->setObjectName("s1");
QState s2;
s1->addTransition(&s2);
machine.setInitialState(s1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: No common ancestor for targets and source of transition from state 's1'");
QTRY_COMPARE(startedSpy.count(), 1);
QCOMPARE(machine.isRunning(), false);
QCOMPARE(stoppedSpy.count(), 1);
QCOMPARE(finishedSpy.count(), 0);
QCOMPARE(machine.error(), QStateMachine::NoCommonAncestorForTransitionError);
}
void tst_QStateMachine::targetStateDeleted()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
s1->setObjectName("s1");
QState *s2 = new QState(&machine);
QAbstractTransition *trans = s1->addTransition(s2);
delete s2;
QCOMPARE(trans->targetState(), (QAbstractState*)0);
QVERIFY(trans->targetStates().isEmpty());
}
void tst_QStateMachine::defaultGlobalRestorePolicy()
{
QStateMachine machine;
QObject *propertyHolder = new QObject(&machine);
propertyHolder->setProperty("a", 1);
propertyHolder->setProperty("b", 2);
QState *s1 = new QState(&machine);
s1->assignProperty(propertyHolder, "a", 3);
QState *s2 = new QState(&machine);
s2->assignProperty(propertyHolder, "b", 4);
QState *s3 = new QState(&machine);
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::noInitialStateForInitialState()
{
QStateMachine machine;
QState *initialState = new QState(&machine);
initialState->setObjectName("initialState");
machine.setInitialState(initialState);
QState *childState = new QState(initialState);
(void)childState;
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
"Missing initial state in compound state 'initialState'");
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.isRunning(), false);
QCOMPARE(int(machine.error()), int(QStateMachine::NoInitialStateError));
}
/*
void tst_QStateMachine::restorePolicyNotInherited()
{
QStateMachine machine;
QObject *propertyHolder = new QObject();
propertyHolder->setProperty("a", 1);
propertyHolder->setProperty("b", 2);
QState *parentState = new QState(&machine);
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::globalRestorePolicySetToDontRestore()
{
QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::DontRestoreProperties);
QObject *propertyHolder = new QObject(&machine);
propertyHolder->setProperty("a", 1);
propertyHolder->setProperty("b", 2);
QState *s1 = new QState(&machine);
s1->assignProperty(propertyHolder, "a", 3);
QState *s2 = new QState(&machine);
s2->assignProperty(propertyHolder, "b", 4);
QState *s3 = new QState(&machine);
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);
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(&machine);
propertyHolder->setProperty("a", 1);
propertyHolder->setProperty("b", 2);
QState *s1 = new QState(&machine);
s1->assignProperty(propertyHolder, "a", 3);
QState *s2 = new QState(&machine);
s2->assignProperty(propertyHolder, "b", 4);
QState *s3 = new QState(&machine);
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);
s1->setRestorePolicy(QState::RestoreProperties);
s1->assignProperty(propertyHolder, "a", 3);
QState *s2 = new QState(&machine);
s2->assignProperty(propertyHolder, "a", 4);
QState *s3 = new QState(&machine);
QState *s4 = new QState(&machine);
s4->assignProperty(propertyHolder, "a", 5);
QState *s5 = new QState(&machine);
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);
QState *s2 = new QState(&machine);
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(&machine);
object->setProperty("fooBar", 1.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
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);
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(&machine);
object->setProperty("foo", 1.0);
object->setProperty("bar", 3.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
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);
QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
s2->addTransition(s2, SIGNAL(propertiesAssigned()), 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(&machine);
object->setProperty("foo", 1.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 5.0);
QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2);
EventTransition *trans = new EventTransition(QEvent::User, s2);
s1->addTransition(trans);
trans->addAnimation(fooAnimation);
QState *s3 = new QState(&machine);
QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
s2->addTransition(fooAnimation, SIGNAL(finished()), s3);
QState *s4 = new QState(&machine);
s4->assignProperty(object, "foo", 2.0);
QPropertyAnimation *fooAnimation2 = new QPropertyAnimation(object, "foo", s4);
trans = new EventTransition(QEvent::User, s4);
s3->addTransition(trans);
trans->addAnimation(fooAnimation2);
QState *s5 = new QState(&machine);
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(&machine);
object->setProperty("foo", 1.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 5.0);
QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2);
EventTransition *trans = new EventTransition(QEvent::User, s2);
s1->addTransition(trans);
trans->addAnimation(fooAnimation);
QState *s3 = new QState(&machine);
QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
s2->addTransition(fooAnimation, SIGNAL(finished()), s3);
QState *s4 = new QState(&machine);
s4->assignProperty(object, "foo", 2.0);
trans = new EventTransition(QEvent::User, s4);
s3->addTransition(trans);
trans->addAnimation(fooAnimation);
QState *s5 = new QState(&machine);
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(&machine);
object->setProperty("foo", 1.0);
object->setProperty("bar", 3.0);
SlotCalledCounter counter;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
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 = new EventTransition(QEvent::User, s2Child2);
s2Child->addTransition(at);
QPropertyAnimation *animation = new QPropertyAnimation(object, "bar", s2);
animation->setDuration(2000);
connect(animation, SIGNAL(finished()), &counter, SLOT(slot()));
at->addAnimation(animation);
at = new EventTransition(QEvent::User, s2);
s1->addTransition(at);
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);
s2->addTransition(s2Child, SIGNAL(propertiesAssigned()), 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("foo").toDouble(), 2.0);
QCOMPARE(object->property("bar").toDouble(), 10.0);
QCOMPARE(counter.counter, 2);
}
void tst_QStateMachine::propertiesAssignedSignalTransitionsReuseAnimationGroup()
{
QStateMachine machine;
QObject *object = new QObject(&machine);
object->setProperty("foo", 0);
QState *s1 = new QState(&machine);
s1->assignProperty(object, "foo", 123);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 456);
QState *s3 = new QState(&machine);
s3->assignProperty(object, "foo", 789);
QFinalState *s4 = new QFinalState(&machine);
QParallelAnimationGroup animationGroup;
animationGroup.addAnimation(new QPropertyAnimation(object, "foo"));
QSignalSpy animationFinishedSpy(&animationGroup, SIGNAL(finished()));
s1->addTransition(s1, SIGNAL(propertiesAssigned()), s2)->addAnimation(&animationGroup);
s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3)->addAnimation(&animationGroup);
s3->addTransition(s3, SIGNAL(propertiesAssigned()), s4);
machine.setInitialState(s1);
QSignalSpy machineFinishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(machineFinishedSpy.count(), 1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s4));
QCOMPARE(object->property("foo").toInt(), 789);
QCOMPARE(animationFinishedSpy.count(), 2);
}
void tst_QStateMachine::animatedGlobalRestoreProperty()
{
QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
QObject *object = new QObject(&machine);
object->setProperty("foo", 1.0);
SlotCalledCounter counter;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
QState *s4 = new QState(&machine);
QObject::connect(s4, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
QAbstractTransition *at = new EventTransition(QEvent::User, s2);
s1->addTransition(at);
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(&machine);
object->setProperty("foo", 1.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QPropertyAnimation *anim = new QPropertyAnimation(object, "foo");
anim->setEndValue(10.0);
EventTransition *trans = new EventTransition(QEvent::User, s2);
s1->addTransition(trans);
trans->addAnimation(anim);
QState *s3 = new QState(&machine);
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);
delete anim;
}
void tst_QStateMachine::addDefaultAnimation()
{
QStateMachine machine;
QObject *object = new QObject();
object->setProperty("foo", 1.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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);
delete object;
}
void tst_QStateMachine::addDefaultAnimationWithUnusedAnimation()
{
QStateMachine machine;
QObject *object = new QObject(&machine);
object->setProperty("foo", 1.0);
object->setProperty("bar", 2.0);
SlotCalledCounter counter;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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::removeDefaultAnimation()
{
QStateMachine machine;
QObject propertyHolder;
propertyHolder.setProperty("foo", 0);
QCOMPARE(machine.defaultAnimations().size(), 0);
QPropertyAnimation *anim = new QPropertyAnimation(&propertyHolder, "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(&propertyHolder, "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);
delete anim;
delete anim2;
}
void tst_QStateMachine::overrideDefaultAnimationWithSpecific()
{
QStateMachine machine;
QObject *object = new QObject(&machine);
object->setProperty("foo", 1.0);
SlotCalledCounter counter;
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
QAbstractTransition *at = new EventTransition(QEvent::User, s2);
s1->addTransition(at);
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
delete defaultAnimation;
delete moreSpecificAnimation;
}
/*
void tst_QStateMachine::addDefaultAnimationForSource()
{
QStateMachine machine;
QObject *object = new QObject();
object->setProperty("foo", 1.0);
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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::removeDefaultAnimationForSource()
{
QStateMachine machine;
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 0);
QPropertyAnimation *anim = new QPropertyAnimation(this, "foo");
machine.addDefaultAnimationForSourceState(&machine, anim);
QCOMPARE(machine.defaultAnimations().size(), 0);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 0);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 1);
QVERIFY(machine.defaultAnimationsForSourceState(&machine).contains(anim));
machine.removeDefaultAnimationForTargetState(&machine, anim);
QCOMPARE(machine.defaultAnimations().size(), 0);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 0);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 1);
QVERIFY(machine.defaultAnimationsForSourceState(&machine).contains(anim));
machine.removeDefaultAnimationForSourceState(&machine, anim);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 0);
machine.addDefaultAnimationForSourceState(&machine, anim);
QPropertyAnimation *anim2 = new QPropertyAnimation(this, "foo");
machine.addDefaultAnimationForSourceState(&machine, anim2);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 2);
QVERIFY(machine.defaultAnimationsForSourceState(&machine).contains(anim));
QVERIFY(machine.defaultAnimationsForSourceState(&machine).contains(anim2));
machine.removeDefaultAnimationForSourceState(&machine, anim);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 1);
QVERIFY(machine.defaultAnimationsForSourceState(&machine).contains(anim2));
machine.removeDefaultAnimationForSourceState(&machine, anim2);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 0);
}
void tst_QStateMachine::removeDefaultAnimationForTarget()
{
QStateMachine machine;
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 0);
QPropertyAnimation *anim = new QPropertyAnimation(this, "foo");
machine.addDefaultAnimationForTargetState(&machine, anim);
QCOMPARE(machine.defaultAnimations().size(), 0);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 0);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 1);
QVERIFY(machine.defaultAnimationsForTargetState(&machine).contains(anim));
machine.removeDefaultAnimationForSourceState(&machine, anim);
QCOMPARE(machine.defaultAnimations().size(), 0);
QCOMPARE(machine.defaultAnimationsForSourceState(&machine).size(), 0);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 1);
QVERIFY(machine.defaultAnimationsForTargetState(&machine).contains(anim));
machine.removeDefaultAnimationForTargetState(&machine, anim);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 0);
machine.addDefaultAnimationForTargetState(&machine, anim);
QPropertyAnimation *anim2 = new QPropertyAnimation(this, "foo");
machine.addDefaultAnimationForTargetState(&machine, anim2);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 2);
QVERIFY(machine.defaultAnimationsForTargetState(&machine).contains(anim));
QVERIFY(machine.defaultAnimationsForTargetState(&machine).contains(anim2));
machine.removeDefaultAnimationForTargetState(&machine, anim);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 1);
QVERIFY(machine.defaultAnimationsForTargetState(&machine).contains(anim2));
machine.removeDefaultAnimationForTargetState(&machine, anim2);
QCOMPARE(machine.defaultAnimationsForTargetState(&machine).size(), 0);
}
void tst_QStateMachine::overrideDefaultAnimationWithSource()
{
QStateMachine machine;
QObject *object = new QObject();
object->setProperty("foo", 1.0);
SlotCalledCounter counter;
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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::overrideDefaultSourceAnimationWithSpecific()
{
QStateMachine machine;
QObject *object = new QObject();
object->setProperty("foo", 1.0);
SlotCalledCounter counter;
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(object, "foo", 2.0);
QState *s3 = new QState(&machine);
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.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::parallelStateAssignmentsDone()
{
QStateMachine machine;
QObject *propertyHolder = new QObject(&machine);
propertyHolder->setProperty("foo", 123);
propertyHolder->setProperty("bar", 456);
propertyHolder->setProperty("zoot", 789);
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *parallelState = new QState(QState::ParallelStates, &machine);
parallelState->assignProperty(propertyHolder, "foo", 321);
QState *s2 = new QState(parallelState);
s2->assignProperty(propertyHolder, "bar", 654);
QState *s3 = new QState(parallelState);
s3->assignProperty(propertyHolder, "zoot", 987);
s1->addTransition(new EventTransition(QEvent::User, parallelState));
machine.start();
QCoreApplication::processEvents();
QCOMPARE(propertyHolder->property("foo").toInt(), 123);
QCOMPARE(propertyHolder->property("bar").toInt(), 456);
QCOMPARE(propertyHolder->property("zoot").toInt(), 789);
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(propertyHolder->property("foo").toInt(), 321);
QCOMPARE(propertyHolder->property("bar").toInt(), 654);
QCOMPARE(propertyHolder->property("zoot").toInt(), 987);
}
void tst_QStateMachine::transitionsFromParallelStateWithNoChildren()
{
QStateMachine machine;
QState *parallelState = new QState(QState::ParallelStates, &machine);
machine.setInitialState(parallelState);
QState *s1 = new QState(&machine);
parallelState->addTransition(new EventTransition(QEvent::User, s1));
machine.start();
QCoreApplication::processEvents();
QCOMPARE(1, machine.configuration().size());
QVERIFY(machine.configuration().contains(parallelState));
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(1, machine.configuration().size());
QVERIFY(machine.configuration().contains(s1));
}
void tst_QStateMachine::parallelStateTransition()
{
QStateMachine machine;
QState *parallelState = new QState(QState::ParallelStates, &machine);
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);
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));
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s1OtherChild));
QVERIFY(machine.configuration().contains(s2InitialChild));
QCOMPARE(machine.configuration().size(), 5);
}
void tst_QStateMachine::nestedRestoreProperties()
{
QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
QObject *propertyHolder = new QObject(&machine);
propertyHolder->setProperty("foo", 1);
propertyHolder->setProperty("bar", 2);
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(propertyHolder, "foo", 3);
QState *s21 = new QState(s2);
s21->assignProperty(propertyHolder, "bar", 4);
s2->setInitialState(s21);
QState *s22 = new QState(s2);
s22->assignProperty(propertyHolder, "bar", 5);
s1->addTransition(new EventTransition(QEvent::User, s2));
s21->addTransition(new EventTransition(QEvent::User, s22));
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
QCOMPARE(propertyHolder->property("foo").toInt(), 1);
QCOMPARE(propertyHolder->property("bar").toInt(), 2);
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s21));
QCOMPARE(propertyHolder->property("foo").toInt(), 3);
QCOMPARE(propertyHolder->property("bar").toInt(), 4);
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s22));
QCOMPARE(propertyHolder->property("foo").toInt(), 3);
QCOMPARE(propertyHolder->property("bar").toInt(), 5);
}
void tst_QStateMachine::nestedRestoreProperties2()
{
QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
QObject *propertyHolder = new QObject(&machine);
propertyHolder->setProperty("foo", 1);
propertyHolder->setProperty("bar", 2);
QState *s1 = new QState(&machine);
machine.setInitialState(s1);
QState *s2 = new QState(&machine);
s2->assignProperty(propertyHolder, "foo", 3);
QState *s21 = new QState(s2);
s21->assignProperty(propertyHolder, "bar", 4);
s2->setInitialState(s21);
QState *s22 = new QState(s2);
s22->assignProperty(propertyHolder, "foo", 6);
s22->assignProperty(propertyHolder, "bar", 5);
s1->addTransition(new EventTransition(QEvent::User, s2));
s21->addTransition(new EventTransition(QEvent::User, s22));
s22->addTransition(new EventTransition(QEvent::User, s21));
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
QCOMPARE(propertyHolder->property("foo").toInt(), 1);
QCOMPARE(propertyHolder->property("bar").toInt(), 2);
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s21));
QCOMPARE(propertyHolder->property("foo").toInt(), 3);
QCOMPARE(propertyHolder->property("bar").toInt(), 4);
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s22));
QCOMPARE(propertyHolder->property("foo").toInt(), 6);
QCOMPARE(propertyHolder->property("bar").toInt(), 5);
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s21));
QCOMPARE(propertyHolder->property("foo").toInt(), 3);
QCOMPARE(propertyHolder->property("bar").toInt(), 4);
}
void tst_QStateMachine::nestedStateMachines()
{
QStateMachine machine;
QState *group = new QState(&machine);
group->setChildMode(QState::ParallelStates);
QStateMachine *subMachines[3];
for (int i = 0; i < 3; ++i) {
QState *subGroup = new QState(group);
QStateMachine *subMachine = new QStateMachine(subGroup);
{
QState *initial = new QState(subMachine);
QFinalState *done = new QFinalState(subMachine);
initial->addTransition(new EventTransition(QEvent::User, done));
subMachine->setInitialState(initial);
}
QFinalState *subMachineDone = new QFinalState(subGroup);
subMachine->addTransition(subMachine, SIGNAL(finished()), subMachineDone);
subGroup->setInitialState(subMachine);
subMachines[i] = subMachine;
}
QFinalState *final = new QFinalState(&machine);
group->addTransition(group, SIGNAL(finished()), final);
machine.setInitialState(group);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QTRY_COMPARE(machine.configuration().count(), 1+2*3);
QVERIFY(machine.configuration().contains(group));
for (int i = 0; i < 3; ++i)
QVERIFY(machine.configuration().contains(subMachines[i]));
QCoreApplication::processEvents(); // starts the submachines
for (int i = 0; i < 3; ++i)
subMachines[i]->postEvent(new QEvent(QEvent::User));
QTRY_COMPARE(finishedSpy.count(), 1);
}
void tst_QStateMachine::goToState()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
machine.setInitialState(s1);
QSignalSpy startedSpy(&machine, SIGNAL(started()));
machine.start();
QTRY_COMPARE(startedSpy.count(), 1);
QStateMachinePrivate::get(&machine)->goToState(s2);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
QStateMachinePrivate::get(&machine)->goToState(s2);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
QStateMachinePrivate::get(&machine)->goToState(s1);
QStateMachinePrivate::get(&machine)->goToState(s2);
QStateMachinePrivate::get(&machine)->goToState(s1);
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
// go to state in group
QState *s2_1 = new QState(s2);
s2->setInitialState(s2_1);
QStateMachinePrivate::get(&machine)->goToState(s2_1);
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s2_1));
}
class CloneSignalTransition : public QSignalTransition
{
public:
CloneSignalTransition(QObject *sender, const char *signal, QAbstractState *target)
: QSignalTransition(sender, signal)
{
setTargetState(target);
}
void onTransition(QEvent *e)
{
QSignalTransition::onTransition(e);
QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
eventSignalIndex = se->signalIndex();
}
int eventSignalIndex;
};
void tst_QStateMachine::task260403_clonedSignals()
{
SignalEmitter emitter;
QStateMachine machine;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
CloneSignalTransition *t1 = new CloneSignalTransition(&emitter, SIGNAL(signalWithDefaultArg()), s2);
s1->addTransition(t1);
machine.setInitialState(s1);
machine.start();
QTest::qWait(1);
emitter.emitSignalWithDefaultArg();
QTest::qWait(1);
QCOMPARE(t1->eventSignalIndex, emitter.metaObject()->indexOfSignal("signalWithDefaultArg()"));
}
class EventPosterThread : public QThread
{
Q_OBJECT
public:
EventPosterThread(QStateMachine *machine, QObject *parent = 0)
: QThread(parent), m_machine(machine), m_count(0)
{
moveToThread(this);
QObject::connect(m_machine, SIGNAL(started()),
this, SLOT(postEvent()));
}
protected:
virtual void run()
{
exec();
}
private Q_SLOTS:
void postEvent()
{
m_machine->postEvent(new QEvent(QEvent::User));
if (++m_count < 10000)
QTimer::singleShot(0, this, SLOT(postEvent()));
else
quit();
}
private:
QStateMachine *m_machine;
int m_count;
};
void tst_QStateMachine::postEventFromOtherThread()
{
QStateMachine machine;
EventPosterThread poster(&machine);
StringEventPoster *s1 = new StringEventPoster("foo", &machine);
s1->addTransition(new EventTransition(QEvent::User, s1));
QFinalState *f = new QFinalState(&machine);
s1->addTransition(&poster, SIGNAL(finished()), f);
machine.setInitialState(s1);
poster.start();
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTRY_COMPARE(finishedSpy.count(), 1);
}
void tst_QStateMachine::eventFilterForApplication()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
{
machine.setInitialState(s1);
}
QState *s2 = new QState(&machine);
QEventTransition *transition = new QEventTransition(QCoreApplication::instance(),
QEvent::ApplicationActivate);
transition->setTargetState(s2);
s1->addTransition(transition);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
QCoreApplication::postEvent(QCoreApplication::instance(),
new QEvent(QEvent::ApplicationActivate));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s2));
}
void tst_QStateMachine::eventClassesExported()
{
// make sure this links
QStateMachine::WrappedEvent *wrappedEvent = new QStateMachine::WrappedEvent(0, 0);
QStateMachine::SignalEvent *signalEvent = new QStateMachine::SignalEvent(0, 0, QList<QVariant>());
}
QTEST_MAIN(tst_QStateMachine)
#include "tst_qstatemachine.moc"