summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/statemachine/qabstracttransition.cpp33
-rw-r--r--src/corelib/statemachine/qabstracttransition_p.h3
-rw-r--r--src/corelib/statemachine/qstate.cpp31
-rw-r--r--src/corelib/statemachine/qstatemachine.cpp18
-rw-r--r--src/corelib/statemachine/qstatemachine_p.h1
-rw-r--r--tests/auto/qstatemachine/tst_qstatemachine.cpp97
6 files changed, 165 insertions, 18 deletions
diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp
index 1897aa6..6ddb416 100644
--- a/src/corelib/statemachine/qabstracttransition.cpp
+++ b/src/corelib/statemachine/qabstracttransition.cpp
@@ -179,8 +179,7 @@ QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets,
#ifdef QT_STATEMACHINE_SOLUTION
d_ptr->q_ptr = this;
#endif
- Q_D(QAbstractTransition);
- d->targetStates = targets;
+ setTargetStates(targets);
}
/*!
@@ -220,8 +219,7 @@ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd,
#ifdef QT_STATEMACHINE_SOLUTION
d_ptr->q_ptr = this;
#endif
- Q_D(QAbstractTransition);
- d->targetStates = targets;
+ setTargetStates(targets);
}
/*!
@@ -265,7 +263,7 @@ void QAbstractTransition::setTargetState(QAbstractState* target)
if (!target)
d->targetStates.clear();
else
- d->targetStates = QList<QAbstractState*>() << target;
+ setTargetStates(QList<QAbstractState*>() << target);
}
/*!
@@ -275,7 +273,13 @@ void QAbstractTransition::setTargetState(QAbstractState* target)
QList<QAbstractState*> QAbstractTransition::targetStates() const
{
Q_D(const QAbstractTransition);
- return d->targetStates;
+ QList<QAbstractState*> result;
+ for (int i = 0; i < d->targetStates.size(); ++i) {
+ QAbstractState *target = d->targetStates.at(i);
+ if (target)
+ result.append(target);
+ }
+ return result;
}
/*!
@@ -284,7 +288,22 @@ QList<QAbstractState*> QAbstractTransition::targetStates() const
void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets)
{
Q_D(QAbstractTransition);
- d->targetStates = targets;
+
+ for (int i=0; i<targets.size(); ++i) {
+ QAbstractState *target = targets.at(i);
+ if (!target) {
+ qWarning("QAbstractTransition::setTargetStates: target state(s) cannot be null");
+ return;
+ }
+ if (target->machine() != 0 && target->machine()->rootState() == target) {
+ qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition");
+ return;
+ }
+ }
+
+ d->targetStates.clear();
+ for (int i = 0; i < targets.size(); ++i)
+ d->targetStates.append(targets.at(i));
}
/*!
diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h
index b4e1c88..eb0ec21 100644
--- a/src/corelib/statemachine/qabstracttransition_p.h
+++ b/src/corelib/statemachine/qabstracttransition_p.h
@@ -58,6 +58,7 @@
#endif
#include <QtCore/qlist.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -83,7 +84,7 @@ public:
QState *sourceState() const;
QStateMachine *machine() const;
- QList<QAbstractState*> targetStates;
+ QList<QPointer<QAbstractState> > targetStates;
#ifndef QT_NO_ANIMATION
QList<QAbstractAnimation*> animations;
diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp
index 4c9e033..5f61865 100644
--- a/src/corelib/statemachine/qstate.cpp
+++ b/src/corelib/statemachine/qstate.cpp
@@ -280,11 +280,15 @@ QAbstractState *QState::errorState() const
void QState::setErrorState(QAbstractState *state)
{
Q_D(QState);
- if (state != 0 && QAbstractStatePrivate::get(state)->machine() != d->machine()) {
+ if (state != 0 && state->machine() != machine()) {
qWarning("QState::setErrorState: error state cannot belong "
"to a different state machine");
return;
}
+ if (state->machine() != 0 && state->machine()->rootState() == state) {
+ qWarning("QStateMachine::setErrorState: root state cannot be error state");
+ return;
+ }
d->errorState = state;
}
@@ -301,7 +305,14 @@ QAbstractTransition *QState::addTransition(QAbstractTransition *transition)
qWarning("QState::addTransition: cannot add null transition");
return 0;
}
- const QList<QAbstractState*> &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
+
+ // machine() will always be non-null for root state
+ if (machine() != 0 && machine()->rootState() == this) {
+ qWarning("QState::addTransition: cannot add transition from root state");
+ return 0;
+ }
+
+ const QList<QPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
for (int i = 0; i < targets.size(); ++i) {
QAbstractState *t = targets.at(i);
if (!t) {
@@ -335,6 +346,15 @@ QSignalTransition *QState::addTransition(QObject *sender, const char *signal,
qWarning("QState::addTransition: signal cannot be null");
return 0;
}
+ if (!target) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return 0;
+ }
+ if (*signal && sender->metaObject()->indexOfSignal(signal+1) == -1) {
+ qWarning("QState::addTransition: no such signal %s::%s",
+ sender->metaObject()->className(), signal+1);
+ return 0;
+ }
QSignalTransition *trans = new QSignalTransition(sender, signal, QList<QAbstractState*>() << target);
addTransition(trans);
return trans;
@@ -361,9 +381,12 @@ protected:
*/
QAbstractTransition *QState::addTransition(QAbstractState *target)
{
+ if (!target) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return 0;
+ }
UnconditionalTransition *trans = new UnconditionalTransition(target);
- addTransition(trans);
- return trans;
+ return addTransition(trans);
}
/*!
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index 21e564c..24af8e4 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -1011,6 +1011,8 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta
}
Q_ASSERT(currentErrorState != 0);
+ Q_ASSERT(currentErrorState != rootState);
+
QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry);
}
@@ -1222,10 +1224,12 @@ void QStateMachinePrivate::_q_process()
break;
case Finished:
state = NotRunning;
+ unregisterAllTransitions();
emit q->finished();
break;
case Stopped:
state = NotRunning;
+ unregisterAllTransitions();
emit q->stopped();
break;
}
@@ -1356,6 +1360,20 @@ void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transit
#endif
}
+void QStateMachinePrivate::unregisterAllTransitions()
+{
+ {
+ QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState);
+ for (int i = 0; i < transitions.size(); ++i)
+ unregisterSignalTransition(transitions.at(i));
+ }
+ {
+ QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState);
+ for (int i = 0; i < transitions.size(); ++i)
+ unregisterEventTransition(transitions.at(i));
+ }
+}
+
#ifndef QT_NO_STATEMACHINE_EVENTFILTER
void QStateMachinePrivate::registerEventTransition(QEventTransition *transition)
{
diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h
index bb4a78c..47b139c 100644
--- a/src/corelib/statemachine/qstatemachine_p.h
+++ b/src/corelib/statemachine/qstatemachine_p.h
@@ -150,6 +150,7 @@ public:
void unregisterEventTransition(QEventTransition *transition);
#endif
void unregisterTransition(QAbstractTransition *transition);
+ void unregisterAllTransitions();
void handleTransitionSignal(const QObject *sender, int signalIndex,
void **args);
void scheduleProcess();
diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp
index edd6459..dbc67d1 100644
--- a/tests/auto/qstatemachine/tst_qstatemachine.cpp
+++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp
@@ -105,6 +105,8 @@ private slots:
void eventTransitions();
void historyStates();
void startAndStop();
+ void targetStateWithNoParent();
+ void targetStateDeleted();
void transitionToRootState();
void transitionEntersParent();
@@ -254,6 +256,8 @@ private:
void tst_QStateMachine::transitionToRootState()
{
+ s_countWarnings = false;
+
QStateMachine machine;
QState *initialState = new QState();
@@ -668,6 +672,8 @@ void tst_QStateMachine::errorStateHasErrors()
void tst_QStateMachine::errorStateIsRootState()
{
+ s_countWarnings = false;
+
QStateMachine machine;
machine.setErrorState(machine.rootState());
@@ -691,9 +697,8 @@ void tst_QStateMachine::errorStateIsRootState()
machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
- QEXPECT_FAIL("", "Covered by transitionToRootState test. Remove this when that test passes", Continue);
QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(initialState));
+ QVERIFY(machine.configuration().contains(machine.errorState()));
}
void tst_QStateMachine::errorStateEntersParentFirst()
@@ -1088,11 +1093,28 @@ void tst_QStateMachine::stateEntryAndExit()
QStateMachine machine;
TestState *s1 = new TestState(machine.rootState());
+ 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.rootState());
QFinalState *s3 = new QFinalState(machine.rootState());
TestTransition *t = new TestTransition(s2);
- s1->addTransition(t);
- s2->addTransition(s3);
+ QCOMPARE(s1->addTransition(t), (QAbstractTransition*)t);
+ {
+ QAbstractTransition *trans = s2->addTransition(s3);
+ QVERIFY(trans != 0);
+ QCOMPARE(trans->sourceState(), (QAbstractState*)s2);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s3);
+ s2->removeTransition(trans);
+ QCOMPARE(trans->sourceState(), (QAbstractState*)0);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s3);
+ QCOMPARE(s2->addTransition(trans), trans);
+ QCOMPARE(trans->sourceState(), (QAbstractState*)s2);
+ }
QSignalSpy startedSpy(&machine, SIGNAL(started()));
QSignalSpy stoppedSpy(&machine, SIGNAL(stopped()));
@@ -1308,6 +1330,8 @@ void tst_QStateMachine::assignPropertyWithAnimation()
QStateMachine machine;
QObject obj;
QState *s1 = new QState(machine.rootState());
+ QCOMPARE(s1->childMode(), QState::ExclusiveStates);
+ QCOMPARE(s1->initialState(), (QAbstractState*)0);
s1->setObjectName("s1");
s1->assignProperty(&obj, "foo", 123);
s1->assignProperty(&obj, "bar", 456);
@@ -1321,6 +1345,7 @@ void tst_QStateMachine::assignPropertyWithAnimation()
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");
@@ -1445,6 +1470,7 @@ 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);
@@ -1466,6 +1492,8 @@ void tst_QStateMachine::parallelStates()
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::allSourceToTargetConfigurations()
@@ -1576,9 +1604,28 @@ void tst_QStateMachine::signalTransitions()
{
QStateMachine machine;
QState *s0 = new QState(machine.rootState());
- QFinalState *s1 = new QFinalState(machine.rootState());
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: sender cannot be null");
+ QCOMPARE(s0->addTransition(0, SIGNAL(noSuchSignal()), 0), (QObject*)0);
+
SignalEmitter emitter;
- s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: signal cannot be null");
+ QCOMPARE(s0->addTransition(&emitter, 0, 0), (QObject*)0);
+
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state");
+ QCOMPARE(s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), 0), (QObject*)0);
+
+ QFinalState *s1 = new QFinalState(machine.rootState());
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: no such signal SignalEmitter::noSuchSignal()");
+ QCOMPARE(s0->addTransition(&emitter, SIGNAL(noSuchSignal()), s1), (QObject*)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);
@@ -1588,6 +1635,8 @@ void tst_QStateMachine::signalTransitions()
emitter.emitSignalWithNoArg();
QTRY_COMPARE(finishedSpy.count(), 1);
+
+ emitter.emitSignalWithNoArg();
}
{
QStateMachine machine;
@@ -1679,6 +1728,8 @@ void tst_QStateMachine::eventTransitions()
QCoreApplication::processEvents();
QTRY_COMPARE(finishedSpy.count(), 1);
+
+ QTest::mousePress(&button, Qt::LeftButton);
}
{
QStateMachine machine;
@@ -1868,6 +1919,40 @@ void tst_QStateMachine::startAndStop()
QVERIFY(machine.configuration().contains(s1));
}
+void tst_QStateMachine::targetStateWithNoParent()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(machine.rootState());
+ s1->setObjectName("s1");
+ QState *s2 = new QState();
+ 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(machine.isRunning(), true);
+ QTRY_COMPARE(startedSpy.count(), 1);
+ QCOMPARE(stoppedSpy.count(), 0);
+ QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(machine.errorState()));
+ QCOMPARE(machine.error(), QStateMachine::NoCommonAncestorForTransitionError);
+}
+
+void tst_QStateMachine::targetStateDeleted()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(machine.rootState());
+ s1->setObjectName("s1");
+ QState *s2 = new QState(machine.rootState());
+ QAbstractTransition *trans = s1->addTransition(s2);
+ delete s2;
+ QCOMPARE(trans->targetState(), (QAbstractState*)0);
+ QVERIFY(trans->targetStates().isEmpty());
+}
+
void tst_QStateMachine::defaultGlobalRestorePolicy()
{
QStateMachine machine;