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 module 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
31#include "qstate.h"
32#include "qstatemachine.h"
33#include "qsignaltransition.h"
34
35class tst_QState : public QObject
36{
37 Q_OBJECT
38
39private slots:
40 void assignProperty();
41 void assignPropertyTwice();
42 void historyInitialState();
43 void transitions();
44 void privateSignals();
45 void parallelStateAndInitialState();
46};
47
48class TestClass: public QObject
49{
50 Q_OBJECT
51public:
52 TestClass() : called(false) {}
53 bool called;
54
55public slots:
56 void slot() { called = true; }
57
58
59};
60
61void tst_QState::assignProperty()
62{
63 QStateMachine machine;
64
65 QObject object;
66 object.setProperty(name: "fooBar", value: 10);
67
68 QState *s1 = new QState(&machine);
69 s1->assignProperty(object: &object, name: "fooBar", value: 20);
70
71 machine.setInitialState(s1);
72 machine.start();
73 QCoreApplication::processEvents();
74
75 QCOMPARE(object.property("fooBar").toInt(), 20);
76}
77
78void tst_QState::assignPropertyTwice()
79{
80 QStateMachine machine;
81
82 QObject object;
83 object.setProperty(name: "fooBar", value: 10);
84
85 QState *s1 = new QState(&machine);
86 s1->assignProperty(object: &object, name: "fooBar", value: 20);
87 s1->assignProperty(object: &object, name: "fooBar", value: 30);
88
89 machine.setInitialState(s1);
90 machine.start();
91 QCoreApplication::processEvents();
92
93 QCOMPARE(object.property("fooBar").toInt(), 30);
94}
95
96class EventTestTransition: public QAbstractTransition
97{
98public:
99 EventTestTransition(QEvent::Type type, QState *targetState)
100 : QAbstractTransition(), m_type(type)
101 {
102 setTargetState(targetState);
103 }
104
105protected:
106 bool eventTest(QEvent *e)
107 {
108 return e->type() == m_type;
109 }
110
111 void onTransition(QEvent *) {}
112
113private:
114 QEvent::Type m_type;
115
116};
117
118void tst_QState::historyInitialState()
119{
120 QStateMachine machine;
121
122 QState *s1 = new QState(&machine);
123
124 QState *s2 = new QState(&machine);
125 QHistoryState *h1 = new QHistoryState(s2);
126
127 s2->setInitialState(h1);
128
129 QState *s3 = new QState(s2);
130 h1->setDefaultState(s3);
131
132 QState *s4 = new QState(s2);
133
134 s1->addTransition(transition: new EventTestTransition(QEvent::User, s2));
135 s2->addTransition(transition: new EventTestTransition(QEvent::User, s1));
136 s3->addTransition(transition: new EventTestTransition(QEvent::Type(QEvent::User+1), s4));
137
138 machine.setInitialState(s1);
139 machine.start();
140 QCoreApplication::processEvents();
141
142 QCOMPARE(machine.configuration().size(), 1);
143 QVERIFY(machine.configuration().contains(s1));
144
145 machine.postEvent(event: new QEvent(QEvent::User));
146 QCoreApplication::processEvents();
147
148 QCOMPARE(machine.configuration().size(), 2);
149 QVERIFY(machine.configuration().contains(s2));
150 QVERIFY(machine.configuration().contains(s3));
151
152 machine.postEvent(event: new QEvent(QEvent::User));
153 QCoreApplication::processEvents();
154
155 QCOMPARE(machine.configuration().size(), 1);
156 QVERIFY(machine.configuration().contains(s1));
157
158 machine.postEvent(event: new QEvent(QEvent::User));
159 QCoreApplication::processEvents();
160
161 QCOMPARE(machine.configuration().size(), 2);
162 QVERIFY(machine.configuration().contains(s2));
163 QVERIFY(machine.configuration().contains(s3));
164
165 machine.postEvent(event: new QEvent(QEvent::Type(QEvent::User+1)));
166 QCoreApplication::processEvents();
167
168 QCOMPARE(machine.configuration().size(), 2);
169 QVERIFY(machine.configuration().contains(s2));
170 QVERIFY(machine.configuration().contains(s4));
171
172 machine.postEvent(event: new QEvent(QEvent::User));
173 QCoreApplication::processEvents();
174
175 QCOMPARE(machine.configuration().size(), 1);
176 QVERIFY(machine.configuration().contains(s1));
177
178 machine.postEvent(event: new QEvent(QEvent::User));
179 QCoreApplication::processEvents();
180
181 QCOMPARE(machine.configuration().size(), 2);
182 QVERIFY(machine.configuration().contains(s2));
183 QVERIFY(machine.configuration().contains(s4));
184}
185
186void tst_QState::transitions()
187{
188 QState s1;
189 QState s2;
190
191 QVERIFY(s1.transitions().isEmpty());
192
193 QAbstractTransition *t1 = s1.addTransition(sender: this, SIGNAL(destroyed()), target: &s2);
194 QAbstractTransition *t1_1 = s1.addTransition(obj: this, signal: &tst_QState::destroyed, target: &s2);
195 QVERIFY(t1 != 0);
196 QVERIFY(t1_1 != 0);
197 QCOMPARE(s1.transitions().count(), 2);
198 QCOMPARE(s1.transitions().first(), t1);
199 QCOMPARE(s1.transitions().last(), t1_1);
200 QVERIFY(s2.transitions().isEmpty());
201
202 s1.removeTransition(transition: t1);
203 s1.removeTransition(transition: t1_1);
204 QVERIFY(s1.transitions().isEmpty());
205
206 s1.addTransition(transition: t1);
207 QCOMPARE(s1.transitions().count(), 1);
208 QCOMPARE(s1.transitions().first(), t1);
209
210 QAbstractTransition *t2 = new QEventTransition(&s1);
211 QCOMPARE(s1.transitions().count(), 2);
212 QVERIFY(s1.transitions().contains(t1));
213 QVERIFY(s1.transitions().contains(t2));
214
215 // Transitions from child states should not be reported.
216 QState *s21 = new QState(&s2);
217 QAbstractTransition *t3 = s21->addTransition(sender: this, SIGNAL(destroyed()), target: &s2);
218 QVERIFY(s2.transitions().isEmpty());
219 QCOMPARE(s21->transitions().count(), 1);
220 QCOMPARE(s21->transitions().first(), t3);
221}
222
223class MyState : public QState
224{
225 Q_OBJECT
226public:
227 MyState(QState *parent = 0)
228 : QState(parent)
229 {
230
231 }
232
233 void emitPrivateSignals()
234 {
235 // These deliberately do not compile
236// emit entered();
237// emit exited();
238//
239// emit entered(QPrivateSignal());
240// emit exited(QPrivateSignal());
241//
242// emit entered(QAbstractState::QPrivateSignal());
243// emit exited(QAbstractState::QPrivateSignal());
244 }
245
246};
247
248class MyTransition : public QSignalTransition
249{
250 Q_OBJECT
251public:
252 MyTransition(QObject * sender, const char * signal, QState *sourceState = 0)
253 : QSignalTransition(sender, signal, sourceState)
254 {
255
256 }
257
258 void emitPrivateSignals()
259 {
260 // These deliberately do not compile
261// emit triggered();
262//
263// emit triggered(QPrivateSignal());
264//
265// emit triggered(QAbstractTransition::QPrivateSignal());
266 }
267};
268
269class SignalConnectionTester : public QObject
270{
271 Q_OBJECT
272public:
273 SignalConnectionTester(QObject *parent = 0)
274 : QObject(parent), testPassed(false)
275 {
276
277 }
278
279public Q_SLOTS:
280 void testSlot()
281 {
282 testPassed = true;
283 }
284
285public:
286 bool testPassed;
287};
288
289class TestTrigger : public QObject
290{
291 Q_OBJECT
292public:
293 TestTrigger(QObject *parent = 0)
294 : QObject(parent)
295 {
296
297 }
298
299 void emitTrigger()
300 {
301 emit trigger();
302 }
303
304signals:
305 void trigger();
306};
307
308void tst_QState::privateSignals()
309{
310 QStateMachine machine;
311
312 QState *s1 = new QState(&machine);
313 MyState *s2 = new MyState(&machine);
314
315 TestTrigger testTrigger;
316
317 MyTransition *t1 = new MyTransition(&testTrigger, SIGNAL(trigger()), s1);
318 s1->addTransition(transition: t1);
319 t1->setTargetState(s2);
320
321 machine.setInitialState(s1);
322 machine.start();
323 QCoreApplication::processEvents();
324
325 SignalConnectionTester s1Tester;
326 SignalConnectionTester s2Tester;
327 SignalConnectionTester t1Tester;
328
329 QObject::connect(sender: s1, signal: &QState::exited, receiver: &s1Tester, slot: &SignalConnectionTester::testSlot);
330 QObject::connect(sender: s2, signal: &QState::entered, receiver: &s2Tester, slot: &SignalConnectionTester::testSlot);
331 QObject::connect(sender: t1, signal: &QSignalTransition::triggered, receiver: &t1Tester, slot: &SignalConnectionTester::testSlot);
332
333 testTrigger.emitTrigger();
334
335 QCoreApplication::processEvents();
336
337 QVERIFY(s1Tester.testPassed);
338 QVERIFY(s2Tester.testPassed);
339 QVERIFY(t1Tester.testPassed);
340
341}
342
343void tst_QState::parallelStateAndInitialState()
344{
345 QStateMachine machine;
346
347 { // setting an initial state on a parallel state:
348 QState a(QState::ParallelStates, &machine);
349 QState b(&a);
350 QVERIFY(!a.initialState());
351 const QString warning
352 = QString::asprintf(format: "QState::setInitialState: ignoring attempt to set initial state of parallel state group %p", &a);
353 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
354 a.setInitialState(&b); // should produce a warning and do nothing.
355 QVERIFY(!a.initialState());
356 }
357
358 { // setting the child-mode from ExclusiveStates to ParallelStates should remove the initial state:
359 QState a(QState::ExclusiveStates, &machine);
360 QState b(&a);
361 a.setInitialState(&b);
362 QCOMPARE(a.initialState(), &b);
363 const QString warning
364 = QString::asprintf(format: "QState::setChildMode: setting the child-mode of state %p to "
365 "parallel removes the initial state", &a);
366 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
367 a.setChildMode(QState::ParallelStates); // should produce a warning and remove the initial state
368 QVERIFY(!a.initialState());
369 QCOMPARE(a.childMode(), QState::ParallelStates);
370 }
371}
372
373QTEST_MAIN(tst_QState)
374#include "tst_qstate.moc"
375

source code of qtbase/tests/auto/corelib/statemachine/qstate/tst_qstate.cpp