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 | |
35 | class tst_QState : public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | private slots: |
40 | void assignProperty(); |
41 | void assignPropertyTwice(); |
42 | void historyInitialState(); |
43 | void transitions(); |
44 | void privateSignals(); |
45 | void parallelStateAndInitialState(); |
46 | }; |
47 | |
48 | class TestClass: public QObject |
49 | { |
50 | Q_OBJECT |
51 | public: |
52 | TestClass() : called(false) {} |
53 | bool called; |
54 | |
55 | public slots: |
56 | void slot() { called = true; } |
57 | |
58 | |
59 | }; |
60 | |
61 | void 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 | |
78 | void 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 | |
96 | class EventTestTransition: public QAbstractTransition |
97 | { |
98 | public: |
99 | EventTestTransition(QEvent::Type type, QState *targetState) |
100 | : QAbstractTransition(), m_type(type) |
101 | { |
102 | setTargetState(targetState); |
103 | } |
104 | |
105 | protected: |
106 | bool eventTest(QEvent *e) |
107 | { |
108 | return e->type() == m_type; |
109 | } |
110 | |
111 | void onTransition(QEvent *) {} |
112 | |
113 | private: |
114 | QEvent::Type m_type; |
115 | |
116 | }; |
117 | |
118 | void 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 | |
186 | void 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 | |
223 | class MyState : public QState |
224 | { |
225 | Q_OBJECT |
226 | public: |
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 | |
248 | class MyTransition : public QSignalTransition |
249 | { |
250 | Q_OBJECT |
251 | public: |
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 | |
269 | class SignalConnectionTester : public QObject |
270 | { |
271 | Q_OBJECT |
272 | public: |
273 | SignalConnectionTester(QObject *parent = 0) |
274 | : QObject(parent), testPassed(false) |
275 | { |
276 | |
277 | } |
278 | |
279 | public Q_SLOTS: |
280 | void testSlot() |
281 | { |
282 | testPassed = true; |
283 | } |
284 | |
285 | public: |
286 | bool testPassed; |
287 | }; |
288 | |
289 | class TestTrigger : public QObject |
290 | { |
291 | Q_OBJECT |
292 | public: |
293 | TestTrigger(QObject *parent = 0) |
294 | : QObject(parent) |
295 | { |
296 | |
297 | } |
298 | |
299 | void emitTrigger() |
300 | { |
301 | emit trigger(); |
302 | } |
303 | |
304 | signals: |
305 | void trigger(); |
306 | }; |
307 | |
308 | void 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 | |
343 | void 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 | |
373 | QTEST_MAIN(tst_QState) |
374 | #include "tst_qstate.moc" |
375 | |