1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/QCoreApplication>
31#ifndef QT_NO_WIDGETS
32#include <QtWidgets/QPushButton>
33#include <QtWidgets/QGraphicsScene>
34#include <QtWidgets/QGraphicsSceneEvent>
35#include <QtWidgets/QGraphicsTextItem>
36#endif
37
38#include "qstatemachine.h"
39#include "qstate.h"
40#include "qhistorystate.h"
41#ifndef QT_NO_WIDGETS
42#include "qkeyeventtransition.h"
43#include "qmouseeventtransition.h"
44#endif
45#include "private/qstate_p.h"
46#include "private/qstatemachine_p.h"
47
48static int globalTick;
49
50// Run exec for a maximum of TIMEOUT msecs
51#define QCOREAPPLICATION_EXEC(TIMEOUT) \
52{ \
53 QTimer timer; \
54 timer.setSingleShot(true); \
55 timer.setInterval(TIMEOUT); \
56 timer.start(); \
57 connect(&timer, SIGNAL(timeout()), QCoreApplication::instance(), SLOT(quit())); \
58 QCoreApplication::exec(); \
59}
60
61#define TEST_RUNNING_CHANGED(RUNNING) \
62{ \
63 QTRY_COMPARE(runningSpy.count(), 1); \
64 QList<QVariant> runningArgs = runningSpy.takeFirst(); \
65 QVERIFY(runningArgs.at(0).type() == QVariant::Bool); \
66 QVERIFY(runningArgs.at(0).toBool() == RUNNING); \
67 QCOMPARE(machine.isRunning(), runningArgs.at(0).toBool()); \
68}
69
70#define TEST_RUNNING_CHANGED_STARTED_STOPPED \
71{ \
72 QTRY_COMPARE(runningSpy.count(), 2); \
73 QList<QVariant> runningArgs = runningSpy.takeFirst(); \
74 QVERIFY(runningArgs.at(0).type() == QVariant::Bool); \
75 QVERIFY(runningArgs.at(0).toBool() == true); \
76 runningArgs = runningSpy.takeFirst(); \
77 QVERIFY(runningArgs.at(0).type() == QVariant::Bool); \
78 QVERIFY(runningArgs.at(0).toBool() == false); \
79 QCOMPARE(machine.isRunning(), runningArgs.at(0).toBool()); \
80}
81
82#define DEFINE_ACTIVE_SPY(VAR) \
83 QSignalSpy VAR##_activeSpy(VAR, &QState::activeChanged); \
84 QVERIFY(VAR##_activeSpy.isValid());
85
86#define TEST_ACTIVE_CHANGED(VAR, COUNT) \
87{ \
88 QTRY_COMPARE(VAR##_activeSpy.count(), COUNT); \
89 bool active = true; \
90 foreach (const QList<QVariant> &activeArgs, static_cast<QList<QList<QVariant> > >(VAR##_activeSpy)) { \
91 QVERIFY(activeArgs.at(0).type() == QVariant::Bool); \
92 QVERIFY(activeArgs.at(0).toBool() == active); \
93 active = !active; \
94 } \
95 QCOMPARE(VAR->active(), !active); \
96}
97
98class SignalEmitter : public QObject
99{
100Q_OBJECT
101 public:
102 SignalEmitter(QObject *parent = 0)
103 : QObject(parent) {}
104public Q_SLOTS:
105 void emitSignalWithNoArg()
106 { emit signalWithNoArg(); }
107 void emitSignalWithIntArg(int arg)
108 { emit signalWithIntArg(arg); }
109 void emitSignalWithStringArg(const QString &arg)
110 { emit signalWithStringArg(arg); }
111 void emitSignalWithDefaultArg()
112 { emit signalWithDefaultArg(); }
113Q_SIGNALS:
114 void signalWithNoArg();
115 void signalWithIntArg(int);
116 void signalWithStringArg(const QString &);
117 void signalWithDefaultArg(int i = 42);
118};
119
120class tst_QStateMachine : public QObject
121{
122 Q_OBJECT
123private slots:
124 void rootState();
125 void machineWithParent();
126#ifdef QT_BUILD_INTERNAL
127 void addAndRemoveState();
128#endif
129 void stateEntryAndExit();
130 void assignProperty();
131 void assignPropertyWithAnimation();
132 void postEvent();
133 void cancelDelayedEvent();
134 void postDelayedEventAndStop();
135 void postDelayedEventFromThread();
136 void stopAndPostEvent();
137 void stateFinished();
138 void parallelStates();
139 void parallelRootState();
140 void allSourceToTargetConfigurations();
141 void signalTransitions();
142#ifndef QT_NO_WIDGETS
143 void eventTransitions();
144 void graphicsSceneEventTransitions();
145#endif
146 void historyStates();
147 void startAndStop();
148 void setRunning();
149 void targetStateWithNoParent();
150 void targetStateDeleted();
151 void transitionToRootState();
152 void transitionFromRootState();
153 void transitionEntersParent();
154
155 void defaultErrorState();
156 void customGlobalErrorState();
157 void customLocalErrorStateInBrokenState();
158 void customLocalErrorStateInOtherState();
159 void customLocalErrorStateInParentOfBrokenState();
160 void customLocalErrorStateOverridesParent();
161 void errorStateHasChildren();
162 void errorStateHasErrors();
163 void errorStateIsRootState();
164 void errorStateEntersParentFirst();
165 void customErrorStateIsNull();
166 void clearError();
167 void historyStateHasNowhereToGo();
168 void historyStateAsInitialState();
169 void historyStateAfterRestart();
170 void brokenStateIsNeverEntered();
171 void customErrorStateNotInGraph();
172 void transitionToStateNotInGraph();
173 void restoreProperties();
174
175 void defaultGlobalRestorePolicy();
176 void globalRestorePolicySetToRestore();
177 void globalRestorePolicySetToDontRestore();
178
179 void noInitialStateForInitialState();
180
181 void transitionWithParent();
182 void transitionsFromParallelStateWithNoChildren();
183 void parallelStateTransition();
184 void parallelStateAssignmentsDone();
185 void nestedRestoreProperties();
186 void nestedRestoreProperties2();
187
188 void simpleAnimation();
189 void twoAnimations();
190 void twoAnimatedTransitions();
191 void playAnimationTwice();
192 void nestedTargetStateForAnimation();
193 void propertiesAssignedSignalTransitionsReuseAnimationGroup();
194 void animatedGlobalRestoreProperty();
195 void specificTargetValueOfAnimation();
196
197 void addDefaultAnimation();
198 void addDefaultAnimationWithUnusedAnimation();
199 void removeDefaultAnimation();
200 void overrideDefaultAnimationWithSpecific();
201
202 void nestedStateMachines();
203 void goToState();
204 void goToStateFromSourceWithTransition();
205
206 void clonedSignals();
207 void postEventFromOtherThread();
208#ifndef QT_NO_WIDGETS
209 void eventFilterForApplication();
210#endif
211 void eventClassesExported();
212 void stopInTransitionToFinalState();
213 void stopInEventTest_data();
214 void stopInEventTest();
215
216 void testIncrementReceivers();
217 void initialStateIsEnteredBeforeStartedEmitted();
218 void deletePropertyAssignmentObjectBeforeEntry();
219 void deletePropertyAssignmentObjectBeforeRestore();
220 void deleteInitialState();
221 void setPropertyAfterRestore();
222 void transitionWithNoTarget_data();
223 void transitionWithNoTarget();
224 void initialStateIsFinal();
225
226 void restorePropertiesSimple();
227 void restoreProperties2();
228 void restoreProperties3();
229 void restoreProperties4();
230 void restorePropertiesSelfTransition();
231 void changeStateWhileAnimatingProperty();
232 void propertiesAreAssignedBeforeEntryCallbacks_data();
233 void propertiesAreAssignedBeforeEntryCallbacks();
234
235 void multiTargetTransitionInsideParallelStateGroup();
236 void signalTransitionNormalizeSignature();
237#ifdef Q_COMPILER_DELEGATING_CONSTRUCTORS
238 void createPointerToMemberSignalTransition();
239#endif
240 void createSignalTransitionWhenRunning();
241 void createEventTransitionWhenRunning();
242 void signalTransitionSenderInDifferentThread();
243 void signalTransitionSenderInDifferentThread2();
244 void signalTransitionRegistrationThreadSafety();
245 void childModeConstructor();
246
247 void qtbug_44963();
248 void qtbug_44783();
249 void internalTransition();
250 void conflictingTransition();
251 void conflictingTransition2();
252 void qtbug_46059();
253 void qtbug_46703();
254 void postEventFromBeginSelectTransitions();
255 void dontProcessSlotsWhenMachineIsNotRunning();
256
257 void cancelDelayedEventWithChrono();
258 void postDelayedEventWithChronoAndStop();
259 void postDelayedEventWithChronoFromThread();
260};
261
262class TestState : public QState
263{
264public:
265 enum Event {
266 Entry,
267 Exit
268 };
269 TestState(QState *parent, const QString &objectName = QString())
270 : QState(parent)
271 { setObjectName(objectName); }
272 TestState(ChildMode mode, const QString &objectName = QString())
273 : QState(mode)
274 { setObjectName(objectName); }
275 QVector<QPair<int, Event> > events;
276protected:
277 virtual void onEntry(QEvent *) {
278 events.append(t: qMakePair(x: globalTick++, y: Entry));
279 }
280 virtual void onExit(QEvent *) {
281 events.append(t: qMakePair(x: globalTick++, y: Exit));
282 }
283};
284
285class TestTransition : public QAbstractTransition
286{
287public:
288 TestTransition(QAbstractState *target, const QString &objectName = QString())
289 : QAbstractTransition()
290 { setTargetState(target); setObjectName(objectName); }
291 QVector<int> triggers;
292protected:
293 virtual bool eventTest(QEvent *) {
294 return true;
295 }
296 virtual void onTransition(QEvent *) {
297 triggers.append(t: globalTick++);
298 }
299};
300
301class EventTransition : public QAbstractTransition
302{
303public:
304 EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0)
305 : QAbstractTransition(parent), m_type(type)
306 { setTargetState(target); }
307 EventTransition(QEvent::Type type, const QList<QAbstractState *> &targets, QState *parent = 0)
308 : QAbstractTransition(parent), m_type(type)
309 { setTargetStates(targets); }
310protected:
311 virtual bool eventTest(QEvent *e) {
312 return (e->type() == m_type);
313 }
314 virtual void onTransition(QEvent *) {}
315private:
316 QEvent::Type m_type;
317};
318
319void tst_QStateMachine::transitionToRootState()
320{
321 QStateMachine machine;
322 machine.setObjectName("machine");
323
324 QState *initialState = new QState();
325 DEFINE_ACTIVE_SPY(initialState);
326 initialState->setObjectName("initial");
327 machine.addState(state: initialState);
328 machine.setInitialState(initialState);
329
330 QAbstractTransition *trans = new EventTransition(QEvent::User, &machine);
331 initialState->addTransition(transition: trans);
332 QCOMPARE(trans->sourceState(), initialState);
333 QCOMPARE(trans->targetState(), static_cast<QAbstractState *>(&machine));
334
335 machine.start();
336 QCoreApplication::processEvents();
337
338 QCOMPARE(machine.configuration().count(), 1);
339 QVERIFY(machine.configuration().contains(initialState));
340 TEST_ACTIVE_CHANGED(initialState, 1);
341
342 machine.postEvent(event: new QEvent(QEvent::User));
343 QTest::ignoreMessage(type: QtWarningMsg,
344 message: "Unrecoverable error detected in running state machine: "
345 "Child mode of state machine 'machine' is not 'ExclusiveStates'.");
346 QCoreApplication::processEvents();
347 QVERIFY(machine.configuration().isEmpty());
348 QVERIFY(!machine.isRunning());
349 TEST_ACTIVE_CHANGED(initialState, 2);
350}
351
352void tst_QStateMachine::transitionFromRootState()
353{
354 QStateMachine machine;
355 QState *root = &machine;
356 QState *s1 = new QState(root);
357 EventTransition *trans = new EventTransition(QEvent::User, s1);
358 root->addTransition(transition: trans);
359 QCOMPARE(trans->sourceState(), root);
360 QCOMPARE(trans->targetState(), static_cast<QAbstractState *>(s1));
361}
362
363void tst_QStateMachine::transitionEntersParent()
364{
365 QStateMachine machine;
366
367 QObject *entryController = new QObject(&machine);
368 entryController->setObjectName("entryController");
369 entryController->setProperty(name: "greatGrandParentEntered", value: false);
370 entryController->setProperty(name: "grandParentEntered", value: false);
371 entryController->setProperty(name: "parentEntered", value: false);
372 entryController->setProperty(name: "stateEntered", value: false);
373
374 QState *greatGrandParent = new QState();
375 greatGrandParent->setObjectName("grandParent");
376 greatGrandParent->assignProperty(object: entryController, name: "greatGrandParentEntered", value: true);
377 machine.addState(state: greatGrandParent);
378 machine.setInitialState(greatGrandParent);
379
380 QState *grandParent = new QState(greatGrandParent);
381 grandParent->setObjectName("grandParent");
382 grandParent->assignProperty(object: entryController, name: "grandParentEntered", value: true);
383
384 QState *parent = new QState(grandParent);
385 parent->setObjectName("parent");
386 parent->assignProperty(object: entryController, name: "parentEntered", value: true);
387
388 QState *state = new QState(parent);
389 state->setObjectName("state");
390 state->assignProperty(object: entryController, name: "stateEntered", value: true);
391
392 QState *initialStateOfGreatGrandParent = new QState(greatGrandParent);
393 initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent");
394 greatGrandParent->setInitialState(initialStateOfGreatGrandParent);
395
396 initialStateOfGreatGrandParent->addTransition(transition: new EventTransition(QEvent::User, state));
397
398 machine.start();
399 QCoreApplication::processEvents();
400
401 QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), true);
402 QCOMPARE(entryController->property("grandParentEntered").toBool(), false);
403 QCOMPARE(entryController->property("parentEntered").toBool(), false);
404 QCOMPARE(entryController->property("stateEntered").toBool(), false);
405 QCOMPARE(machine.configuration().count(), 2);
406 QVERIFY(machine.configuration().contains(greatGrandParent));
407 QVERIFY(machine.configuration().contains(initialStateOfGreatGrandParent));
408
409 entryController->setProperty(name: "greatGrandParentEntered", value: false);
410 entryController->setProperty(name: "grandParentEntered", value: false);
411 entryController->setProperty(name: "parentEntered", value: false);
412 entryController->setProperty(name: "stateEntered", value: false);
413
414 machine.postEvent(event: new QEvent(QEvent::User));
415 QCoreApplication::processEvents();
416
417 QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), false);
418 QCOMPARE(entryController->property("grandParentEntered").toBool(), true);
419 QCOMPARE(entryController->property("parentEntered").toBool(), true);
420 QCOMPARE(entryController->property("stateEntered").toBool(), true);
421 QCOMPARE(machine.configuration().count(), 4);
422 QVERIFY(machine.configuration().contains(greatGrandParent));
423 QVERIFY(machine.configuration().contains(grandParent));
424 QVERIFY(machine.configuration().contains(parent));
425 QVERIFY(machine.configuration().contains(state));
426}
427
428void tst_QStateMachine::defaultErrorState()
429{
430 QStateMachine machine;
431 QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
432
433 QState *brokenState = new QState();
434 brokenState->setObjectName("MyInitialState");
435
436 machine.addState(state: brokenState);
437 machine.setInitialState(brokenState);
438
439 QState *childState = new QState(brokenState);
440 childState->setObjectName("childState");
441
442 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: Missing initial state in compound state 'MyInitialState'");
443
444 // initialState has no initial state
445 machine.start();
446 QCoreApplication::processEvents();
447
448 QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
449 QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'MyInitialState'"));
450 QCOMPARE(machine.isRunning(), false);
451}
452
453class CustomErrorState: public QState
454{
455public:
456 CustomErrorState(QStateMachine *machine, QState *parent = 0)
457 : QState(parent), error(QStateMachine::NoError), m_machine(machine)
458 {
459 }
460
461 void onEntry(QEvent *)
462 {
463 error = m_machine->error();
464 errorString = m_machine->errorString();
465 }
466
467 QStateMachine::Error error;
468 QString errorString;
469
470private:
471 QStateMachine *m_machine;
472};
473
474void tst_QStateMachine::customGlobalErrorState()
475{
476 QStateMachine machine;
477
478 CustomErrorState *customErrorState = new CustomErrorState(&machine);
479 customErrorState->setObjectName("customErrorState");
480 machine.addState(state: customErrorState);
481 machine.setErrorState(customErrorState);
482
483 QState *initialState = new QState();
484 initialState->setObjectName("initialState");
485 machine.addState(state: initialState);
486 machine.setInitialState(initialState);
487
488 QState *brokenState = new QState();
489 brokenState->setObjectName("brokenState");
490 machine.addState(state: brokenState);
491 QState *childState = new QState(brokenState);
492 childState->setObjectName("childState");
493
494 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
495 machine.start();
496 QCoreApplication::processEvents();
497
498 QCOMPARE(machine.errorState(), static_cast<QAbstractState*>(customErrorState));
499 QCOMPARE(machine.configuration().count(), 1);
500 QVERIFY(machine.configuration().contains(initialState));
501
502 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
503 QCOMPARE(machine.configuration().count(), 1);
504 QVERIFY(machine.configuration().contains(initialState));
505
506 QCoreApplication::processEvents();
507
508 QCOMPARE(machine.isRunning(), true);
509 QCOMPARE(machine.configuration().count(), 1);
510 QVERIFY(machine.configuration().contains(customErrorState));
511 QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
512 QCOMPARE(customErrorState->errorString, QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
513 QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
514 QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
515}
516
517void tst_QStateMachine::customLocalErrorStateInBrokenState()
518{
519 QStateMachine machine;
520 CustomErrorState *customErrorState = new CustomErrorState(&machine);
521 machine.addState(state: customErrorState);
522
523 QState *initialState = new QState();
524 initialState->setObjectName("initialState");
525 machine.addState(state: initialState);
526 machine.setInitialState(initialState);
527
528 QState *brokenState = new QState();
529 brokenState->setObjectName("brokenState");
530 machine.addState(state: brokenState);
531 brokenState->setErrorState(customErrorState);
532
533 QState *childState = new QState(brokenState);
534 childState->setObjectName("childState");
535
536 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
537
538 machine.start();
539 QCoreApplication::processEvents();
540
541 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
542 QCoreApplication::processEvents();
543
544 QCOMPARE(machine.isRunning(), true);
545 QCOMPARE(machine.configuration().count(), 1);
546 QVERIFY(machine.configuration().contains(customErrorState));
547 QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
548}
549
550void tst_QStateMachine::customLocalErrorStateInOtherState()
551{
552 QStateMachine machine;
553 CustomErrorState *customErrorState = new CustomErrorState(&machine);
554 machine.addState(state: customErrorState);
555
556 QState *initialState = new QState();
557 initialState->setObjectName("initialState");
558 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::setErrorState: error state cannot belong to a different state machine");
559 initialState->setErrorState(customErrorState);
560 machine.addState(state: initialState);
561 machine.setInitialState(initialState);
562
563 QState *brokenState = new QState();
564 brokenState->setObjectName("brokenState");
565
566 machine.addState(state: brokenState);
567
568 QState *childState = new QState(brokenState);
569 childState->setObjectName("childState");
570
571 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
572
573 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
574 machine.start();
575 QCoreApplication::processEvents();
576
577 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
578 QCoreApplication::processEvents();
579
580 QCOMPARE(machine.isRunning(), false);
581}
582
583void tst_QStateMachine::customLocalErrorStateInParentOfBrokenState()
584{
585 QStateMachine machine;
586 CustomErrorState *customErrorState = new CustomErrorState(&machine);
587 machine.addState(state: customErrorState);
588
589 QState *initialState = new QState();
590 initialState->setObjectName("initialState");
591 machine.addState(state: initialState);
592 machine.setInitialState(initialState);
593
594 QState *parentOfBrokenState = new QState();
595 machine.addState(state: parentOfBrokenState);
596 parentOfBrokenState->setObjectName("parentOfBrokenState");
597 parentOfBrokenState->setErrorState(customErrorState);
598
599 QState *brokenState = new QState(parentOfBrokenState);
600 brokenState->setObjectName("brokenState");
601 parentOfBrokenState->setInitialState(brokenState);
602
603 QState *childState = new QState(brokenState);
604 childState->setObjectName("childState");
605
606 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
607
608 machine.start();
609 QCoreApplication::processEvents();
610
611 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
612 QCoreApplication::processEvents();
613
614 QCOMPARE(machine.isRunning(), true);
615 QCOMPARE(machine.configuration().count(), 1);
616 QVERIFY(machine.configuration().contains(customErrorState));
617}
618
619void tst_QStateMachine::customLocalErrorStateOverridesParent()
620{
621 QStateMachine machine;
622 CustomErrorState *customErrorStateForParent = new CustomErrorState(&machine);
623 machine.addState(state: customErrorStateForParent);
624
625 CustomErrorState *customErrorStateForBrokenState = new CustomErrorState(&machine);
626 machine.addState(state: customErrorStateForBrokenState);
627
628 QState *initialState = new QState();
629 initialState->setObjectName("initialState");
630 machine.addState(state: initialState);
631 machine.setInitialState(initialState);
632
633 QState *parentOfBrokenState = new QState();
634 machine.addState(state: parentOfBrokenState);
635 parentOfBrokenState->setObjectName("parentOfBrokenState");
636 parentOfBrokenState->setErrorState(customErrorStateForParent);
637
638 QState *brokenState = new QState(parentOfBrokenState);
639 brokenState->setObjectName("brokenState");
640 brokenState->setErrorState(customErrorStateForBrokenState);
641 parentOfBrokenState->setInitialState(brokenState);
642
643 QState *childState = new QState(brokenState);
644 childState->setObjectName("childState");
645
646 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
647
648 machine.start();
649 QCoreApplication::processEvents();
650
651 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
652 QCoreApplication::processEvents();
653
654 QCOMPARE(machine.configuration().count(), 1);
655 QVERIFY(machine.configuration().contains(customErrorStateForBrokenState));
656 QCOMPARE(customErrorStateForBrokenState->error, QStateMachine::NoInitialStateError);
657 QCOMPARE(customErrorStateForParent->error, QStateMachine::NoError);
658}
659
660void tst_QStateMachine::errorStateHasChildren()
661{
662 QStateMachine machine;
663 CustomErrorState *customErrorState = new CustomErrorState(&machine);
664 customErrorState->setObjectName("customErrorState");
665 machine.addState(state: customErrorState);
666
667 machine.setErrorState(customErrorState);
668
669 QState *childOfErrorState = new QState(customErrorState);
670 childOfErrorState->setObjectName("childOfErrorState");
671 customErrorState->setInitialState(childOfErrorState);
672
673 QState *initialState = new QState();
674 initialState->setObjectName("initialState");
675 machine.addState(state: initialState);
676 machine.setInitialState(initialState);
677
678 QState *brokenState = new QState();
679 brokenState->setObjectName("brokenState");
680 machine.addState(state: brokenState);
681
682 QState *childState = new QState(brokenState);
683 childState->setObjectName("childState");
684
685 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
686
687 machine.start();
688 QCoreApplication::processEvents();
689
690 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
691 QCoreApplication::processEvents();
692
693 QCOMPARE(machine.isRunning(), true);
694 QCOMPARE(machine.configuration().count(), 2);
695 QVERIFY(machine.configuration().contains(customErrorState));
696 QVERIFY(machine.configuration().contains(childOfErrorState));
697}
698
699
700void tst_QStateMachine::errorStateHasErrors()
701{
702 QStateMachine machine;
703 CustomErrorState *customErrorState = new CustomErrorState(&machine);
704 customErrorState->setObjectName("customErrorState");
705 machine.addState(state: customErrorState);
706
707 machine.setErrorState(customErrorState);
708
709 QState *childOfErrorState = new QState(customErrorState);
710 childOfErrorState->setObjectName("childOfErrorState");
711
712 QState *initialState = new QState();
713 initialState->setObjectName("initialState");
714 machine.addState(state: initialState);
715 machine.setInitialState(initialState);
716
717 QState *brokenState = new QState();
718 brokenState->setObjectName("brokenState");
719 machine.addState(state: brokenState);
720
721 QState *childState = new QState(brokenState);
722 childState->setObjectName("childState");
723
724 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
725
726 machine.start();
727 QCoreApplication::processEvents();
728
729 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
730 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: Missing initial state in compound state 'customErrorState'");
731 QCoreApplication::processEvents();
732
733 QCOMPARE(machine.isRunning(), false);
734 QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
735 QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'customErrorState'"));
736}
737
738void tst_QStateMachine::errorStateIsRootState()
739{
740 QStateMachine machine;
741 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::setErrorState: root state cannot be error state");
742 machine.setErrorState(&machine);
743
744 QState *initialState = new QState();
745 initialState->setObjectName("initialState");
746 machine.addState(state: initialState);
747 machine.setInitialState(initialState);
748
749 QState *brokenState = new QState();
750 brokenState->setObjectName("brokenState");
751 machine.addState(state: brokenState);
752
753 QState *childState = new QState(brokenState);
754 childState->setObjectName("childState");
755
756 initialState->addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
757
758 machine.start();
759 QCoreApplication::processEvents();
760
761 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)));
762 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
763 QCoreApplication::processEvents();
764
765 QCOMPARE(machine.isRunning(), false);
766}
767
768void tst_QStateMachine::errorStateEntersParentFirst()
769{
770 QStateMachine machine;
771
772 QObject *entryController = new QObject(&machine);
773 entryController->setObjectName("entryController");
774 entryController->setProperty(name: "greatGrandParentEntered", value: false);
775 entryController->setProperty(name: "grandParentEntered", value: false);
776 entryController->setProperty(name: "parentEntered", value: false);
777 entryController->setProperty(name: "errorStateEntered", value: false);
778
779 QState *greatGrandParent = new QState();
780 greatGrandParent->setObjectName("greatGrandParent");
781 greatGrandParent->assignProperty(object: entryController, name: "greatGrandParentEntered", value: true);
782 machine.addState(state: greatGrandParent);
783 machine.setInitialState(greatGrandParent);
784
785 QState *grandParent = new QState(greatGrandParent);
786 grandParent->setObjectName("grandParent");
787 grandParent->assignProperty(object: entryController, name: "grandParentEntered", value: true);
788
789 QState *parent = new QState(grandParent);
790 parent->setObjectName("parent");
791 parent->assignProperty(object: entryController, name: "parentEntered", value: true);
792
793 QState *errorState = new QState(parent);
794 errorState->setObjectName("errorState");
795 errorState->assignProperty(object: entryController, name: "errorStateEntered", value: true);
796 machine.setErrorState(errorState);
797
798 QState *initialStateOfGreatGrandParent = new QState(greatGrandParent);
799 initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent");
800 greatGrandParent->setInitialState(initialStateOfGreatGrandParent);
801
802 QState *brokenState = new QState(greatGrandParent);
803 brokenState->setObjectName("brokenState");
804
805 QState *childState = new QState(brokenState);
806 childState->setObjectName("childState");
807
808 initialStateOfGreatGrandParent->addTransition(transition: new EventTransition(QEvent::User, brokenState));
809
810 machine.start();
811 QCoreApplication::processEvents();
812
813 QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), true);
814 QCOMPARE(entryController->property("grandParentEntered").toBool(), false);
815 QCOMPARE(entryController->property("parentEntered").toBool(), false);
816 QCOMPARE(entryController->property("errorStateEntered").toBool(), false);
817 QCOMPARE(machine.configuration().count(), 2);
818 QVERIFY(machine.configuration().contains(greatGrandParent));
819 QVERIFY(machine.configuration().contains(initialStateOfGreatGrandParent));
820
821 entryController->setProperty(name: "greatGrandParentEntered", value: false);
822 entryController->setProperty(name: "grandParentEntered", value: false);
823 entryController->setProperty(name: "parentEntered", value: false);
824 entryController->setProperty(name: "errorStateEntered", value: false);
825
826 machine.postEvent(event: new QEvent(QEvent::User));
827 QCoreApplication::processEvents();
828
829 QCOMPARE(entryController->property("greatGrandParentEntered").toBool(), false);
830 QCOMPARE(entryController->property("grandParentEntered").toBool(), true);
831 QCOMPARE(entryController->property("parentEntered").toBool(), true);
832 QCOMPARE(entryController->property("errorStateEntered").toBool(), true);
833 QCOMPARE(machine.configuration().count(), 4);
834 QVERIFY(machine.configuration().contains(greatGrandParent));
835 QVERIFY(machine.configuration().contains(grandParent));
836 QVERIFY(machine.configuration().contains(parent));
837 QVERIFY(machine.configuration().contains(errorState));
838}
839
840void tst_QStateMachine::customErrorStateIsNull()
841{
842 QStateMachine machine;
843 machine.setErrorState(0);
844
845 QState *initialState = new QState();
846 machine.addState(state: initialState);
847 machine.setInitialState(initialState);
848
849 QState *brokenState = new QState();
850 machine.addState(state: brokenState);
851
852 new QState(brokenState);
853 initialState->addTransition(transition: new EventTransition(QEvent::User, brokenState));
854
855 machine.start();
856 QCoreApplication::processEvents();
857
858 machine.postEvent(event: new QEvent(QEvent::User));
859 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: Missing initial state in compound state ''");
860 QCoreApplication::processEvents();
861
862 QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
863 QCOMPARE(machine.isRunning(), false);
864}
865
866void tst_QStateMachine::clearError()
867{
868 QStateMachine machine;
869 machine.setErrorState(new QState(&machine)); // avoid warnings
870
871 QState *brokenState = new QState(&machine);
872 brokenState->setObjectName("brokenState");
873 machine.setInitialState(brokenState);
874 new QState(brokenState);
875
876 machine.start();
877 QCoreApplication::processEvents();
878
879 QCOMPARE(machine.isRunning(), true);
880 QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
881 QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
882
883 machine.clearError();
884
885 QCOMPARE(machine.error(), QStateMachine::NoError);
886 QVERIFY(machine.errorString().isEmpty());
887}
888
889void tst_QStateMachine::historyStateAsInitialState()
890{
891 QStateMachine machine;
892
893 QHistoryState *hs = new QHistoryState(&machine);
894 machine.setInitialState(hs);
895
896 QState *s1 = new QState(&machine);
897 hs->setDefaultState(s1);
898
899 QState *s2 = new QState(&machine);
900
901 QHistoryState *s2h = new QHistoryState(s2);
902 s2->setInitialState(s2h);
903
904 QState *s21 = new QState(s2);
905 s2h->setDefaultState(s21);
906
907 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
908
909 machine.start();
910 QCoreApplication::processEvents();
911
912 QCOMPARE(machine.configuration().size(), 1);
913 QVERIFY(machine.configuration().contains(s1));
914
915 machine.postEvent(event: new QEvent(QEvent::User));
916 QCoreApplication::processEvents();
917
918 QCOMPARE(machine.configuration().size(), 2);
919 QVERIFY(machine.configuration().contains(s2));
920 QVERIFY(machine.configuration().contains(s21));
921}
922
923void tst_QStateMachine::historyStateHasNowhereToGo()
924{
925 QStateMachine machine;
926
927 QState *initialState = new QState(&machine);
928 initialState->setObjectName("initialState");
929 machine.setInitialState(initialState);
930 QState *errorState = new QState(&machine);
931 errorState->setObjectName("errorState");
932 machine.setErrorState(errorState); // avoid warnings
933
934 QState *brokenState = new QState(&machine);
935 brokenState->setObjectName("brokenState");
936 brokenState->setInitialState(new QState(brokenState));
937
938 QHistoryState *historyState = new QHistoryState(brokenState);
939 historyState->setObjectName("historyState");
940 EventTransition *t = new EventTransition(QEvent::User, historyState);
941 t->setObjectName("initialState->historyState");
942 initialState->addTransition(transition: t);
943
944 machine.start();
945 QCoreApplication::processEvents();
946
947 machine.postEvent(event: new QEvent(QEvent::User));
948 QCoreApplication::processEvents();
949
950 QCOMPARE(machine.isRunning(), true);
951 QCOMPARE(machine.configuration().count(), 1);
952 QVERIFY(machine.configuration().contains(machine.errorState()));
953 QCOMPARE(machine.error(), QStateMachine::NoDefaultStateInHistoryStateError);
954 QCOMPARE(machine.errorString(), QString::fromLatin1("Missing default state in history state 'historyState'"));
955}
956
957void tst_QStateMachine::historyStateAfterRestart()
958{
959 // QTBUG-8842
960 QStateMachine machine;
961
962 QState *s1 = new QState(&machine);
963 machine.setInitialState(s1);
964 QState *s2 = new QState(&machine);
965 QState *s21 = new QState(s2);
966 QState *s22 = new QState(s2);
967 QHistoryState *s2h = new QHistoryState(s2);
968 s2h->setDefaultState(s21);
969 s1->addTransition(transition: new EventTransition(QEvent::User, s2h));
970 s21->addTransition(transition: new EventTransition(QEvent::User, s22));
971 s2->addTransition(transition: new EventTransition(QEvent::User, s1));
972
973 for (int x = 0; x < 2; ++x) {
974 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
975 QVERIFY(runningSpy.isValid());
976 QSignalSpy startedSpy(&machine, &QStateMachine::started);
977 QVERIFY(startedSpy.isValid());
978 machine.start();
979 QTRY_COMPARE(startedSpy.count(), 1);
980 TEST_RUNNING_CHANGED(true);
981 QCOMPARE(machine.configuration().count(), 1);
982 QVERIFY(machine.configuration().contains(s1));
983
984 // s1 -> s2h -> s21 (default state)
985 machine.postEvent(event: new QEvent(QEvent::User));
986 QCoreApplication::processEvents();
987 QCOMPARE(machine.configuration().count(), 2);
988 QVERIFY(machine.configuration().contains(s2));
989 // This used to fail on the 2nd run because the
990 // history had not been cleared.
991 QVERIFY(machine.configuration().contains(s21));
992
993 // s21 -> s22
994 machine.postEvent(event: new QEvent(QEvent::User));
995 QCoreApplication::processEvents();
996 QCOMPARE(machine.configuration().count(), 2);
997 QVERIFY(machine.configuration().contains(s2));
998 QVERIFY(machine.configuration().contains(s22));
999
1000 // s2 -> s1 (s22 saved in s2h)
1001 machine.postEvent(event: new QEvent(QEvent::User));
1002 QCoreApplication::processEvents();
1003 QCOMPARE(machine.configuration().count(), 1);
1004 QVERIFY(machine.configuration().contains(s1));
1005
1006 // s1 -> s2h -> s22 (saved state)
1007 machine.postEvent(event: new QEvent(QEvent::User));
1008 QCoreApplication::processEvents();
1009 QCOMPARE(machine.configuration().count(), 2);
1010 QVERIFY(machine.configuration().contains(s2));
1011 QVERIFY(machine.configuration().contains(s22));
1012
1013 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
1014 QVERIFY(stoppedSpy.isValid());
1015 machine.stop();
1016 QTRY_COMPARE(stoppedSpy.count(), 1);
1017 TEST_RUNNING_CHANGED(false);
1018 }
1019}
1020
1021void tst_QStateMachine::brokenStateIsNeverEntered()
1022{
1023 QStateMachine machine;
1024
1025 QObject *entryController = new QObject(&machine);
1026 entryController->setProperty(name: "brokenStateEntered", value: false);
1027 entryController->setProperty(name: "childStateEntered", value: false);
1028 entryController->setProperty(name: "errorStateEntered", value: false);
1029
1030 QState *initialState = new QState(&machine);
1031 machine.setInitialState(initialState);
1032
1033 QState *errorState = new QState(&machine);
1034 errorState->assignProperty(object: entryController, name: "errorStateEntered", value: true);
1035 machine.setErrorState(errorState);
1036
1037 QState *brokenState = new QState(&machine);
1038 brokenState->assignProperty(object: entryController, name: "brokenStateEntered", value: true);
1039 brokenState->setObjectName("brokenState");
1040
1041 QState *childState = new QState(brokenState);
1042 childState->assignProperty(object: entryController, name: "childStateEntered", value: true);
1043
1044 initialState->addTransition(transition: new EventTransition(QEvent::User, brokenState));
1045
1046 machine.start();
1047 QCoreApplication::processEvents();
1048
1049 machine.postEvent(event: new QEvent(QEvent::User));
1050 QCoreApplication::processEvents();
1051
1052 QCOMPARE(entryController->property("errorStateEntered").toBool(), true);
1053 QCOMPARE(entryController->property("brokenStateEntered").toBool(), false);
1054 QCOMPARE(entryController->property("childStateEntered").toBool(), false);
1055}
1056
1057void tst_QStateMachine::transitionToStateNotInGraph()
1058{
1059 QStateMachine machine;
1060
1061 QState *initialState = new QState(&machine);
1062 initialState->setObjectName("initialState");
1063 machine.setInitialState(initialState);
1064
1065 QState independentState;
1066 independentState.setObjectName("independentState");
1067 initialState->addTransition(target: &independentState);
1068
1069 machine.start();
1070 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: "
1071 "Child mode of state machine '' is not 'ExclusiveStates'.");
1072 QCoreApplication::processEvents();
1073
1074 QCOMPARE(machine.isRunning(), false);
1075}
1076
1077void tst_QStateMachine::customErrorStateNotInGraph()
1078{
1079 QStateMachine machine;
1080
1081 QState errorState;
1082 errorState.setObjectName("errorState");
1083 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::setErrorState: error state cannot belong to a different state machine");
1084 machine.setErrorState(&errorState);
1085 QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
1086
1087 QState *initialBrokenState = new QState(&machine);
1088 initialBrokenState->setObjectName("initialBrokenState");
1089 machine.setInitialState(initialBrokenState);
1090 new QState(initialBrokenState);
1091
1092 machine.start();
1093 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: Missing initial state in compound state 'initialBrokenState'");
1094 QCoreApplication::processEvents();
1095
1096 QCOMPARE(machine.isRunning(), false);
1097}
1098
1099void tst_QStateMachine::restoreProperties()
1100{
1101 QStateMachine machine;
1102 QCOMPARE(machine.globalRestorePolicy(), QState::DontRestoreProperties);
1103 machine.setGlobalRestorePolicy(QState::RestoreProperties);
1104
1105 QObject *object = new QObject(&machine);
1106 object->setProperty(name: "a", value: 1);
1107 object->setProperty(name: "b", value: 2);
1108
1109 QState *S1 = new QState();
1110 S1->setObjectName("S1");
1111 S1->assignProperty(object, name: "a", value: 3);
1112 machine.addState(state: S1);
1113
1114 QState *S2 = new QState();
1115 S2->setObjectName("S2");
1116 S2->assignProperty(object, name: "b", value: 5);
1117 machine.addState(state: S2);
1118
1119 QState *S3 = new QState();
1120 S3->setObjectName("S3");
1121 machine.addState(state: S3);
1122
1123 QFinalState *S4 = new QFinalState();
1124 machine.addState(state: S4);
1125
1126 S1->addTransition(transition: new EventTransition(QEvent::User, S2));
1127 S2->addTransition(transition: new EventTransition(QEvent::User, S3));
1128 S3->addTransition(target: S4);
1129
1130 machine.setInitialState(S1);
1131 machine.start();
1132 QCoreApplication::processEvents();
1133
1134 QCOMPARE(object->property("a").toInt(), 3);
1135 QCOMPARE(object->property("b").toInt(), 2);
1136
1137 machine.postEvent(event: new QEvent(QEvent::User));
1138 QCoreApplication::processEvents();
1139
1140 QCOMPARE(object->property("a").toInt(), 1);
1141 QCOMPARE(object->property("b").toInt(), 5);
1142
1143 machine.postEvent(event: new QEvent(QEvent::User));
1144 QCoreApplication::processEvents();
1145
1146 QCOMPARE(object->property("a").toInt(), 1);
1147 QCOMPARE(object->property("b").toInt(), 2);
1148}
1149
1150void tst_QStateMachine::rootState()
1151{
1152 QStateMachine machine;
1153 QCOMPARE(qobject_cast<QState*>(machine.parentState()), (QState*)0);
1154 QCOMPARE(machine.machine(), (QStateMachine*)0);
1155
1156 QState *s1 = new QState(&machine);
1157 DEFINE_ACTIVE_SPY(s1);
1158 QCOMPARE(s1->parentState(), static_cast<QState*>(&machine));
1159
1160 QState *s2 = new QState();
1161 DEFINE_ACTIVE_SPY(s2);
1162 s2->setParent(&machine);
1163 QCOMPARE(s2->parentState(), static_cast<QState*>(&machine));
1164 TEST_ACTIVE_CHANGED(s1, 0);
1165 TEST_ACTIVE_CHANGED(s2, 0);
1166}
1167
1168void tst_QStateMachine::machineWithParent()
1169{
1170 QObject object;
1171 QStateMachine *machine = new QStateMachine(&object);
1172 QCOMPARE(machine->parent(), &object);
1173 QCOMPARE(machine->parentState(), static_cast<QState*>(0));
1174}
1175
1176#ifdef QT_BUILD_INTERNAL
1177void tst_QStateMachine::addAndRemoveState()
1178{
1179 QStateMachine machine;
1180 QStatePrivate *root_d = QStatePrivate::get(q: &machine);
1181 QCOMPARE(root_d->childStates().size(), 0);
1182
1183 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::addState: cannot add null state");
1184 machine.addState(state: 0);
1185
1186 QState *s1 = new QState();
1187 QCOMPARE(s1->parentState(), (QState*)0);
1188 QCOMPARE(s1->machine(), (QStateMachine*)0);
1189 machine.addState(state: s1);
1190 QCOMPARE(s1->machine(), static_cast<QStateMachine*>(&machine));
1191 QCOMPARE(s1->parentState(), static_cast<QState*>(&machine));
1192 QCOMPARE(root_d->childStates().size(), 1);
1193 QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
1194
1195 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::addState: state has already been added to this machine");
1196 machine.addState(state: s1);
1197
1198 QState *s2 = new QState();
1199 QCOMPARE(s2->parentState(), (QState*)0);
1200 machine.addState(state: s2);
1201 QCOMPARE(s2->parentState(), static_cast<QState*>(&machine));
1202 QCOMPARE(root_d->childStates().size(), 2);
1203 QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
1204 QCOMPARE(root_d->childStates().at(1), (QAbstractState*)s2);
1205
1206 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::addState: state has already been added to this machine");
1207 machine.addState(state: s2);
1208
1209 machine.removeState(state: s1);
1210 QCOMPARE(s1->parentState(), (QState*)0);
1211 QCOMPARE(root_d->childStates().size(), 1);
1212 QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s2);
1213
1214 machine.removeState(state: s2);
1215 QCOMPARE(s2->parentState(), (QState*)0);
1216 QCOMPARE(root_d->childStates().size(), 0);
1217
1218 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::removeState: cannot remove null state");
1219 machine.removeState(state: 0);
1220
1221 {
1222 QStateMachine machine2;
1223 {
1224 const QString warning
1225 = QString::asprintf(format: "QStateMachine::removeState: state %p's machine (%p) is different from this machine (%p)",
1226 &machine2, (void*)0, &machine);
1227 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1228 machine.removeState(state: &machine2);
1229 }
1230 // ### check this behavior
1231 machine.addState(state: &machine2);
1232 QCOMPARE(machine2.parent(), (QObject*)&machine);
1233 }
1234
1235 delete s1;
1236 delete s2;
1237 // ### how to deal with this?
1238 // machine.removeState(machine.errorState());
1239}
1240#endif
1241
1242void tst_QStateMachine::stateEntryAndExit()
1243{
1244 // Two top-level states
1245 {
1246 QStateMachine machine;
1247
1248 TestState *s1 = new TestState(&machine);
1249 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::addTransition: cannot add transition to null state");
1250 s1->addTransition(target: (QAbstractState*)0);
1251 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::addTransition: cannot add null transition");
1252 s1->addTransition(transition: (QAbstractTransition*)0);
1253 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::removeTransition: cannot remove null transition");
1254 s1->removeTransition(transition: (QAbstractTransition*)0);
1255
1256 TestState *s2 = new TestState(&machine);
1257 QFinalState *s3 = new QFinalState(&machine);
1258
1259 TestTransition *t = new TestTransition(s2);
1260 QCOMPARE(t->machine(), (QStateMachine*)0);
1261 QCOMPARE(t->sourceState(), (QState*)0);
1262 QCOMPARE(t->targetState(), (QAbstractState*)s2);
1263 QCOMPARE(t->targetStates().size(), 1);
1264 QCOMPARE(t->targetStates().at(0), (QAbstractState*)s2);
1265 t->setTargetState(0);
1266 QCOMPARE(t->targetState(), (QAbstractState*)0);
1267 QVERIFY(t->targetStates().isEmpty());
1268 t->setTargetState(s2);
1269 QCOMPARE(t->targetState(), (QAbstractState*)s2);
1270 QTest::ignoreMessage(type: QtWarningMsg, message: "QAbstractTransition::setTargetStates: target state(s) cannot be null");
1271 t->setTargetStates(QList<QAbstractState*>() << 0);
1272 QCOMPARE(t->targetState(), (QAbstractState*)s2);
1273 t->setTargetStates(QList<QAbstractState*>() << s2);
1274 QCOMPARE(t->targetState(), (QAbstractState*)s2);
1275 QCOMPARE(t->targetStates().size(), 1);
1276 QCOMPARE(t->targetStates().at(0), (QAbstractState*)s2);
1277 s1->addTransition(transition: t);
1278 QCOMPARE(t->sourceState(), (QState*)s1);
1279 QCOMPARE(t->machine(), &machine);
1280
1281 {
1282 QAbstractTransition *trans = s2->addTransition(target: s3);
1283 QVERIFY(trans != 0);
1284 QCOMPARE(trans->sourceState(), (QState*)s2);
1285 QCOMPARE(trans->targetState(), (QAbstractState*)s3);
1286 {
1287 const QString warning
1288 = QString::asprintf(format: "QState::removeTransition: transition %p's source state (%p) is different from this state (%p)", trans, s2, s1);
1289 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1290 s1->removeTransition(transition: trans);
1291 }
1292 s2->removeTransition(transition: trans);
1293 QCOMPARE(trans->sourceState(), (QState*)0);
1294 QCOMPARE(trans->targetState(), (QAbstractState*)s3);
1295 s2->addTransition(transition: trans);
1296 QCOMPARE(trans->sourceState(), (QState*)s2);
1297 }
1298
1299 QSignalSpy startedSpy(&machine, &QStateMachine::started);
1300 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
1301 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1302 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1303
1304 QVERIFY(startedSpy.isValid());
1305 QVERIFY(stoppedSpy.isValid());
1306 QVERIFY(finishedSpy.isValid());
1307 QVERIFY(runningSpy.isValid());
1308
1309 machine.setInitialState(s1);
1310 QCOMPARE(machine.initialState(), (QAbstractState*)s1);
1311 {
1312 QString warning
1313 = QString::asprintf(format: "QState::setInitialState: state %p is not a child of this state (%p)", &machine, &machine);
1314 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1315 machine.setInitialState(&machine);
1316 QCOMPARE(machine.initialState(), (QAbstractState*)s1);
1317 }
1318 QVERIFY(machine.configuration().isEmpty());
1319 globalTick = 0;
1320 QVERIFY(!machine.isRunning());
1321 QSignalSpy s1EnteredSpy(s1, &TestState::entered);
1322 QSignalSpy s1ExitedSpy(s1, &TestState::exited);
1323 QSignalSpy tTriggeredSpy(t, &TestTransition::triggered);
1324 QSignalSpy s2EnteredSpy(s2, &TestState::entered);
1325 QSignalSpy s2ExitedSpy(s2, &TestState::exited);
1326
1327 QVERIFY(s1EnteredSpy.isValid());
1328 QVERIFY(s1ExitedSpy.isValid());
1329 QVERIFY(tTriggeredSpy.isValid());
1330 QVERIFY(s2EnteredSpy.isValid());
1331 QVERIFY(s2ExitedSpy.isValid());
1332
1333 machine.start();
1334
1335 QTRY_COMPARE(startedSpy.count(), 1);
1336 QTRY_COMPARE(finishedSpy.count(), 1);
1337 QTRY_COMPARE(stoppedSpy.count(), 0);
1338 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1339 QCOMPARE(machine.configuration().count(), 1);
1340 QVERIFY(machine.configuration().contains(s3));
1341
1342 // s1 is entered
1343 QCOMPARE(s1->events.count(), 2);
1344 QCOMPARE(s1->events.at(0).first, 0);
1345 QCOMPARE(s1->events.at(0).second, TestState::Entry);
1346 // s1 is exited
1347 QCOMPARE(s1->events.at(1).first, 1);
1348 QCOMPARE(s1->events.at(1).second, TestState::Exit);
1349 // t is triggered
1350 QCOMPARE(t->triggers.count(), 1);
1351 QCOMPARE(t->triggers.at(0), 2);
1352 // s2 is entered
1353 QCOMPARE(s2->events.count(), 2);
1354 QCOMPARE(s2->events.at(0).first, 3);
1355 QCOMPARE(s2->events.at(0).second, TestState::Entry);
1356 // s2 is exited
1357 QCOMPARE(s2->events.at(1).first, 4);
1358 QCOMPARE(s2->events.at(1).second, TestState::Exit);
1359
1360 QCOMPARE(s1EnteredSpy.count(), 1);
1361 QCOMPARE(s1ExitedSpy.count(), 1);
1362 QCOMPARE(tTriggeredSpy.count(), 1);
1363 QCOMPARE(s2EnteredSpy.count(), 1);
1364 QCOMPARE(s2ExitedSpy.count(), 1);
1365 }
1366 // Two top-level states, one has two child states
1367 {
1368 QStateMachine machine;
1369
1370 TestState *s1 = new TestState(&machine, "s1");
1371 TestState *s11 = new TestState(s1, "s11");
1372 TestState *s12 = new TestState(s1, "s12");
1373 TestState *s2 = new TestState(&machine, "s2");
1374 QFinalState *s3 = new QFinalState(&machine);
1375 s3->setObjectName("s3");
1376 s1->setInitialState(s11);
1377 TestTransition *t1 = new TestTransition(s12, "t1");
1378 s11->addTransition(transition: t1);
1379 TestTransition *t2 = new TestTransition(s2, "t2");
1380 s12->addTransition(transition: t2);
1381 s2->addTransition(target: s3);
1382
1383 QSignalSpy startedSpy(&machine, &QStateMachine::started);
1384 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1385 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1386 QVERIFY(startedSpy.isValid());
1387 QVERIFY(finishedSpy.isValid());
1388 QVERIFY(runningSpy.isValid());
1389 machine.setInitialState(s1);
1390 globalTick = 0;
1391 machine.start();
1392
1393 QTRY_COMPARE(startedSpy.count(), 1);
1394 QTRY_COMPARE(finishedSpy.count(), 1);
1395 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1396 QCOMPARE(machine.configuration().count(), 1);
1397 QVERIFY(machine.configuration().contains(s3));
1398
1399 // s1 is entered
1400 QCOMPARE(s1->events.count(), 2);
1401 QCOMPARE(s1->events.at(0).first, 0);
1402 QCOMPARE(s1->events.at(0).second, TestState::Entry);
1403 // s11 is entered
1404 QCOMPARE(s11->events.count(), 2);
1405 QCOMPARE(s11->events.at(0).first, 1);
1406 QCOMPARE(s11->events.at(0).second, TestState::Entry);
1407 // s11 is exited
1408 QCOMPARE(s11->events.at(1).first, 2);
1409 QCOMPARE(s11->events.at(1).second, TestState::Exit);
1410 // t1 is triggered
1411 QCOMPARE(t1->triggers.count(), 1);
1412 QCOMPARE(t1->triggers.at(0), 3);
1413 // s12 is entered
1414 QCOMPARE(s12->events.count(), 2);
1415 QCOMPARE(s12->events.at(0).first, 4);
1416 QCOMPARE(s12->events.at(0).second, TestState::Entry);
1417 // s12 is exited
1418 QCOMPARE(s12->events.at(1).first, 5);
1419 QCOMPARE(s12->events.at(1).second, TestState::Exit);
1420 // s1 is exited
1421 QCOMPARE(s1->events.at(1).first, 6);
1422 QCOMPARE(s1->events.at(1).second, TestState::Exit);
1423 // t2 is triggered
1424 QCOMPARE(t2->triggers.count(), 1);
1425 QCOMPARE(t2->triggers.at(0), 7);
1426 // s2 is entered
1427 QCOMPARE(s2->events.count(), 2);
1428 QCOMPARE(s2->events.at(0).first, 8);
1429 QCOMPARE(s2->events.at(0).second, TestState::Entry);
1430 // s2 is exited
1431 QCOMPARE(s2->events.at(1).first, 9);
1432 QCOMPARE(s2->events.at(1).second, TestState::Exit);
1433 }
1434}
1435
1436void tst_QStateMachine::assignProperty()
1437{
1438 QStateMachine machine;
1439 QState *s1 = new QState(&machine);
1440 DEFINE_ACTIVE_SPY(s1);
1441
1442 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::assignProperty: cannot assign property 'foo' of null object");
1443 s1->assignProperty(object: 0, name: "foo", value: QVariant());
1444
1445 s1->assignProperty(object: s1, name: "objectName", value: "s1");
1446 QFinalState *s2 = new QFinalState(&machine);
1447 s1->addTransition(target: s2);
1448 machine.setInitialState(s1);
1449 machine.start();
1450 QTRY_COMPARE(s1->objectName(), QString::fromLatin1("s1"));
1451 TEST_ACTIVE_CHANGED(s1, 2);
1452
1453 s1->assignProperty(object: s1, name: "objectName", value: "foo");
1454 machine.start();
1455 QTRY_COMPARE(s1->objectName(), QString::fromLatin1("foo"));
1456 TEST_ACTIVE_CHANGED(s1, 4);
1457
1458 s1->assignProperty(object: s1, name: "noSuchProperty", value: 123);
1459 machine.start();
1460 QTRY_COMPARE(s1->dynamicPropertyNames().size(), 1);
1461 QCOMPARE(s1->dynamicPropertyNames().at(0), QByteArray("noSuchProperty"));
1462 QCOMPARE(s1->objectName(), QString::fromLatin1("foo"));
1463 TEST_ACTIVE_CHANGED(s1, 6);
1464
1465 {
1466 QSignalSpy propertiesAssignedSpy(s1, &QState::propertiesAssigned);
1467 QVERIFY(propertiesAssignedSpy.isValid());
1468 machine.start();
1469 QTRY_COMPARE(propertiesAssignedSpy.count(), 1);
1470 TEST_ACTIVE_CHANGED(s1, 8);
1471 }
1472
1473 // nested states
1474 {
1475 QState *s11 = new QState(s1);
1476 DEFINE_ACTIVE_SPY(s11);
1477 QString str = QString::fromLatin1(str: "set by nested state");
1478 s11->assignProperty(object: s11, name: "objectName", value: str);
1479 s1->setInitialState(s11);
1480 machine.start();
1481 QTRY_COMPARE(s11->objectName(), str);
1482 TEST_ACTIVE_CHANGED(s1, 10);
1483 TEST_ACTIVE_CHANGED(s11, 2);
1484 }
1485}
1486
1487void tst_QStateMachine::assignPropertyWithAnimation()
1488{
1489 // Single animation
1490 {
1491 QStateMachine machine;
1492 QVERIFY(machine.isAnimated());
1493 machine.setAnimated(false);
1494 QVERIFY(!machine.isAnimated());
1495 machine.setAnimated(true);
1496 QVERIFY(machine.isAnimated());
1497 QObject obj;
1498 obj.setProperty(name: "foo", value: 321);
1499 obj.setProperty(name: "bar", value: 654);
1500 QState *s1 = new QState(&machine);
1501 DEFINE_ACTIVE_SPY(s1);
1502 s1->assignProperty(object: &obj, name: "foo", value: 123);
1503 QState *s2 = new QState(&machine);
1504 DEFINE_ACTIVE_SPY(s2);
1505 s2->assignProperty(object: &obj, name: "foo", value: 456);
1506 s2->assignProperty(object: &obj, name: "bar", value: 789);
1507 QAbstractTransition *trans = s1->addTransition(target: s2);
1508 QVERIFY(trans->animations().isEmpty());
1509 QTest::ignoreMessage(type: QtWarningMsg, message: "QAbstractTransition::addAnimation: cannot add null animation");
1510 trans->addAnimation(animation: 0);
1511 QPropertyAnimation anim(&obj, "foo");
1512 anim.setDuration(250);
1513 trans->addAnimation(animation: &anim);
1514 QCOMPARE(trans->animations().size(), 1);
1515 QCOMPARE(trans->animations().at(0), (QAbstractAnimation*)&anim);
1516 QCOMPARE(anim.parent(), (QObject*)0);
1517 QTest::ignoreMessage(type: QtWarningMsg, message: "QAbstractTransition::removeAnimation: cannot remove null animation");
1518 trans->removeAnimation(animation: 0);
1519 trans->removeAnimation(animation: &anim);
1520 QVERIFY(trans->animations().isEmpty());
1521 trans->addAnimation(animation: &anim);
1522 QCOMPARE(trans->animations().size(), 1);
1523 QCOMPARE(trans->animations().at(0), (QAbstractAnimation*)&anim);
1524 QFinalState *s3 = new QFinalState(&machine);
1525 s2->addTransition(sender: s2, SIGNAL(propertiesAssigned()), target: s3);
1526
1527 machine.setInitialState(s1);
1528 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1529 QVERIFY(runningSpy.isValid());
1530 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1531 QVERIFY(finishedSpy.isValid());
1532 machine.start();
1533 QTRY_COMPARE(finishedSpy.count(), 1);
1534 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1535 QCOMPARE(obj.property("foo").toInt(), 456);
1536 QCOMPARE(obj.property("bar").toInt(), 789);
1537 TEST_ACTIVE_CHANGED(s1, 2);
1538 TEST_ACTIVE_CHANGED(s2, 2);
1539 }
1540 // Two animations
1541 {
1542 QStateMachine machine;
1543 QObject obj;
1544 obj.setProperty(name: "foo", value: 321);
1545 obj.setProperty(name: "bar", value: 654);
1546 QState *s1 = new QState(&machine);
1547 DEFINE_ACTIVE_SPY(s1);
1548 s1->assignProperty(object: &obj, name: "foo", value: 123);
1549 QState *s2 = new QState(&machine);
1550 DEFINE_ACTIVE_SPY(s2);
1551 s2->assignProperty(object: &obj, name: "foo", value: 456);
1552 s2->assignProperty(object: &obj, name: "bar", value: 789);
1553 QAbstractTransition *trans = s1->addTransition(target: s2);
1554 QPropertyAnimation anim(&obj, "foo");
1555 anim.setDuration(150);
1556 trans->addAnimation(animation: &anim);
1557 QPropertyAnimation anim2(&obj, "bar");
1558 anim2.setDuration(150);
1559 trans->addAnimation(animation: &anim2);
1560 QFinalState *s3 = new QFinalState(&machine);
1561 s2->addTransition(sender: s2, SIGNAL(propertiesAssigned()), target: s3);
1562
1563 machine.setInitialState(s1);
1564 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1565 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1566 QVERIFY(finishedSpy.isValid());
1567 QVERIFY(runningSpy.isValid());
1568 machine.start();
1569 QTRY_COMPARE(finishedSpy.count(), 1);
1570 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1571 QCOMPARE(obj.property("foo").toInt(), 456);
1572 QCOMPARE(obj.property("bar").toInt(), 789);
1573 TEST_ACTIVE_CHANGED(s1, 2);
1574 TEST_ACTIVE_CHANGED(s2, 2);
1575 }
1576 // Animation group
1577 {
1578 QStateMachine machine;
1579 QObject obj;
1580 obj.setProperty(name: "foo", value: 321);
1581 obj.setProperty(name: "bar", value: 654);
1582 QState *s1 = new QState(&machine);
1583 DEFINE_ACTIVE_SPY(s1);
1584 s1->assignProperty(object: &obj, name: "foo", value: 123);
1585 s1->assignProperty(object: &obj, name: "bar", value: 321);
1586 QState *s2 = new QState(&machine);
1587 DEFINE_ACTIVE_SPY(s2);
1588 s2->assignProperty(object: &obj, name: "foo", value: 456);
1589 s2->assignProperty(object: &obj, name: "bar", value: 654);
1590 s2->assignProperty(object: &obj, name: "baz", value: 789);
1591 QAbstractTransition *trans = s1->addTransition(target: s2);
1592 QSequentialAnimationGroup group;
1593 group.addAnimation(animation: new QPropertyAnimation(&obj, "foo"));
1594 group.addAnimation(animation: new QPropertyAnimation(&obj, "bar"));
1595 trans->addAnimation(animation: &group);
1596 QFinalState *s3 = new QFinalState(&machine);
1597 s2->addTransition(sender: s2, SIGNAL(propertiesAssigned()), target: s3);
1598
1599 machine.setInitialState(s1);
1600 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1601 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1602 QVERIFY(finishedSpy.isValid());
1603 QVERIFY(runningSpy.isValid());
1604 machine.start();
1605 QTRY_COMPARE(finishedSpy.count(), 1);
1606 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1607 QCOMPARE(obj.property("foo").toInt(), 456);
1608 QCOMPARE(obj.property("bar").toInt(), 654);
1609 QCOMPARE(obj.property("baz").toInt(), 789);
1610 TEST_ACTIVE_CHANGED(s1, 2);
1611 TEST_ACTIVE_CHANGED(s2, 2);
1612 }
1613 // Nested states
1614 {
1615 QStateMachine machine;
1616 QObject obj;
1617 obj.setProperty(name: "foo", value: 321);
1618 obj.setProperty(name: "bar", value: 654);
1619 QState *s1 = new QState(&machine);
1620 DEFINE_ACTIVE_SPY(s1);
1621 QCOMPARE(s1->childMode(), QState::ExclusiveStates);
1622 s1->setChildMode(QState::ParallelStates);
1623 QCOMPARE(s1->childMode(), QState::ParallelStates);
1624 s1->setChildMode(QState::ExclusiveStates);
1625 QCOMPARE(s1->childMode(), QState::ExclusiveStates);
1626 QCOMPARE(s1->initialState(), (QAbstractState*)0);
1627 s1->setObjectName("s1");
1628 s1->assignProperty(object: &obj, name: "foo", value: 123);
1629 s1->assignProperty(object: &obj, name: "bar", value: 456);
1630 QState *s2 = new QState(&machine);
1631 DEFINE_ACTIVE_SPY(s2);
1632 s2->setObjectName("s2");
1633 s2->assignProperty(object: &obj, name: "foo", value: 321);
1634 QState *s21 = new QState(s2);
1635 DEFINE_ACTIVE_SPY(s21);
1636 s21->setObjectName("s21");
1637 s21->assignProperty(object: &obj, name: "bar", value: 654);
1638 QState *s22 = new QState(s2);
1639 DEFINE_ACTIVE_SPY(s22);
1640 s22->setObjectName("s22");
1641 s22->assignProperty(object: &obj, name: "bar", value: 789);
1642 s2->setInitialState(s21);
1643 QCOMPARE(s2->initialState(), (QAbstractState*)s21);
1644
1645 QAbstractTransition *trans = s1->addTransition(target: s2);
1646 QPropertyAnimation anim(&obj, "foo");
1647 anim.setDuration(500);
1648 trans->addAnimation(animation: &anim);
1649 QPropertyAnimation anim2(&obj, "bar");
1650 anim2.setDuration(250);
1651 trans->addAnimation(animation: &anim2);
1652
1653 s21->addTransition(sender: s21, SIGNAL(propertiesAssigned()), target: s22);
1654
1655 QFinalState *s3 = new QFinalState(&machine);
1656 s22->addTransition(sender: s2, SIGNAL(propertiesAssigned()), target: s3);
1657
1658 machine.setInitialState(s1);
1659 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1660 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1661 QVERIFY(finishedSpy.isValid());
1662 QVERIFY(runningSpy.isValid());
1663 machine.start();
1664 QTRY_COMPARE(finishedSpy.count(), 1);
1665 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1666 QCOMPARE(obj.property("foo").toInt(), 321);
1667 QCOMPARE(obj.property("bar").toInt(), 789);
1668 TEST_ACTIVE_CHANGED(s1, 2);
1669 TEST_ACTIVE_CHANGED(s2, 2);
1670 TEST_ACTIVE_CHANGED(s21, 2);
1671 TEST_ACTIVE_CHANGED(s22, 2);
1672 }
1673 // Aborted animation
1674 {
1675 QStateMachine machine;
1676 SignalEmitter emitter;
1677 QObject obj;
1678 obj.setProperty(name: "foo", value: 321);
1679 obj.setProperty(name: "bar", value: 654);
1680 QState *group = new QState(&machine);
1681 QState *s1 = new QState(group);
1682 DEFINE_ACTIVE_SPY(s1);
1683 group->setInitialState(s1);
1684 s1->assignProperty(object: &obj, name: "foo", value: 123);
1685 QState *s2 = new QState(group);
1686 DEFINE_ACTIVE_SPY(s2);
1687 s2->assignProperty(object: &obj, name: "foo", value: 456);
1688 s2->assignProperty(object: &obj, name: "bar", value: 789);
1689 QAbstractTransition *trans = s1->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s2);
1690 QPropertyAnimation anim(&obj, "foo");
1691 anim.setDuration(8000);
1692 trans->addAnimation(animation: &anim);
1693 QPropertyAnimation anim2(&obj, "bar");
1694 anim2.setDuration(8000);
1695 trans->addAnimation(animation: &anim2);
1696 QState *s3 = new QState(group);
1697 DEFINE_ACTIVE_SPY(s3);
1698 s3->assignProperty(object: &obj, name: "foo", value: 911);
1699 s2->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s3);
1700
1701 machine.setInitialState(group);
1702 machine.start();
1703 QTRY_COMPARE(machine.configuration().contains(s1), true);
1704 QSignalSpy propertiesAssignedSpy(s2, &QState::propertiesAssigned);
1705 QVERIFY(propertiesAssignedSpy.isValid());
1706 emitter.emitSignalWithNoArg();
1707 QTRY_COMPARE(machine.configuration().contains(s2), true);
1708 QVERIFY(propertiesAssignedSpy.isEmpty());
1709 emitter.emitSignalWithNoArg(); // will cause animations from s1-->s2 to abort
1710 QTRY_COMPARE(machine.configuration().contains(s3), true);
1711 QVERIFY(propertiesAssignedSpy.isEmpty());
1712 QCOMPARE(obj.property("foo").toInt(), 911);
1713 QCOMPARE(obj.property("bar").toInt(), 789);
1714 TEST_ACTIVE_CHANGED(s1, 2);
1715 TEST_ACTIVE_CHANGED(s2, 2);
1716 TEST_ACTIVE_CHANGED(s3, 1);
1717 QVERIFY(machine.isRunning());
1718 }
1719}
1720
1721struct StringEvent : public QEvent
1722{
1723public:
1724 StringEvent(const QString &val)
1725 : QEvent(QEvent::Type(QEvent::User+2)),
1726 value(val) {}
1727
1728 QString value;
1729};
1730
1731class StringTransition : public QAbstractTransition
1732{
1733public:
1734 StringTransition(const QString &value, QAbstractState *target)
1735 : QAbstractTransition(), m_value(value)
1736 { setTargetState(target); }
1737
1738protected:
1739 virtual bool eventTest(QEvent *e)
1740 {
1741 if (e->type() != QEvent::Type(QEvent::User+2))
1742 return false;
1743 StringEvent *se = static_cast<StringEvent*>(e);
1744 return (m_value == se->value) && (!m_cond.isValid() || m_cond.match(subject: m_value).hasMatch());
1745 }
1746 virtual void onTransition(QEvent *) {}
1747
1748private:
1749 QString m_value;
1750 QRegularExpression m_cond;
1751};
1752
1753class StringEventPoster : public QState
1754{
1755public:
1756 StringEventPoster(const QString &value, QState *parent = 0)
1757 : QState(parent), m_value(value), m_delay(-1) {}
1758
1759 void setString(const QString &value)
1760 { m_value = value; }
1761 void setDelay(int delay)
1762 { m_delay = delay; }
1763
1764protected:
1765 virtual void onEntry(QEvent *)
1766 {
1767 if (m_delay == -1)
1768 machine()->postEvent(event: new StringEvent(m_value));
1769 else
1770 machine()->postDelayedEvent(event: new StringEvent(m_value), delay: m_delay);
1771 }
1772 virtual void onExit(QEvent *) {}
1773
1774private:
1775 QString m_value;
1776 int m_delay;
1777};
1778
1779void tst_QStateMachine::postEvent()
1780{
1781 for (int x = 0; x < 2; ++x) {
1782 QStateMachine machine;
1783 {
1784 QEvent e(QEvent::None);
1785 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::postEvent: cannot post event when the state machine is not running");
1786 machine.postEvent(event: &e);
1787 }
1788 StringEventPoster *s1 = new StringEventPoster("a");
1789 DEFINE_ACTIVE_SPY(s1);
1790 if (x == 1)
1791 s1->setDelay(100);
1792 QFinalState *s2 = new QFinalState;
1793 s1->addTransition(transition: new StringTransition("a", s2));
1794 machine.addState(state: s1);
1795 machine.addState(state: s2);
1796 machine.setInitialState(s1);
1797 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1798 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1799 QVERIFY(finishedSpy.isValid());
1800 QVERIFY(runningSpy.isValid());
1801 machine.start();
1802 QTRY_COMPARE(finishedSpy.count(), 1);
1803 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1804 QCOMPARE(machine.configuration().size(), 1);
1805 QVERIFY(machine.configuration().contains(s2));
1806 TEST_ACTIVE_CHANGED(s1, 2);
1807
1808 s1->setString("b");
1809 QFinalState *s3 = new QFinalState();
1810 machine.addState(state: s3);
1811 s1->addTransition(transition: new StringTransition("b", s3));
1812 finishedSpy.clear();
1813 machine.start();
1814 QTRY_COMPARE(finishedSpy.count(), 1);
1815 QCOMPARE(machine.configuration().size(), 1);
1816 QVERIFY(machine.configuration().contains(s3));
1817 TEST_ACTIVE_CHANGED(s1, 4);
1818 }
1819}
1820
1821void tst_QStateMachine::cancelDelayedEvent()
1822{
1823 QStateMachine machine;
1824 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::cancelDelayedEvent: the machine is not running");
1825 QVERIFY(!machine.cancelDelayedEvent(-1));
1826
1827 QState *s1 = new QState(&machine);
1828 DEFINE_ACTIVE_SPY(s1);
1829 QFinalState *s2 = new QFinalState(&machine);
1830 s1->addTransition(transition: new StringTransition("a", s2));
1831 machine.setInitialState(s1);
1832
1833 QSignalSpy startedSpy(&machine, &QStateMachine::started);
1834 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1835 QVERIFY(startedSpy.isValid());
1836 QVERIFY(runningSpy.isValid());
1837 machine.start();
1838 QTRY_COMPARE(startedSpy.count(), 1);
1839 TEST_RUNNING_CHANGED(true);
1840 TEST_ACTIVE_CHANGED(s1, 1);
1841 QCOMPARE(machine.configuration().size(), 1);
1842 QVERIFY(machine.configuration().contains(s1));
1843 int id1 = machine.postDelayedEvent(event: new StringEvent("c"), delay: 50000);
1844 QVERIFY(id1 != -1);
1845 int id2 = machine.postDelayedEvent(event: new StringEvent("b"), delay: 25000);
1846 QVERIFY(id2 != -1);
1847 QVERIFY(id2 != id1);
1848 int id3 = machine.postDelayedEvent(event: new StringEvent("a"), delay: 100);
1849 QVERIFY(id3 != -1);
1850 QVERIFY(id3 != id2);
1851 QVERIFY(machine.cancelDelayedEvent(id1));
1852 QVERIFY(!machine.cancelDelayedEvent(id1));
1853 QVERIFY(machine.cancelDelayedEvent(id2));
1854 QVERIFY(!machine.cancelDelayedEvent(id2));
1855
1856 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1857 QVERIFY(finishedSpy.isValid());
1858 QTRY_COMPARE(finishedSpy.count(), 1);
1859 TEST_RUNNING_CHANGED(false);
1860 TEST_ACTIVE_CHANGED(s1, 2);
1861 QCOMPARE(machine.configuration().size(), 1);
1862 QVERIFY(machine.configuration().contains(s2));
1863}
1864
1865void tst_QStateMachine::postDelayedEventAndStop()
1866{
1867 QStateMachine machine;
1868 QState *s1 = new QState(&machine);
1869 DEFINE_ACTIVE_SPY(s1);
1870 QFinalState *s2 = new QFinalState(&machine);
1871 s1->addTransition(transition: new StringTransition("a", s2));
1872 machine.setInitialState(s1);
1873
1874 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1875 QVERIFY(runningSpy.isValid());
1876 QSignalSpy startedSpy(&machine, &QStateMachine::started);
1877 QVERIFY(startedSpy.isValid());
1878 machine.start();
1879 QTRY_COMPARE(startedSpy.count(), 1);
1880 TEST_RUNNING_CHANGED(true);
1881 TEST_ACTIVE_CHANGED(s1, 1);
1882 QCOMPARE(machine.configuration().size(), 1);
1883 QVERIFY(machine.configuration().contains(s1));
1884
1885 int id1 = machine.postDelayedEvent(event: new StringEvent("a"), delay: 0);
1886 QVERIFY(id1 != -1);
1887 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
1888 QVERIFY(stoppedSpy.isValid());
1889 machine.stop();
1890 QTRY_COMPARE(stoppedSpy.count(), 1);
1891 TEST_RUNNING_CHANGED(false);
1892 TEST_ACTIVE_CHANGED(s1, 1);
1893 QCOMPARE(machine.configuration().size(), 1);
1894 QVERIFY(machine.configuration().contains(s1));
1895
1896 machine.start();
1897 QTRY_COMPARE(startedSpy.count(), 2);
1898 TEST_RUNNING_CHANGED(true);
1899 TEST_ACTIVE_CHANGED(s1, 3);
1900 QCOMPARE(machine.configuration().size(), 1);
1901 QVERIFY(machine.configuration().contains(s1));
1902
1903 int id2 = machine.postDelayedEvent(event: new StringEvent("a"), delay: 1000);
1904 QVERIFY(id2 != -1);
1905 machine.stop();
1906 QTRY_COMPARE(stoppedSpy.count(), 2);
1907 TEST_RUNNING_CHANGED(false);
1908 TEST_ACTIVE_CHANGED(s1, 3);
1909 machine.start();
1910 QTRY_COMPARE(startedSpy.count(), 3);
1911 TEST_RUNNING_CHANGED(true);
1912 QTestEventLoop::instance().enterLoop(secs: 2);
1913 QCOMPARE(machine.configuration().size(), 1);
1914 QVERIFY(machine.configuration().contains(s1));
1915 TEST_ACTIVE_CHANGED(s1, 5);
1916 QVERIFY(machine.isRunning());
1917}
1918
1919class DelayedEventPosterThread : public QThread
1920{
1921 Q_OBJECT
1922public:
1923 DelayedEventPosterThread(QStateMachine *machine, QObject *parent = 0)
1924 : QThread(parent), firstEventWasCancelled(false),
1925 m_machine(machine)
1926 {
1927 moveToThread(thread: this);
1928 QObject::connect(sender: m_machine, SIGNAL(started()),
1929 receiver: this, SLOT(postEvent()));
1930 }
1931
1932 mutable bool firstEventWasCancelled;
1933
1934private Q_SLOTS:
1935 void postEvent()
1936 {
1937 int id = m_machine->postDelayedEvent(event: new QEvent(QEvent::User), delay: 1000);
1938 firstEventWasCancelled = m_machine->cancelDelayedEvent(id);
1939
1940 m_machine->postDelayedEvent(event: new QEvent(QEvent::User), delay: 1);
1941
1942 quit();
1943 }
1944private:
1945 QStateMachine *m_machine;
1946};
1947
1948void tst_QStateMachine::postDelayedEventFromThread()
1949{
1950 QStateMachine machine;
1951 QState *s1 = new QState(&machine);
1952 DEFINE_ACTIVE_SPY(s1);
1953 QFinalState *f = new QFinalState(&machine);
1954 s1->addTransition(transition: new EventTransition(QEvent::User, f));
1955 machine.setInitialState(s1);
1956
1957 DelayedEventPosterThread poster(&machine);
1958 poster.start();
1959
1960 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1961 QVERIFY(runningSpy.isValid());
1962 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
1963 QVERIFY(finishedSpy.isValid());
1964 machine.start();
1965 QTRY_COMPARE(finishedSpy.count(), 1);
1966 TEST_RUNNING_CHANGED_STARTED_STOPPED;
1967 TEST_ACTIVE_CHANGED(s1, 2);
1968 QVERIFY(poster.firstEventWasCancelled);
1969}
1970
1971void tst_QStateMachine::stopAndPostEvent()
1972{
1973 QStateMachine machine;
1974 QState *s1 = new QState(&machine);
1975 DEFINE_ACTIVE_SPY(s1);
1976 machine.setInitialState(s1);
1977 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
1978 QVERIFY(runningSpy.isValid());
1979 QSignalSpy startedSpy(&machine, &QStateMachine::started);
1980 QVERIFY(startedSpy.isValid());
1981 machine.start();
1982 QTRY_COMPARE(startedSpy.count(), 1);
1983 TEST_RUNNING_CHANGED(true);
1984 TEST_ACTIVE_CHANGED(s1, 1);
1985 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
1986 QVERIFY(stoppedSpy.isValid());
1987 machine.stop();
1988 QCOMPARE(stoppedSpy.count(), 0);
1989 machine.postEvent(event: new QEvent(QEvent::User));
1990 QTRY_COMPARE(stoppedSpy.count(), 1);
1991 TEST_RUNNING_CHANGED(false);
1992 TEST_ACTIVE_CHANGED(s1, 1);
1993 QCoreApplication::processEvents();
1994}
1995
1996void tst_QStateMachine::stateFinished()
1997{
1998 QStateMachine machine;
1999 QState *s1 = new QState(&machine);
2000 DEFINE_ACTIVE_SPY(s1);
2001 QState *s1_1 = new QState(s1);
2002 DEFINE_ACTIVE_SPY(s1_1);
2003 QFinalState *s1_2 = new QFinalState(s1);
2004 s1_1->addTransition(target: s1_2);
2005 s1->setInitialState(s1_1);
2006 QFinalState *s2 = new QFinalState(&machine);
2007 s1->addTransition(sender: s1, SIGNAL(finished()), target: s2);
2008 machine.setInitialState(s1);
2009 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2010 QVERIFY(runningSpy.isValid());
2011 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2012 QVERIFY(finishedSpy.isValid());
2013 machine.start();
2014 QTRY_COMPARE(finishedSpy.count(), 1);
2015 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2016 TEST_ACTIVE_CHANGED(s1, 2);
2017 TEST_ACTIVE_CHANGED(s1_1, 2);
2018 QCOMPARE(machine.configuration().size(), 1);
2019 QVERIFY(machine.configuration().contains(s2));
2020}
2021
2022void tst_QStateMachine::parallelStates()
2023{
2024 QStateMachine machine;
2025
2026 TestState *s1 = new TestState(QState::ParallelStates);
2027 QCOMPARE(s1->childMode(), QState::ParallelStates);
2028 TestState *s1_1 = new TestState(s1);
2029 QState *s1_1_1 = new QState(s1_1);
2030 QFinalState *s1_1_f = new QFinalState(s1_1);
2031 s1_1_1->addTransition(target: s1_1_f);
2032 s1_1->setInitialState(s1_1_1);
2033 TestState *s1_2 = new TestState(s1);
2034 QState *s1_2_1 = new QState(s1_2);
2035 QFinalState *s1_2_f = new QFinalState(s1_2);
2036 s1_2_1->addTransition(target: s1_2_f);
2037 s1_2->setInitialState(s1_2_1);
2038 {
2039 const QString warning
2040 = QString::asprintf(format: "QState::setInitialState: ignoring attempt to set initial state of parallel state group %p", s1);
2041 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
2042 s1->setInitialState(0);
2043 }
2044 machine.addState(state: s1);
2045
2046 QFinalState *s2 = new QFinalState();
2047 machine.addState(state: s2);
2048
2049 s1->addTransition(sender: s1, SIGNAL(finished()), target: s2);
2050
2051 machine.setInitialState(s1);
2052 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2053 QVERIFY(runningSpy.isValid());
2054 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2055 QVERIFY(finishedSpy.isValid());
2056 globalTick = 0;
2057 machine.start();
2058 QTRY_COMPARE(finishedSpy.count(), 1);
2059 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2060 QCOMPARE(machine.configuration().size(), 1);
2061 QVERIFY(machine.configuration().contains(s2));
2062
2063 QCOMPARE(s1->events.count(), 2);
2064 // s1 is entered
2065 QCOMPARE(s1->events.at(0).first, 0);
2066 QCOMPARE(s1->events.at(0).second, TestState::Entry);
2067 // s1_1 is entered
2068 QCOMPARE(s1_1->events.count(), 2);
2069 QCOMPARE(s1_1->events.at(0).first, 1);
2070 QCOMPARE(s1_1->events.at(0).second, TestState::Entry);
2071 // s1_2 is entered
2072 QCOMPARE(s1_2->events.at(0).first, 2);
2073 QCOMPARE(s1_2->events.at(0).second, TestState::Entry);
2074 // s1_2 is exited
2075 QCOMPARE(s1_2->events.at(1).first, 3);
2076 QCOMPARE(s1_2->events.at(1).second, TestState::Exit);
2077 // s1_1 is exited
2078 QCOMPARE(s1_1->events.at(1).first, 4);
2079 QCOMPARE(s1_1->events.at(1).second, TestState::Exit);
2080 // s1 is exited
2081 QCOMPARE(s1->events.at(1).first, 5);
2082 QCOMPARE(s1->events.at(1).second, TestState::Exit);
2083}
2084
2085void tst_QStateMachine::parallelRootState()
2086{
2087 QStateMachine machine;
2088 QState *root = &machine;
2089 QCOMPARE(root->childMode(), QState::ExclusiveStates);
2090 root->setChildMode(QState::ParallelStates);
2091 QCOMPARE(root->childMode(), QState::ParallelStates);
2092
2093 QState *s1 = new QState(root);
2094 DEFINE_ACTIVE_SPY(s1);
2095 QFinalState *s1_f = new QFinalState(s1);
2096 s1->setInitialState(s1_f);
2097 QState *s2 = new QState(root);
2098 DEFINE_ACTIVE_SPY(s2);
2099 QFinalState *s2_f = new QFinalState(s2);
2100 s2->setInitialState(s2_f);
2101
2102 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2103 QVERIFY(runningSpy.isValid());
2104 QSignalSpy startedSpy(&machine, &QStateMachine::started);
2105 QVERIFY(startedSpy.isValid());
2106 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2107 QVERIFY(finishedSpy.isValid());
2108 machine.start();
2109 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: "
2110 "Child mode of state machine '' is not 'ExclusiveStates'.");
2111 QTRY_COMPARE(startedSpy.count(), 1);
2112 QCOMPARE(machine.configuration().size(), 4);
2113 QVERIFY(machine.configuration().contains(s1));
2114 QVERIFY(machine.configuration().contains(s1_f));
2115 QVERIFY(machine.configuration().contains(s2));
2116 QVERIFY(machine.configuration().contains(s2_f));
2117 QTRY_COMPARE(finishedSpy.count(), 1);
2118 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2119 TEST_ACTIVE_CHANGED(s1, 1);
2120 TEST_ACTIVE_CHANGED(s2, 1);
2121 QVERIFY(!machine.isRunning());
2122}
2123
2124void tst_QStateMachine::allSourceToTargetConfigurations()
2125{
2126 QStateMachine machine;
2127 QState *s0 = new QState(&machine);
2128 DEFINE_ACTIVE_SPY(s0);
2129 s0->setObjectName("s0");
2130 QState *s1 = new QState(s0);
2131 DEFINE_ACTIVE_SPY(s1);
2132 s1->setObjectName("s1");
2133 QState *s11 = new QState(s1);
2134 DEFINE_ACTIVE_SPY(s11);
2135 s11->setObjectName("s11");
2136 QState *s2 = new QState(s0);
2137 DEFINE_ACTIVE_SPY(s2);
2138 s2->setObjectName("s2");
2139 QState *s21 = new QState(s2);
2140 DEFINE_ACTIVE_SPY(s21);
2141 s21->setObjectName("s21");
2142 QState *s211 = new QState(s21);
2143 DEFINE_ACTIVE_SPY(s211);
2144 s211->setObjectName("s211");
2145 QFinalState *f = new QFinalState(&machine);
2146 f->setObjectName("f");
2147
2148 s0->setInitialState(s1);
2149 s1->setInitialState(s11);
2150 s2->setInitialState(s21);
2151 s21->setInitialState(s211);
2152
2153 s11->addTransition(transition: new StringTransition("g", s211));
2154 s1->addTransition(transition: new StringTransition("a", s1));
2155 s1->addTransition(transition: new StringTransition("b", s11));
2156 s1->addTransition(transition: new StringTransition("c", s2));
2157 s1->addTransition(transition: new StringTransition("d", s0));
2158 s1->addTransition(transition: new StringTransition("f", s211));
2159 s211->addTransition(transition: new StringTransition("d", s21));
2160 s211->addTransition(transition: new StringTransition("g", s0));
2161 s211->addTransition(transition: new StringTransition("h", f));
2162 s21->addTransition(transition: new StringTransition("b", s211));
2163 s2->addTransition(transition: new StringTransition("c", s1));
2164 s2->addTransition(transition: new StringTransition("f", s11));
2165 s0->addTransition(transition: new StringTransition("e", s211));
2166
2167 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2168 QVERIFY(runningSpy.isValid());
2169 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2170 QVERIFY(finishedSpy.isValid());
2171 machine.setInitialState(s0);
2172 machine.start();
2173 QCoreApplication::processEvents();
2174 TEST_ACTIVE_CHANGED(s0, 1);
2175 TEST_ACTIVE_CHANGED(s1, 1);
2176 TEST_ACTIVE_CHANGED(s11, 1);
2177 TEST_ACTIVE_CHANGED(s2, 0);
2178 TEST_ACTIVE_CHANGED(s21, 0);
2179 TEST_ACTIVE_CHANGED(s211, 0);
2180
2181 machine.postEvent(event: new StringEvent("a"));
2182 QCoreApplication::processEvents();
2183 TEST_ACTIVE_CHANGED(s0, 1);
2184 TEST_ACTIVE_CHANGED(s1, 3);
2185 TEST_ACTIVE_CHANGED(s11, 3);
2186 TEST_ACTIVE_CHANGED(s2, 0);
2187 TEST_ACTIVE_CHANGED(s21, 0);
2188 TEST_ACTIVE_CHANGED(s211, 0);
2189
2190 machine.postEvent(event: new StringEvent("b"));
2191 QCoreApplication::processEvents();
2192 TEST_ACTIVE_CHANGED(s0, 1);
2193 TEST_ACTIVE_CHANGED(s1, 5);
2194 TEST_ACTIVE_CHANGED(s11, 5);
2195 TEST_ACTIVE_CHANGED(s2, 0);
2196 TEST_ACTIVE_CHANGED(s21, 0);
2197 TEST_ACTIVE_CHANGED(s211, 0);
2198
2199 machine.postEvent(event: new StringEvent("c"));
2200 QCoreApplication::processEvents();
2201 TEST_ACTIVE_CHANGED(s0, 1);
2202 TEST_ACTIVE_CHANGED(s1, 6);
2203 TEST_ACTIVE_CHANGED(s11, 6);
2204 TEST_ACTIVE_CHANGED(s2, 1);
2205 TEST_ACTIVE_CHANGED(s21, 1);
2206 TEST_ACTIVE_CHANGED(s211, 1);
2207
2208 machine.postEvent(event: new StringEvent("d"));
2209 QCoreApplication::processEvents();
2210 TEST_ACTIVE_CHANGED(s0, 1);
2211 TEST_ACTIVE_CHANGED(s1, 6);
2212 TEST_ACTIVE_CHANGED(s11, 6);
2213 TEST_ACTIVE_CHANGED(s2, 1);
2214 TEST_ACTIVE_CHANGED(s21, 3);
2215 TEST_ACTIVE_CHANGED(s211, 3);
2216
2217 machine.postEvent(event: new StringEvent("e"));
2218 QCoreApplication::processEvents();
2219 TEST_ACTIVE_CHANGED(s0, 3);
2220 TEST_ACTIVE_CHANGED(s1, 6);
2221 TEST_ACTIVE_CHANGED(s11, 6);
2222 TEST_ACTIVE_CHANGED(s2, 3);
2223 TEST_ACTIVE_CHANGED(s21, 5);
2224 TEST_ACTIVE_CHANGED(s211, 5);
2225
2226 machine.postEvent(event: new StringEvent("f"));
2227 QCoreApplication::processEvents();
2228 TEST_ACTIVE_CHANGED(s0, 3);
2229 TEST_ACTIVE_CHANGED(s1, 7);
2230 TEST_ACTIVE_CHANGED(s11, 7);
2231 TEST_ACTIVE_CHANGED(s2, 4);
2232 TEST_ACTIVE_CHANGED(s21, 6);
2233 TEST_ACTIVE_CHANGED(s211, 6);
2234
2235 machine.postEvent(event: new StringEvent("g"));
2236 QCoreApplication::processEvents();
2237 TEST_ACTIVE_CHANGED(s0, 3);
2238 TEST_ACTIVE_CHANGED(s1, 8);
2239 TEST_ACTIVE_CHANGED(s11, 8);
2240 TEST_ACTIVE_CHANGED(s2, 5);
2241 TEST_ACTIVE_CHANGED(s21, 7);
2242 TEST_ACTIVE_CHANGED(s211, 7);
2243
2244 machine.postEvent(event: new StringEvent("h"));
2245 QCoreApplication::processEvents();
2246 TEST_ACTIVE_CHANGED(s0, 4);
2247 TEST_ACTIVE_CHANGED(s1, 8);
2248 TEST_ACTIVE_CHANGED(s11, 8);
2249 TEST_ACTIVE_CHANGED(s2, 6);
2250 TEST_ACTIVE_CHANGED(s21, 8);
2251 TEST_ACTIVE_CHANGED(s211, 8);
2252
2253 QTRY_COMPARE(finishedSpy.count(), 1);
2254 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2255}
2256
2257class TestSignalTransition : public QSignalTransition
2258{
2259public:
2260 TestSignalTransition(QState *sourceState = 0)
2261 : QSignalTransition(sourceState),
2262 m_eventTestSender(0), m_eventTestSignalIndex(-1),
2263 m_transitionSender(0), m_transitionSignalIndex(-1)
2264 {}
2265 TestSignalTransition(QObject *sender, const char *signal,
2266 QAbstractState *target)
2267 : QSignalTransition(sender, signal),
2268 m_eventTestSender(0), m_eventTestSignalIndex(-1),
2269 m_transitionSender(0), m_transitionSignalIndex(-1)
2270 { setTargetState(target); }
2271 QObject *eventTestSenderReceived() const {
2272 return m_eventTestSender;
2273 }
2274 int eventTestSignalIndexReceived() const {
2275 return m_eventTestSignalIndex;
2276 }
2277 QVariantList eventTestArgumentsReceived() const {
2278 return m_eventTestArgs;
2279 }
2280 QObject *transitionSenderReceived() const {
2281 return m_transitionSender;
2282 }
2283 int transitionSignalIndexReceived() const {
2284 return m_transitionSignalIndex;
2285 }
2286 QVariantList transitionArgumentsReceived() const {
2287 return m_transitionArgs;
2288 }
2289protected:
2290 bool eventTest(QEvent *e) {
2291 if (!QSignalTransition::eventTest(event: e))
2292 return false;
2293 QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
2294 m_eventTestSender = se->sender();
2295 m_eventTestSignalIndex = se->signalIndex();
2296 m_eventTestArgs = se->arguments();
2297 return true;
2298 }
2299 void onTransition(QEvent *e) {
2300 QSignalTransition::onTransition(event: e);
2301 QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
2302 m_transitionSender = se->sender();
2303 m_transitionSignalIndex = se->signalIndex();
2304 m_transitionArgs = se->arguments();
2305 }
2306private:
2307 QObject *m_eventTestSender;
2308 int m_eventTestSignalIndex;
2309 QVariantList m_eventTestArgs;
2310 QObject *m_transitionSender;
2311 int m_transitionSignalIndex;
2312 QVariantList m_transitionArgs;
2313};
2314
2315void tst_QStateMachine::signalTransitions()
2316{
2317 {
2318 QStateMachine machine;
2319 QState *s0 = new QState(&machine);
2320 DEFINE_ACTIVE_SPY(s0);
2321 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::addTransition: sender cannot be null");
2322 QCOMPARE(s0->addTransition(0, SIGNAL(noSuchSignal()), 0), (QSignalTransition*)0);
2323
2324 SignalEmitter emitter;
2325 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::addTransition: signal cannot be null");
2326 QCOMPARE(s0->addTransition(&emitter, 0, 0), (QSignalTransition*)0);
2327
2328 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::addTransition: cannot add transition to null state");
2329 QCOMPARE(s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), 0), (QSignalTransition*)0);
2330
2331 QFinalState *s1 = new QFinalState(&machine);
2332 QTest::ignoreMessage(type: QtWarningMsg, message: "QState::addTransition: no such signal SignalEmitter::noSuchSignal()");
2333 QCOMPARE(s0->addTransition(&emitter, SIGNAL(noSuchSignal()), s1), (QSignalTransition*)0);
2334
2335 QSignalTransition *trans = s0->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s1);
2336 QVERIFY(trans != 0);
2337 QCOMPARE(trans->sourceState(), s0);
2338 QCOMPARE(trans->targetState(), (QAbstractState*)s1);
2339 QCOMPARE(trans->senderObject(), (QObject*)&emitter);
2340 QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
2341
2342 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2343 QVERIFY(runningSpy.isValid());
2344 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2345 QVERIFY(finishedSpy.isValid());
2346 machine.setInitialState(s0);
2347 machine.start();
2348 QCoreApplication::processEvents();
2349
2350 emitter.emitSignalWithNoArg();
2351
2352 QTRY_COMPARE(finishedSpy.count(), 1);
2353 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2354 TEST_ACTIVE_CHANGED(s0, 2);
2355 emitter.emitSignalWithNoArg();
2356
2357 trans->setSignal(SIGNAL(signalWithIntArg(int)));
2358 QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithIntArg(int))));
2359 machine.start();
2360 QCoreApplication::processEvents();
2361 emitter.emitSignalWithIntArg(arg: 123);
2362 QTRY_COMPARE(finishedSpy.count(), 2);
2363 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2364 TEST_ACTIVE_CHANGED(s0, 4);
2365
2366 machine.start();
2367 QCoreApplication::processEvents();
2368 trans->setSignal(SIGNAL(signalWithNoArg()));
2369 QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
2370 emitter.emitSignalWithNoArg();
2371 QTRY_COMPARE(finishedSpy.count(), 3);
2372 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2373 TEST_ACTIVE_CHANGED(s0, 6);
2374
2375 SignalEmitter emitter2;
2376 machine.start();
2377 QCoreApplication::processEvents();
2378 trans->setSenderObject(&emitter2);
2379 emitter2.emitSignalWithNoArg();
2380 QTRY_COMPARE(finishedSpy.count(), 4);
2381 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2382 TEST_ACTIVE_CHANGED(s0, 8);
2383
2384 machine.start();
2385 QCoreApplication::processEvents();
2386 QTest::ignoreMessage(type: QtWarningMsg, message: "QSignalTransition: no such signal: SignalEmitter::noSuchSignal()");
2387 trans->setSignal(SIGNAL(noSuchSignal()));
2388 QCOMPARE(trans->signal(), QByteArray(SIGNAL(noSuchSignal())));
2389 TEST_RUNNING_CHANGED(true);
2390 TEST_ACTIVE_CHANGED(s0, 9);
2391 QVERIFY(machine.isRunning());
2392 }
2393 {
2394 QStateMachine machine;
2395 QState *s0 = new QState(&machine);
2396 DEFINE_ACTIVE_SPY(s0);
2397 QFinalState *s1 = new QFinalState(&machine);
2398 SignalEmitter emitter;
2399 QSignalTransition *trans = s0->addTransition(sender: &emitter, signal: "signalWithNoArg()", target: s1);
2400 QVERIFY(trans != 0);
2401 QCOMPARE(trans->sourceState(), s0);
2402 QCOMPARE(trans->targetState(), (QAbstractState*)s1);
2403 QCOMPARE(trans->senderObject(), (QObject*)&emitter);
2404 QCOMPARE(trans->signal(), QByteArray("signalWithNoArg()"));
2405
2406 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2407 QVERIFY(runningSpy.isValid());
2408 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2409 QVERIFY(finishedSpy.isValid());
2410 machine.setInitialState(s0);
2411 machine.start();
2412 QCoreApplication::processEvents();
2413 TEST_ACTIVE_CHANGED(s0, 1);
2414 emitter.emitSignalWithNoArg();
2415
2416 QTRY_COMPARE(finishedSpy.count(), 1);
2417 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2418 TEST_ACTIVE_CHANGED(s0, 2);
2419
2420 trans->setSignal("signalWithIntArg(int)");
2421 QCOMPARE(trans->signal(), QByteArray("signalWithIntArg(int)"));
2422 machine.start();
2423 QCoreApplication::processEvents();
2424 TEST_ACTIVE_CHANGED(s0, 3);
2425 emitter.emitSignalWithIntArg(arg: 123);
2426 QTRY_COMPARE(finishedSpy.count(), 2);
2427 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2428 TEST_ACTIVE_CHANGED(s0, 4);
2429 }
2430 {
2431 QStateMachine machine;
2432 QState *s0 = new QState(&machine);
2433 DEFINE_ACTIVE_SPY(s0);
2434 QFinalState *s1 = new QFinalState(&machine);
2435 SignalEmitter emitter;
2436 TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithIntArg(int)), s1);
2437 s0->addTransition(transition: trans);
2438
2439 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2440 QVERIFY(runningSpy.isValid());
2441 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2442 QVERIFY(finishedSpy.isValid());
2443 machine.setInitialState(s0);
2444 machine.start();
2445 QCoreApplication::processEvents();
2446 TEST_ACTIVE_CHANGED(s0, 1);
2447 emitter.emitSignalWithIntArg(arg: 123);
2448
2449 QTRY_COMPARE(finishedSpy.count(), 1);
2450 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2451 TEST_ACTIVE_CHANGED(s0, 2);
2452 QCOMPARE(trans->eventTestSenderReceived(), (QObject*)&emitter);
2453 QCOMPARE(trans->eventTestSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithIntArg(int)"));
2454 QCOMPARE(trans->eventTestArgumentsReceived().size(), 1);
2455 QCOMPARE(trans->eventTestArgumentsReceived().at(0).toInt(), 123);
2456 QCOMPARE(trans->transitionSenderReceived(), (QObject*)&emitter);
2457 QCOMPARE(trans->transitionSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithIntArg(int)"));
2458 QCOMPARE(trans->transitionArgumentsReceived().size(), 1);
2459 QCOMPARE(trans->transitionArgumentsReceived().at(0).toInt(), 123);
2460 }
2461 {
2462 QStateMachine machine;
2463 QState *s0 = new QState(&machine);
2464 DEFINE_ACTIVE_SPY(s0);
2465 QFinalState *s1 = new QFinalState(&machine);
2466 SignalEmitter emitter;
2467 TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s1);
2468 s0->addTransition(transition: trans);
2469
2470 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2471 QVERIFY(runningSpy.isValid());
2472 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2473 QVERIFY(finishedSpy.isValid());
2474 machine.setInitialState(s0);
2475 machine.start();
2476 QCoreApplication::processEvents();
2477 TEST_ACTIVE_CHANGED(s0, 1);
2478
2479 QString testString = QString::fromLatin1(str: "hello");
2480 emitter.emitSignalWithStringArg(arg: testString);
2481
2482 QTRY_COMPARE(finishedSpy.count(), 1);
2483 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2484 TEST_ACTIVE_CHANGED(s0, 2);
2485 QCOMPARE(trans->eventTestSenderReceived(), (QObject*)&emitter);
2486 QCOMPARE(trans->eventTestSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithStringArg(QString)"));
2487 QCOMPARE(trans->eventTestArgumentsReceived().size(), 1);
2488 QCOMPARE(trans->eventTestArgumentsReceived().at(0).toString(), testString);
2489 QCOMPARE(trans->transitionSenderReceived(), (QObject*)&emitter);
2490 QCOMPARE(trans->transitionSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithStringArg(QString)"));
2491 QCOMPARE(trans->transitionArgumentsReceived().size(), 1);
2492 QCOMPARE(trans->transitionArgumentsReceived().at(0).toString(), testString);
2493 }
2494 {
2495 QStateMachine machine;
2496 QState *s0 = new QState(&machine);
2497 DEFINE_ACTIVE_SPY(s0);
2498 QFinalState *s1 = new QFinalState(&machine);
2499
2500 TestSignalTransition *trans = new TestSignalTransition();
2501 QCOMPARE(trans->senderObject(), (QObject*)0);
2502 QCOMPARE(trans->signal(), QByteArray());
2503
2504 SignalEmitter emitter;
2505 trans->setSenderObject(&emitter);
2506 QCOMPARE(trans->senderObject(), (QObject*)&emitter);
2507 trans->setSignal(SIGNAL(signalWithNoArg()));
2508 QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
2509 trans->setTargetState(s1);
2510 s0->addTransition(transition: trans);
2511
2512 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2513 QVERIFY(runningSpy.isValid());
2514 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2515 QVERIFY(finishedSpy.isValid());
2516 machine.setInitialState(s0);
2517 machine.start();
2518 QCoreApplication::processEvents();
2519 TEST_ACTIVE_CHANGED(s0, 1);
2520
2521 emitter.emitSignalWithNoArg();
2522
2523 QTRY_COMPARE(finishedSpy.count(), 1);
2524 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2525 TEST_ACTIVE_CHANGED(s0, 2);
2526 }
2527 // Multiple transitions for same (object,signal)
2528 {
2529 QStateMachine machine;
2530 SignalEmitter emitter;
2531 QState *s0 = new QState(&machine);
2532 DEFINE_ACTIVE_SPY(s0);
2533 QState *s1 = new QState(&machine);
2534 DEFINE_ACTIVE_SPY(s1);
2535 QSignalTransition *t0 = s0->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s1);
2536 QSignalTransition *t1 = s1->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s0);
2537
2538 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2539 QVERIFY(finishedSpy.isValid());
2540 machine.setInitialState(s0);
2541 machine.start();
2542 QCoreApplication::processEvents();
2543 TEST_ACTIVE_CHANGED(s0, 1);
2544 TEST_ACTIVE_CHANGED(s1, 0);
2545 QCOMPARE(machine.configuration().size(), 1);
2546 QVERIFY(machine.configuration().contains(s0));
2547
2548 emitter.emitSignalWithNoArg();
2549 QCoreApplication::processEvents();
2550 TEST_ACTIVE_CHANGED(s0, 2);
2551 TEST_ACTIVE_CHANGED(s1, 1);
2552 QCOMPARE(machine.configuration().size(), 1);
2553 QVERIFY(machine.configuration().contains(s1));
2554
2555 s0->removeTransition(transition: t0);
2556 emitter.emitSignalWithNoArg();
2557 QCoreApplication::processEvents();
2558 TEST_ACTIVE_CHANGED(s0, 3);
2559 TEST_ACTIVE_CHANGED(s1, 2);
2560 QCOMPARE(machine.configuration().size(), 1);
2561 QVERIFY(machine.configuration().contains(s0));
2562
2563 emitter.emitSignalWithNoArg();
2564 QCoreApplication::processEvents();
2565 TEST_ACTIVE_CHANGED(s0, 3);
2566 TEST_ACTIVE_CHANGED(s1, 2);
2567 QCOMPARE(machine.configuration().size(), 1);
2568 QVERIFY(machine.configuration().contains(s0));
2569
2570 s1->removeTransition(transition: t1);
2571 emitter.emitSignalWithNoArg();
2572 QCoreApplication::processEvents();
2573 TEST_ACTIVE_CHANGED(s0, 3);
2574 TEST_ACTIVE_CHANGED(s1, 2);
2575 QCOMPARE(machine.configuration().size(), 1);
2576 QVERIFY(machine.configuration().contains(s0));
2577
2578 s0->addTransition(transition: t0);
2579 s1->addTransition(transition: t1);
2580 emitter.emitSignalWithNoArg();
2581 QCoreApplication::processEvents();
2582 TEST_ACTIVE_CHANGED(s0, 4);
2583 TEST_ACTIVE_CHANGED(s1, 3);
2584 QVERIFY(machine.isRunning());
2585 QCOMPARE(machine.configuration().size(), 1);
2586 QVERIFY(machine.configuration().contains(s1));
2587 }
2588 // multiple signal transitions from same source
2589 {
2590 QStateMachine machine;
2591 SignalEmitter emitter;
2592 QState *s0 = new QState(&machine);
2593 DEFINE_ACTIVE_SPY(s0);
2594 QFinalState *s1 = new QFinalState(&machine);
2595 s0->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s1);
2596 QFinalState *s2 = new QFinalState(&machine);
2597 s0->addTransition(sender: &emitter, SIGNAL(signalWithIntArg(int)), target: s2);
2598 QFinalState *s3 = new QFinalState(&machine);
2599 s0->addTransition(sender: &emitter, SIGNAL(signalWithStringArg(QString)), target: s3);
2600
2601 QSignalSpy startedSpy(&machine, &QStateMachine::started);
2602 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2603 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2604 QVERIFY(startedSpy.isValid());
2605 QVERIFY(finishedSpy.isValid());
2606 QVERIFY(runningSpy.isValid());
2607 machine.setInitialState(s0);
2608
2609 machine.start();
2610 TEST_ACTIVE_CHANGED(s0, 1);
2611 QTRY_COMPARE(startedSpy.count(), 1);
2612 TEST_RUNNING_CHANGED(true);
2613 emitter.emitSignalWithNoArg();
2614 TEST_ACTIVE_CHANGED(s0, 2);
2615 QTRY_COMPARE(finishedSpy.count(), 1);
2616 TEST_RUNNING_CHANGED(false);
2617 QCOMPARE(machine.configuration().size(), 1);
2618 QVERIFY(machine.configuration().contains(s1));
2619
2620 machine.start();
2621 TEST_ACTIVE_CHANGED(s0, 3);
2622 QTRY_COMPARE(startedSpy.count(), 2);
2623 TEST_RUNNING_CHANGED(true);
2624 emitter.emitSignalWithIntArg(arg: 123);
2625 TEST_ACTIVE_CHANGED(s0, 4);
2626 QTRY_COMPARE(finishedSpy.count(), 2);
2627 TEST_RUNNING_CHANGED(false);
2628 QCOMPARE(machine.configuration().size(), 1);
2629 QVERIFY(machine.configuration().contains(s2));
2630
2631 machine.start();
2632 QCoreApplication::processEvents();
2633 TEST_ACTIVE_CHANGED(s0, 5);
2634 QTRY_COMPARE(startedSpy.count(), 3);
2635 TEST_RUNNING_CHANGED(true);
2636 emitter.emitSignalWithStringArg(arg: "hello");
2637 TEST_ACTIVE_CHANGED(s0, 6);
2638 QTRY_COMPARE(finishedSpy.count(), 3);
2639 TEST_RUNNING_CHANGED(false);
2640 QCOMPARE(machine.configuration().size(), 1);
2641 QVERIFY(machine.configuration().contains(s3));
2642 }
2643 // signature normalization
2644 {
2645 QStateMachine machine;
2646 SignalEmitter emitter;
2647 QState *s0 = new QState(&machine);
2648 DEFINE_ACTIVE_SPY(s0);
2649 QFinalState *s1 = new QFinalState(&machine);
2650 QSignalTransition *t0 = s0->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s1);
2651 QVERIFY(t0 != 0);
2652 QCOMPARE(t0->signal(), QByteArray(SIGNAL(signalWithNoArg())));
2653
2654 QSignalTransition *t1 = s0->addTransition(sender: &emitter, SIGNAL(signalWithStringArg(QString)), target: s1);
2655 QVERIFY(t1 != 0);
2656 QCOMPARE(t1->signal(), QByteArray(SIGNAL(signalWithStringArg(QString))));
2657
2658 QSignalSpy startedSpy(&machine, &QStateMachine::started);
2659 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2660 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2661 QVERIFY(startedSpy.isValid());
2662 QVERIFY(finishedSpy.isValid());
2663 QVERIFY(runningSpy.isValid());
2664 machine.setInitialState(s0);
2665 machine.start();
2666 QCoreApplication::processEvents();
2667 TEST_ACTIVE_CHANGED(s0, 1);
2668 QTRY_COMPARE(startedSpy.count(), 1);
2669 QCOMPARE(finishedSpy.count(), 0);
2670 TEST_RUNNING_CHANGED(true);
2671
2672 emitter.emitSignalWithNoArg();
2673
2674 TEST_ACTIVE_CHANGED(s0, 2);
2675 QTRY_COMPARE(finishedSpy.count(), 1);
2676 TEST_RUNNING_CHANGED(false);
2677 }
2678}
2679
2680class TestEventTransition : public QEventTransition
2681{
2682public:
2683 TestEventTransition(QState *sourceState = 0)
2684 : QEventTransition(sourceState),
2685 m_eventSource(0), m_eventType(QEvent::None)
2686 {}
2687 TestEventTransition(QObject *object, QEvent::Type type,
2688 QAbstractState *target)
2689 : QEventTransition(object, type),
2690 m_eventSource(0), m_eventType(QEvent::None)
2691 { setTargetState(target); }
2692 QObject *eventSourceReceived() const {
2693 return m_eventSource;
2694 }
2695 QEvent::Type eventTypeReceived() const {
2696 return m_eventType;
2697 }
2698protected:
2699 bool eventTest(QEvent *e) {
2700 if (!QEventTransition::eventTest(event: e))
2701 return false;
2702 QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(e);
2703 m_eventSource = we->object();
2704 m_eventType = we->event()->type();
2705 return true;
2706 }
2707private:
2708 QObject *m_eventSource;
2709 QEvent::Type m_eventType;
2710};
2711
2712#ifndef QT_NO_WIDGETS
2713void tst_QStateMachine::eventTransitions()
2714{
2715 QPushButton button;
2716 {
2717 QStateMachine machine;
2718 QState *s0 = new QState(&machine);
2719 QFinalState *s1 = new QFinalState(&machine);
2720
2721 QMouseEventTransition *trans;
2722 trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton);
2723 QCOMPARE(trans->targetState(), (QAbstractState*)0);
2724 trans->setTargetState(s1);
2725 QCOMPARE(trans->eventType(), QEvent::MouseButtonPress);
2726 QCOMPARE(trans->button(), Qt::LeftButton);
2727 QCOMPARE(trans->targetState(), (QAbstractState*)s1);
2728 s0->addTransition(transition: trans);
2729
2730 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2731 QVERIFY(runningSpy.isValid());
2732 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2733 QVERIFY(finishedSpy.isValid());
2734 machine.setInitialState(s0);
2735 machine.start();
2736 QCoreApplication::processEvents();
2737
2738 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2739 QTRY_COMPARE(finishedSpy.count(), 1);
2740 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2741
2742 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2743
2744 trans->setEventType(QEvent::MouseButtonRelease);
2745 QCOMPARE(trans->eventType(), QEvent::MouseButtonRelease);
2746 machine.start();
2747 QCoreApplication::processEvents();
2748 QTest::mouseRelease(widget: &button, button: Qt::LeftButton);
2749 QTRY_COMPARE(finishedSpy.count(), 2);
2750 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2751
2752 machine.start();
2753 QCoreApplication::processEvents();
2754 trans->setEventType(QEvent::MouseButtonPress);
2755 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2756 QTRY_COMPARE(finishedSpy.count(), 3);
2757 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2758
2759 QPushButton button2;
2760 machine.start();
2761 QCoreApplication::processEvents();
2762 trans->setEventSource(&button2);
2763 QTest::mousePress(widget: &button2, button: Qt::LeftButton);
2764 QTRY_COMPARE(finishedSpy.count(), 4);
2765 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2766 }
2767 for (int x = 0; x < 2; ++x) {
2768 QStateMachine machine;
2769 QState *s0 = new QState(&machine);
2770 QFinalState *s1 = new QFinalState(&machine);
2771
2772 QEventTransition *trans = 0;
2773 if (x == 0) {
2774 trans = new QEventTransition();
2775 QCOMPARE(trans->eventSource(), (QObject*)0);
2776 QCOMPARE(trans->eventType(), QEvent::None);
2777 trans->setEventSource(&button);
2778 trans->setEventType(QEvent::MouseButtonPress);
2779 trans->setTargetState(s1);
2780 } else if (x == 1) {
2781 trans = new QEventTransition(&button, QEvent::MouseButtonPress);
2782 trans->setTargetState(s1);
2783 }
2784 QCOMPARE(trans->eventSource(), (QObject*)&button);
2785 QCOMPARE(trans->eventType(), QEvent::MouseButtonPress);
2786 QCOMPARE(trans->targetState(), (QAbstractState*)s1);
2787 s0->addTransition(transition: trans);
2788
2789 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2790 QVERIFY(runningSpy.isValid());
2791 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2792 QVERIFY(finishedSpy.isValid());
2793 machine.setInitialState(s0);
2794 machine.start();
2795 QCoreApplication::processEvents();
2796
2797 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2798 QCoreApplication::processEvents();
2799
2800 QTRY_COMPARE(finishedSpy.count(), 1);
2801 TEST_RUNNING_CHANGED_STARTED_STOPPED;
2802 }
2803 {
2804 QStateMachine machine;
2805 QState *s0 = new QState(&machine);
2806 QFinalState *s1 = new QFinalState(&machine);
2807
2808 QMouseEventTransition *trans = new QMouseEventTransition();
2809 QCOMPARE(trans->eventSource(), (QObject*)0);
2810 QCOMPARE(trans->eventType(), QEvent::None);
2811 QCOMPARE(trans->button(), Qt::NoButton);
2812 trans->setEventSource(&button);
2813 trans->setEventType(QEvent::MouseButtonPress);
2814 trans->setButton(Qt::LeftButton);
2815 trans->setTargetState(s1);
2816 s0->addTransition(transition: trans);
2817
2818 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2819 QVERIFY(runningSpy.isValid());
2820 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2821 QVERIFY(finishedSpy.isValid());
2822 machine.setInitialState(s0);
2823 machine.start();
2824 QCoreApplication::processEvents();
2825 TEST_RUNNING_CHANGED(true);
2826 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2827 QCoreApplication::processEvents();
2828
2829 QTRY_COMPARE(finishedSpy.count(), 1);
2830 TEST_RUNNING_CHANGED(false);
2831 }
2832
2833 {
2834 QStateMachine machine;
2835 QState *s0 = new QState(&machine);
2836 QFinalState *s1 = new QFinalState(&machine);
2837
2838 QKeyEventTransition *trans = new QKeyEventTransition(&button, QEvent::KeyPress, Qt::Key_A);
2839 QCOMPARE(trans->eventType(), QEvent::KeyPress);
2840 QCOMPARE(trans->key(), (int)Qt::Key_A);
2841 trans->setTargetState(s1);
2842 s0->addTransition(transition: trans);
2843
2844 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2845 QVERIFY(runningSpy.isValid());
2846 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2847 QVERIFY(finishedSpy.isValid());
2848 machine.setInitialState(s0);
2849 machine.start();
2850 QCoreApplication::processEvents();
2851 TEST_RUNNING_CHANGED(true);
2852
2853 QTest::keyPress(widget: &button, key: Qt::Key_A);
2854 QCoreApplication::processEvents();
2855
2856 QTRY_COMPARE(finishedSpy.count(), 1);
2857 TEST_RUNNING_CHANGED(false);
2858 }
2859 {
2860 QStateMachine machine;
2861 QState *s0 = new QState(&machine);
2862 QFinalState *s1 = new QFinalState(&machine);
2863
2864 QKeyEventTransition *trans = new QKeyEventTransition();
2865 QCOMPARE(trans->eventSource(), (QObject*)0);
2866 QCOMPARE(trans->eventType(), QEvent::None);
2867 QCOMPARE(trans->key(), 0);
2868 trans->setEventSource(&button);
2869 trans->setEventType(QEvent::KeyPress);
2870 trans->setKey(Qt::Key_A);
2871 trans->setTargetState(s1);
2872 s0->addTransition(transition: trans);
2873
2874 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2875 QVERIFY(runningSpy.isValid());
2876 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2877 QVERIFY(finishedSpy.isValid());
2878 machine.setInitialState(s0);
2879 machine.start();
2880 QCoreApplication::processEvents();
2881 TEST_RUNNING_CHANGED(true);
2882
2883 QTest::keyPress(widget: &button, key: Qt::Key_A);
2884 QCoreApplication::processEvents();
2885
2886 QTRY_COMPARE(finishedSpy.count(), 1);
2887 TEST_RUNNING_CHANGED(false);
2888 }
2889 // Multiple transitions for same (object,event)
2890 {
2891 QStateMachine machine;
2892 QState *s0 = new QState(&machine);
2893 QState *s1 = new QState(&machine);
2894 QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress);
2895 t0->setTargetState(s1);
2896 s0->addTransition(transition: t0);
2897 QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonPress);
2898 t1->setTargetState(s0);
2899 s1->addTransition(transition: t1);
2900
2901 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2902 QVERIFY(finishedSpy.isValid());
2903 machine.setInitialState(s0);
2904 machine.start();
2905 QCoreApplication::processEvents();
2906 QCOMPARE(machine.configuration().size(), 1);
2907 QVERIFY(machine.configuration().contains(s0));
2908
2909 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2910 QCoreApplication::processEvents();
2911 QCOMPARE(machine.configuration().size(), 1);
2912 QVERIFY(machine.configuration().contains(s1));
2913
2914 s0->removeTransition(transition: t0);
2915 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2916 QCoreApplication::processEvents();
2917 QCOMPARE(machine.configuration().size(), 1);
2918 QVERIFY(machine.configuration().contains(s0));
2919
2920 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2921 QCoreApplication::processEvents();
2922 QCOMPARE(machine.configuration().size(), 1);
2923 QVERIFY(machine.configuration().contains(s0));
2924
2925 s1->removeTransition(transition: t1);
2926 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2927 QCoreApplication::processEvents();
2928 QCOMPARE(machine.configuration().size(), 1);
2929 QVERIFY(machine.configuration().contains(s0));
2930
2931 s0->addTransition(transition: t0);
2932 s1->addTransition(transition: t1);
2933 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2934 QCoreApplication::processEvents();
2935 QCOMPARE(machine.configuration().size(), 1);
2936 QVERIFY(machine.configuration().contains(s1));
2937 }
2938 // multiple event transitions from same source
2939 {
2940 QStateMachine machine;
2941 QState *s0 = new QState(&machine);
2942 QFinalState *s1 = new QFinalState(&machine);
2943 QFinalState *s2 = new QFinalState(&machine);
2944 QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress);
2945 t0->setTargetState(s1);
2946 s0->addTransition(transition: t0);
2947 QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonRelease);
2948 t1->setTargetState(s2);
2949 s0->addTransition(transition: t1);
2950
2951 QSignalSpy startedSpy(&machine, &QStateMachine::started);
2952 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
2953 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2954 QVERIFY(startedSpy.isValid());
2955 QVERIFY(finishedSpy.isValid());
2956 QVERIFY(runningSpy.isValid());
2957 machine.setInitialState(s0);
2958
2959 machine.start();
2960 QTRY_COMPARE(startedSpy.count(), 1);
2961 TEST_RUNNING_CHANGED(true);
2962 QTest::mousePress(widget: &button, button: Qt::LeftButton);
2963 QTRY_COMPARE(finishedSpy.count(), 1);
2964 TEST_RUNNING_CHANGED(false);
2965 QCOMPARE(machine.configuration().size(), 1);
2966 QVERIFY(machine.configuration().contains(s1));
2967
2968 machine.start();
2969 QTRY_COMPARE(startedSpy.count(), 2);
2970 TEST_RUNNING_CHANGED(true);
2971 QTest::mouseRelease(widget: &button, button: Qt::LeftButton);
2972 QTRY_COMPARE(finishedSpy.count(), 2);
2973 TEST_RUNNING_CHANGED(false);
2974 QCOMPARE(machine.configuration().size(), 1);
2975 QVERIFY(machine.configuration().contains(s2));
2976 }
2977 // custom event
2978 {
2979 QStateMachine machine;
2980 QState *s0 = new QState(&machine);
2981 QFinalState *s1 = new QFinalState(&machine);
2982
2983 QEventTransition *trans = new QEventTransition(&button, QEvent::Type(QEvent::User+1));
2984 trans->setTargetState(s1);
2985 s0->addTransition(transition: trans);
2986
2987 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
2988 QVERIFY(runningSpy.isValid());
2989 QSignalSpy startedSpy(&machine, &QStateMachine::started);
2990 QVERIFY(startedSpy.isValid());
2991 machine.setInitialState(s0);
2992 machine.start();
2993 QTest::ignoreMessage(type: QtWarningMsg, message: "QObject event transitions are not supported for custom types");
2994 QTRY_COMPARE(startedSpy.count(), 1);
2995 TEST_RUNNING_CHANGED(true);
2996 }
2997 // custom transition
2998 {
2999 QStateMachine machine;
3000 QState *s0 = new QState(&machine);
3001 QFinalState *s1 = new QFinalState(&machine);
3002
3003 TestEventTransition *trans = new TestEventTransition(&button, QEvent::MouseButtonPress, s1);
3004 s0->addTransition(transition: trans);
3005 QCOMPARE(trans->eventSourceReceived(), (QObject*)0);
3006 QCOMPARE(trans->eventTypeReceived(), QEvent::None);
3007
3008 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3009 QVERIFY(runningSpy.isValid());
3010 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
3011 QVERIFY(finishedSpy.isValid());
3012 machine.setInitialState(s0);
3013 machine.start();
3014 QCoreApplication::processEvents();
3015 TEST_RUNNING_CHANGED(true);
3016
3017 QTest::mousePress(widget: &button, button: Qt::LeftButton);
3018 QCoreApplication::processEvents();
3019
3020 QTRY_COMPARE(finishedSpy.count(), 1);
3021 TEST_RUNNING_CHANGED(false);
3022
3023 QCOMPARE(trans->eventSourceReceived(), (QObject*)&button);
3024 QCOMPARE(trans->eventTypeReceived(), QEvent::MouseButtonPress);
3025 }
3026}
3027
3028void tst_QStateMachine::graphicsSceneEventTransitions()
3029{
3030 QGraphicsScene scene;
3031 QGraphicsTextItem *textItem = scene.addText(text: "foo");
3032
3033 QStateMachine machine;
3034 QState *s1 = new QState(&machine);
3035 QFinalState *s2 = new QFinalState(&machine);
3036 QEventTransition *t = new QEventTransition(textItem, QEvent::GraphicsSceneMouseMove);
3037 t->setTargetState(s2);
3038 s1->addTransition(transition: t);
3039 machine.setInitialState(s1);
3040
3041 QSignalSpy startedSpy(&machine, &QStateMachine::started);
3042 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
3043 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3044 QVERIFY(startedSpy.isValid());
3045 QVERIFY(finishedSpy.isValid());
3046 QVERIFY(runningSpy.isValid());
3047 machine.start();
3048 QTRY_COMPARE(startedSpy.count(), 1);
3049 QCOMPARE(finishedSpy.count(), 0);
3050 TEST_RUNNING_CHANGED(true);
3051 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
3052 scene.sendEvent(item: textItem, event: &mouseEvent);
3053 QTRY_COMPARE(finishedSpy.count(), 1);
3054 TEST_RUNNING_CHANGED(false);
3055}
3056#endif
3057
3058void tst_QStateMachine::historyStates()
3059{
3060 for (int x = 0; x < 2; ++x) {
3061 QStateMachine machine;
3062 QState *root = &machine;
3063 QState *s0 = new QState(root);
3064 DEFINE_ACTIVE_SPY(s0);
3065 QState *s00 = new QState(s0);
3066 DEFINE_ACTIVE_SPY(s00);
3067 QState *s01 = new QState(s0);
3068 DEFINE_ACTIVE_SPY(s01);
3069 QHistoryState *s0h;
3070 if (x == 0) {
3071 s0h = new QHistoryState(s0);
3072 QCOMPARE(s0h->historyType(), QHistoryState::ShallowHistory);
3073 s0h->setHistoryType(QHistoryState::DeepHistory);
3074 } else {
3075 s0h = new QHistoryState(QHistoryState::DeepHistory, s0);
3076 }
3077 QCOMPARE(s0h->historyType(), QHistoryState::DeepHistory);
3078 s0h->setHistoryType(QHistoryState::ShallowHistory);
3079 QCOMPARE(s0h->historyType(), QHistoryState::ShallowHistory);
3080 QCOMPARE(s0h->defaultState(), (QAbstractState*)0);
3081 s0h->setDefaultState(s00);
3082 QCOMPARE(s0h->defaultState(), (QAbstractState*)s00);
3083 const QString warning
3084 = QString::asprintf(format: "QHistoryState::setDefaultState: state %p does not belong to this history state's group (%p)", s0, s0);
3085 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
3086 s0h->setDefaultState(s0);
3087 QState *s1 = new QState(root);
3088 DEFINE_ACTIVE_SPY(s1);
3089 QFinalState *s2 = new QFinalState(root);
3090
3091 s00->addTransition(transition: new StringTransition("a", s01));
3092 s0->addTransition(transition: new StringTransition("b", s1));
3093 s1->addTransition(transition: new StringTransition("c", s0h));
3094 s0->addTransition(transition: new StringTransition("d", s2));
3095
3096 root->setInitialState(s0);
3097 s0->setInitialState(s00);
3098
3099 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3100 QVERIFY(runningSpy.isValid());
3101 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
3102 QVERIFY(finishedSpy.isValid());
3103 machine.start();
3104 QCoreApplication::processEvents();
3105 TEST_ACTIVE_CHANGED(s0, 1);
3106 TEST_ACTIVE_CHANGED(s00, 1);
3107 TEST_ACTIVE_CHANGED(s01, 0);
3108 TEST_ACTIVE_CHANGED(s1, 0);
3109 QCOMPARE(machine.configuration().size(), 2);
3110 QVERIFY(machine.configuration().contains(s0));
3111 QVERIFY(machine.configuration().contains(s00));
3112
3113 machine.postEvent(event: new StringEvent("a"));
3114 QCoreApplication::processEvents();
3115 TEST_ACTIVE_CHANGED(s0, 1);
3116 TEST_ACTIVE_CHANGED(s00, 2);
3117 TEST_ACTIVE_CHANGED(s01, 1);
3118 TEST_ACTIVE_CHANGED(s1, 0);
3119 QCOMPARE(machine.configuration().size(), 2);
3120 QVERIFY(machine.configuration().contains(s0));
3121 QVERIFY(machine.configuration().contains(s01));
3122
3123 machine.postEvent(event: new StringEvent("b"));
3124 QCoreApplication::processEvents();
3125 TEST_ACTIVE_CHANGED(s0, 2);
3126 TEST_ACTIVE_CHANGED(s00, 2);
3127 TEST_ACTIVE_CHANGED(s01, 2);
3128 TEST_ACTIVE_CHANGED(s1, 1);
3129 QCOMPARE(machine.configuration().size(), 1);
3130 QVERIFY(machine.configuration().contains(s1));
3131
3132 machine.postEvent(event: new StringEvent("c"));
3133 QCoreApplication::processEvents();
3134 TEST_ACTIVE_CHANGED(s0, 3);
3135 TEST_ACTIVE_CHANGED(s00, 2);
3136 TEST_ACTIVE_CHANGED(s01, 3);
3137 TEST_ACTIVE_CHANGED(s1, 2);
3138 QCOMPARE(machine.configuration().size(), 2);
3139 QVERIFY(machine.configuration().contains(s0));
3140 QVERIFY(machine.configuration().contains(s01));
3141
3142 machine.postEvent(event: new StringEvent("d"));
3143 QCoreApplication::processEvents();
3144 TEST_ACTIVE_CHANGED(s0, 4);
3145 TEST_ACTIVE_CHANGED(s00, 2);
3146 TEST_ACTIVE_CHANGED(s01, 4);
3147 TEST_ACTIVE_CHANGED(s1, 2);
3148 QCOMPARE(machine.configuration().size(), 1);
3149 QVERIFY(machine.configuration().contains(s2));
3150
3151 QTRY_COMPARE(finishedSpy.count(), 1);
3152 TEST_RUNNING_CHANGED_STARTED_STOPPED;
3153 }
3154}
3155
3156void tst_QStateMachine::startAndStop()
3157{
3158 QStateMachine machine;
3159 QSignalSpy startedSpy(&machine, &QStateMachine::started);
3160 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
3161 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
3162 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3163
3164 QVERIFY(startedSpy.isValid());
3165 QVERIFY(stoppedSpy.isValid());
3166 QVERIFY(finishedSpy.isValid());
3167 QVERIFY(runningSpy.isValid());
3168
3169 QVERIFY(!machine.isRunning());
3170 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::start: No initial state set for machine. Refusing to start.");
3171 machine.start();
3172 QCOMPARE(startedSpy.count(), 0);
3173 QCOMPARE(stoppedSpy.count(), 0);
3174 QCOMPARE(finishedSpy.count(), 0);
3175 QCOMPARE(runningSpy.count(), 0);
3176 QVERIFY(!machine.isRunning());
3177 machine.stop();
3178 QCOMPARE(startedSpy.count(), 0);
3179 QCOMPARE(stoppedSpy.count(), 0);
3180 QCOMPARE(finishedSpy.count(), 0);
3181 QCOMPARE(runningSpy.count(), 0);
3182
3183 QState *s1 = new QState(&machine);
3184 DEFINE_ACTIVE_SPY(s1);
3185 machine.setInitialState(s1);
3186 machine.start();
3187 TEST_ACTIVE_CHANGED(s1, 1);
3188 QTRY_COMPARE(machine.isRunning(), true);
3189 QTRY_COMPARE(startedSpy.count(), 1);
3190 QCOMPARE(stoppedSpy.count(), 0);
3191 QCOMPARE(finishedSpy.count(), 0);
3192 TEST_RUNNING_CHANGED(true);
3193 QCOMPARE(machine.configuration().count(), 1);
3194 QVERIFY(machine.configuration().contains(s1));
3195
3196 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::start(): already running");
3197 machine.start();
3198 QCOMPARE(runningSpy.count(), 0);
3199
3200 machine.stop();
3201 TEST_ACTIVE_CHANGED(s1, 1);
3202 QTRY_COMPARE(machine.isRunning(), false);
3203 QTRY_COMPARE(stoppedSpy.count(), 1);
3204 QCOMPARE(startedSpy.count(), 1);
3205 QCOMPARE(finishedSpy.count(), 0);
3206 TEST_RUNNING_CHANGED(false);
3207
3208 QCOMPARE(machine.configuration().count(), 1);
3209 QVERIFY(machine.configuration().contains(s1));
3210
3211 machine.start();
3212 TEST_ACTIVE_CHANGED(s1, 3);
3213 machine.stop();
3214 TEST_ACTIVE_CHANGED(s1, 3);
3215 QTRY_COMPARE(startedSpy.count(), 2);
3216 QTRY_COMPARE(stoppedSpy.count(), 2);
3217 TEST_RUNNING_CHANGED_STARTED_STOPPED;
3218}
3219
3220void tst_QStateMachine::setRunning()
3221{
3222 QStateMachine machine;
3223 QSignalSpy startedSpy(&machine, &QStateMachine::started);
3224 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
3225 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
3226 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3227
3228 QVERIFY(startedSpy.isValid());
3229 QVERIFY(stoppedSpy.isValid());
3230 QVERIFY(finishedSpy.isValid());
3231 QVERIFY(runningSpy.isValid());
3232
3233 QVERIFY(!machine.isRunning());
3234 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::start: No initial state set for machine. Refusing to start.");
3235 machine.setRunning(true);
3236 QCOMPARE(startedSpy.count(), 0);
3237 QCOMPARE(stoppedSpy.count(), 0);
3238 QCOMPARE(finishedSpy.count(), 0);
3239 QCOMPARE(runningSpy.count(), 0);
3240 QVERIFY(!machine.isRunning());
3241 machine.setRunning(false);
3242 QCOMPARE(startedSpy.count(), 0);
3243 QCOMPARE(stoppedSpy.count(), 0);
3244 QCOMPARE(finishedSpy.count(), 0);
3245 QCOMPARE(runningSpy.count(), 0);
3246
3247 QState *s1 = new QState(&machine);
3248 DEFINE_ACTIVE_SPY(s1);
3249 machine.setInitialState(s1);
3250 machine.setRunning(true);
3251 TEST_ACTIVE_CHANGED(s1, 1);
3252 QTRY_COMPARE(machine.isRunning(), true);
3253 QTRY_COMPARE(startedSpy.count(), 1);
3254 QCOMPARE(stoppedSpy.count(), 0);
3255 QCOMPARE(finishedSpy.count(), 0);
3256 TEST_RUNNING_CHANGED(true);
3257 QCOMPARE(machine.configuration().count(), 1);
3258 QVERIFY(machine.configuration().contains(s1));
3259
3260 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::start(): already running");
3261 machine.setRunning(true);
3262 TEST_ACTIVE_CHANGED(s1, 1);
3263 QCOMPARE(runningSpy.count(), 0);
3264
3265 machine.setRunning(false);
3266 TEST_ACTIVE_CHANGED(s1, 1);
3267 QTRY_COMPARE(machine.isRunning(), false);
3268 QTRY_COMPARE(stoppedSpy.count(), 1);
3269 QCOMPARE(startedSpy.count(), 1);
3270 QCOMPARE(finishedSpy.count(), 0);
3271 TEST_RUNNING_CHANGED(false);
3272 QCOMPARE(machine.configuration().count(), 1);
3273 QVERIFY(machine.configuration().contains(s1));
3274
3275 machine.setRunning(false);
3276 QCOMPARE(runningSpy.count(), 0);
3277 TEST_ACTIVE_CHANGED(s1, 1);
3278
3279 machine.start();
3280 TEST_ACTIVE_CHANGED(s1, 3);
3281 machine.setRunning(false);
3282 TEST_ACTIVE_CHANGED(s1, 3);
3283 QTRY_COMPARE(startedSpy.count(), 2);
3284 QTRY_COMPARE(stoppedSpy.count(), 2);
3285 TEST_RUNNING_CHANGED_STARTED_STOPPED;
3286 QState *s1_1 = new QState(s1);
3287 QFinalState *s1_2 = new QFinalState(s1);
3288 s1_1->addTransition(target: s1_2);
3289 s1->setInitialState(s1_1);
3290 QFinalState *s2 = new QFinalState(&machine);
3291 s1->addTransition(sender: s1, SIGNAL(finished()), target: s2);
3292 machine.setRunning(false);
3293 QCOMPARE(runningSpy.count(), 0);
3294 machine.setRunning(true);
3295 TEST_ACTIVE_CHANGED(s1, 6);
3296 TEST_RUNNING_CHANGED_STARTED_STOPPED;
3297 QTRY_COMPARE(startedSpy.count(), 3);
3298 QCOMPARE(stoppedSpy.count(), 2);
3299 QCOMPARE(finishedSpy.count(), 1);
3300}
3301
3302void tst_QStateMachine::targetStateWithNoParent()
3303{
3304 QStateMachine machine;
3305 QState *s1 = new QState(&machine);
3306 DEFINE_ACTIVE_SPY(s1);
3307 s1->setObjectName("s1");
3308 QState s2;
3309 s1->addTransition(target: &s2);
3310 machine.setInitialState(s1);
3311 QSignalSpy startedSpy(&machine, &QStateMachine::started);
3312 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
3313 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
3314 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3315
3316 QVERIFY(startedSpy.isValid());
3317 QVERIFY(stoppedSpy.isValid());
3318 QVERIFY(finishedSpy.isValid());
3319 QVERIFY(runningSpy.isValid());
3320
3321 machine.start();
3322 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: "
3323 "Child mode of state machine '' is not 'ExclusiveStates'.");
3324 TEST_ACTIVE_CHANGED(s1, 2);
3325 QTRY_COMPARE(startedSpy.count(), 1);
3326 QCOMPARE(machine.isRunning(), false);
3327 QCOMPARE(stoppedSpy.count(), 1);
3328 QCOMPARE(finishedSpy.count(), 0);
3329 TEST_RUNNING_CHANGED_STARTED_STOPPED;
3330 QCOMPARE(machine.error(), QStateMachine::StateMachineChildModeSetToParallelError);
3331}
3332
3333void tst_QStateMachine::targetStateDeleted()
3334{
3335 QStateMachine machine;
3336 QState *s1 = new QState(&machine);
3337 s1->setObjectName("s1");
3338 QState *s2 = new QState(&machine);
3339 QAbstractTransition *trans = s1->addTransition(target: s2);
3340 delete s2;
3341 QCOMPARE(trans->targetState(), (QAbstractState*)0);
3342 QVERIFY(trans->targetStates().isEmpty());
3343}
3344
3345void tst_QStateMachine::defaultGlobalRestorePolicy()
3346{
3347 QStateMachine machine;
3348
3349 QObject *propertyHolder = new QObject(&machine);
3350 propertyHolder->setProperty(name: "a", value: 1);
3351 propertyHolder->setProperty(name: "b", value: 2);
3352
3353 QState *s1 = new QState(&machine);
3354 DEFINE_ACTIVE_SPY(s1);
3355 s1->assignProperty(object: propertyHolder, name: "a", value: 3);
3356
3357 QState *s2 = new QState(&machine);
3358 DEFINE_ACTIVE_SPY(s2);
3359 s2->assignProperty(object: propertyHolder, name: "b", value: 4);
3360
3361 QState *s3 = new QState(&machine);
3362 DEFINE_ACTIVE_SPY(s3);
3363
3364 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
3365 s2->addTransition(transition: new EventTransition(QEvent::User, s3));
3366
3367 machine.setInitialState(s1);
3368 machine.start();
3369 QCoreApplication::processEvents();
3370
3371 TEST_ACTIVE_CHANGED(s1, 1);
3372 TEST_ACTIVE_CHANGED(s2, 0);
3373 TEST_ACTIVE_CHANGED(s3, 0);
3374 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3375 QCOMPARE(propertyHolder->property("b").toInt(), 2);
3376
3377 machine.postEvent(event: new QEvent(QEvent::User));
3378 QCoreApplication::processEvents();
3379
3380 TEST_ACTIVE_CHANGED(s1, 2);
3381 TEST_ACTIVE_CHANGED(s2, 1);
3382 TEST_ACTIVE_CHANGED(s3, 0);
3383 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3384 QCOMPARE(propertyHolder->property("b").toInt(), 4);
3385
3386 machine.postEvent(event: new QEvent(QEvent::User));
3387 QCoreApplication::processEvents();
3388
3389 TEST_ACTIVE_CHANGED(s1, 2);
3390 TEST_ACTIVE_CHANGED(s2, 2);
3391 TEST_ACTIVE_CHANGED(s3, 1);
3392 QVERIFY(machine.isRunning());
3393 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3394 QCOMPARE(propertyHolder->property("b").toInt(), 4);
3395}
3396
3397void tst_QStateMachine::noInitialStateForInitialState()
3398{
3399 QStateMachine machine;
3400
3401 QState *initialState = new QState(&machine);
3402 DEFINE_ACTIVE_SPY(initialState);
3403 initialState->setObjectName("initialState");
3404 machine.setInitialState(initialState);
3405
3406 QState *childState = new QState(initialState);
3407 DEFINE_ACTIVE_SPY(childState);
3408 (void)childState;
3409
3410 QTest::ignoreMessage(type: QtWarningMsg, message: "Unrecoverable error detected in running state machine: "
3411 "Missing initial state in compound state 'initialState'");
3412 machine.start();
3413 QCoreApplication::processEvents();
3414 TEST_ACTIVE_CHANGED(initialState, 1);
3415 TEST_ACTIVE_CHANGED(childState, 0);
3416 QCOMPARE(machine.isRunning(), false);
3417 QCOMPARE(int(machine.error()), int(QStateMachine::NoInitialStateError));
3418}
3419
3420void tst_QStateMachine::globalRestorePolicySetToDontRestore()
3421{
3422 QStateMachine machine;
3423 machine.setGlobalRestorePolicy(QState::DontRestoreProperties);
3424
3425 QObject *propertyHolder = new QObject(&machine);
3426 propertyHolder->setProperty(name: "a", value: 1);
3427 propertyHolder->setProperty(name: "b", value: 2);
3428
3429 QState *s1 = new QState(&machine);
3430 DEFINE_ACTIVE_SPY(s1);
3431 s1->assignProperty(object: propertyHolder, name: "a", value: 3);
3432
3433 QState *s2 = new QState(&machine);
3434 DEFINE_ACTIVE_SPY(s2);
3435 s2->assignProperty(object: propertyHolder, name: "b", value: 4);
3436
3437 QState *s3 = new QState(&machine);
3438 DEFINE_ACTIVE_SPY(s3);
3439
3440 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
3441 s2->addTransition(transition: new EventTransition(QEvent::User, s3));
3442
3443 machine.setInitialState(s1);
3444 machine.start();
3445 QCoreApplication::processEvents();
3446
3447 TEST_ACTIVE_CHANGED(s1, 1);
3448 TEST_ACTIVE_CHANGED(s2, 0);
3449 TEST_ACTIVE_CHANGED(s3, 0);
3450 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3451 QCOMPARE(propertyHolder->property("b").toInt(), 2);
3452
3453 machine.postEvent(event: new QEvent(QEvent::User));
3454 QCoreApplication::processEvents();
3455
3456 TEST_ACTIVE_CHANGED(s1, 2);
3457 TEST_ACTIVE_CHANGED(s2, 1);
3458 TEST_ACTIVE_CHANGED(s3, 0);
3459 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3460 QCOMPARE(propertyHolder->property("b").toInt(), 4);
3461
3462 machine.postEvent(event: new QEvent(QEvent::User));
3463 QCoreApplication::processEvents();
3464
3465 TEST_ACTIVE_CHANGED(s1, 2);
3466 TEST_ACTIVE_CHANGED(s2, 2);
3467 TEST_ACTIVE_CHANGED(s3, 1);
3468 QVERIFY(machine.isRunning());
3469 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3470 QCOMPARE(propertyHolder->property("b").toInt(), 4);
3471}
3472
3473void tst_QStateMachine::globalRestorePolicySetToRestore()
3474{
3475 QStateMachine machine;
3476 machine.setGlobalRestorePolicy(QState::RestoreProperties);
3477
3478 QObject *propertyHolder = new QObject(&machine);
3479 propertyHolder->setProperty(name: "a", value: 1);
3480 propertyHolder->setProperty(name: "b", value: 2);
3481
3482 QState *s1 = new QState(&machine);
3483 DEFINE_ACTIVE_SPY(s1);
3484 s1->assignProperty(object: propertyHolder, name: "a", value: 3);
3485
3486 QState *s2 = new QState(&machine);
3487 DEFINE_ACTIVE_SPY(s2);
3488 s2->assignProperty(object: propertyHolder, name: "b", value: 4);
3489
3490 QState *s3 = new QState(&machine);
3491 DEFINE_ACTIVE_SPY(s3);
3492
3493 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
3494 s2->addTransition(transition: new EventTransition(QEvent::User, s3));
3495
3496 machine.setInitialState(s1);
3497 machine.start();
3498 QCoreApplication::processEvents();
3499
3500 TEST_ACTIVE_CHANGED(s1, 1);
3501 TEST_ACTIVE_CHANGED(s2, 0);
3502 TEST_ACTIVE_CHANGED(s3, 0);
3503 QCOMPARE(propertyHolder->property("a").toInt(), 3);
3504 QCOMPARE(propertyHolder->property("b").toInt(), 2);
3505
3506 machine.postEvent(event: new QEvent(QEvent::User));
3507 QCoreApplication::processEvents();
3508
3509 TEST_ACTIVE_CHANGED(s1, 2);
3510 TEST_ACTIVE_CHANGED(s2, 1);
3511 TEST_ACTIVE_CHANGED(s3, 0);
3512 QCOMPARE(propertyHolder->property("a").toInt(), 1);
3513 QCOMPARE(propertyHolder->property("b").toInt(), 4);
3514
3515 machine.postEvent(event: new QEvent(QEvent::User));
3516 QCoreApplication::processEvents();
3517
3518 TEST_ACTIVE_CHANGED(s1, 2);
3519 TEST_ACTIVE_CHANGED(s2, 2);
3520 TEST_ACTIVE_CHANGED(s3, 1);
3521 QVERIFY(machine.isRunning());
3522 QCOMPARE(propertyHolder->property("a").toInt(), 1);
3523 QCOMPARE(propertyHolder->property("b").toInt(), 2);
3524}
3525
3526void tst_QStateMachine::transitionWithParent()
3527{
3528 QStateMachine machine;
3529 QState *s1 = new QState(&machine);
3530 QState *s2 = new QState(&machine);
3531 EventTransition *trans = new EventTransition(QEvent::User, s2, s1);
3532 QCOMPARE(trans->sourceState(), s1);
3533 QCOMPARE(trans->targetState(), (QAbstractState*)s2);
3534 QCOMPARE(trans->targetStates().size(), 1);
3535 QCOMPARE(trans->targetStates().at(0), (QAbstractState*)s2);
3536}
3537
3538void tst_QStateMachine::simpleAnimation()
3539{
3540 QStateMachine machine;
3541
3542 QObject *object = new QObject(&machine);
3543 object->setProperty(name: "fooBar", value: 1.0);
3544
3545 QState *s1 = new QState(&machine);
3546 DEFINE_ACTIVE_SPY(s1);
3547 QState *s2 = new QState(&machine);
3548 DEFINE_ACTIVE_SPY(s2);
3549 s2->assignProperty(object, name: "fooBar", value: 2.0);
3550
3551 EventTransition *et = new EventTransition(QEvent::User, s2);
3552 QPropertyAnimation *animation = new QPropertyAnimation(object, "fooBar", s2);
3553 et->addAnimation(animation);
3554 s1->addTransition(transition: et);
3555
3556 QState *s3 = new QState(&machine);
3557 DEFINE_ACTIVE_SPY(s3);
3558 s2->addTransition(sender: animation, SIGNAL(finished()), target: s3);
3559 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3560
3561 machine.setInitialState(s1);
3562 machine.start();
3563 QCoreApplication::processEvents();
3564 TEST_ACTIVE_CHANGED(s1, 1);
3565 TEST_ACTIVE_CHANGED(s2, 0);
3566 TEST_ACTIVE_CHANGED(s3, 0);
3567
3568 machine.postEvent(event: new QEvent(QEvent::User));
3569 QCOREAPPLICATION_EXEC(5000);
3570
3571 TEST_ACTIVE_CHANGED(s1, 2);
3572 TEST_ACTIVE_CHANGED(s2, 2);
3573 TEST_ACTIVE_CHANGED(s3, 1);
3574 QVERIFY(machine.isRunning());
3575 QVERIFY(machine.configuration().contains(s3));
3576 QCOMPARE(object->property("fooBar").toDouble(), 2.0);
3577}
3578
3579class SlotCalledCounter: public QObject
3580{
3581 Q_OBJECT
3582public:
3583 SlotCalledCounter() : counter(0) {}
3584
3585 int counter;
3586
3587public slots:
3588 void slot() { counter++; }
3589};
3590
3591void tst_QStateMachine::twoAnimations()
3592{
3593 QStateMachine machine;
3594
3595 QObject *object = new QObject(&machine);
3596 object->setProperty(name: "foo", value: 1.0);
3597 object->setProperty(name: "bar", value: 3.0);
3598
3599 QState *s1 = new QState(&machine);
3600 DEFINE_ACTIVE_SPY(s1);
3601 QState *s2 = new QState(&machine);
3602 DEFINE_ACTIVE_SPY(s2);
3603 s2->assignProperty(object, name: "foo", value: 2.0);
3604 s2->assignProperty(object, name: "bar", value: 10.0);
3605
3606 QPropertyAnimation *animationFoo = new QPropertyAnimation(object, "foo", s2);
3607 QPropertyAnimation *animationBar = new QPropertyAnimation(object, "bar", s2);
3608 animationBar->setDuration(900);
3609
3610 SlotCalledCounter counter;
3611 connect(sender: animationFoo, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3612 connect(sender: animationBar, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3613
3614 EventTransition *et = new EventTransition(QEvent::User, s2);
3615 et->addAnimation(animation: animationFoo);
3616 et->addAnimation(animation: animationBar);
3617 s1->addTransition(transition: et);
3618
3619 QState *s3 = new QState(&machine);
3620 DEFINE_ACTIVE_SPY(s3);
3621 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3622 s2->addTransition(sender: s2, SIGNAL(propertiesAssigned()), target: s3);
3623
3624 machine.setInitialState(s1);
3625 machine.start();
3626 QCoreApplication::processEvents();
3627 TEST_ACTIVE_CHANGED(s1, 1);
3628 TEST_ACTIVE_CHANGED(s2, 0);
3629 TEST_ACTIVE_CHANGED(s3, 0);
3630
3631 machine.postEvent(event: new QEvent(QEvent::User));
3632 QCOREAPPLICATION_EXEC(5000);
3633 TEST_ACTIVE_CHANGED(s1, 2);
3634 TEST_ACTIVE_CHANGED(s2, 2);
3635 TEST_ACTIVE_CHANGED(s3, 1);
3636 QVERIFY(machine.isRunning());
3637
3638 QVERIFY(machine.configuration().contains(s3));
3639 QCOMPARE(object->property("foo").toDouble(), 2.0);
3640 QCOMPARE(object->property("bar").toDouble(), 10.0);
3641
3642 QCOMPARE(counter.counter, 2);
3643}
3644
3645void tst_QStateMachine::twoAnimatedTransitions()
3646{
3647 QStateMachine machine;
3648
3649 QObject *object = new QObject(&machine);
3650 object->setProperty(name: "foo", value: 1.0);
3651
3652 QState *s1 = new QState(&machine);
3653 DEFINE_ACTIVE_SPY(s1);
3654
3655 QState *s2 = new QState(&machine);
3656 DEFINE_ACTIVE_SPY(s2);
3657 s2->assignProperty(object, name: "foo", value: 5.0);
3658 QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2);
3659 EventTransition *trans = new EventTransition(QEvent::User, s2);
3660 s1->addTransition(transition: trans);
3661 trans->addAnimation(animation: fooAnimation);
3662
3663 QState *s3 = new QState(&machine);
3664 DEFINE_ACTIVE_SPY(s3);
3665 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3666 s2->addTransition(sender: fooAnimation, SIGNAL(finished()), target: s3);
3667
3668 QState *s4 = new QState(&machine);
3669 DEFINE_ACTIVE_SPY(s4);
3670 s4->assignProperty(object, name: "foo", value: 2.0);
3671 QPropertyAnimation *fooAnimation2 = new QPropertyAnimation(object, "foo", s4);
3672 trans = new EventTransition(QEvent::User, s4);
3673 s3->addTransition(transition: trans);
3674 trans->addAnimation(animation: fooAnimation2);
3675
3676 QState *s5 = new QState(&machine);
3677 DEFINE_ACTIVE_SPY(s5);
3678 QObject::connect(sender: s5, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3679 s4->addTransition(sender: fooAnimation2, SIGNAL(finished()), target: s5);
3680
3681 machine.setInitialState(s1);
3682 machine.start();
3683 QCoreApplication::processEvents();
3684 TEST_ACTIVE_CHANGED(s1, 1);
3685 TEST_ACTIVE_CHANGED(s2, 0);
3686 TEST_ACTIVE_CHANGED(s3, 0);
3687 TEST_ACTIVE_CHANGED(s4, 0);
3688 TEST_ACTIVE_CHANGED(s5, 0);
3689
3690 machine.postEvent(event: new QEvent(QEvent::User));
3691 QCOREAPPLICATION_EXEC(5000);
3692
3693 TEST_ACTIVE_CHANGED(s1, 2);
3694 TEST_ACTIVE_CHANGED(s2, 2);
3695 TEST_ACTIVE_CHANGED(s3, 1);
3696 TEST_ACTIVE_CHANGED(s4, 0);
3697 TEST_ACTIVE_CHANGED(s5, 0);
3698 QVERIFY(machine.configuration().contains(s3));
3699 QCOMPARE(object->property("foo").toDouble(), 5.0);
3700
3701 machine.postEvent(event: new QEvent(QEvent::User));
3702 QCOREAPPLICATION_EXEC(5000);
3703
3704 TEST_ACTIVE_CHANGED(s1, 2);
3705 TEST_ACTIVE_CHANGED(s2, 2);
3706 TEST_ACTIVE_CHANGED(s3, 2);
3707 TEST_ACTIVE_CHANGED(s4, 2);
3708 TEST_ACTIVE_CHANGED(s5, 1);
3709 QVERIFY(machine.isRunning());
3710 QVERIFY(machine.configuration().contains(s5));
3711 QCOMPARE(object->property("foo").toDouble(), 2.0);
3712}
3713
3714void tst_QStateMachine::playAnimationTwice()
3715{
3716 QStateMachine machine;
3717
3718 QObject *object = new QObject(&machine);
3719 object->setProperty(name: "foo", value: 1.0);
3720
3721 QState *s1 = new QState(&machine);
3722 DEFINE_ACTIVE_SPY(s1);
3723
3724 QState *s2 = new QState(&machine);
3725 DEFINE_ACTIVE_SPY(s2);
3726 s2->assignProperty(object, name: "foo", value: 5.0);
3727 QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2);
3728 EventTransition *trans = new EventTransition(QEvent::User, s2);
3729 s1->addTransition(transition: trans);
3730 trans->addAnimation(animation: fooAnimation);
3731
3732 QState *s3 = new QState(&machine);
3733 DEFINE_ACTIVE_SPY(s3);
3734 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3735 s2->addTransition(sender: fooAnimation, SIGNAL(finished()), target: s3);
3736
3737 QState *s4 = new QState(&machine);
3738 DEFINE_ACTIVE_SPY(s4);
3739 s4->assignProperty(object, name: "foo", value: 2.0);
3740 trans = new EventTransition(QEvent::User, s4);
3741 s3->addTransition(transition: trans);
3742 trans->addAnimation(animation: fooAnimation);
3743
3744 QState *s5 = new QState(&machine);
3745 DEFINE_ACTIVE_SPY(s5);
3746 QObject::connect(sender: s5, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3747 s4->addTransition(sender: fooAnimation, SIGNAL(finished()), target: s5);
3748
3749 machine.setInitialState(s1);
3750 machine.start();
3751 QCoreApplication::processEvents();
3752
3753 TEST_ACTIVE_CHANGED(s1, 1);
3754 TEST_ACTIVE_CHANGED(s2, 0);
3755 TEST_ACTIVE_CHANGED(s3, 0);
3756 TEST_ACTIVE_CHANGED(s4, 0);
3757 TEST_ACTIVE_CHANGED(s5, 0);
3758 machine.postEvent(event: new QEvent(QEvent::User));
3759 QCOREAPPLICATION_EXEC(5000);
3760
3761 TEST_ACTIVE_CHANGED(s1, 2);
3762 TEST_ACTIVE_CHANGED(s2, 2);
3763 TEST_ACTIVE_CHANGED(s3, 1);
3764 TEST_ACTIVE_CHANGED(s4, 0);
3765 TEST_ACTIVE_CHANGED(s5, 0);
3766 QVERIFY(machine.configuration().contains(s3));
3767 QCOMPARE(object->property("foo").toDouble(), 5.0);
3768
3769 machine.postEvent(event: new QEvent(QEvent::User));
3770 QCOREAPPLICATION_EXEC(5000);
3771
3772 TEST_ACTIVE_CHANGED(s1, 2);
3773 TEST_ACTIVE_CHANGED(s2, 2);
3774 TEST_ACTIVE_CHANGED(s3, 2);
3775 TEST_ACTIVE_CHANGED(s4, 2);
3776 TEST_ACTIVE_CHANGED(s5, 1);
3777 QVERIFY(machine.isRunning());
3778 QVERIFY(machine.configuration().contains(s5));
3779 QCOMPARE(object->property("foo").toDouble(), 2.0);
3780}
3781
3782void tst_QStateMachine::nestedTargetStateForAnimation()
3783{
3784 QStateMachine machine;
3785
3786 QObject *object = new QObject(&machine);
3787 object->setProperty(name: "foo", value: 1.0);
3788 object->setProperty(name: "bar", value: 3.0);
3789
3790 SlotCalledCounter counter;
3791
3792 QState *s1 = new QState(&machine);
3793 DEFINE_ACTIVE_SPY(s1);
3794 QState *s2 = new QState(&machine);
3795 DEFINE_ACTIVE_SPY(s2);
3796
3797 s2->assignProperty(object, name: "foo", value: 2.0);
3798
3799 QState *s2Child = new QState(s2);
3800 DEFINE_ACTIVE_SPY(s2Child);
3801 s2Child->assignProperty(object, name: "bar", value: 10.0);
3802 s2->setInitialState(s2Child);
3803
3804 QState *s2Child2 = new QState(s2);
3805 DEFINE_ACTIVE_SPY(s2Child2);
3806 s2Child2->assignProperty(object, name: "bar", value: 11.0);
3807 QAbstractTransition *at = new EventTransition(QEvent::User, s2Child2);
3808 s2Child->addTransition(transition: at);
3809
3810 QPropertyAnimation *animation = new QPropertyAnimation(object, "bar", s2);
3811 animation->setDuration(2000);
3812 connect(sender: animation, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3813 at->addAnimation(animation);
3814
3815 at = new EventTransition(QEvent::User, s2);
3816 s1->addTransition(transition: at);
3817
3818 animation = new QPropertyAnimation(object, "foo", s2);
3819 connect(sender: animation, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3820 at->addAnimation(animation);
3821
3822 animation = new QPropertyAnimation(object, "bar", s2);
3823 connect(sender: animation, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3824 at->addAnimation(animation);
3825
3826 QState *s3 = new QState(&machine);
3827 DEFINE_ACTIVE_SPY(s3);
3828 s2->addTransition(sender: s2Child, SIGNAL(propertiesAssigned()), target: s3);
3829
3830 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3831
3832 machine.setInitialState(s1);
3833 machine.start();
3834 QCoreApplication::processEvents();
3835 TEST_ACTIVE_CHANGED(s1, 1);
3836 TEST_ACTIVE_CHANGED(s2, 0);
3837 TEST_ACTIVE_CHANGED(s2Child, 0);
3838 TEST_ACTIVE_CHANGED(s2Child2, 0);
3839 TEST_ACTIVE_CHANGED(s3, 0);
3840 machine.postEvent(event: new QEvent(QEvent::User));
3841
3842 TEST_ACTIVE_CHANGED(s1, 2);
3843 TEST_ACTIVE_CHANGED(s2, 1);
3844 TEST_ACTIVE_CHANGED(s2Child, 1);
3845 TEST_ACTIVE_CHANGED(s2Child2, 0);
3846 TEST_ACTIVE_CHANGED(s3, 0);
3847
3848 QCOREAPPLICATION_EXEC(5000);
3849
3850 TEST_ACTIVE_CHANGED(s1, 2);
3851 TEST_ACTIVE_CHANGED(s2, 2);
3852 TEST_ACTIVE_CHANGED(s2Child, 2);
3853 TEST_ACTIVE_CHANGED(s2Child2, 0);
3854 TEST_ACTIVE_CHANGED(s3, 1);
3855 QVERIFY(machine.isRunning());
3856 QVERIFY(machine.configuration().contains(s3));
3857 QCOMPARE(object->property("foo").toDouble(), 2.0);
3858 QCOMPARE(object->property("bar").toDouble(), 10.0);
3859 QCOMPARE(counter.counter, 2);
3860}
3861
3862void tst_QStateMachine::propertiesAssignedSignalTransitionsReuseAnimationGroup()
3863{
3864 QStateMachine machine;
3865 QObject *object = new QObject(&machine);
3866 object->setProperty(name: "foo", value: 0);
3867
3868 QState *s1 = new QState(&machine);
3869 DEFINE_ACTIVE_SPY(s1);
3870 s1->assignProperty(object, name: "foo", value: 123);
3871 QState *s2 = new QState(&machine);
3872 DEFINE_ACTIVE_SPY(s2);
3873 s2->assignProperty(object, name: "foo", value: 456);
3874 QState *s3 = new QState(&machine);
3875 DEFINE_ACTIVE_SPY(s3);
3876 s3->assignProperty(object, name: "foo", value: 789);
3877 QFinalState *s4 = new QFinalState(&machine);
3878
3879 QParallelAnimationGroup animationGroup;
3880 animationGroup.addAnimation(animation: new QPropertyAnimation(object, "foo"));
3881 QSignalSpy animationFinishedSpy(&animationGroup, &QParallelAnimationGroup::finished);
3882 QVERIFY(animationFinishedSpy.isValid());
3883 s1->addTransition(sender: s1, SIGNAL(propertiesAssigned()), target: s2)->addAnimation(animation: &animationGroup);
3884 s2->addTransition(sender: s2, SIGNAL(propertiesAssigned()), target: s3)->addAnimation(animation: &animationGroup);
3885 s3->addTransition(sender: s3, SIGNAL(propertiesAssigned()), target: s4);
3886
3887 machine.setInitialState(s1);
3888 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
3889 QVERIFY(runningSpy.isValid());
3890 QSignalSpy machineFinishedSpy(&machine, &QStateMachine::finished);
3891 QVERIFY(machineFinishedSpy.isValid());
3892 machine.start();
3893 TEST_ACTIVE_CHANGED(s1, 2);
3894 TEST_ACTIVE_CHANGED(s2, 2);
3895 TEST_ACTIVE_CHANGED(s3, 2);
3896 QTRY_COMPARE(machineFinishedSpy.count(), 1);
3897 TEST_RUNNING_CHANGED_STARTED_STOPPED;
3898 QVERIFY(!machine.isRunning());
3899 QCOMPARE(machine.configuration().size(), 1);
3900 QVERIFY(machine.configuration().contains(s4));
3901 QCOMPARE(object->property("foo").toInt(), 789);
3902 QCOMPARE(animationFinishedSpy.count(), 2);
3903
3904}
3905
3906void tst_QStateMachine::animatedGlobalRestoreProperty()
3907{
3908 QStateMachine machine;
3909 machine.setGlobalRestorePolicy(QState::RestoreProperties);
3910
3911 QObject *object = new QObject(&machine);
3912 object->setProperty(name: "foo", value: 1.0);
3913
3914 SlotCalledCounter counter;
3915
3916 QState *s1 = new QState(&machine);
3917 DEFINE_ACTIVE_SPY(s1);
3918 QState *s2 = new QState(&machine);
3919 DEFINE_ACTIVE_SPY(s2);
3920 s2->assignProperty(object, name: "foo", value: 2.0);
3921
3922 QState *s3 = new QState(&machine);
3923 DEFINE_ACTIVE_SPY(s3);
3924
3925 QState *s4 = new QState(&machine);
3926 DEFINE_ACTIVE_SPY(s4);
3927 QObject::connect(sender: s4, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3928
3929 QAbstractTransition *at = new EventTransition(QEvent::User, s2);
3930 s1->addTransition(transition: at);
3931 QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", s2);
3932 connect(sender: pa, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3933 at->addAnimation(animation: pa);
3934
3935 at = s2->addTransition(sender: pa, SIGNAL(finished()), target: s3);
3936 pa = new QPropertyAnimation(object, "foo", s3);
3937 connect(sender: pa, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3938 at->addAnimation(animation: pa);
3939
3940 at = s3->addTransition(sender: pa, SIGNAL(finished()), target: s4);
3941 pa = new QPropertyAnimation(object, "foo", s4);
3942 connect(sender: pa, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
3943 at->addAnimation(animation: pa);
3944
3945 machine.setInitialState(s1);
3946 machine.start();
3947 QCoreApplication::processEvents();
3948 TEST_ACTIVE_CHANGED(s1, 1);
3949 TEST_ACTIVE_CHANGED(s2, 0);
3950 TEST_ACTIVE_CHANGED(s3, 0);
3951 TEST_ACTIVE_CHANGED(s4, 0);
3952
3953 machine.postEvent(event: new QEvent(QEvent::User));
3954 TEST_ACTIVE_CHANGED(s1, 2);
3955 TEST_ACTIVE_CHANGED(s2, 1);
3956 TEST_ACTIVE_CHANGED(s3, 0);
3957 TEST_ACTIVE_CHANGED(s4, 0);
3958
3959 QCOREAPPLICATION_EXEC(5000);
3960
3961 TEST_ACTIVE_CHANGED(s1, 2);
3962 TEST_ACTIVE_CHANGED(s2, 2);
3963 TEST_ACTIVE_CHANGED(s3, 2);
3964 TEST_ACTIVE_CHANGED(s4, 1);
3965 QVERIFY(machine.isRunning());
3966 QVERIFY(machine.configuration().contains(s4));
3967 QCOMPARE(object->property("foo").toDouble(), 1.0);
3968 QCOMPARE(counter.counter, 2);
3969}
3970
3971void tst_QStateMachine::specificTargetValueOfAnimation()
3972{
3973 QStateMachine machine;
3974
3975 QObject *object = new QObject(&machine);
3976 object->setProperty(name: "foo", value: 1.0);
3977
3978 QState *s1 = new QState(&machine);
3979 DEFINE_ACTIVE_SPY(s1);
3980
3981 QState *s2 = new QState(&machine);
3982 DEFINE_ACTIVE_SPY(s2);
3983 s2->assignProperty(object, name: "foo", value: 2.0);
3984
3985 QPropertyAnimation *anim = new QPropertyAnimation(object, "foo");
3986 anim->setEndValue(10.0);
3987 EventTransition *trans = new EventTransition(QEvent::User, s2);
3988 s1->addTransition(transition: trans);
3989 trans->addAnimation(animation: anim);
3990
3991 QState *s3 = new QState(&machine);
3992 DEFINE_ACTIVE_SPY(s3);
3993 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
3994 s2->addTransition(sender: anim, SIGNAL(finished()), target: s3);
3995
3996 machine.setInitialState(s1);
3997 machine.start();
3998 QCoreApplication::processEvents();
3999 TEST_ACTIVE_CHANGED(s1, 1);
4000 TEST_ACTIVE_CHANGED(s2, 0);
4001 TEST_ACTIVE_CHANGED(s3, 0);
4002
4003 machine.postEvent(event: new QEvent(QEvent::User));
4004 TEST_ACTIVE_CHANGED(s1, 2);
4005 TEST_ACTIVE_CHANGED(s2, 1);
4006 TEST_ACTIVE_CHANGED(s3, 0);
4007
4008 QCOREAPPLICATION_EXEC(5000);
4009
4010 TEST_ACTIVE_CHANGED(s1, 2);
4011 TEST_ACTIVE_CHANGED(s2, 2);
4012 TEST_ACTIVE_CHANGED(s3, 1);
4013 QVERIFY(machine.isRunning());
4014 QVERIFY(machine.configuration().contains(s3));
4015 QCOMPARE(object->property("foo").toDouble(), 2.0);
4016 QCOMPARE(anim->endValue().toDouble(), 10.0);
4017
4018 delete anim;
4019}
4020
4021void tst_QStateMachine::addDefaultAnimation()
4022{
4023 QStateMachine machine;
4024
4025 QObject *object = new QObject();
4026 object->setProperty(name: "foo", value: 1.0);
4027
4028 QState *s1 = new QState(&machine);
4029 DEFINE_ACTIVE_SPY(s1);
4030
4031 QState *s2 = new QState(&machine);
4032 DEFINE_ACTIVE_SPY(s2);
4033 s2->assignProperty(object, name: "foo", value: 2.0);
4034
4035 QState *s3 = new QState(&machine);
4036 DEFINE_ACTIVE_SPY(s3);
4037 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
4038
4039 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
4040
4041 QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine);
4042 machine.addDefaultAnimation(animation: pa);
4043 s2->addTransition(sender: pa, SIGNAL(finished()), target: s3);
4044
4045 machine.setInitialState(s1);
4046 machine.start();
4047 QCoreApplication::processEvents();
4048 TEST_ACTIVE_CHANGED(s1, 1);
4049 TEST_ACTIVE_CHANGED(s2, 0);
4050 TEST_ACTIVE_CHANGED(s3, 0);
4051
4052 machine.postEvent(event: new QEvent(QEvent::User));
4053 TEST_ACTIVE_CHANGED(s1, 2);
4054 TEST_ACTIVE_CHANGED(s2, 1);
4055 TEST_ACTIVE_CHANGED(s3, 0);
4056
4057 QCOREAPPLICATION_EXEC(5000);
4058
4059 TEST_ACTIVE_CHANGED(s1, 2);
4060 TEST_ACTIVE_CHANGED(s2, 2);
4061 TEST_ACTIVE_CHANGED(s3, 1);
4062 QVERIFY(machine.isRunning());
4063 QVERIFY(machine.configuration().contains(s3));
4064 QCOMPARE(object->property("foo").toDouble(), 2.0);
4065
4066 delete object;
4067}
4068
4069void tst_QStateMachine::addDefaultAnimationWithUnusedAnimation()
4070{
4071 QStateMachine machine;
4072
4073 QObject *object = new QObject(&machine);
4074 object->setProperty(name: "foo", value: 1.0);
4075 object->setProperty(name: "bar", value: 2.0);
4076
4077 SlotCalledCounter counter;
4078
4079 QState *s1 = new QState(&machine);
4080 DEFINE_ACTIVE_SPY(s1);
4081
4082 QState *s2 = new QState(&machine);
4083 DEFINE_ACTIVE_SPY(s2);
4084 s2->assignProperty(object, name: "foo", value: 2.0);
4085
4086 QState *s3 = new QState(&machine);
4087 DEFINE_ACTIVE_SPY(s3);
4088 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
4089
4090 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
4091
4092 QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine);
4093 connect(sender: pa, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
4094 machine.addDefaultAnimation(animation: pa);
4095 s2->addTransition(sender: pa, SIGNAL(finished()), target: s3);
4096
4097 pa = new QPropertyAnimation(object, "bar", &machine);
4098 connect(sender: pa, SIGNAL(finished()), receiver: &counter, SLOT(slot()));
4099 machine.addDefaultAnimation(animation: pa);
4100
4101 machine.setInitialState(s1);
4102 machine.start();
4103 QCoreApplication::processEvents();
4104 TEST_ACTIVE_CHANGED(s1, 1);
4105 TEST_ACTIVE_CHANGED(s2, 0);
4106 TEST_ACTIVE_CHANGED(s3, 0);
4107
4108 machine.postEvent(event: new QEvent(QEvent::User));
4109 TEST_ACTIVE_CHANGED(s1, 2);
4110 TEST_ACTIVE_CHANGED(s2, 1);
4111 TEST_ACTIVE_CHANGED(s3, 0);
4112
4113 QCOREAPPLICATION_EXEC(5000);
4114
4115 TEST_ACTIVE_CHANGED(s1, 2);
4116 TEST_ACTIVE_CHANGED(s2, 2);
4117 TEST_ACTIVE_CHANGED(s3, 1);
4118 QVERIFY(machine.isRunning());
4119 QVERIFY(machine.configuration().contains(s3));
4120 QCOMPARE(object->property("foo").toDouble(), 2.0);
4121 QCOMPARE(counter.counter, 1);
4122}
4123
4124void tst_QStateMachine::removeDefaultAnimation()
4125{
4126 QStateMachine machine;
4127
4128 QObject propertyHolder;
4129 propertyHolder.setProperty(name: "foo", value: 0);
4130
4131 QCOMPARE(machine.defaultAnimations().size(), 0);
4132
4133 QPropertyAnimation *anim = new QPropertyAnimation(&propertyHolder, "foo");
4134
4135 machine.addDefaultAnimation(animation: anim);
4136
4137 QCOMPARE(machine.defaultAnimations().size(), 1);
4138 QVERIFY(machine.defaultAnimations().contains(anim));
4139
4140 machine.removeDefaultAnimation(animation: anim);
4141
4142 QCOMPARE(machine.defaultAnimations().size(), 0);
4143
4144 machine.addDefaultAnimation(animation: anim);
4145
4146 QPropertyAnimation *anim2 = new QPropertyAnimation(&propertyHolder, "foo");
4147 machine.addDefaultAnimation(animation: anim2);
4148
4149 QCOMPARE(machine.defaultAnimations().size(), 2);
4150 QVERIFY(machine.defaultAnimations().contains(anim));
4151 QVERIFY(machine.defaultAnimations().contains(anim2));
4152
4153 machine.removeDefaultAnimation(animation: anim);
4154
4155 QCOMPARE(machine.defaultAnimations().size(), 1);
4156 QVERIFY(machine.defaultAnimations().contains(anim2));
4157
4158 machine.removeDefaultAnimation(animation: anim2);
4159 QCOMPARE(machine.defaultAnimations().size(), 0);
4160
4161 delete anim;
4162 delete anim2;
4163}
4164
4165void tst_QStateMachine::overrideDefaultAnimationWithSpecific()
4166{
4167 QStateMachine machine;
4168
4169 QObject *object = new QObject(&machine);
4170 object->setProperty(name: "foo", value: 1.0);
4171
4172 SlotCalledCounter counter;
4173
4174 QState *s1 = new QState(&machine);
4175 DEFINE_ACTIVE_SPY(s1);
4176 machine.setInitialState(s1);
4177
4178 QState *s2 = new QState(&machine);
4179 DEFINE_ACTIVE_SPY(s2);
4180 s2->assignProperty(object, name: "foo", value: 2.0);
4181
4182 QState *s3 = new QState(&machine);
4183 DEFINE_ACTIVE_SPY(s3);
4184 QObject::connect(sender: s3, SIGNAL(entered()), receiver: QCoreApplication::instance(), SLOT(quit()));
4185
4186 QAbstractTransition *at = new EventTransition(QEvent::User, s2);
4187 s1->addTransition(transition: at);
4188
4189 QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo");
4190 connect(sender: defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), receiver: &counter, SLOT(slot()));
4191
4192 QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo");
4193 s2->addTransition(sender: moreSpecificAnimation, SIGNAL(finished()), target: s3);
4194 connect(sender: moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), receiver: &counter, SLOT(slot()));
4195
4196 machine.addDefaultAnimation(animation: defaultAnimation);
4197 at->addAnimation(animation: moreSpecificAnimation);
4198
4199 machine.start();
4200 QCoreApplication::processEvents();
4201 TEST_ACTIVE_CHANGED(s1, 1);
4202 TEST_ACTIVE_CHANGED(s2, 0);
4203 TEST_ACTIVE_CHANGED(s3, 0);
4204
4205 machine.postEvent(event: new QEvent(QEvent::User));
4206 TEST_ACTIVE_CHANGED(s1, 2);
4207 TEST_ACTIVE_CHANGED(s2, 1);
4208 TEST_ACTIVE_CHANGED(s3, 0);
4209
4210 QCOREAPPLICATION_EXEC(5000);
4211
4212 TEST_ACTIVE_CHANGED(s1, 2);
4213 TEST_ACTIVE_CHANGED(s2, 2);
4214 TEST_ACTIVE_CHANGED(s3, 1);
4215 QVERIFY(machine.isRunning());
4216 QVERIFY(machine.configuration().contains(s3));
4217 QCOMPARE(counter.counter, 2); // specific animation started and stopped
4218
4219 delete defaultAnimation;
4220 delete moreSpecificAnimation;
4221}
4222
4223void tst_QStateMachine::parallelStateAssignmentsDone()
4224{
4225 QStateMachine machine;
4226
4227 QObject *propertyHolder = new QObject(&machine);
4228 propertyHolder->setProperty(name: "foo", value: 123);
4229 propertyHolder->setProperty(name: "bar", value: 456);
4230 propertyHolder->setProperty(name: "zoot", value: 789);
4231
4232 QState *s1 = new QState(&machine);
4233 DEFINE_ACTIVE_SPY(s1);
4234 machine.setInitialState(s1);
4235
4236 QState *parallelState = new QState(QState::ParallelStates, &machine);
4237 parallelState->assignProperty(object: propertyHolder, name: "foo", value: 321);
4238
4239 QState *s2 = new QState(parallelState);
4240 DEFINE_ACTIVE_SPY(s2);
4241 s2->assignProperty(object: propertyHolder, name: "bar", value: 654);
4242
4243 QState *s3 = new QState(parallelState);
4244 DEFINE_ACTIVE_SPY(s3);
4245 s3->assignProperty(object: propertyHolder, name: "zoot", value: 987);
4246
4247 s1->addTransition(transition: new EventTransition(QEvent::User, parallelState));
4248 machine.start();
4249 QCoreApplication::processEvents();
4250 TEST_ACTIVE_CHANGED(s1, 1);
4251 TEST_ACTIVE_CHANGED(s2, 0);
4252 TEST_ACTIVE_CHANGED(s3, 0);
4253
4254 QCOMPARE(propertyHolder->property("foo").toInt(), 123);
4255 QCOMPARE(propertyHolder->property("bar").toInt(), 456);
4256 QCOMPARE(propertyHolder->property("zoot").toInt(), 789);
4257
4258 machine.postEvent(event: new QEvent(QEvent::User));
4259 QCoreApplication::processEvents();
4260
4261 TEST_ACTIVE_CHANGED(s1, 2);
4262 TEST_ACTIVE_CHANGED(s2, 1);
4263 TEST_ACTIVE_CHANGED(s3, 1);
4264 QVERIFY(machine.isRunning());
4265 QCOMPARE(propertyHolder->property("foo").toInt(), 321);
4266 QCOMPARE(propertyHolder->property("bar").toInt(), 654);
4267 QCOMPARE(propertyHolder->property("zoot").toInt(), 987);
4268}
4269
4270void tst_QStateMachine::transitionsFromParallelStateWithNoChildren()
4271{
4272 QStateMachine machine;
4273
4274 QState *parallelState = new QState(QState::ParallelStates, &machine);
4275 DEFINE_ACTIVE_SPY(parallelState);
4276 machine.setInitialState(parallelState);
4277
4278 QState *s1 = new QState(&machine);
4279 DEFINE_ACTIVE_SPY(s1);
4280 parallelState->addTransition(transition: new EventTransition(QEvent::User, s1));
4281
4282 machine.start();
4283 QCoreApplication::processEvents();
4284 TEST_ACTIVE_CHANGED(parallelState, 1);
4285 TEST_ACTIVE_CHANGED(s1, 0);
4286
4287 QCOMPARE(1, machine.configuration().size());
4288 QVERIFY(machine.configuration().contains(parallelState));
4289
4290 machine.postEvent(event: new QEvent(QEvent::User));
4291 QCoreApplication::processEvents();
4292
4293 TEST_ACTIVE_CHANGED(parallelState, 2);
4294 TEST_ACTIVE_CHANGED(s1, 1);
4295 QVERIFY(machine.isRunning());
4296 QCOMPARE(1, machine.configuration().size());
4297 QVERIFY(machine.configuration().contains(s1));
4298}
4299
4300void tst_QStateMachine::parallelStateTransition()
4301{
4302 // This test checks if the parallel state is exited and re-entered if one compound state
4303 // is exited and subsequently re-entered. When the parallel state is exited, the other compound
4304 // state in the parallel state has to be exited too. When the parallel state is re-entered, the
4305 // other state also needs to be re-entered.
4306
4307 QStateMachine machine;
4308
4309 QState *parallelState = new QState(QState::ParallelStates, &machine);
4310 parallelState->setObjectName("parallelState");
4311 DEFINE_ACTIVE_SPY(parallelState);
4312 machine.setInitialState(parallelState);
4313
4314 QState *s1 = new QState(parallelState);
4315 s1->setObjectName("s1");
4316 DEFINE_ACTIVE_SPY(s1);
4317 QState *s2 = new QState(parallelState);
4318 s2->setObjectName("s2");
4319 DEFINE_ACTIVE_SPY(s2);
4320
4321 QState *s1InitialChild = new QState(s1);
4322 s1InitialChild->setObjectName("s1InitialChild");
4323 DEFINE_ACTIVE_SPY(s1InitialChild);
4324 s1->setInitialState(s1InitialChild);
4325
4326 QState *s2InitialChild = new QState(s2);
4327 s2InitialChild->setObjectName("s2InitialChild");
4328 DEFINE_ACTIVE_SPY(s2InitialChild);
4329 s2->setInitialState(s2InitialChild);
4330
4331 QState *s1OtherChild = new QState(s1);
4332 s1OtherChild->setObjectName("s1OtherChild");
4333 DEFINE_ACTIVE_SPY(s1OtherChild);
4334
4335 // The following transition will exit s1 (which means that parallelState is also exited), and
4336 // subsequently re-entered (which means that parallelState is also re-entered).
4337 EventTransition *et = new EventTransition(QEvent::User, s1OtherChild);
4338 et->setObjectName("s1->s1OtherChild");
4339 s1->addTransition(transition: et);
4340
4341 machine.start();
4342 QCoreApplication::processEvents();
4343
4344 // Initial entrance of the parallel state and its sub-states:
4345 TEST_ACTIVE_CHANGED(parallelState, 1);
4346 TEST_ACTIVE_CHANGED(s1, 1);
4347 TEST_ACTIVE_CHANGED(s1InitialChild, 1);
4348 TEST_ACTIVE_CHANGED(s2, 1);
4349 TEST_ACTIVE_CHANGED(s2InitialChild, 1);
4350 TEST_ACTIVE_CHANGED(s1OtherChild, 0);
4351
4352 QVERIFY(machine.configuration().contains(parallelState));
4353 QVERIFY(machine.configuration().contains(s1));
4354 QVERIFY(machine.configuration().contains(s2));
4355 QVERIFY(machine.configuration().contains(s1InitialChild));
4356 QVERIFY(machine.configuration().contains(s2InitialChild));
4357 QCOMPARE(machine.configuration().size(), 5);
4358
4359 machine.postEvent(event: new QEvent(QEvent::User));
4360 QCoreApplication::processEvents();
4361
4362 TEST_ACTIVE_CHANGED(parallelState, 3); // initial + exit + entry
4363 TEST_ACTIVE_CHANGED(s1, 3); // initial + exit + entry
4364 TEST_ACTIVE_CHANGED(s1InitialChild, 2); // initial + exit
4365 TEST_ACTIVE_CHANGED(s2, 3); // initial + exit due to parent exit + entry due to parent re-entry
4366 TEST_ACTIVE_CHANGED(s2InitialChild, 3); // initial + exit due to parent exit + re-entry due to parent re-entry
4367 TEST_ACTIVE_CHANGED(s1OtherChild, 1); // entry due to transition
4368 QVERIFY(machine.isRunning());
4369
4370 // Check that s1InitialChild is not in the configuration, because although s1 is re-entered,
4371 // another child state (s1OtherChild) is active, so the initial child should not be activated.
4372 QVERIFY(machine.configuration().contains(parallelState));
4373 QVERIFY(machine.configuration().contains(s1));
4374 QVERIFY(machine.configuration().contains(s2));
4375 QVERIFY(machine.configuration().contains(s1OtherChild));
4376 QVERIFY(machine.configuration().contains(s2InitialChild));
4377 QCOMPARE(machine.configuration().size(), 5);
4378}
4379
4380void tst_QStateMachine::nestedRestoreProperties()
4381{
4382 QStateMachine machine;
4383 machine.setGlobalRestorePolicy(QState::RestoreProperties);
4384
4385 QObject *propertyHolder = new QObject(&machine);
4386 propertyHolder->setProperty(name: "foo", value: 1);
4387 propertyHolder->setProperty(name: "bar", value: 2);
4388
4389 QState *s1 = new QState(&machine);
4390 DEFINE_ACTIVE_SPY(s1);
4391 machine.setInitialState(s1);
4392
4393 QState *s2 = new QState(&machine);
4394 DEFINE_ACTIVE_SPY(s2);
4395 s2->assignProperty(object: propertyHolder, name: "foo", value: 3);
4396
4397 QState *s21 = new QState(s2);
4398 DEFINE_ACTIVE_SPY(s21);
4399 s21->assignProperty(object: propertyHolder, name: "bar", value: 4);
4400 s2->setInitialState(s21);
4401
4402 QState *s22 = new QState(s2);
4403 DEFINE_ACTIVE_SPY(s22);
4404 s22->assignProperty(object: propertyHolder, name: "bar", value: 5);
4405
4406 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
4407 s21->addTransition(transition: new EventTransition(QEvent::User, s22));
4408
4409 machine.start();
4410 QCoreApplication::processEvents();
4411
4412 TEST_ACTIVE_CHANGED(s1, 1);
4413 TEST_ACTIVE_CHANGED(s2, 0);
4414 TEST_ACTIVE_CHANGED(s21, 0);
4415 TEST_ACTIVE_CHANGED(s22, 0);
4416 QCOMPARE(machine.configuration().size(), 1);
4417 QVERIFY(machine.configuration().contains(s1));
4418 QCOMPARE(propertyHolder->property("foo").toInt(), 1);
4419 QCOMPARE(propertyHolder->property("bar").toInt(), 2);
4420
4421 machine.postEvent(event: new QEvent(QEvent::User));
4422 QCoreApplication::processEvents();
4423
4424 TEST_ACTIVE_CHANGED(s1, 2);
4425 TEST_ACTIVE_CHANGED(s2, 1);
4426 TEST_ACTIVE_CHANGED(s21, 1);
4427 TEST_ACTIVE_CHANGED(s22, 0);
4428 QCOMPARE(machine.configuration().size(), 2);
4429 QVERIFY(machine.configuration().contains(s2));
4430 QVERIFY(machine.configuration().contains(s21));
4431 QCOMPARE(propertyHolder->property("foo").toInt(), 3);
4432 QCOMPARE(propertyHolder->property("bar").toInt(), 4);
4433
4434 machine.postEvent(event: new QEvent(QEvent::User));
4435 QCoreApplication::processEvents();
4436
4437 TEST_ACTIVE_CHANGED(s1, 2);
4438 TEST_ACTIVE_CHANGED(s2, 1);
4439 TEST_ACTIVE_CHANGED(s21, 2);
4440 TEST_ACTIVE_CHANGED(s22, 1);
4441 QVERIFY(machine.isRunning());
4442 QCOMPARE(machine.configuration().size(), 2);
4443 QVERIFY(machine.configuration().contains(s2));
4444 QVERIFY(machine.configuration().contains(s22));
4445 QCOMPARE(propertyHolder->property("foo").toInt(), 3);
4446 QCOMPARE(propertyHolder->property("bar").toInt(), 5);
4447}
4448
4449void tst_QStateMachine::nestedRestoreProperties2()
4450{
4451 QStateMachine machine;
4452 machine.setGlobalRestorePolicy(QState::RestoreProperties);
4453
4454 QObject *propertyHolder = new QObject(&machine);
4455 propertyHolder->setProperty(name: "foo", value: 1);
4456 propertyHolder->setProperty(name: "bar", value: 2);
4457
4458 QState *s1 = new QState(&machine);
4459 DEFINE_ACTIVE_SPY(s1);
4460 machine.setInitialState(s1);
4461
4462 QState *s2 = new QState(&machine);
4463 DEFINE_ACTIVE_SPY(s2);
4464 s2->assignProperty(object: propertyHolder, name: "foo", value: 3);
4465
4466 QState *s21 = new QState(s2);
4467 DEFINE_ACTIVE_SPY(s21);
4468 s21->assignProperty(object: propertyHolder, name: "bar", value: 4);
4469 s2->setInitialState(s21);
4470
4471 QState *s22 = new QState(s2);
4472 DEFINE_ACTIVE_SPY(s22);
4473 s22->assignProperty(object: propertyHolder, name: "foo", value: 6);
4474 s22->assignProperty(object: propertyHolder, name: "bar", value: 5);
4475
4476 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
4477 s21->addTransition(transition: new EventTransition(QEvent::User, s22));
4478 s22->addTransition(transition: new EventTransition(QEvent::User, s21));
4479
4480 machine.start();
4481 QCoreApplication::processEvents();
4482
4483 TEST_ACTIVE_CHANGED(s1, 1);
4484 TEST_ACTIVE_CHANGED(s2, 0);
4485 TEST_ACTIVE_CHANGED(s21, 0);
4486 TEST_ACTIVE_CHANGED(s22, 0);
4487 QCOMPARE(machine.configuration().size(), 1);
4488 QVERIFY(machine.configuration().contains(s1));
4489 QCOMPARE(propertyHolder->property("foo").toInt(), 1);
4490 QCOMPARE(propertyHolder->property("bar").toInt(), 2);
4491
4492 machine.postEvent(event: new QEvent(QEvent::User));
4493 QCoreApplication::processEvents();
4494
4495 TEST_ACTIVE_CHANGED(s1, 2);
4496 TEST_ACTIVE_CHANGED(s2, 1);
4497 TEST_ACTIVE_CHANGED(s21, 1);
4498 TEST_ACTIVE_CHANGED(s22, 0);
4499 QCOMPARE(machine.configuration().size(), 2);
4500 QVERIFY(machine.configuration().contains(s2));
4501 QVERIFY(machine.configuration().contains(s21));
4502 QCOMPARE(propertyHolder->property("foo").toInt(), 3);
4503 QCOMPARE(propertyHolder->property("bar").toInt(), 4);
4504
4505 machine.postEvent(event: new QEvent(QEvent::User));
4506 QCoreApplication::processEvents();
4507
4508 TEST_ACTIVE_CHANGED(s1, 2);
4509 TEST_ACTIVE_CHANGED(s2, 1);
4510 TEST_ACTIVE_CHANGED(s21, 2);
4511 TEST_ACTIVE_CHANGED(s22, 1);
4512 QCOMPARE(machine.configuration().size(), 2);
4513 QVERIFY(machine.configuration().contains(s2));
4514 QVERIFY(machine.configuration().contains(s22));
4515 QCOMPARE(propertyHolder->property("foo").toInt(), 6);
4516 QCOMPARE(propertyHolder->property("bar").toInt(), 5);
4517
4518 machine.postEvent(event: new QEvent(QEvent::User));
4519 QCoreApplication::processEvents();
4520
4521 TEST_ACTIVE_CHANGED(s1, 2);
4522 TEST_ACTIVE_CHANGED(s2, 1);
4523 TEST_ACTIVE_CHANGED(s21, 3);
4524 TEST_ACTIVE_CHANGED(s22, 2);
4525 QCOMPARE(machine.configuration().size(), 2);
4526 QVERIFY(machine.configuration().contains(s2));
4527 QVERIFY(machine.configuration().contains(s21));
4528 QCOMPARE(propertyHolder->property("foo").toInt(), 3);
4529 QCOMPARE(propertyHolder->property("bar").toInt(), 4);
4530
4531}
4532
4533void tst_QStateMachine::nestedStateMachines()
4534{
4535 QStateMachine machine;
4536 QState *group = new QState(&machine);
4537 DEFINE_ACTIVE_SPY(group);
4538 group->setChildMode(QState::ParallelStates);
4539 QStateMachine *subMachines[3];
4540 for (int i = 0; i < 3; ++i) {
4541 QState *subGroup = new QState(group);
4542 QStateMachine *subMachine = new QStateMachine(subGroup);
4543 {
4544 QState *initial = new QState(subMachine);
4545 QFinalState *done = new QFinalState(subMachine);
4546 initial->addTransition(transition: new EventTransition(QEvent::User, done));
4547 subMachine->setInitialState(initial);
4548 }
4549 QFinalState *subMachineDone = new QFinalState(subGroup);
4550 subMachine->addTransition(sender: subMachine, SIGNAL(finished()), target: subMachineDone);
4551 subGroup->setInitialState(subMachine);
4552 subMachines[i] = subMachine;
4553 }
4554 QFinalState *final = new QFinalState(&machine);
4555 group->addTransition(sender: group, SIGNAL(finished()), target: final);
4556 machine.setInitialState(group);
4557
4558 QSignalSpy startedSpy(&machine, &QStateMachine::started);
4559 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
4560 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4561 QVERIFY(startedSpy.isValid());
4562 QVERIFY(finishedSpy.isValid());
4563 QVERIFY(runningSpy.isValid());
4564 machine.start();
4565 QTRY_COMPARE(startedSpy.count(), 1);
4566 TEST_RUNNING_CHANGED(true);
4567 QTRY_COMPARE(machine.configuration().count(), 1+2*3);
4568 QVERIFY(machine.configuration().contains(group));
4569 for (int i = 0; i < 3; ++i)
4570 QVERIFY(machine.configuration().contains(subMachines[i]));
4571
4572 QCoreApplication::processEvents(); // starts the submachines
4573 TEST_ACTIVE_CHANGED(group, 1);
4574
4575 for (int i = 0; i < 3; ++i)
4576 subMachines[i]->postEvent(event: new QEvent(QEvent::User));
4577
4578 QTRY_COMPARE(finishedSpy.count(), 1);
4579 TEST_RUNNING_CHANGED(false);
4580 TEST_ACTIVE_CHANGED(group, 2);
4581}
4582
4583void tst_QStateMachine::goToState()
4584{
4585 QStateMachine machine;
4586 QState *s1 = new QState(&machine);
4587 QState *s2 = new QState(&machine);
4588 machine.setInitialState(s1);
4589 QSignalSpy startedSpy(&machine, &QStateMachine::started);
4590 QVERIFY(startedSpy.isValid());
4591 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4592 QVERIFY(runningSpy.isValid());
4593 machine.start();
4594 QTRY_COMPARE(startedSpy.count(), 1);
4595 TEST_RUNNING_CHANGED(true);
4596
4597 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s2);
4598 QCoreApplication::processEvents();
4599 QCOMPARE(machine.configuration().size(), 1);
4600 QVERIFY(machine.configuration().contains(s2));
4601
4602 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s2);
4603 QCoreApplication::processEvents();
4604 QCOMPARE(machine.configuration().size(), 1);
4605 QVERIFY(machine.configuration().contains(s2));
4606
4607 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s1);
4608 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s2);
4609 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s1);
4610 QCOMPARE(machine.configuration().size(), 1);
4611 QVERIFY(machine.configuration().contains(s2));
4612
4613 QCoreApplication::processEvents();
4614 QCOMPARE(machine.configuration().size(), 1);
4615 QVERIFY(machine.configuration().contains(s1));
4616
4617 // go to state in group
4618 QState *s2_1 = new QState(s2);
4619 s2->setInitialState(s2_1);
4620 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s2_1);
4621 QCoreApplication::processEvents();
4622 QCOMPARE(machine.configuration().size(), 2);
4623 QVERIFY(machine.configuration().contains(s2));
4624 QVERIFY(machine.configuration().contains(s2_1));
4625}
4626
4627void tst_QStateMachine::goToStateFromSourceWithTransition()
4628{
4629 // QTBUG-21813
4630 QStateMachine machine;
4631 QState *s1 = new QState(&machine);
4632 s1->addTransition(transition: new QSignalTransition);
4633 QState *s2 = new QState(&machine);
4634 machine.setInitialState(s1);
4635 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4636 QVERIFY(runningSpy.isValid());
4637 QSignalSpy startedSpy(&machine, &QStateMachine::started);
4638 QVERIFY(startedSpy.isValid());
4639 machine.start();
4640 QTRY_COMPARE(startedSpy.count(), 1);
4641 TEST_RUNNING_CHANGED(true);
4642
4643 QStateMachinePrivate::get(q: &machine)->goToState(targetState: s2);
4644 QCoreApplication::processEvents();
4645 QCOMPARE(machine.configuration().size(), 1);
4646 QVERIFY(machine.configuration().contains(s2));
4647}
4648
4649class CloneSignalTransition : public QSignalTransition
4650{
4651public:
4652 CloneSignalTransition(QObject *sender, const char *signal, QAbstractState *target)
4653 : QSignalTransition(sender, signal)
4654 {
4655 setTargetState(target);
4656 }
4657
4658 void onTransition(QEvent *e)
4659 {
4660 QSignalTransition::onTransition(event: e);
4661 QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
4662 eventSignalIndex = se->signalIndex();
4663 }
4664
4665 int eventSignalIndex;
4666};
4667
4668void tst_QStateMachine::clonedSignals()
4669{
4670 SignalEmitter emitter;
4671 QStateMachine machine;
4672 QState *s1 = new QState(&machine);
4673 DEFINE_ACTIVE_SPY(s1);
4674 QState *s2 = new QState(&machine);
4675 DEFINE_ACTIVE_SPY(s2);
4676 CloneSignalTransition *t1 = new CloneSignalTransition(&emitter, SIGNAL(signalWithDefaultArg()), s2);
4677 s1->addTransition(transition: t1);
4678
4679 machine.setInitialState(s1);
4680 QSignalSpy startedSpy(&machine, &QStateMachine::started);
4681 machine.start();
4682 QVERIFY(startedSpy.wait());
4683
4684 QSignalSpy transitionSpy(t1, &CloneSignalTransition::triggered);
4685 emitter.emitSignalWithDefaultArg();
4686 QTRY_COMPARE(transitionSpy.count(), 1);
4687
4688 QCOMPARE(t1->eventSignalIndex, emitter.metaObject()->indexOfSignal("signalWithDefaultArg()"));
4689 TEST_ACTIVE_CHANGED(s1, 2);
4690 TEST_ACTIVE_CHANGED(s2, 1);
4691 QVERIFY(machine.isRunning());
4692}
4693
4694class EventPosterThread : public QThread
4695{
4696 Q_OBJECT
4697public:
4698 EventPosterThread(QStateMachine *machine, QObject *parent = 0)
4699 : QThread(parent), m_machine(machine), m_count(0)
4700 {
4701 moveToThread(thread: this);
4702 QObject::connect(sender: m_machine, SIGNAL(started()),
4703 receiver: this, SLOT(postEvent()));
4704 }
4705protected:
4706 virtual void run()
4707 {
4708 exec();
4709 }
4710private Q_SLOTS:
4711 void postEvent()
4712 {
4713 m_machine->postEvent(event: new QEvent(QEvent::User));
4714 if (++m_count < 1000)
4715 QTimer::singleShot(msec: 0, receiver: this, SLOT(postEvent()));
4716 else
4717 quit();
4718 }
4719private:
4720 QStateMachine *m_machine;
4721 int m_count;
4722};
4723
4724void tst_QStateMachine::postEventFromOtherThread()
4725{
4726 QStateMachine machine;
4727 EventPosterThread poster(&machine);
4728 StringEventPoster *s1 = new StringEventPoster("foo", &machine);
4729 s1->addTransition(transition: new EventTransition(QEvent::User, s1));
4730 QFinalState *f = new QFinalState(&machine);
4731 s1->addTransition(sender: &poster, SIGNAL(finished()), target: f);
4732 machine.setInitialState(s1);
4733
4734 poster.start();
4735
4736 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4737 QVERIFY(runningSpy.isValid());
4738 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
4739 QVERIFY(finishedSpy.isValid());
4740 machine.start();
4741 QTRY_COMPARE(finishedSpy.count(), 1);
4742 TEST_RUNNING_CHANGED_STARTED_STOPPED;
4743}
4744
4745#ifndef QT_NO_WIDGETS
4746void tst_QStateMachine::eventFilterForApplication()
4747{
4748 QStateMachine machine;
4749
4750 QState *s1 = new QState(&machine);
4751 {
4752 machine.setInitialState(s1);
4753 }
4754
4755 QState *s2 = new QState(&machine);
4756
4757 QEventTransition *transition = new QEventTransition(QCoreApplication::instance(),
4758 QEvent::ApplicationActivate);
4759 transition->setTargetState(s2);
4760 s1->addTransition(transition);
4761
4762 machine.start();
4763 QCoreApplication::processEvents();
4764
4765 QCOMPARE(machine.configuration().size(), 1);
4766 QVERIFY(machine.configuration().contains(s1));
4767
4768 QCoreApplication::postEvent(receiver: QCoreApplication::instance(),
4769 event: new QEvent(QEvent::ApplicationActivate));
4770 QCoreApplication::processEvents();
4771
4772 QCOMPARE(machine.configuration().size(), 1);
4773 QVERIFY(machine.configuration().contains(s2));
4774}
4775#endif
4776
4777void tst_QStateMachine::eventClassesExported()
4778{
4779 // make sure this links
4780 QStateMachine::WrappedEvent *wrappedEvent = new QStateMachine::WrappedEvent(0, 0);
4781 Q_UNUSED(wrappedEvent);
4782 QStateMachine::SignalEvent *signalEvent = new QStateMachine::SignalEvent(0, 0, QList<QVariant>());
4783 Q_UNUSED(signalEvent);
4784}
4785
4786void tst_QStateMachine::stopInTransitionToFinalState()
4787{
4788 QStateMachine machine;
4789 QState *s1 = new QState(&machine);
4790 DEFINE_ACTIVE_SPY(s1);
4791 QFinalState *s2 = new QFinalState(&machine);
4792 QAbstractTransition *t1 = s1->addTransition(target: s2);
4793 machine.setInitialState(s1);
4794
4795 QObject::connect(sender: t1, SIGNAL(triggered()), receiver: &machine, SLOT(stop()));
4796 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
4797 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
4798 QSignalSpy s2EnteredSpy(s2, &QFinalState::entered);
4799 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4800 QVERIFY(stoppedSpy.isValid());
4801 QVERIFY(finishedSpy.isValid());
4802 QVERIFY(s2EnteredSpy.isValid());
4803 QVERIFY(runningSpy.isValid());
4804 machine.start();
4805 // Stopping should take precedence over finished.
4806 QTRY_COMPARE(stoppedSpy.count(), 1);
4807 QCOMPARE(finishedSpy.count(), 0);
4808 QCOMPARE(s2EnteredSpy.count(), 1);
4809 TEST_RUNNING_CHANGED_STARTED_STOPPED;
4810 QCOMPARE(machine.configuration().size(), 1);
4811 QVERIFY(machine.configuration().contains(s2));
4812 TEST_ACTIVE_CHANGED(s1, 2);
4813}
4814
4815class StopInEventTestTransition : public QAbstractTransition
4816{
4817public:
4818 bool eventTest(QEvent *e)
4819 {
4820 if (e->type() == QEvent::User)
4821 machine()->stop();
4822 return false;
4823 }
4824 void onTransition(QEvent *)
4825 { }
4826};
4827
4828void tst_QStateMachine::stopInEventTest_data()
4829{
4830 QTest::addColumn<int>(name: "eventPriority");
4831 QTest::newRow(dataTag: "NormalPriority") << int(QStateMachine::NormalPriority);
4832 QTest::newRow(dataTag: "HighPriority") << int(QStateMachine::HighPriority);
4833}
4834
4835void tst_QStateMachine::stopInEventTest()
4836{
4837 QFETCH(int, eventPriority);
4838
4839 QStateMachine machine;
4840 QState *s1 = new QState(&machine);
4841 DEFINE_ACTIVE_SPY(s1);
4842 s1->addTransition(transition: new StopInEventTestTransition());
4843 machine.setInitialState(s1);
4844
4845 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4846 QVERIFY(runningSpy.isValid());
4847 QSignalSpy startedSpy(&machine, &QStateMachine::started);
4848 QVERIFY(startedSpy.isValid());
4849 machine.start();
4850 TEST_ACTIVE_CHANGED(s1, 1);
4851 QTRY_COMPARE(startedSpy.count(), 1);
4852 TEST_RUNNING_CHANGED(true);
4853
4854 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
4855 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
4856 QVERIFY(stoppedSpy.isValid());
4857 QVERIFY(finishedSpy.isValid());
4858 machine.postEvent(event: new QEvent(QEvent::User), priority: QStateMachine::EventPriority(eventPriority));
4859
4860 QTRY_COMPARE(stoppedSpy.count(), 1);
4861 QCOMPARE(finishedSpy.count(), 0);
4862 TEST_RUNNING_CHANGED(false);
4863 QCOMPARE(machine.configuration().size(), 1);
4864 QVERIFY(machine.configuration().contains(s1));
4865 TEST_ACTIVE_CHANGED(s1, 1);
4866}
4867
4868class IncrementReceiversTest : public QObject
4869{
4870 Q_OBJECT
4871signals:
4872 void mySignal();
4873public:
4874 virtual void connectNotify(const QMetaMethod &signal)
4875 {
4876 signalList.append(t: signal);
4877 }
4878
4879 QVector<QMetaMethod> signalList;
4880};
4881
4882void tst_QStateMachine::testIncrementReceivers()
4883{
4884 QStateMachine machine;
4885 QState *s1 = new QState(&machine);
4886 DEFINE_ACTIVE_SPY(s1);
4887 machine.setInitialState(s1);
4888 QFinalState *s2 = new QFinalState(&machine);
4889
4890 IncrementReceiversTest testObject;
4891 s1->addTransition(sender: &testObject, SIGNAL(mySignal()), target: s2);
4892
4893 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4894 QVERIFY(runningSpy.isValid());
4895 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
4896 machine.start();
4897 TEST_RUNNING_CHANGED(true);
4898
4899 QMetaObject::invokeMethod(obj: &testObject, member: "mySignal", type: Qt::QueuedConnection);
4900
4901 QTRY_COMPARE(finishedSpy.count(), 1);
4902 TEST_RUNNING_CHANGED(false);
4903 QCOMPARE(testObject.signalList.size(), 1);
4904 QCOMPARE(testObject.signalList.at(0), QMetaMethod::fromSignal(&IncrementReceiversTest::mySignal));
4905 TEST_ACTIVE_CHANGED(s1, 2);
4906}
4907
4908void tst_QStateMachine::initialStateIsEnteredBeforeStartedEmitted()
4909{
4910 QStateMachine machine;
4911 QState *s1 = new QState(&machine);
4912 DEFINE_ACTIVE_SPY(s1);
4913 machine.setInitialState(s1);
4914 QFinalState *s2 = new QFinalState(&machine);
4915
4916 // When started() is emitted, s1 should be the active state, and this
4917 // transition should trigger.
4918 s1->addTransition(sender: &machine, SIGNAL(started()), target: s2);
4919
4920 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
4921 QVERIFY(runningSpy.isValid());
4922 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
4923 machine.start();
4924 QTRY_COMPARE(finishedSpy.count(), 1);
4925 TEST_RUNNING_CHANGED_STARTED_STOPPED;
4926 TEST_ACTIVE_CHANGED(s1, 2);
4927}
4928
4929void tst_QStateMachine::deletePropertyAssignmentObjectBeforeEntry()
4930{
4931 QStateMachine machine;
4932 QState *s1 = new QState(&machine);
4933 DEFINE_ACTIVE_SPY(s1);
4934 machine.setInitialState(s1);
4935
4936 QObject *o1 = new QObject;
4937 s1->assignProperty(object: o1, name: "objectName", value: "foo");
4938 delete o1;
4939 QObject *o2 = new QObject;
4940 s1->assignProperty(object: o2, name: "objectName", value: "bar");
4941
4942 machine.start();
4943 // Shouldn't crash
4944 QTRY_VERIFY(machine.configuration().contains(s1));
4945
4946 QCOMPARE(o2->objectName(), QString::fromLatin1("bar"));
4947 delete o2;
4948 TEST_ACTIVE_CHANGED(s1, 1);
4949 QVERIFY(machine.isRunning());
4950}
4951
4952void tst_QStateMachine::deletePropertyAssignmentObjectBeforeRestore()
4953{
4954 QStateMachine machine;
4955 machine.setGlobalRestorePolicy(QState::RestoreProperties);
4956 QState *s1 = new QState(&machine);
4957 DEFINE_ACTIVE_SPY(s1);
4958 machine.setInitialState(s1);
4959 QState *s2 = new QState(&machine);
4960 DEFINE_ACTIVE_SPY(s2);
4961 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
4962
4963 QObject *o1 = new QObject;
4964 s1->assignProperty(object: o1, name: "objectName", value: "foo");
4965 QObject *o2 = new QObject;
4966 s1->assignProperty(object: o2, name: "objectName", value: "bar");
4967
4968 QVERIFY(o1->objectName().isEmpty());
4969 QVERIFY(o2->objectName().isEmpty());
4970 machine.start();
4971 TEST_ACTIVE_CHANGED(s1, 1);
4972 TEST_ACTIVE_CHANGED(s2, 0);
4973 QTRY_VERIFY(machine.configuration().contains(s1));
4974 QCOMPARE(o1->objectName(), QString::fromLatin1("foo"));
4975 QCOMPARE(o2->objectName(), QString::fromLatin1("bar"));
4976
4977 delete o1;
4978 machine.postEvent(event: new QEvent(QEvent::User));
4979 // Shouldn't crash
4980 QTRY_VERIFY(machine.configuration().contains(s2));
4981
4982 QVERIFY(o2->objectName().isEmpty());
4983 delete o2;
4984 TEST_ACTIVE_CHANGED(s1, 2);
4985 TEST_ACTIVE_CHANGED(s2, 1);
4986 QVERIFY(machine.isRunning());
4987}
4988
4989void tst_QStateMachine::deleteInitialState()
4990{
4991 QStateMachine machine;
4992 QState *s1 = new QState(&machine);
4993 machine.setInitialState(s1);
4994 delete s1;
4995 QTest::ignoreMessage(type: QtWarningMsg, message: "QStateMachine::start: No initial state set for machine. Refusing to start.");
4996 machine.start();
4997 // Shouldn't crash
4998 QCoreApplication::processEvents();
4999}
5000
5001void tst_QStateMachine::setPropertyAfterRestore()
5002{
5003 QStateMachine machine;
5004 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5005
5006 QObject *object = new QObject(&machine);
5007 object->setProperty(name: "a", value: 1);
5008
5009 QState *s1 = new QState(&machine);
5010 DEFINE_ACTIVE_SPY(s1);
5011 machine.setInitialState(s1);
5012 s1->assignProperty(object, name: "a", value: 2);
5013
5014 QState *s2 = new QState(&machine);
5015 DEFINE_ACTIVE_SPY(s2);
5016 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
5017
5018 QState *s3 = new QState(&machine);
5019 DEFINE_ACTIVE_SPY(s3);
5020 s3->assignProperty(object, name: "a", value: 4);
5021 s2->addTransition(transition: new EventTransition(QEvent::User, s3));
5022
5023 QState *s4 = new QState(&machine);
5024 DEFINE_ACTIVE_SPY(s4);
5025 s3->addTransition(transition: new EventTransition(QEvent::User, s4));
5026
5027 machine.start();
5028 TEST_ACTIVE_CHANGED(s1, 1);
5029 TEST_ACTIVE_CHANGED(s2, 0);
5030 TEST_ACTIVE_CHANGED(s3, 0);
5031 TEST_ACTIVE_CHANGED(s4, 0);
5032 QTRY_VERIFY(machine.configuration().contains(s1));
5033 QCOMPARE(object->property("a").toInt(), 2);
5034
5035 machine.postEvent(event: new QEvent(QEvent::User));
5036 TEST_ACTIVE_CHANGED(s1, 2);
5037 TEST_ACTIVE_CHANGED(s2, 1);
5038 TEST_ACTIVE_CHANGED(s3, 0);
5039 TEST_ACTIVE_CHANGED(s4, 0);
5040 QTRY_VERIFY(machine.configuration().contains(s2));
5041 QCOMPARE(object->property("a").toInt(), 1); // restored
5042
5043 // Set property outside of state machine; this is the value
5044 // that should be remembered in the next transition
5045 object->setProperty(name: "a", value: 3);
5046
5047 machine.postEvent(event: new QEvent(QEvent::User));
5048 TEST_ACTIVE_CHANGED(s1, 2);
5049 TEST_ACTIVE_CHANGED(s2, 2);
5050 TEST_ACTIVE_CHANGED(s3, 1);
5051 TEST_ACTIVE_CHANGED(s4, 0);
5052 QTRY_VERIFY(machine.configuration().contains(s3));
5053 QCOMPARE(object->property("a").toInt(), 4);
5054
5055 machine.postEvent(event: new QEvent(QEvent::User));
5056 TEST_ACTIVE_CHANGED(s1, 2);
5057 TEST_ACTIVE_CHANGED(s2, 2);
5058 TEST_ACTIVE_CHANGED(s3, 2);
5059 TEST_ACTIVE_CHANGED(s4, 1);
5060 QVERIFY(machine.isRunning());
5061 QTRY_VERIFY(machine.configuration().contains(s4));
5062 QCOMPARE(object->property("a").toInt(), 3); // restored
5063
5064 delete object;
5065}
5066
5067void tst_QStateMachine::transitionWithNoTarget_data()
5068{
5069 QTest::addColumn<int>(name: "restorePolicy");
5070 QTest::newRow(dataTag: "DontRestoreProperties") << int(QState::DontRestoreProperties);
5071 QTest::newRow(dataTag: "RestoreProperties") << int(QState::RestoreProperties);
5072}
5073
5074void tst_QStateMachine::transitionWithNoTarget()
5075{
5076 QFETCH(int, restorePolicy);
5077
5078 QStateMachine machine;
5079 machine.setGlobalRestorePolicy(static_cast<QState::RestorePolicy>(restorePolicy));
5080
5081 QObject *object = new QObject;
5082 object->setProperty(name: "a", value: 1);
5083
5084 QState *s1 = new QState(&machine);
5085 DEFINE_ACTIVE_SPY(s1);
5086 machine.setInitialState(s1);
5087 s1->assignProperty(object, name: "a", value: 2);
5088 EventTransition *t1 = new EventTransition(QEvent::User, /*target=*/0);
5089 s1->addTransition(transition: t1);
5090
5091 QSignalSpy s1EnteredSpy(s1, &QState::entered);
5092 QSignalSpy s1ExitedSpy(s1, &QState::exited);
5093 QSignalSpy t1TriggeredSpy(t1, &EventTransition::triggered);
5094
5095 machine.start();
5096 QTRY_VERIFY(machine.configuration().contains(s1));
5097 QCOMPARE(s1EnteredSpy.count(), 1);
5098 QCOMPARE(s1ExitedSpy.count(), 0);
5099 QCOMPARE(t1TriggeredSpy.count(), 0);
5100 QCOMPARE(object->property("a").toInt(), 2);
5101
5102 object->setProperty(name: "a", value: 3);
5103
5104 machine.postEvent(event: new QEvent(QEvent::User));
5105 QTRY_COMPARE(t1TriggeredSpy.count(), 1);
5106 QCOMPARE(s1EnteredSpy.count(), 1);
5107 QCOMPARE(s1ExitedSpy.count(), 0);
5108 // the assignProperty should not be re-executed, nor should the old value
5109 // be restored
5110 QCOMPARE(object->property("a").toInt(), 3);
5111
5112 machine.postEvent(event: new QEvent(QEvent::User));
5113 QTRY_COMPARE(t1TriggeredSpy.count(), 2);
5114 QCOMPARE(s1EnteredSpy.count(), 1);
5115 QCOMPARE(s1ExitedSpy.count(), 0);
5116 QCOMPARE(object->property("a").toInt(), 3);
5117
5118 delete object;
5119 TEST_ACTIVE_CHANGED(s1, 1);
5120 QVERIFY(machine.isRunning());
5121}
5122
5123void tst_QStateMachine::initialStateIsFinal()
5124{
5125 QStateMachine machine;
5126 QFinalState *f = new QFinalState(&machine);
5127 machine.setInitialState(f);
5128 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
5129 QVERIFY(runningSpy.isValid());
5130 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
5131 machine.start();
5132 QTRY_VERIFY(machine.configuration().contains(f));
5133 QTRY_COMPARE(finishedSpy.count(), 1);
5134 TEST_RUNNING_CHANGED_STARTED_STOPPED;
5135}
5136
5137class PropertyObject : public QObject
5138{
5139 Q_OBJECT
5140 Q_PROPERTY(int prop READ prop WRITE setProp)
5141public:
5142 PropertyObject(QObject *parent = 0)
5143 : QObject(parent), m_propValue(0), m_propWriteCount(0)
5144 {}
5145 int prop() const { return m_propValue; }
5146 void setProp(int value) { m_propValue = value; ++m_propWriteCount; }
5147 int propWriteCount() const { return m_propWriteCount; }
5148private:
5149 int m_propValue;
5150 int m_propWriteCount;
5151};
5152
5153void tst_QStateMachine::restorePropertiesSimple()
5154{
5155 QStateMachine machine;
5156 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5157
5158 PropertyObject *po = new PropertyObject;
5159 po->setProp(2);
5160 QCOMPARE(po->propWriteCount(), 1);
5161
5162 QState *s1 = new QState(&machine);
5163 DEFINE_ACTIVE_SPY(s1);
5164 s1->assignProperty(object: po, name: "prop", value: 4);
5165 machine.setInitialState(s1);
5166
5167 QState *s2 = new QState(&machine);
5168 DEFINE_ACTIVE_SPY(s2);
5169 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
5170
5171 QState *s3 = new QState(&machine);
5172 DEFINE_ACTIVE_SPY(s3);
5173 s3->assignProperty(object: po, name: "prop", value: 6);
5174 s2->addTransition(transition: new EventTransition(QEvent::User, s3));
5175
5176 QState *s4 = new QState(&machine);
5177 DEFINE_ACTIVE_SPY(s4);
5178 s4->assignProperty(object: po, name: "prop", value: 8);
5179 s3->addTransition(transition: new EventTransition(QEvent::User, s4));
5180
5181 QState *s5 = new QState(&machine);
5182 DEFINE_ACTIVE_SPY(s5);
5183 s4->addTransition(transition: new EventTransition(QEvent::User, s5));
5184
5185 QState *s6 = new QState(&machine);
5186 DEFINE_ACTIVE_SPY(s6);
5187 s5->addTransition(transition: new EventTransition(QEvent::User, s6));
5188
5189 machine.start();
5190 TEST_ACTIVE_CHANGED(s1, 1);
5191 TEST_ACTIVE_CHANGED(s2, 0);
5192 TEST_ACTIVE_CHANGED(s3, 0);
5193 TEST_ACTIVE_CHANGED(s4, 0);
5194 TEST_ACTIVE_CHANGED(s5, 0);
5195 TEST_ACTIVE_CHANGED(s6, 0);
5196 QTRY_VERIFY(machine.configuration().contains(s1));
5197 QCOMPARE(po->propWriteCount(), 2);
5198 QCOMPARE(po->prop(), 4);
5199
5200 machine.postEvent(event: new QEvent(QEvent::User));
5201 TEST_ACTIVE_CHANGED(s1, 2);
5202 TEST_ACTIVE_CHANGED(s2, 1);
5203 TEST_ACTIVE_CHANGED(s3, 0);
5204 TEST_ACTIVE_CHANGED(s4, 0);
5205 TEST_ACTIVE_CHANGED(s5, 0);
5206 TEST_ACTIVE_CHANGED(s6, 0);
5207 QTRY_VERIFY(machine.configuration().contains(s2));
5208 QCOMPARE(po->propWriteCount(), 3);
5209 QCOMPARE(po->prop(), 2); // restored
5210
5211 machine.postEvent(event: new QEvent(QEvent::User));
5212 TEST_ACTIVE_CHANGED(s1, 2);
5213 TEST_ACTIVE_CHANGED(s2, 2);
5214 TEST_ACTIVE_CHANGED(s3, 1);
5215 TEST_ACTIVE_CHANGED(s4, 0);
5216 TEST_ACTIVE_CHANGED(s5, 0);
5217 TEST_ACTIVE_CHANGED(s6, 0);
5218 QTRY_VERIFY(machine.configuration().contains(s3));
5219 QCOMPARE(po->propWriteCount(), 4);
5220 QCOMPARE(po->prop(), 6);
5221
5222 machine.postEvent(event: new QEvent(QEvent::User));
5223 TEST_ACTIVE_CHANGED(s1, 2);
5224 TEST_ACTIVE_CHANGED(s2, 2);
5225 TEST_ACTIVE_CHANGED(s3, 2);
5226 TEST_ACTIVE_CHANGED(s4, 1);
5227 TEST_ACTIVE_CHANGED(s5, 0);
5228 TEST_ACTIVE_CHANGED(s6, 0);
5229 QTRY_VERIFY(machine.configuration().contains(s4));
5230 QCOMPARE(po->propWriteCount(), 5);
5231 QCOMPARE(po->prop(), 8);
5232
5233 machine.postEvent(event: new QEvent(QEvent::User));
5234 TEST_ACTIVE_CHANGED(s1, 2);
5235 TEST_ACTIVE_CHANGED(s2, 2);
5236 TEST_ACTIVE_CHANGED(s3, 2);
5237 TEST_ACTIVE_CHANGED(s4, 2);
5238 TEST_ACTIVE_CHANGED(s5, 1);
5239 TEST_ACTIVE_CHANGED(s6, 0);
5240 QTRY_VERIFY(machine.configuration().contains(s5));
5241 QCOMPARE(po->propWriteCount(), 6);
5242 QCOMPARE(po->prop(), 2); // restored
5243
5244 machine.postEvent(event: new QEvent(QEvent::User));
5245 TEST_ACTIVE_CHANGED(s1, 2);
5246 TEST_ACTIVE_CHANGED(s2, 2);
5247 TEST_ACTIVE_CHANGED(s3, 2);
5248 TEST_ACTIVE_CHANGED(s4, 2);
5249 TEST_ACTIVE_CHANGED(s5, 2);
5250 TEST_ACTIVE_CHANGED(s6, 1);
5251 QVERIFY(machine.isRunning());
5252 QTRY_VERIFY(machine.configuration().contains(s6));
5253 QCOMPARE(po->propWriteCount(), 6);
5254
5255 delete po;
5256}
5257
5258void tst_QStateMachine::restoreProperties2()
5259{
5260 QStateMachine machine;
5261 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5262
5263 PropertyObject *po = new PropertyObject;
5264 po->setProp(2);
5265 QCOMPARE(po->propWriteCount(), 1);
5266
5267 QState *s1 = new QState(&machine);
5268 DEFINE_ACTIVE_SPY(s1);
5269 s1->assignProperty(object: po, name: "prop", value: 4);
5270 machine.setInitialState(s1);
5271
5272 QState *s11 = new QState(s1);
5273 DEFINE_ACTIVE_SPY(s11);
5274 s1->setInitialState(s11);
5275
5276 QState *s12 = new QState(s1);
5277 DEFINE_ACTIVE_SPY(s12);
5278 s11->addTransition(transition: new EventTransition(QEvent::User, s12));
5279
5280 QState *s13 = new QState(s1);
5281 DEFINE_ACTIVE_SPY(s13);
5282 s13->assignProperty(object: po, name: "prop", value: 6);
5283 s12->addTransition(transition: new EventTransition(QEvent::User, s13));
5284
5285 QState *s14 = new QState(s1);
5286 DEFINE_ACTIVE_SPY(s14);
5287 s14->assignProperty(object: po, name: "prop", value: 8);
5288 s13->addTransition(transition: new EventTransition(QEvent::User, s14));
5289
5290 QState *s15 = new QState(s1);
5291 DEFINE_ACTIVE_SPY(s15);
5292 s14->addTransition(transition: new EventTransition(QEvent::User, s15));
5293
5294 QState *s16 = new QState(s1);
5295 DEFINE_ACTIVE_SPY(s16);
5296 s15->addTransition(transition: new EventTransition(QEvent::User, s16));
5297
5298 QState *s2 = new QState(&machine);
5299 DEFINE_ACTIVE_SPY(s2);
5300 s2->assignProperty(object: po, name: "prop", value: 10);
5301 s16->addTransition(transition: new EventTransition(QEvent::User, s2));
5302
5303 QState *s3 = new QState(&machine);
5304 DEFINE_ACTIVE_SPY(s3);
5305 s2->addTransition(transition: new EventTransition(QEvent::User, s3));
5306
5307 machine.start();
5308 TEST_ACTIVE_CHANGED(s1, 1);
5309 TEST_ACTIVE_CHANGED(s11, 1);
5310 TEST_ACTIVE_CHANGED(s12, 0);
5311 TEST_ACTIVE_CHANGED(s13, 0);
5312 TEST_ACTIVE_CHANGED(s14, 0);
5313 TEST_ACTIVE_CHANGED(s15, 0);
5314 TEST_ACTIVE_CHANGED(s16, 0);
5315 TEST_ACTIVE_CHANGED(s2, 0);
5316 TEST_ACTIVE_CHANGED(s3, 0);
5317 QTRY_VERIFY(machine.configuration().contains(s11));
5318 QCOMPARE(po->propWriteCount(), 2);
5319 QCOMPARE(po->prop(), 4);
5320
5321 machine.postEvent(event: new QEvent(QEvent::User));
5322 TEST_ACTIVE_CHANGED(s1, 1);
5323 TEST_ACTIVE_CHANGED(s11, 2);
5324 TEST_ACTIVE_CHANGED(s12, 1);
5325 TEST_ACTIVE_CHANGED(s13, 0);
5326 TEST_ACTIVE_CHANGED(s14, 0);
5327 TEST_ACTIVE_CHANGED(s15, 0);
5328 TEST_ACTIVE_CHANGED(s16, 0);
5329 TEST_ACTIVE_CHANGED(s2, 0);
5330 TEST_ACTIVE_CHANGED(s3, 0);
5331 QTRY_VERIFY(machine.configuration().contains(s12));
5332 QCOMPARE(po->propWriteCount(), 2);
5333
5334 machine.postEvent(event: new QEvent(QEvent::User));
5335 TEST_ACTIVE_CHANGED(s1, 1);
5336 TEST_ACTIVE_CHANGED(s11, 2);
5337 TEST_ACTIVE_CHANGED(s12, 2);
5338 TEST_ACTIVE_CHANGED(s13, 1);
5339 TEST_ACTIVE_CHANGED(s14, 0);
5340 TEST_ACTIVE_CHANGED(s15, 0);
5341 TEST_ACTIVE_CHANGED(s16, 0);
5342 TEST_ACTIVE_CHANGED(s2, 0);
5343 TEST_ACTIVE_CHANGED(s3, 0);
5344 QTRY_VERIFY(machine.configuration().contains(s13));
5345 QCOMPARE(po->propWriteCount(), 3);
5346 QCOMPARE(po->prop(), 6);
5347
5348 machine.postEvent(event: new QEvent(QEvent::User));
5349 TEST_ACTIVE_CHANGED(s1, 1);
5350 TEST_ACTIVE_CHANGED(s11, 2);
5351 TEST_ACTIVE_CHANGED(s12, 2);
5352 TEST_ACTIVE_CHANGED(s13, 2);
5353 TEST_ACTIVE_CHANGED(s14, 1);
5354 TEST_ACTIVE_CHANGED(s15, 0);
5355 TEST_ACTIVE_CHANGED(s16, 0);
5356 TEST_ACTIVE_CHANGED(s2, 0);
5357 TEST_ACTIVE_CHANGED(s3, 0);
5358 QTRY_VERIFY(machine.configuration().contains(s14));
5359 QCOMPARE(po->propWriteCount(), 4);
5360 QCOMPARE(po->prop(), 8);
5361
5362 machine.postEvent(event: new QEvent(QEvent::User));
5363 TEST_ACTIVE_CHANGED(s1, 1);
5364 TEST_ACTIVE_CHANGED(s11, 2);
5365 TEST_ACTIVE_CHANGED(s12, 2);
5366 TEST_ACTIVE_CHANGED(s13, 2);
5367 TEST_ACTIVE_CHANGED(s14, 2);
5368 TEST_ACTIVE_CHANGED(s15, 1);
5369 TEST_ACTIVE_CHANGED(s16, 0);
5370 TEST_ACTIVE_CHANGED(s2, 0);
5371 TEST_ACTIVE_CHANGED(s3, 0);
5372 QTRY_VERIFY(machine.configuration().contains(s15));
5373 QCOMPARE(po->propWriteCount(), 5);
5374 QCOMPARE(po->prop(), 4); // restored s1
5375
5376 machine.postEvent(event: new QEvent(QEvent::User));
5377 TEST_ACTIVE_CHANGED(s1, 1);
5378 TEST_ACTIVE_CHANGED(s11, 2);
5379 TEST_ACTIVE_CHANGED(s12, 2);
5380 TEST_ACTIVE_CHANGED(s13, 2);
5381 TEST_ACTIVE_CHANGED(s14, 2);
5382 TEST_ACTIVE_CHANGED(s15, 2);
5383 TEST_ACTIVE_CHANGED(s16, 1);
5384 TEST_ACTIVE_CHANGED(s2, 0);
5385 TEST_ACTIVE_CHANGED(s3, 0);
5386 QTRY_VERIFY(machine.configuration().contains(s16));
5387 QCOMPARE(po->propWriteCount(), 5);
5388
5389 machine.postEvent(event: new QEvent(QEvent::User));
5390 TEST_ACTIVE_CHANGED(s1, 2);
5391 TEST_ACTIVE_CHANGED(s11, 2);
5392 TEST_ACTIVE_CHANGED(s12, 2);
5393 TEST_ACTIVE_CHANGED(s13, 2);
5394 TEST_ACTIVE_CHANGED(s14, 2);
5395 TEST_ACTIVE_CHANGED(s15, 2);
5396 TEST_ACTIVE_CHANGED(s16, 2);
5397 TEST_ACTIVE_CHANGED(s2, 1);
5398 TEST_ACTIVE_CHANGED(s3, 0);
5399 QTRY_VERIFY(machine.configuration().contains(s2));
5400 QCOMPARE(po->propWriteCount(), 6);
5401 QCOMPARE(po->prop(), 10);
5402
5403 machine.postEvent(event: new QEvent(QEvent::User));
5404 TEST_ACTIVE_CHANGED(s1, 2);
5405 TEST_ACTIVE_CHANGED(s11, 2);
5406 TEST_ACTIVE_CHANGED(s12, 2);
5407 TEST_ACTIVE_CHANGED(s13, 2);
5408 TEST_ACTIVE_CHANGED(s14, 2);
5409 TEST_ACTIVE_CHANGED(s15, 2);
5410 TEST_ACTIVE_CHANGED(s16, 2);
5411 TEST_ACTIVE_CHANGED(s2, 2);
5412 TEST_ACTIVE_CHANGED(s3, 1);
5413 QVERIFY(machine.isRunning());
5414 QTRY_VERIFY(machine.configuration().contains(s3));
5415 QCOMPARE(po->propWriteCount(), 7);
5416 QCOMPARE(po->prop(), 2); // restored original
5417
5418 delete po;
5419
5420}
5421
5422void tst_QStateMachine::restoreProperties3()
5423{
5424 QStateMachine machine;
5425 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5426
5427 PropertyObject *po = new PropertyObject;
5428 po->setProp(2);
5429 QCOMPARE(po->propWriteCount(), 1);
5430
5431 QState *s1 = new QState(&machine);
5432 DEFINE_ACTIVE_SPY(s1);
5433 s1->assignProperty(object: po, name: "prop", value: 4);
5434 machine.setInitialState(s1);
5435
5436 QState *s11 = new QState(s1);
5437 DEFINE_ACTIVE_SPY(s11);
5438 s11->assignProperty(object: po, name: "prop", value: 6);
5439 s1->setInitialState(s11);
5440
5441 QState *s12 = new QState(s1);
5442 DEFINE_ACTIVE_SPY(s12);
5443 s11->addTransition(transition: new EventTransition(QEvent::User, s12));
5444
5445 QState *s13 = new QState(s1);
5446 DEFINE_ACTIVE_SPY(s13);
5447 s13->assignProperty(object: po, name: "prop", value: 8);
5448 s12->addTransition(transition: new EventTransition(QEvent::User, s13));
5449
5450 QState *s2 = new QState(&machine);
5451 DEFINE_ACTIVE_SPY(s2);
5452 s13->addTransition(transition: new EventTransition(QEvent::User, s2));
5453
5454 machine.start();
5455 TEST_ACTIVE_CHANGED(s1, 1);
5456 TEST_ACTIVE_CHANGED(s11, 1);
5457 TEST_ACTIVE_CHANGED(s12, 0);
5458 TEST_ACTIVE_CHANGED(s13, 0);
5459 TEST_ACTIVE_CHANGED(s2, 0);
5460
5461 QTRY_VERIFY(machine.configuration().contains(s11));
5462 QCOMPARE(po->propWriteCount(), 3);
5463 QCOMPARE(po->prop(), 6); // s11
5464
5465 machine.postEvent(event: new QEvent(QEvent::User));
5466 TEST_ACTIVE_CHANGED(s1, 1);
5467 TEST_ACTIVE_CHANGED(s11, 2);
5468 TEST_ACTIVE_CHANGED(s12, 1);
5469 TEST_ACTIVE_CHANGED(s13, 0);
5470 TEST_ACTIVE_CHANGED(s2, 0);
5471 QTRY_VERIFY(machine.configuration().contains(s12));
5472 QCOMPARE(po->propWriteCount(), 4);
5473 QCOMPARE(po->prop(), 4); // restored s1
5474
5475 machine.postEvent(event: new QEvent(QEvent::User));
5476 TEST_ACTIVE_CHANGED(s1, 1);
5477 TEST_ACTIVE_CHANGED(s11, 2);
5478 TEST_ACTIVE_CHANGED(s12, 2);
5479 TEST_ACTIVE_CHANGED(s13, 1);
5480 TEST_ACTIVE_CHANGED(s2, 0);
5481 QTRY_VERIFY(machine.configuration().contains(s13));
5482 QCOMPARE(po->propWriteCount(), 5);
5483 QCOMPARE(po->prop(), 8);
5484
5485 machine.postEvent(event: new QEvent(QEvent::User));
5486 TEST_ACTIVE_CHANGED(s1, 2);
5487 TEST_ACTIVE_CHANGED(s11, 2);
5488 TEST_ACTIVE_CHANGED(s12, 2);
5489 TEST_ACTIVE_CHANGED(s13, 2);
5490 TEST_ACTIVE_CHANGED(s2, 1);
5491 QVERIFY(machine.isRunning());
5492 QTRY_VERIFY(machine.configuration().contains(s2));
5493 QCOMPARE(po->propWriteCount(), 6);
5494 QCOMPARE(po->prop(), 2); // restored original
5495
5496 delete po;
5497}
5498
5499// QTBUG-20362
5500void tst_QStateMachine::restoreProperties4()
5501{
5502 QStateMachine machine;
5503 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5504
5505 PropertyObject *po1 = new PropertyObject;
5506 po1->setProp(2);
5507 QCOMPARE(po1->propWriteCount(), 1);
5508 PropertyObject *po2 = new PropertyObject;
5509 po2->setProp(4);
5510 QCOMPARE(po2->propWriteCount(), 1);
5511
5512 QState *s1 = new QState(&machine);
5513 DEFINE_ACTIVE_SPY(s1);
5514 s1->setChildMode(QState::ParallelStates);
5515 machine.setInitialState(s1);
5516
5517 QState *s11 = new QState(s1);
5518 DEFINE_ACTIVE_SPY(s11);
5519 QState *s111 = new QState(s11);
5520 DEFINE_ACTIVE_SPY(s111);
5521 s111->assignProperty(object: po1, name: "prop", value: 6);
5522 s11->setInitialState(s111);
5523
5524 QState *s112 = new QState(s11);
5525 DEFINE_ACTIVE_SPY(s112);
5526 s112->assignProperty(object: po1, name: "prop", value: 8);
5527 s111->addTransition(transition: new EventTransition(QEvent::User, s112));
5528
5529 QState *s12 = new QState(s1);
5530 DEFINE_ACTIVE_SPY(s12);
5531 QState *s121 = new QState(s12);
5532 DEFINE_ACTIVE_SPY(s121);
5533 s121->assignProperty(object: po2, name: "prop", value: 10);
5534 s12->setInitialState(s121);
5535
5536 QState *s122 = new QState(s12);
5537 DEFINE_ACTIVE_SPY(s122);
5538 s122->assignProperty(object: po2, name: "prop", value: 12);
5539 s121->addTransition(transition: new EventTransition(static_cast<QEvent::Type>(QEvent::User+1), s122));
5540
5541 QState *s2 = new QState(&machine);
5542 s112->addTransition(transition: new EventTransition(QEvent::User, s2));
5543 DEFINE_ACTIVE_SPY(s2);
5544
5545 machine.start();
5546 TEST_ACTIVE_CHANGED(s1, 1);
5547 TEST_ACTIVE_CHANGED(s11, 1);
5548 TEST_ACTIVE_CHANGED(s111, 1);
5549 TEST_ACTIVE_CHANGED(s112, 0);
5550 TEST_ACTIVE_CHANGED(s12, 1);
5551 TEST_ACTIVE_CHANGED(s121, 1);
5552 TEST_ACTIVE_CHANGED(s122, 0);
5553 TEST_ACTIVE_CHANGED(s2, 0);
5554
5555 QTRY_VERIFY(machine.configuration().contains(s1));
5556 QVERIFY(machine.configuration().contains(s11));
5557 QVERIFY(machine.configuration().contains(s111));
5558 QVERIFY(machine.configuration().contains(s12));
5559 QVERIFY(machine.configuration().contains(s121));
5560 QCOMPARE(po1->propWriteCount(), 2);
5561 QCOMPARE(po1->prop(), 6);
5562 QCOMPARE(po2->propWriteCount(), 2);
5563 QCOMPARE(po2->prop(), 10);
5564
5565 machine.postEvent(event: new QEvent(QEvent::User));
5566 TEST_ACTIVE_CHANGED(s1, 1);
5567 TEST_ACTIVE_CHANGED(s11, 1);
5568 TEST_ACTIVE_CHANGED(s111, 2);
5569 TEST_ACTIVE_CHANGED(s112, 1);
5570 TEST_ACTIVE_CHANGED(s12, 1);
5571 TEST_ACTIVE_CHANGED(s121, 1);
5572 TEST_ACTIVE_CHANGED(s122, 0);
5573 TEST_ACTIVE_CHANGED(s2, 0);
5574 QTRY_VERIFY(machine.configuration().contains(s112));
5575 QCOMPARE(po1->propWriteCount(), 3);
5576 QCOMPARE(po1->prop(), 8);
5577 QCOMPARE(po2->propWriteCount(), 2);
5578
5579 machine.postEvent(event: new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
5580 TEST_ACTIVE_CHANGED(s1, 1);
5581 TEST_ACTIVE_CHANGED(s11, 1);
5582 TEST_ACTIVE_CHANGED(s111, 2);
5583 TEST_ACTIVE_CHANGED(s112, 1);
5584 TEST_ACTIVE_CHANGED(s12, 1);
5585 TEST_ACTIVE_CHANGED(s121, 2);
5586 TEST_ACTIVE_CHANGED(s122, 1);
5587 TEST_ACTIVE_CHANGED(s2, 0);
5588 QTRY_VERIFY(machine.configuration().contains(s122));
5589 QCOMPARE(po1->propWriteCount(), 3);
5590 QCOMPARE(po2->propWriteCount(), 3);
5591 QCOMPARE(po2->prop(), 12);
5592
5593 machine.postEvent(event: new QEvent(QEvent::User));
5594 TEST_ACTIVE_CHANGED(s1, 2);
5595 TEST_ACTIVE_CHANGED(s11, 2);
5596 TEST_ACTIVE_CHANGED(s111, 2);
5597 TEST_ACTIVE_CHANGED(s112, 2);
5598 TEST_ACTIVE_CHANGED(s12, 2);
5599 TEST_ACTIVE_CHANGED(s121, 2);
5600 TEST_ACTIVE_CHANGED(s122, 2);
5601 TEST_ACTIVE_CHANGED(s2, 1);
5602 QTRY_VERIFY(machine.configuration().contains(s2));
5603 QCOMPARE(po1->propWriteCount(), 4);
5604 QCOMPARE(po1->prop(), 2); // restored original
5605 QCOMPARE(po2->propWriteCount(), 4);
5606 QCOMPARE(po2->prop(), 4); // restored original
5607
5608 delete po1;
5609 delete po2;
5610}
5611
5612void tst_QStateMachine::restorePropertiesSelfTransition()
5613{
5614 QStateMachine machine;
5615 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5616
5617 PropertyObject *po = new PropertyObject;
5618 po->setProp(2);
5619 QCOMPARE(po->propWriteCount(), 1);
5620
5621 QState *s1 = new QState(&machine);
5622 DEFINE_ACTIVE_SPY(s1);
5623 s1->assignProperty(object: po, name: "prop", value: 4);
5624 s1->addTransition(transition: new EventTransition(QEvent::User, s1));
5625 machine.setInitialState(s1);
5626
5627 QState *s2 = new QState(&machine);
5628 DEFINE_ACTIVE_SPY(s2);
5629 s1->addTransition(transition: new EventTransition(static_cast<QEvent::Type>(QEvent::User+1), s2));
5630
5631 machine.start();
5632 TEST_ACTIVE_CHANGED(s1, 1);
5633 TEST_ACTIVE_CHANGED(s2, 0);
5634 QTRY_VERIFY(machine.configuration().contains(s1));
5635 QCOMPARE(po->propWriteCount(), 2);
5636 QCOMPARE(po->prop(), 4);
5637
5638 machine.postEvent(event: new QEvent(QEvent::User));
5639 TEST_ACTIVE_CHANGED(s1, 3);
5640 TEST_ACTIVE_CHANGED(s2, 0);
5641 QTRY_COMPARE(po->propWriteCount(), 3);
5642 QCOMPARE(po->prop(), 4);
5643
5644 machine.postEvent(event: new QEvent(QEvent::User));
5645 TEST_ACTIVE_CHANGED(s1, 5);
5646 TEST_ACTIVE_CHANGED(s2, 0);
5647 QTRY_COMPARE(po->propWriteCount(), 4);
5648 QCOMPARE(po->prop(), 4);
5649
5650 machine.postEvent(event: new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
5651 TEST_ACTIVE_CHANGED(s1, 6);
5652 TEST_ACTIVE_CHANGED(s2, 1);
5653 QTRY_VERIFY(machine.configuration().contains(s2));
5654 QCOMPARE(po->propWriteCount(), 5);
5655 QCOMPARE(po->prop(), 2); // restored
5656
5657 delete po;
5658}
5659
5660void tst_QStateMachine::changeStateWhileAnimatingProperty()
5661{
5662 QStateMachine machine;
5663 machine.setGlobalRestorePolicy(QState::RestoreProperties);
5664
5665 QObject *o1 = new QObject;
5666 o1->setProperty(name: "x", value: 10.);
5667 QObject *o2 = new QObject;
5668 o2->setProperty(name: "y", value: 20.);
5669
5670 QState *group = new QState(&machine);
5671 DEFINE_ACTIVE_SPY(group);
5672 machine.setInitialState(group);
5673
5674 QState *s0 = new QState(group);
5675 DEFINE_ACTIVE_SPY(s0);
5676 group->setInitialState(s0);
5677
5678 QState *s1 = new QState(group);
5679 DEFINE_ACTIVE_SPY(s1);
5680 s1->assignProperty(object: o1, name: "x", value: 15.);
5681 QPropertyAnimation *a1 = new QPropertyAnimation(o1, "x", s1);
5682 a1->setDuration(800);
5683 machine.addDefaultAnimation(animation: a1);
5684 group->addTransition(transition: new EventTransition(QEvent::User, s1));
5685
5686 QState *s2 = new QState(group);
5687 DEFINE_ACTIVE_SPY(s2);
5688 s2->assignProperty(object: o2, name: "y", value: 25.);
5689 QPropertyAnimation *a2 = new QPropertyAnimation(o2, "y", s2);
5690 a2->setDuration(800);
5691 machine.addDefaultAnimation(animation: a2);
5692 group->addTransition(transition: new EventTransition(static_cast<QEvent::Type>(QEvent::User+1), s2));
5693
5694 machine.start();
5695 TEST_ACTIVE_CHANGED(group, 1);
5696 TEST_ACTIVE_CHANGED(s0, 1);
5697 TEST_ACTIVE_CHANGED(s1, 0);
5698 TEST_ACTIVE_CHANGED(s2, 0);
5699 QTRY_VERIFY(machine.configuration().contains(s0));
5700
5701 machine.postEvent(event: new QEvent(QEvent::User));
5702 TEST_ACTIVE_CHANGED(group, 3);
5703 TEST_ACTIVE_CHANGED(s0, 2);
5704 TEST_ACTIVE_CHANGED(s1, 1);
5705 TEST_ACTIVE_CHANGED(s2, 0);
5706 QTRY_VERIFY(machine.configuration().contains(s1));
5707 QCOREAPPLICATION_EXEC(400);
5708 machine.postEvent(event: new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
5709 TEST_ACTIVE_CHANGED(group, 5);
5710 TEST_ACTIVE_CHANGED(s0, 2);
5711 TEST_ACTIVE_CHANGED(s1, 2);
5712 TEST_ACTIVE_CHANGED(s2, 1);
5713 QTRY_VERIFY(machine.configuration().contains(s2));
5714 QCOREAPPLICATION_EXEC(300);
5715 machine.postEvent(event: new QEvent(QEvent::User));
5716 TEST_ACTIVE_CHANGED(group, 7);
5717 TEST_ACTIVE_CHANGED(s0, 2);
5718 TEST_ACTIVE_CHANGED(s1, 3);
5719 TEST_ACTIVE_CHANGED(s2, 2);
5720 QTRY_VERIFY(machine.configuration().contains(s1));
5721 QCOREAPPLICATION_EXEC(200);
5722 machine.postEvent(event: new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
5723 TEST_ACTIVE_CHANGED(group, 9);
5724 TEST_ACTIVE_CHANGED(s0, 2);
5725 TEST_ACTIVE_CHANGED(s1, 4);
5726 TEST_ACTIVE_CHANGED(s2, 3);
5727 QTRY_VERIFY(machine.configuration().contains(s2));
5728 QCOREAPPLICATION_EXEC(100);
5729 machine.postEvent(event: new QEvent(QEvent::User));
5730 TEST_ACTIVE_CHANGED(group, 11);
5731 TEST_ACTIVE_CHANGED(s0, 2);
5732 TEST_ACTIVE_CHANGED(s1, 5);
5733 TEST_ACTIVE_CHANGED(s2, 4);
5734 QTRY_VERIFY(machine.configuration().contains(s1));
5735 QTRY_COMPARE(o1->property("x").toDouble(), 15.);
5736 QTRY_COMPARE(o2->property("y").toDouble(), 20.);
5737
5738 delete o1;
5739 delete o2;
5740}
5741
5742class AssignPropertyTestState : public QState
5743{
5744 Q_OBJECT
5745public:
5746 AssignPropertyTestState(QState *parent = 0)
5747 : QState(parent), onEntryPassed(false), enteredPassed(false)
5748 { QObject::connect(sender: this, SIGNAL(entered()), receiver: this, SLOT(onEntered())); }
5749
5750 virtual void onEntry(QEvent *)
5751 { onEntryPassed = property(name: "wasAssigned").toBool(); }
5752
5753 bool onEntryPassed;
5754 bool enteredPassed;
5755
5756private Q_SLOTS:
5757 void onEntered()
5758 { enteredPassed = property(name: "wasAssigned").toBool(); }
5759};
5760
5761void tst_QStateMachine::propertiesAreAssignedBeforeEntryCallbacks_data()
5762{
5763 QTest::addColumn<int>(name: "restorePolicy");
5764 QTest::newRow(dataTag: "DontRestoreProperties") << int(QState::DontRestoreProperties);
5765 QTest::newRow(dataTag: "RestoreProperties") << int(QState::RestoreProperties);
5766}
5767
5768void tst_QStateMachine::propertiesAreAssignedBeforeEntryCallbacks()
5769{
5770 QFETCH(int, restorePolicy);
5771
5772 QStateMachine machine;
5773 machine.setGlobalRestorePolicy(static_cast<QState::RestorePolicy>(restorePolicy));
5774
5775 AssignPropertyTestState *s1 = new AssignPropertyTestState(&machine);
5776 DEFINE_ACTIVE_SPY(s1);
5777 s1->assignProperty(object: s1, name: "wasAssigned", value: true);
5778 machine.setInitialState(s1);
5779
5780 AssignPropertyTestState *s2 = new AssignPropertyTestState(&machine);
5781 DEFINE_ACTIVE_SPY(s2);
5782 s2->assignProperty(object: s2, name: "wasAssigned", value: true);
5783 s1->addTransition(transition: new EventTransition(QEvent::User, s2));
5784
5785 QVERIFY(!s1->property("wasAssigned").toBool());
5786 machine.start();
5787 TEST_ACTIVE_CHANGED(s1, 1);
5788 TEST_ACTIVE_CHANGED(s2, 0);
5789 QTRY_VERIFY(machine.configuration().contains(s1));
5790
5791 QVERIFY(s1->onEntryPassed);
5792 QVERIFY(s1->enteredPassed);
5793
5794 QVERIFY(!s2->property("wasAssigned").toBool());
5795 machine.postEvent(event: new QEvent(QEvent::User));
5796 TEST_ACTIVE_CHANGED(s1, 2);
5797 TEST_ACTIVE_CHANGED(s2, 1);
5798 QTRY_VERIFY(machine.configuration().contains(s2));
5799
5800 QVERIFY(s2->onEntryPassed);
5801 QVERIFY(s2->enteredPassed);
5802}
5803
5804// QTBUG-25958
5805void tst_QStateMachine::multiTargetTransitionInsideParallelStateGroup()
5806{
5807 // TODO QTBUG-25958 was reopened, see https://codereview.qt-project.org/89775
5808 return;
5809
5810 QStateMachine machine;
5811 QState *s1 = new QState(&machine);
5812 DEFINE_ACTIVE_SPY(s1);
5813 machine.setInitialState(s1);
5814
5815 QState *s2 = new QState(QState::ParallelStates, &machine);
5816 DEFINE_ACTIVE_SPY(s2);
5817
5818 QState *s21 = new QState(s2);
5819 DEFINE_ACTIVE_SPY(s21);
5820 QState *s211 = new QState(s21);
5821 DEFINE_ACTIVE_SPY(s211);
5822 QState *s212 = new QState(s21);
5823 DEFINE_ACTIVE_SPY(s212);
5824 s21->setInitialState(s212);
5825
5826 QState *s22 = new QState(s2);
5827 DEFINE_ACTIVE_SPY(s22);
5828 QState *s221 = new QState(s22);
5829 DEFINE_ACTIVE_SPY(s221);
5830 QState *s222 = new QState(s22);
5831 DEFINE_ACTIVE_SPY(s222);
5832 s22->setInitialState(s222);
5833
5834 QAbstractTransition *t1 = new EventTransition(QEvent::User, QList<QAbstractState *>() << s211 << s221);
5835 s1->addTransition(transition: t1);
5836
5837 machine.start();
5838 QTRY_VERIFY(machine.configuration().contains(s1));
5839 TEST_ACTIVE_CHANGED(s1, 1);
5840 TEST_ACTIVE_CHANGED(s2, 0);
5841 TEST_ACTIVE_CHANGED(s21, 0);
5842 TEST_ACTIVE_CHANGED(s211, 0);
5843 TEST_ACTIVE_CHANGED(s212, 0);
5844 TEST_ACTIVE_CHANGED(s22, 0);
5845 TEST_ACTIVE_CHANGED(s221, 0);
5846 TEST_ACTIVE_CHANGED(s222, 0);
5847 machine.postEvent(event: new QEvent(QEvent::User));
5848 QTRY_VERIFY(machine.configuration().contains(s2));
5849 QCOMPARE(machine.configuration().size(), 5);
5850 QVERIFY(machine.configuration().contains(s21));
5851 QVERIFY(machine.configuration().contains(s211));
5852 QVERIFY(machine.configuration().contains(s22));
5853 QVERIFY(machine.configuration().contains(s221));
5854 TEST_ACTIVE_CHANGED(s1, 2);
5855 TEST_ACTIVE_CHANGED(s2, 1);
5856 TEST_ACTIVE_CHANGED(s21, 1);
5857 TEST_ACTIVE_CHANGED(s211, 1);
5858 TEST_ACTIVE_CHANGED(s212, 0);
5859 TEST_ACTIVE_CHANGED(s22, 1);
5860 TEST_ACTIVE_CHANGED(s221, 1);
5861 TEST_ACTIVE_CHANGED(s222, 0);
5862}
5863
5864void tst_QStateMachine::signalTransitionNormalizeSignature()
5865{
5866 QStateMachine machine;
5867 QState *s0 = new QState(&machine);
5868 DEFINE_ACTIVE_SPY(s0);
5869 machine.setInitialState(s0);
5870 QState *s1 = new QState(&machine);
5871 DEFINE_ACTIVE_SPY(s1);
5872 SignalEmitter emitter;
5873 TestSignalTransition *t0 = new TestSignalTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
5874 s0->addTransition(transition: t0);
5875
5876 machine.start();
5877 TEST_ACTIVE_CHANGED(s0, 1);
5878 TEST_ACTIVE_CHANGED(s1, 0);
5879 QTRY_VERIFY(machine.configuration().contains(s0));
5880 emitter.emitSignalWithNoArg();
5881 QTRY_VERIFY(machine.configuration().contains(s1));
5882
5883 QCOMPARE(t0->eventTestSenderReceived(), (QObject*)&emitter);
5884 QCOMPARE(t0->eventTestSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithNoArg()"));
5885 QCOMPARE(t0->eventTestArgumentsReceived().size(), 0);
5886 QCOMPARE(t0->transitionSenderReceived(), (QObject*)&emitter);
5887 QCOMPARE(t0->transitionSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithNoArg()"));
5888 QCOMPARE(t0->transitionArgumentsReceived().size(), 0);
5889 TEST_ACTIVE_CHANGED(s0, 2);
5890 TEST_ACTIVE_CHANGED(s1, 1);
5891}
5892
5893#ifdef Q_COMPILER_DELEGATING_CONSTRUCTORS
5894void tst_QStateMachine::createPointerToMemberSignalTransition()
5895{
5896 QStateMachine machine;
5897 QState *s1 = new QState(&machine);
5898 DEFINE_ACTIVE_SPY(s1);
5899 machine.setInitialState(s1);
5900 machine.start();
5901 TEST_ACTIVE_CHANGED(s1, 1);
5902 QTRY_VERIFY(machine.configuration().contains(s1));
5903
5904 QState *s2 = new QState(&machine);
5905 DEFINE_ACTIVE_SPY(s2);
5906 SignalEmitter emitter;
5907 QSignalTransition *t1 = new QSignalTransition(&emitter, &SignalEmitter::signalWithNoArg, s1);
5908 QCOMPARE(t1->sourceState(), s1);
5909 t1->setTargetState(s2);
5910 s1->addTransition(transition: t1);
5911 emitter.emitSignalWithNoArg();
5912 TEST_ACTIVE_CHANGED(s1, 2);
5913 TEST_ACTIVE_CHANGED(s2, 1);
5914 QTRY_VERIFY(machine.configuration().contains(s2));
5915}
5916#endif
5917
5918void tst_QStateMachine::createSignalTransitionWhenRunning()
5919{
5920 QStateMachine machine;
5921 QState *s1 = new QState(&machine);
5922 DEFINE_ACTIVE_SPY(s1);
5923 machine.setInitialState(s1);
5924 machine.start();
5925 TEST_ACTIVE_CHANGED(s1, 1);
5926 QTRY_VERIFY(machine.configuration().contains(s1));
5927 // Create by addTransition()
5928 QState *s2 = new QState(&machine);
5929 DEFINE_ACTIVE_SPY(s2);
5930 SignalEmitter emitter;
5931 QAbstractTransition *t1 = s1->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s2);
5932 QCOMPARE(t1->sourceState(), s1);
5933 emitter.emitSignalWithNoArg();
5934 TEST_ACTIVE_CHANGED(s1, 2);
5935 TEST_ACTIVE_CHANGED(s2, 1);
5936 QTRY_VERIFY(machine.configuration().contains(s2));
5937
5938 // Create by constructor that takes sender, signal, source (parent) state
5939 QState *s3 = new QState(&machine);
5940 DEFINE_ACTIVE_SPY(s3);
5941 QSignalTransition *t2 = new QSignalTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
5942 QCOMPARE(t2->sourceState(), s2);
5943 t2->setTargetState(s3);
5944 emitter.emitSignalWithNoArg();
5945 TEST_ACTIVE_CHANGED(s1, 2);
5946 TEST_ACTIVE_CHANGED(s2, 2);
5947 TEST_ACTIVE_CHANGED(s3, 1);
5948 QTRY_VERIFY(machine.configuration().contains(s3));
5949
5950 // Create by constructor that takes source (parent) state
5951 QState *s4 = new QState(&machine);
5952 DEFINE_ACTIVE_SPY(s4);
5953 QSignalTransition *t3 = new QSignalTransition(s3);
5954 QCOMPARE(t3->sourceState(), s3);
5955 t3->setSenderObject(&emitter);
5956 t3->setSignal(SIGNAL(signalWithNoArg()));
5957 t3->setTargetState(s4);
5958 emitter.emitSignalWithNoArg();
5959 TEST_ACTIVE_CHANGED(s1, 2);
5960 TEST_ACTIVE_CHANGED(s2, 2);
5961 TEST_ACTIVE_CHANGED(s3, 2);
5962 TEST_ACTIVE_CHANGED(s4, 1);
5963 QTRY_VERIFY(machine.configuration().contains(s4));
5964
5965 // Create by constructor without parent, then set the parent
5966 QState *s5 = new QState(&machine);
5967 DEFINE_ACTIVE_SPY(s5);
5968 QSignalTransition *t4 = new QSignalTransition();
5969 t4->setSenderObject(&emitter);
5970 t4->setParent(s4);
5971 QCOMPARE(t4->sourceState(), s4);
5972 t4->setSignal(SIGNAL(signalWithNoArg()));
5973 t4->setTargetState(s5);
5974 emitter.emitSignalWithNoArg();
5975 TEST_ACTIVE_CHANGED(s1, 2);
5976 TEST_ACTIVE_CHANGED(s2, 2);
5977 TEST_ACTIVE_CHANGED(s3, 2);
5978 TEST_ACTIVE_CHANGED(s4, 2);
5979 TEST_ACTIVE_CHANGED(s5, 1);
5980 QTRY_VERIFY(machine.configuration().contains(s5));
5981}
5982
5983void tst_QStateMachine::createEventTransitionWhenRunning()
5984{
5985 QStateMachine machine;
5986 QState *s1 = new QState(&machine);
5987 DEFINE_ACTIVE_SPY(s1);
5988 machine.setInitialState(s1);
5989 machine.start();
5990 TEST_ACTIVE_CHANGED(s1, 1);
5991 QTRY_VERIFY(machine.configuration().contains(s1));
5992
5993 // Create by constructor that takes event source, type, source (parent) state
5994 QState *s2 = new QState(&machine);
5995 DEFINE_ACTIVE_SPY(s2);
5996 QObject object;
5997 QEventTransition *t1 = new QEventTransition(&object, QEvent::Timer, s1);
5998 QCOMPARE(t1->sourceState(), s1);
5999 t1->setTargetState(s2);
6000
6001 object.startTimer(interval: 10); // Will cause QEvent::Timer to fire every 10ms
6002 QTRY_VERIFY(machine.configuration().contains(s2));
6003
6004 // Create by constructor that takes source (parent) state
6005 QState *s3 = new QState(&machine);
6006 DEFINE_ACTIVE_SPY(s3);
6007 QEventTransition *t2 = new QEventTransition(s2);
6008 QCOMPARE(t2->sourceState(), s2);
6009 t2->setEventSource(&object);
6010 t2->setEventType(QEvent::Timer);
6011 t2->setTargetState(s3);
6012 QTRY_VERIFY(machine.configuration().contains(s3));
6013
6014 // Create by constructor without parent, then set the parent
6015 QState *s4 = new QState(&machine);
6016 DEFINE_ACTIVE_SPY(s4);
6017 QEventTransition *t3 = new QEventTransition();
6018 t3->setEventSource(&object);
6019 t3->setParent(s3);
6020 QCOMPARE(t3->sourceState(), s3);
6021 t3->setEventType(QEvent::Timer);
6022 t3->setTargetState(s4);
6023 QTRY_VERIFY(machine.configuration().contains(s4));
6024 TEST_ACTIVE_CHANGED(s1, 2);
6025 TEST_ACTIVE_CHANGED(s2, 2);
6026 TEST_ACTIVE_CHANGED(s3, 2);
6027 TEST_ACTIVE_CHANGED(s4, 1);
6028}
6029
6030class SignalEmitterThread : public QThread
6031{
6032 Q_OBJECT
6033public:
6034 SignalEmitterThread(QObject *parent = 0)
6035 : QThread(parent)
6036 {
6037 moveToThread(thread: this);
6038 }
6039
6040Q_SIGNALS:
6041 void signal1();
6042 void signal2();
6043
6044public Q_SLOTS:
6045 void emitSignals()
6046 {
6047 emit signal1();
6048 emit signal2();
6049 }
6050};
6051
6052// QTBUG-19789
6053void tst_QStateMachine::signalTransitionSenderInDifferentThread()
6054{
6055 QStateMachine machine;
6056 QState *s1 = new QState(&machine);
6057 DEFINE_ACTIVE_SPY(s1);
6058 machine.setInitialState(s1);
6059
6060 SignalEmitterThread thread;
6061 QState *s2 = new QState(&machine);
6062 DEFINE_ACTIVE_SPY(s2);
6063 s1->addTransition(sender: &thread, SIGNAL(signal1()), target: s2);
6064
6065 QFinalState *s3 = new QFinalState(&machine);
6066 s2->addTransition(sender: &thread, SIGNAL(signal2()), target: s3);
6067
6068 thread.start();
6069 QTRY_VERIFY(thread.isRunning());
6070
6071 machine.start();
6072 TEST_ACTIVE_CHANGED(s1, 1);
6073 TEST_ACTIVE_CHANGED(s2, 0);
6074 QTRY_VERIFY(machine.configuration().contains(s1));
6075
6076 QMetaObject::invokeMethod(obj: &thread, member: "emitSignals");
6077 // thread emits both signal1() and signal2(), so we should end in s3
6078 TEST_ACTIVE_CHANGED(s1, 2);
6079 TEST_ACTIVE_CHANGED(s2, 2);
6080 QTRY_VERIFY(!machine.isRunning());
6081 QTRY_VERIFY(machine.configuration().contains(s3));
6082
6083 // Run the machine again; transitions should still be registered
6084 machine.start();
6085 TEST_ACTIVE_CHANGED(s1, 3);
6086 TEST_ACTIVE_CHANGED(s2, 2);
6087 QTRY_VERIFY(machine.configuration().contains(s1));
6088 QMetaObject::invokeMethod(obj: &thread, member: "emitSignals");
6089 QTRY_VERIFY(machine.configuration().contains(s3));
6090
6091 thread.quit();
6092 QTRY_VERIFY(thread.wait());
6093 TEST_ACTIVE_CHANGED(s1, 4);
6094 TEST_ACTIVE_CHANGED(s2, 4);
6095 QVERIFY(!machine.isRunning());
6096}
6097
6098void tst_QStateMachine::signalTransitionSenderInDifferentThread2()
6099{
6100 QStateMachine machine;
6101 QState *s1 = new QState(&machine);
6102 DEFINE_ACTIVE_SPY(s1);
6103 machine.setInitialState(s1);
6104
6105 QState *s2 = new QState(&machine);
6106 DEFINE_ACTIVE_SPY(s2);
6107 SignalEmitter emitter;
6108 // At the time of the transition creation, the machine and the emitter
6109 // are both in the same thread.
6110 s1->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s2);
6111
6112 QFinalState *s3 = new QFinalState(&machine);
6113 s2->addTransition(sender: &emitter, SIGNAL(signalWithDefaultArg()), target: s3);
6114
6115 QThread thread;
6116 // Move the machine and its states to a secondary thread, but let the
6117 // SignalEmitter stay in the main thread.
6118 machine.moveToThread(thread: &thread);
6119
6120 thread.start();
6121 QTRY_VERIFY(thread.isRunning());
6122
6123 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
6124 QVERIFY(runningSpy.isValid());
6125 QSignalSpy startedSpy(&machine, &QStateMachine::started);
6126 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
6127 machine.start();
6128 QTRY_COMPARE(startedSpy.count(), 1);
6129 TEST_RUNNING_CHANGED(true);
6130
6131 emitter.emitSignalWithNoArg();
6132 // The second emission should not get "lost".
6133 emitter.emitSignalWithDefaultArg();
6134 QTRY_COMPARE(finishedSpy.count(), 1);
6135 TEST_RUNNING_CHANGED(false);
6136
6137 thread.quit();
6138 QTRY_VERIFY(thread.wait());
6139 TEST_ACTIVE_CHANGED(s1, 2);
6140 TEST_ACTIVE_CHANGED(s2, 2);
6141}
6142
6143class SignalTransitionMutatorThread : public QThread
6144{
6145public:
6146 SignalTransitionMutatorThread(QSignalTransition *transition)
6147 : m_transition(transition)
6148 {}
6149 void run()
6150 {
6151 // Cause repeated registration and unregistration
6152 for (int i = 0; i < 50000; ++i) {
6153 m_transition->setSenderObject(this);
6154 m_transition->setSenderObject(0);
6155 }
6156 }
6157private:
6158 QSignalTransition *m_transition;
6159};
6160
6161// Should not crash:
6162void tst_QStateMachine::signalTransitionRegistrationThreadSafety()
6163{
6164 QStateMachine machine;
6165 QState *s1 = new QState(&machine);
6166 DEFINE_ACTIVE_SPY(s1);
6167 machine.setInitialState(s1);
6168 machine.start();
6169 QTRY_VERIFY(machine.configuration().contains(s1));
6170
6171 QSignalTransition *t1 = new QSignalTransition();
6172 t1->setSignal(SIGNAL(objectNameChanged(QString)));
6173 s1->addTransition(transition: t1);
6174
6175 QSignalTransition *t2 = new QSignalTransition();
6176 t2->setSignal(SIGNAL(objectNameChanged(QString)));
6177 s1->addTransition(transition: t2);
6178
6179 SignalTransitionMutatorThread thread(t1);
6180 thread.start();
6181 QTRY_VERIFY(thread.isRunning());
6182
6183 // Cause repeated registration and unregistration
6184 for (int i = 0; i < 50000; ++i) {
6185 t2->setSenderObject(this);
6186 t2->setSenderObject(0);
6187 }
6188
6189 thread.quit();
6190 QTRY_VERIFY(thread.wait());
6191 TEST_ACTIVE_CHANGED(s1, 1);
6192 QVERIFY(machine.isRunning());
6193}
6194
6195void tst_QStateMachine::childModeConstructor()
6196{
6197 {
6198 QStateMachine machine(QState::ExclusiveStates);
6199 QCOMPARE(machine.childMode(), QState::ExclusiveStates);
6200 QVERIFY(!machine.parent());
6201 QVERIFY(!machine.parentState());
6202 }
6203 {
6204 QStateMachine machine(QState::ParallelStates);
6205 QCOMPARE(machine.childMode(), QState::ParallelStates);
6206 QVERIFY(!machine.parent());
6207 QVERIFY(!machine.parentState());
6208 }
6209 {
6210 QStateMachine machine(QState::ExclusiveStates, this);
6211 QCOMPARE(machine.childMode(), QState::ExclusiveStates);
6212 QCOMPARE(machine.parent(), static_cast<QObject *>(this));
6213 QVERIFY(!machine.parentState());
6214 }
6215 {
6216 QStateMachine machine(QState::ParallelStates, this);
6217 QCOMPARE(machine.childMode(), QState::ParallelStates);
6218 QCOMPARE(machine.parent(), static_cast<QObject *>(this));
6219 QVERIFY(!machine.parentState());
6220 }
6221 QState state;
6222 {
6223 QStateMachine machine(QState::ExclusiveStates, &state);
6224 QCOMPARE(machine.childMode(), QState::ExclusiveStates);
6225 QCOMPARE(machine.parent(), static_cast<QObject *>(&state));
6226 QCOMPARE(machine.parentState(), &state);
6227 }
6228 {
6229 QStateMachine machine(QState::ParallelStates, &state);
6230 QCOMPARE(machine.childMode(), QState::ParallelStates);
6231 QCOMPARE(machine.parent(), static_cast<QObject *>(&state));
6232 QCOMPARE(machine.parentState(), &state);
6233 }
6234}
6235
6236void tst_QStateMachine::qtbug_44963()
6237{
6238 SignalEmitter emitter;
6239
6240 QStateMachine machine;
6241 QState a(QState::ParallelStates, &machine);
6242 QHistoryState ha(QHistoryState::DeepHistory, &a);
6243 QState b(QState::ParallelStates, &a);
6244 QState c(QState::ParallelStates, &b);
6245 QState d(QState::ParallelStates, &c);
6246 QState e(QState::ParallelStates, &d);
6247 QState i(&e);
6248 QState i1(&i);
6249 QState i2(&i);
6250 QState j(&e);
6251 QState h(&d);
6252 QState g(&c);
6253 QState k(&a);
6254 QState l(&machine);
6255
6256 machine.setInitialState(&a);
6257 ha.setDefaultState(&b);
6258 i.setInitialState(&i1);
6259 i1.addTransition(sender: &emitter, SIGNAL(signalWithIntArg(int)), target: &i2)->setObjectName("i1->i2");
6260 i2.addTransition(sender: &emitter, SIGNAL(signalWithDefaultArg(int)), target: &l)->setObjectName("i2->l");
6261 l.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &ha)->setObjectName("l->ha");
6262
6263 a.setObjectName("a");
6264 ha.setObjectName("ha");
6265 b.setObjectName("b");
6266 c.setObjectName("c");
6267 d.setObjectName("d");
6268 e.setObjectName("e");
6269 i.setObjectName("i");
6270 i1.setObjectName("i1");
6271 i2.setObjectName("i2");
6272 j.setObjectName("j");
6273 h.setObjectName("h");
6274 g.setObjectName("g");
6275 k.setObjectName("k");
6276 l.setObjectName("l");
6277
6278 machine.start();
6279
6280 QTRY_COMPARE(machine.configuration().contains(&i1), true);
6281 QTRY_COMPARE(machine.configuration().contains(&i2), false);
6282 QTRY_COMPARE(machine.configuration().contains(&j), true);
6283 QTRY_COMPARE(machine.configuration().contains(&h), true);
6284 QTRY_COMPARE(machine.configuration().contains(&g), true);
6285 QTRY_COMPARE(machine.configuration().contains(&k), true);
6286 QTRY_COMPARE(machine.configuration().contains(&l), false);
6287
6288 emitter.emitSignalWithIntArg(arg: 0);
6289
6290 QTRY_COMPARE(machine.configuration().contains(&i1), false);
6291 QTRY_COMPARE(machine.configuration().contains(&i2), true);
6292 QTRY_COMPARE(machine.configuration().contains(&j), true);
6293 QTRY_COMPARE(machine.configuration().contains(&h), true);
6294 QTRY_COMPARE(machine.configuration().contains(&g), true);
6295 QTRY_COMPARE(machine.configuration().contains(&k), true);
6296 QTRY_COMPARE(machine.configuration().contains(&l), false);
6297
6298 emitter.emitSignalWithDefaultArg();
6299
6300 QTRY_COMPARE(machine.configuration().contains(&i1), false);
6301 QTRY_COMPARE(machine.configuration().contains(&i2), false);
6302 QTRY_COMPARE(machine.configuration().contains(&j), false);
6303 QTRY_COMPARE(machine.configuration().contains(&h), false);
6304 QTRY_COMPARE(machine.configuration().contains(&g), false);
6305 QTRY_COMPARE(machine.configuration().contains(&k), false);
6306 QTRY_COMPARE(machine.configuration().contains(&l), true);
6307
6308 emitter.emitSignalWithNoArg();
6309
6310 QTRY_COMPARE(machine.configuration().contains(&i1), false);
6311 QTRY_COMPARE(machine.configuration().contains(&i2), true);
6312 QTRY_COMPARE(machine.configuration().contains(&j), true);
6313 QTRY_COMPARE(machine.configuration().contains(&h), true);
6314 QTRY_COMPARE(machine.configuration().contains(&g), true);
6315 QTRY_COMPARE(machine.configuration().contains(&k), true);
6316 QTRY_COMPARE(machine.configuration().contains(&l), false);
6317
6318 QVERIFY(machine.isRunning());
6319}
6320
6321void tst_QStateMachine::qtbug_44783()
6322{
6323 SignalEmitter emitter;
6324
6325 QStateMachine machine;
6326 QState s(&machine);
6327 QState p(QState::ParallelStates, &s);
6328 QState p1(&p);
6329 QState p1_1(&p1);
6330 QState p1_2(&p1);
6331 QState p2(&p);
6332 QState s1(&machine);
6333
6334 machine.setInitialState(&s);
6335 s.setInitialState(&p);
6336 p1.setInitialState(&p1_1);
6337 p1_1.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &p1_2)->setObjectName("p1_1->p1_2");
6338 p2.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &s1)->setObjectName("p2->s1");
6339
6340 s.setObjectName("s");
6341 p.setObjectName("p");
6342 p1.setObjectName("p1");
6343 p1_1.setObjectName("p1_1");
6344 p1_2.setObjectName("p1_2");
6345 p2.setObjectName("p2");
6346 s1.setObjectName("s1");
6347
6348 machine.start();
6349
6350 QTRY_COMPARE(machine.configuration().contains(&s), true);
6351 QTRY_COMPARE(machine.configuration().contains(&p), true);
6352 QTRY_COMPARE(machine.configuration().contains(&p1), true);
6353 QTRY_COMPARE(machine.configuration().contains(&p1_1), true);
6354 QTRY_COMPARE(machine.configuration().contains(&p1_2), false);
6355 QTRY_COMPARE(machine.configuration().contains(&p2), true);
6356 QTRY_COMPARE(machine.configuration().contains(&s1), false);
6357
6358 emitter.emitSignalWithNoArg();
6359
6360 // Only one of the following two can be true, because the two possible transitions conflict.
6361 if (machine.configuration().contains(value: &s1)) {
6362 // the transition p2 -> s1 was taken, not p1_1 -> p1_2, so:
6363 // the parallel state exited, so none of the states inside it are active
6364 QTRY_COMPARE(machine.configuration().contains(&s), false);
6365 QTRY_COMPARE(machine.configuration().contains(&p), false);
6366 QTRY_COMPARE(machine.configuration().contains(&p1), false);
6367 QTRY_COMPARE(machine.configuration().contains(&p1_1), false);
6368 QTRY_COMPARE(machine.configuration().contains(&p1_2), false);
6369 QTRY_COMPARE(machine.configuration().contains(&p2), false);
6370 } else {
6371 // the transition p1_1 -> p1_2 was taken, not p2 -> s1, so:
6372 // the parallel state was not exited and the state is the same as the start state with one
6373 // difference: p1_1 inactive and p1_2 active:
6374 QTRY_COMPARE(machine.configuration().contains(&s), true);
6375 QTRY_COMPARE(machine.configuration().contains(&p), true);
6376 QTRY_COMPARE(machine.configuration().contains(&p1), true);
6377 QTRY_COMPARE(machine.configuration().contains(&p1_1), false);
6378 QTRY_COMPARE(machine.configuration().contains(&p1_2), true);
6379 QTRY_COMPARE(machine.configuration().contains(&p2), true);
6380 }
6381
6382 QVERIFY(machine.isRunning());
6383}
6384
6385void tst_QStateMachine::internalTransition()
6386{
6387 SignalEmitter emitter;
6388
6389 QStateMachine machine;
6390 QState *s = new QState(&machine);
6391 QState *s1 = new QState(s);
6392 QState *s11 = new QState(s1);
6393
6394 DEFINE_ACTIVE_SPY(s);
6395 DEFINE_ACTIVE_SPY(s1);
6396 DEFINE_ACTIVE_SPY(s11);
6397
6398 machine.setInitialState(s);
6399 s->setInitialState(s1);
6400 s1->setInitialState(s11);
6401 QSignalTransition *t = s1->addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: s11);
6402 t->setObjectName("s1->s11");
6403 t->setTransitionType(QAbstractTransition::InternalTransition);
6404
6405 s->setObjectName("s");
6406 s1->setObjectName("s1");
6407 s11->setObjectName("s11");
6408
6409 machine.start();
6410
6411 QTRY_COMPARE(machine.configuration().contains(s), true);
6412 QTRY_COMPARE(machine.configuration().contains(s1), true);
6413 QTRY_COMPARE(machine.configuration().contains(s11), true);
6414 TEST_ACTIVE_CHANGED(s, 1);
6415 TEST_ACTIVE_CHANGED(s1, 1);
6416 TEST_ACTIVE_CHANGED(s11, 1);
6417
6418 emitter.emitSignalWithNoArg();
6419
6420 QTRY_COMPARE(machine.configuration().contains(s), true);
6421 QTRY_COMPARE(machine.configuration().contains(s1), true);
6422 QTRY_COMPARE(machine.configuration().contains(s11), true);
6423 TEST_ACTIVE_CHANGED(s11, 3);
6424 TEST_ACTIVE_CHANGED(s1, 1); // external transitions will return 3, internal transitions should return 1.
6425 TEST_ACTIVE_CHANGED(s, 1);
6426}
6427
6428void tst_QStateMachine::conflictingTransition()
6429{
6430 SignalEmitter emitter;
6431
6432 QStateMachine machine;
6433 QState b(QState::ParallelStates, &machine);
6434 QState c(&b);
6435 QState d(QState::ParallelStates, &b);
6436 QState e(&d);
6437 QState e1(&e);
6438 QState e2(&e);
6439 QState f(&d);
6440 QState f1(&f);
6441 QState f2(&f);
6442 QState a1(&machine);
6443
6444 machine.setInitialState(&b);
6445 e.setInitialState(&e1);
6446 f.setInitialState(&f1);
6447 c.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &a1)->setObjectName("c->a1");
6448 e1.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &e2)->setObjectName("e1->e2");
6449 f1.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &f2)->setObjectName("f1->f2");
6450
6451 b.setObjectName("b");
6452 c.setObjectName("c");
6453 d.setObjectName("d");
6454 e.setObjectName("e");
6455 e1.setObjectName("e1");
6456 e2.setObjectName("e2");
6457 f.setObjectName("f");
6458 f1.setObjectName("f1");
6459 f2.setObjectName("f2");
6460 a1.setObjectName("a1");
6461
6462 machine.start();
6463
6464 QTRY_COMPARE(machine.configuration().contains(&b), true);
6465 QTRY_COMPARE(machine.configuration().contains(&c), true);
6466 QTRY_COMPARE(machine.configuration().contains(&d), true);
6467 QTRY_COMPARE(machine.configuration().contains(&e), true);
6468 QTRY_COMPARE(machine.configuration().contains(&e1), true);
6469 QTRY_COMPARE(machine.configuration().contains(&e2), false);
6470 QTRY_COMPARE(machine.configuration().contains(&f), true);
6471 QTRY_COMPARE(machine.configuration().contains(&f1), true);
6472 QTRY_COMPARE(machine.configuration().contains(&f2), false);
6473 QTRY_COMPARE(machine.configuration().contains(&a1), false);
6474
6475 emitter.emitSignalWithNoArg();
6476
6477 QTRY_COMPARE(machine.configuration().contains(&b), true);
6478 QTRY_COMPARE(machine.configuration().contains(&c), true);
6479 QTRY_COMPARE(machine.configuration().contains(&d), true);
6480 QTRY_COMPARE(machine.configuration().contains(&e), true);
6481 QTRY_COMPARE(machine.configuration().contains(&e1), false);
6482 QTRY_COMPARE(machine.configuration().contains(&e2), true);
6483 QTRY_COMPARE(machine.configuration().contains(&f), true);
6484 QTRY_COMPARE(machine.configuration().contains(&f1), false);
6485 QTRY_COMPARE(machine.configuration().contains(&f2), true);
6486 QTRY_COMPARE(machine.configuration().contains(&a1), false);
6487
6488 QVERIFY(machine.isRunning());
6489}
6490
6491void tst_QStateMachine::conflictingTransition2()
6492{
6493 SignalEmitter emitter;
6494
6495 QStateMachine machine;
6496 QState s0(&machine);
6497 QState p0(QState::ParallelStates, &s0);
6498 QState p0s1(&p0);
6499 QState p0s2(&p0);
6500 QState p0s3(&p0);
6501 QState s1(&machine);
6502
6503 machine.setInitialState(&s0);
6504 s0.setInitialState(&p0);
6505
6506 QSignalTransition *t1 = new QSignalTransition(&emitter, SIGNAL(signalWithNoArg()));
6507 p0s1.addTransition(transition: t1);
6508 QSignalTransition *t2 = p0s2.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &p0s1);
6509 QSignalTransition *t3 = p0s3.addTransition(sender: &emitter, SIGNAL(signalWithNoArg()), target: &s1);
6510 QSignalSpy t1Spy(t1, &QAbstractTransition::triggered);
6511 QSignalSpy t2Spy(t2, &QAbstractTransition::triggered);
6512 QSignalSpy t3Spy(t3, &QAbstractTransition::triggered);
6513 QVERIFY(t1Spy.isValid());
6514 QVERIFY(t2Spy.isValid());
6515 QVERIFY(t3Spy.isValid());
6516
6517 s0.setObjectName("s0");
6518 p0.setObjectName("p0");
6519 p0s1.setObjectName("p0s1");
6520 p0s2.setObjectName("p0s2");
6521 p0s3.setObjectName("p0s3");
6522 s1.setObjectName("s1");
6523 t1->setObjectName("p0s1->p0s1");
6524 t2->setObjectName("p0s2->p0s1");
6525 t3->setObjectName("p0s3->s1");
6526
6527 machine.start();
6528
6529 QTRY_COMPARE(machine.configuration().contains(&s0), true);
6530 QTRY_COMPARE(machine.configuration().contains(&p0), true);
6531 QTRY_COMPARE(machine.configuration().contains(&p0s1), true);
6532 QTRY_COMPARE(machine.configuration().contains(&p0s2), true);
6533 QTRY_COMPARE(machine.configuration().contains(&p0s3), true);
6534 QTRY_COMPARE(machine.configuration().contains(&s1), false);
6535
6536 QCOMPARE(t1Spy.count(), 0);
6537 QCOMPARE(t2Spy.count(), 0);
6538 QCOMPARE(t3Spy.count(), 0);
6539
6540 emitter.emitSignalWithNoArg();
6541
6542 QTRY_COMPARE(machine.configuration().contains(&s0), true);
6543 QTRY_COMPARE(machine.configuration().contains(&p0), true);
6544 QTRY_COMPARE(machine.configuration().contains(&p0s1), true);
6545 QTRY_COMPARE(machine.configuration().contains(&p0s2), true);
6546 QTRY_COMPARE(machine.configuration().contains(&p0s3), true);
6547 QTRY_COMPARE(machine.configuration().contains(&s1), false);
6548
6549 QCOMPARE(t1Spy.count(), 1);
6550 QCOMPARE(t2Spy.count(), 1);
6551 QCOMPARE(t3Spy.count(), 0); // t3 got preempted by t2
6552
6553 QVERIFY(machine.isRunning());
6554}
6555
6556void tst_QStateMachine::qtbug_46059()
6557{
6558 QStateMachine machine;
6559 QState a(&machine);
6560 QState b(&a);
6561 QState c(&a);
6562 QState success(&a);
6563 QState failure(&machine);
6564
6565 machine.setInitialState(&a);
6566 a.setInitialState(&b);
6567 b.addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), &c));
6568 c.addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 2), &success));
6569 b.addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 2), &failure));
6570
6571 machine.start();
6572 QCoreApplication::processEvents();
6573
6574 QTRY_COMPARE(machine.configuration().contains(&a), true);
6575 QTRY_COMPARE(machine.configuration().contains(&b), true);
6576 QTRY_COMPARE(machine.configuration().contains(&c), false);
6577 QTRY_COMPARE(machine.configuration().contains(&failure), false);
6578 QTRY_COMPARE(machine.configuration().contains(&success), false);
6579
6580 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 0)), priority: QStateMachine::HighPriority);
6581 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)), priority: QStateMachine::HighPriority);
6582 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 2)), priority: QStateMachine::NormalPriority);
6583 QCoreApplication::processEvents();
6584
6585 QTRY_COMPARE(machine.configuration().contains(&a), true);
6586 QTRY_COMPARE(machine.configuration().contains(&b), false);
6587 QTRY_COMPARE(machine.configuration().contains(&c), false);
6588 QTRY_COMPARE(machine.configuration().contains(&failure), false);
6589 QTRY_COMPARE(machine.configuration().contains(&success), true);
6590
6591 QVERIFY(machine.isRunning());
6592}
6593
6594void tst_QStateMachine::qtbug_46703()
6595{
6596 QStateMachine machine;
6597 QState root(&machine);
6598 QHistoryState h(&root);
6599 QState p(QState::ParallelStates, &root);
6600 QState a(&p);
6601 QState a1(&a);
6602 QState a2(&a);
6603 QState a3(&a);
6604 QState b(&p);
6605 QState b1(&b);
6606 QState b2(&b);
6607
6608 machine.setObjectName("machine");
6609 root.setObjectName("root");
6610 h.setObjectName("h");
6611 p.setObjectName("p");
6612 a.setObjectName("a");
6613 a1.setObjectName("a1");
6614 a2.setObjectName("a2");
6615 a3.setObjectName("a3");
6616 b.setObjectName("b");
6617 b1.setObjectName("b1");
6618 b2.setObjectName("b2");
6619
6620 machine.setInitialState(&root);
6621 root.setInitialState(&h);
6622 a.setInitialState(&a3);
6623 b.setInitialState(&b1);
6624 struct : public QAbstractTransition {
6625 virtual bool eventTest(QEvent *) { return false; }
6626 virtual void onTransition(QEvent *) {}
6627 } defaultTransition;
6628 defaultTransition.setTargetStates(QList<QAbstractState*>() << &a2 << &b2);
6629 h.setDefaultTransition(&defaultTransition);
6630
6631 machine.start();
6632 QCoreApplication::processEvents();
6633
6634 QTRY_COMPARE(machine.configuration().contains(&root), true);
6635 QTRY_COMPARE(machine.configuration().contains(&h), false);
6636 QTRY_COMPARE(machine.configuration().contains(&p), true);
6637 QTRY_COMPARE(machine.configuration().contains(&a), true);
6638 QTRY_COMPARE(machine.configuration().contains(&a1), false);
6639 QTRY_COMPARE(machine.configuration().contains(&a2), true);
6640 QTRY_COMPARE(machine.configuration().contains(&a3), false);
6641 QTRY_COMPARE(machine.configuration().contains(&b), true);
6642 QTRY_COMPARE(machine.configuration().contains(&b1), false);
6643 QTRY_COMPARE(machine.configuration().contains(&b2), true);
6644
6645 QVERIFY(machine.isRunning());
6646}
6647
6648void tst_QStateMachine::postEventFromBeginSelectTransitions()
6649{
6650 class StateMachine : public QStateMachine {
6651 protected:
6652 void beginSelectTransitions(QEvent* e) override {
6653 if (e->type() == QEvent::Type(QEvent::User + 2))
6654 postEvent(event: new QEvent(QEvent::Type(QEvent::User + 1)), priority: QStateMachine::HighPriority);
6655 }
6656 } machine;
6657 QState a(&machine);
6658 QState success(&machine);
6659
6660 machine.setInitialState(&a);
6661 a.addTransition(transition: new EventTransition(QEvent::Type(QEvent::User + 1), &success));
6662
6663 machine.start();
6664
6665 QTRY_COMPARE(machine.configuration().contains(&a), true);
6666 QTRY_COMPARE(machine.configuration().contains(&success), false);
6667
6668 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User + 2)), priority: QStateMachine::NormalPriority);
6669
6670 QTRY_COMPARE(machine.configuration().contains(&a), false);
6671 QTRY_COMPARE(machine.configuration().contains(&success), true);
6672
6673 QVERIFY(machine.isRunning());
6674}
6675
6676void tst_QStateMachine::dontProcessSlotsWhenMachineIsNotRunning()
6677{
6678 QStateMachine machine;
6679 QState initialState;
6680 QFinalState finalState;
6681
6682 struct Emitter : SignalEmitter
6683 {
6684 QThread thread;
6685 Emitter(QObject *parent = nullptr) : SignalEmitter(parent)
6686 {
6687 moveToThread(thread: &thread);
6688 thread.start();
6689 }
6690 } emitter;
6691
6692 initialState.addTransition(obj: &emitter, signal: &Emitter::signalWithNoArg, target: &finalState);
6693 machine.addState(state: &initialState);
6694 machine.addState(state: &finalState);
6695 machine.setInitialState(&initialState);
6696 connect(sender: &machine, signal: &QStateMachine::started, context: &emitter, slot: [&]() {
6697 metaObject()->invokeMethod(obj: &emitter, member: "emitSignalWithNoArg");
6698 metaObject()->invokeMethod(obj: &emitter, member: "emitSignalWithNoArg");
6699 });
6700 connect(sender: &machine, signal: &QStateMachine::finished, receiver: &emitter.thread, slot: &QThread::quit);
6701 machine.start();
6702 QSignalSpy emittedSpy(&emitter, &SignalEmitter::signalWithNoArg);
6703 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
6704 QTRY_COMPARE_WITH_TIMEOUT(emittedSpy.count(), 2, 100);
6705 QTRY_COMPARE(finishedSpy.count(), 1);
6706 QTRY_VERIFY(emitter.thread.isFinished());
6707}
6708
6709void tst_QStateMachine::cancelDelayedEventWithChrono()
6710{
6711#if __has_include(<chrono>)
6712 QStateMachine machine;
6713 QTest::ignoreMessage(type: QtWarningMsg,
6714 message: "QStateMachine::cancelDelayedEvent: the machine is not running");
6715 QVERIFY(!machine.cancelDelayedEvent(-1));
6716
6717 QState *s1 = new QState(&machine);
6718 DEFINE_ACTIVE_SPY(s1);
6719 QFinalState *s2 = new QFinalState(&machine);
6720 s1->addTransition(transition: new StringTransition("a", s2));
6721 machine.setInitialState(s1);
6722
6723 QSignalSpy startedSpy(&machine, &QStateMachine::started);
6724 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
6725 QVERIFY(startedSpy.isValid());
6726 QVERIFY(runningSpy.isValid());
6727 machine.start();
6728 QTRY_COMPARE(startedSpy.count(), 1);
6729 TEST_RUNNING_CHANGED(true);
6730 TEST_ACTIVE_CHANGED(s1, 1);
6731 QCOMPARE(machine.configuration().size(), 1);
6732 QVERIFY(machine.configuration().contains(s1));
6733 int id1 = machine.postDelayedEvent(event: new StringEvent("c"), delay: std::chrono::seconds{50});
6734 QVERIFY(id1 != -1);
6735 int id2 = machine.postDelayedEvent(event: new StringEvent("b"), delay: std::chrono::seconds{25});
6736 QVERIFY(id2 != -1);
6737 QVERIFY(id2 != id1);
6738 int id3 = machine.postDelayedEvent(event: new StringEvent("a"), delay: std::chrono::milliseconds{100});
6739 QVERIFY(id3 != -1);
6740 QVERIFY(id3 != id2);
6741 QVERIFY(machine.cancelDelayedEvent(id1));
6742 QVERIFY(!machine.cancelDelayedEvent(id1));
6743 QVERIFY(machine.cancelDelayedEvent(id2));
6744 QVERIFY(!machine.cancelDelayedEvent(id2));
6745
6746 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
6747 QVERIFY(finishedSpy.isValid());
6748 QTRY_COMPARE(finishedSpy.count(), 1);
6749 TEST_RUNNING_CHANGED(false);
6750 TEST_ACTIVE_CHANGED(s1, 2);
6751 QCOMPARE(machine.configuration().size(), 1);
6752 QVERIFY(machine.configuration().contains(s2));
6753#endif
6754}
6755
6756void tst_QStateMachine::postDelayedEventWithChronoAndStop()
6757{
6758#if __has_include(<chrono>)
6759 QStateMachine machine;
6760 QState *s1 = new QState(&machine);
6761 DEFINE_ACTIVE_SPY(s1);
6762 QFinalState *s2 = new QFinalState(&machine);
6763 s1->addTransition(transition: new StringTransition("a", s2));
6764 machine.setInitialState(s1);
6765
6766 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
6767 QVERIFY(runningSpy.isValid());
6768 QSignalSpy startedSpy(&machine, &QStateMachine::started);
6769 QVERIFY(startedSpy.isValid());
6770 machine.start();
6771 QTRY_COMPARE(startedSpy.count(), 1);
6772 TEST_RUNNING_CHANGED(true);
6773 TEST_ACTIVE_CHANGED(s1, 1);
6774 QCOMPARE(machine.configuration().size(), 1);
6775 QVERIFY(machine.configuration().contains(s1));
6776
6777 int id1 = machine.postDelayedEvent(event: new StringEvent("a"), delay: std::chrono::milliseconds{0});
6778 QVERIFY(id1 != -1);
6779 QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
6780 QVERIFY(stoppedSpy.isValid());
6781 machine.stop();
6782 QTRY_COMPARE(stoppedSpy.count(), 1);
6783 TEST_RUNNING_CHANGED(false);
6784 TEST_ACTIVE_CHANGED(s1, 1);
6785 QCOMPARE(machine.configuration().size(), 1);
6786 QVERIFY(machine.configuration().contains(s1));
6787
6788 machine.start();
6789 QTRY_COMPARE(startedSpy.count(), 2);
6790 TEST_RUNNING_CHANGED(true);
6791 TEST_ACTIVE_CHANGED(s1, 3);
6792 QCOMPARE(machine.configuration().size(), 1);
6793 QVERIFY(machine.configuration().contains(s1));
6794
6795 int id2 = machine.postDelayedEvent(event: new StringEvent("a"), delay: std::chrono::seconds{1});
6796 QVERIFY(id2 != -1);
6797 machine.stop();
6798 QTRY_COMPARE(stoppedSpy.count(), 2);
6799 TEST_RUNNING_CHANGED(false);
6800 TEST_ACTIVE_CHANGED(s1, 3);
6801 machine.start();
6802 QTRY_COMPARE(startedSpy.count(), 3);
6803 TEST_RUNNING_CHANGED(true);
6804 QTestEventLoop::instance().enterLoop(secs: 2);
6805 QCOMPARE(machine.configuration().size(), 1);
6806 QVERIFY(machine.configuration().contains(s1));
6807 TEST_ACTIVE_CHANGED(s1, 5);
6808 QVERIFY(machine.isRunning());
6809#endif
6810}
6811
6812class DelayedEventWithChronoPosterThread : public QThread
6813{
6814 Q_OBJECT
6815public:
6816 DelayedEventWithChronoPosterThread(QStateMachine *machine, QObject *parent = 0)
6817 : QThread(parent), firstEventWasCancelled(false), m_machine(machine)
6818 {
6819 moveToThread(thread: this);
6820 QObject::connect(sender: m_machine, SIGNAL(started()), receiver: this, SLOT(postEvent()));
6821 }
6822
6823 mutable bool firstEventWasCancelled;
6824
6825private Q_SLOTS:
6826 void postEvent()
6827 {
6828#if __has_include(<chrono>)
6829 int id = m_machine->postDelayedEvent(event: new QEvent(QEvent::User), delay: std::chrono::seconds{1});
6830 firstEventWasCancelled = m_machine->cancelDelayedEvent(id);
6831
6832 m_machine->postDelayedEvent(event: new QEvent(QEvent::User), delay: std::chrono::milliseconds{1});
6833
6834 quit();
6835#endif
6836 }
6837
6838private:
6839 QStateMachine *m_machine;
6840};
6841
6842void tst_QStateMachine::postDelayedEventWithChronoFromThread()
6843{
6844#if __has_include(<chrono>)
6845 QStateMachine machine;
6846 QState *s1 = new QState(&machine);
6847 DEFINE_ACTIVE_SPY(s1);
6848 QFinalState *f = new QFinalState(&machine);
6849 s1->addTransition(transition: new EventTransition(QEvent::User, f));
6850 machine.setInitialState(s1);
6851
6852 DelayedEventWithChronoPosterThread poster(&machine);
6853 poster.start();
6854
6855 QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
6856 QVERIFY(runningSpy.isValid());
6857 QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
6858 QVERIFY(finishedSpy.isValid());
6859 machine.start();
6860 QTRY_COMPARE(finishedSpy.count(), 1);
6861 TEST_RUNNING_CHANGED_STARTED_STOPPED;
6862 TEST_ACTIVE_CHANGED(s1, 2);
6863 QVERIFY(poster.firstEventWasCancelled);
6864#endif
6865}
6866
6867QTEST_MAIN(tst_QStateMachine)
6868#include "tst_qstatemachine.moc"
6869

source code of qtbase/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp