statemachine: Fix dynamic transition registration edge cases
authorKent Hansen <kent.hansen@nokia.com>
Thu, 12 Jul 2012 17:40:04 +0000 (19:40 +0200)
committerQt by Nokia <qt-info@nokia.com>
Fri, 13 Jul 2012 10:09:42 +0000 (12:09 +0200)
Some of the transition constructors didn't call the maybeRegister()
function, causing the transitions to be ignored if they were created
when the state machine was running and the transition's source state
was active.

Added tests that cover all possible cases.

Change-Id: If1b593b127bd719e3be4e5a2e6949a780c4e97c3
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>
src/corelib/statemachine/qeventtransition.cpp
src/corelib/statemachine/qsignaltransition.cpp
tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp

index ed00d66..3c93281 100644 (file)
@@ -150,6 +150,7 @@ QEventTransition::QEventTransition(QObject *object, QEvent::Type type,
     d->registered = false;
     d->object = object;
     d->eventType = type;
+    d->maybeRegister();
 }
 
 /*!
@@ -171,6 +172,7 @@ QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object,
     d->registered = false;
     d->object = object;
     d->eventType = type;
+    d->maybeRegister();
 }
 
 /*!
index bbd7bc4..26d2add 100644 (file)
@@ -155,6 +155,7 @@ QSignalTransition::QSignalTransition(QObject *sender, const char *signal,
     Q_D(QSignalTransition);
     d->sender = sender;
     d->signal = signal;
+    d->maybeRegister();
 }
 
 /*!
index 88110b1..b14ab7d 100644 (file)
@@ -206,6 +206,8 @@ private slots:
 
     void multiTargetTransitionInsideParallelStateGroup();
     void signalTransitionNormalizeSignature();
+    void createSignalTransitionWhenRunning();
+    void createEventTransitionWhenRunning();
 };
 
 class TestState : public QState
@@ -4741,5 +4743,89 @@ void tst_QStateMachine::signalTransitionNormalizeSignature()
     QCOMPARE(t0->transitionArgumentsReceived().size(), 0);
 }
 
+void tst_QStateMachine::createSignalTransitionWhenRunning()
+{
+    QStateMachine machine;
+    QState *s1 = new QState(&machine);
+    machine.setInitialState(s1);
+    machine.start();
+    QTRY_VERIFY(machine.configuration().contains(s1));
+
+    // Create by addTransition()
+    QState *s2 = new QState(&machine);
+    SignalEmitter emitter;
+    QAbstractTransition *t1 = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
+    QCOMPARE(t1->sourceState(), s1);
+    emitter.emitSignalWithNoArg();
+    QTRY_VERIFY(machine.configuration().contains(s2));
+
+    // Create by constructor that takes sender, signal, source (parent) state
+    QState *s3 = new QState(&machine);
+    QSignalTransition *t2 = new QSignalTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
+    QCOMPARE(t2->sourceState(), s2);
+    t2->setTargetState(s3);
+    emitter.emitSignalWithNoArg();
+    QTRY_VERIFY(machine.configuration().contains(s3));
+
+    // Create by constructor that takes source (parent) state
+    QState *s4 = new QState(&machine);
+    QSignalTransition *t3 = new QSignalTransition(s3);
+    QCOMPARE(t3->sourceState(), s3);
+    t3->setSenderObject(&emitter);
+    t3->setSignal(SIGNAL(signalWithNoArg()));
+    t3->setTargetState(s4);
+    emitter.emitSignalWithNoArg();
+    QTRY_VERIFY(machine.configuration().contains(s4));
+
+    // Create by constructor without parent, then set the parent
+    QState *s5 = new QState(&machine);
+    QSignalTransition *t4 = new QSignalTransition();
+    t4->setSenderObject(&emitter);
+    t4->setParent(s4);
+    QCOMPARE(t4->sourceState(), s4);
+    t4->setSignal(SIGNAL(signalWithNoArg()));
+    t4->setTargetState(s5);
+    emitter.emitSignalWithNoArg();
+    QTRY_VERIFY(machine.configuration().contains(s5));
+}
+
+void tst_QStateMachine::createEventTransitionWhenRunning()
+{
+    QStateMachine machine;
+    QState *s1 = new QState(&machine);
+    machine.setInitialState(s1);
+    machine.start();
+    QTRY_VERIFY(machine.configuration().contains(s1));
+
+    // Create by constructor that takes event source, type, source (parent) state
+    QState *s2 = new QState(&machine);
+    QObject object;
+    QEventTransition *t1 = new QEventTransition(&object, QEvent::Timer, s1);
+    QCOMPARE(t1->sourceState(), s1);
+    t1->setTargetState(s2);
+
+    object.startTimer(10); // Will cause QEvent::Timer to fire every 10ms
+    QTRY_VERIFY(machine.configuration().contains(s2));
+
+    // Create by constructor that takes source (parent) state
+    QState *s3 = new QState(&machine);
+    QEventTransition *t2 = new QEventTransition(s2);
+    QCOMPARE(t2->sourceState(), s2);
+    t2->setEventSource(&object);
+    t2->setEventType(QEvent::Timer);
+    t2->setTargetState(s3);
+    QTRY_VERIFY(machine.configuration().contains(s3));
+
+    // Create by constructor without parent, then set the parent
+    QState *s4 = new QState(&machine);
+    QEventTransition *t3 = new QEventTransition();
+    t3->setEventSource(&object);
+    t3->setParent(s3);
+    QCOMPARE(t3->sourceState(), s3);
+    t3->setEventType(QEvent::Timer);
+    t3->setTargetState(s4);
+    QTRY_VERIFY(machine.configuration().contains(s4));
+}
+
 QTEST_MAIN(tst_QStateMachine)
 #include "tst_qstatemachine.moc"