From 1c79a42d6b0f6c15a0472e0cde3742d0c504ead3 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 26 Jan 2011 12:01:19 +0100 Subject: Make sure QStateMachine stops when it's told to If QStateMachine::stop() was called by an event test or while transitioning to another state, the state machine could end up in an inconsistent internal state (stop==true, but the stopped() signal was not emitted). This also caused the machine to behave incorrectly if it was then restarted (it would immediately stop upon receiving the first event). Solution: Move the stop-handling after the event processing loop, so that it always takes precedence over other possible reasons for exiting the loop (event queues exhausted, final state entered). Task-number: QTBUG-16463 Reviewed-by: Eskil Abrahamsen Blomfeldt --- tests/auto/qstatemachine/tst_qstatemachine.cpp | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'tests/auto/qstatemachine') diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index a03657f7ad..231cab03b2 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -211,6 +211,9 @@ private slots: void postEventFromOtherThread(); void eventFilterForApplication(); void eventClassesExported(); + void stopInTransitionToFinalState(); + void stopInEventTest_data(); + void stopInEventTest(); }; tst_QStateMachine::tst_QStateMachine() @@ -4374,5 +4377,70 @@ void tst_QStateMachine::eventClassesExported() QStateMachine::SignalEvent *signalEvent = new QStateMachine::SignalEvent(0, 0, QList()); } +void tst_QStateMachine::stopInTransitionToFinalState() +{ + QStateMachine machine; + QState *s1 = new QState(&machine); + QFinalState *s2 = new QFinalState(&machine); + QAbstractTransition *t1 = s1->addTransition(s2); + machine.setInitialState(s1); + + QObject::connect(t1, SIGNAL(triggered()), &machine, SLOT(stop())); + QSignalSpy stoppedSpy(&machine, SIGNAL(stopped())); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + QSignalSpy s2EnteredSpy(s2, SIGNAL(entered())); + machine.start(); + + // Stopping should take precedence over finished. + QTRY_COMPARE(stoppedSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(s2EnteredSpy.count(), 1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); +} + +class StopInEventTestTransition : public QAbstractTransition +{ +public: + bool eventTest(QEvent *e) + { + if (e->type() == QEvent::User) + machine()->stop(); + return false; + } + void onTransition(QEvent *) + { } +}; + +void tst_QStateMachine::stopInEventTest_data() +{ + QTest::addColumn("eventPriority"); + QTest::newRow("NormalPriority") << int(QStateMachine::NormalPriority); + QTest::newRow("HighPriority") << int(QStateMachine::HighPriority); +} + +void tst_QStateMachine::stopInEventTest() +{ + QFETCH(int, eventPriority); + + QStateMachine machine; + QState *s1 = new QState(&machine); + s1->addTransition(new StopInEventTestTransition()); + machine.setInitialState(s1); + + QSignalSpy startedSpy(&machine, SIGNAL(started())); + machine.start(); + QTRY_COMPARE(startedSpy.count(), 1); + + QSignalSpy stoppedSpy(&machine, SIGNAL(stopped())); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.postEvent(new QEvent(QEvent::User), QStateMachine::EventPriority(eventPriority)); + + QTRY_COMPARE(stoppedSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); +} + QTEST_MAIN(tst_QStateMachine) #include "tst_qstatemachine.moc" -- cgit v1.2.3