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 QtScxml 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>
30#include <QtScxml/qscxmlstatemachine.h>
31#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
32
33class tst_StateMachineInfo: public QObject
34{
35 Q_OBJECT
36
37private Q_SLOTS:
38 void checkInfo();
39};
40
41class Recorder: public QObject
42{
43 Q_OBJECT
44
45public:
46 void clear()
47 {
48 enterCount = 0;
49 entered.clear();
50 exitCount = 0;
51 exited.clear();
52 transitionTriggerCount = 0;
53 transitions.clear();
54 macroStepDone = false;
55 }
56
57public slots:
58 void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states)
59 { entered = states; ++enterCount; }
60
61 void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states)
62 { exited = states; ++exitCount; }
63
64 void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions)
65 { this->transitions = transitions; ++transitionTriggerCount; }
66
67 void reachedStableState()
68 { macroStepDone = true; }
69
70 bool finishMacroStep() const
71 {
72 for (int i = 0; i < 100; ++i) {
73 if (!macroStepDone)
74 QCoreApplication::processEvents();
75 }
76
77 return macroStepDone;
78 }
79
80public:
81 int enterCount = 0;
82 QVector<QScxmlStateMachineInfo::StateId> entered;
83 int exitCount = 0;
84 QVector<QScxmlStateMachineInfo::StateId> exited;
85 int transitionTriggerCount = 0;
86 QVector<QScxmlStateMachineInfo::TransitionId> transitions;
87 bool macroStepDone = false;
88};
89
90void tst_StateMachineInfo::checkInfo()
91{
92 QScopedPointer<QScxmlStateMachine> stateMachine(
93 QScxmlStateMachine::fromFile(fileName: QString(":/tst_statemachineinfo/statemachine.scxml")));
94 QVERIFY(!stateMachine.isNull());
95 QVERIFY(stateMachine->parseErrors().isEmpty());
96 auto info = new QScxmlStateMachineInfo(stateMachine.data());
97
98 const QString machineName = QLatin1String("InfoTest");
99 QCOMPARE(stateMachine->name(), machineName);
100
101 auto states = info->allStates();
102 QCOMPARE(states.size(), 5);
103 QCOMPARE(info->stateName(states.at(0)), QLatin1String(""));
104 QCOMPARE(info->stateName(states.at(1)), QLatin1String("next"));
105 QCOMPARE(info->stateName(states.at(2)), QLatin1String("a"));
106 QCOMPARE(info->stateName(states.at(3)), QLatin1String("b"));
107 QCOMPARE(info->stateName(states.at(4)), QLatin1String("theEnd"));
108
109 QCOMPARE(info->stateParent(QScxmlStateMachineInfo::InvalidState),
110 static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
111 QCOMPARE(info->stateParent(states.at(0)), static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
112 QCOMPARE(info->stateParent(states.at(1)), static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
113 QCOMPARE(info->stateParent(states.at(2)), 1);
114 QCOMPARE(info->stateParent(states.at(3)), 1);
115 QCOMPARE(info->stateParent(states.at(4)), static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
116
117 QCOMPARE(info->stateType(states.at(0)), QScxmlStateMachineInfo::NormalState);
118 QCOMPARE(info->stateType(states.at(1)), QScxmlStateMachineInfo::ParallelState);
119 QCOMPARE(info->stateType(states.at(2)), QScxmlStateMachineInfo::NormalState);
120 QCOMPARE(info->stateType(states.at(3)), QScxmlStateMachineInfo::NormalState);
121 QCOMPARE(info->stateType(states.at(4)), QScxmlStateMachineInfo::FinalState);
122
123 QCOMPARE(info->stateChildren(QScxmlStateMachineInfo::InvalidStateId),
124 QVector<int>() << 0 << 1 << 4);
125 QCOMPARE(info->stateChildren(states.at(0)), QVector<int>());
126 QCOMPARE(info->stateChildren(states.at(1)), QVector<int>() << 2 << 3);
127 QCOMPARE(info->stateChildren(states.at(2)), QVector<int>());
128 QCOMPARE(info->stateChildren(states.at(3)), QVector<int>());
129 QCOMPARE(info->stateChildren(states.at(4)), QVector<int>());
130
131 QCOMPARE(info->initialTransition(QScxmlStateMachineInfo::InvalidStateId), 4);
132 QCOMPARE(info->initialTransition(states.at(0)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
133 QCOMPARE(info->initialTransition(states.at(1)), 5);
134 QCOMPARE(info->initialTransition(states.at(2)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
135 QCOMPARE(info->initialTransition(states.at(3)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
136 QCOMPARE(info->initialTransition(states.at(4)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
137
138 auto transitions = info->allTransitions();
139 QCOMPARE(transitions.size(), 6);
140
141 // targetless transition on top level
142 QCOMPARE(info->transitionType(transitions.at(0)), QScxmlStateMachineInfo::ExternalTransition);
143 QCOMPARE(info->stateType(info->transitionSource(transitions.at(0))),
144 QScxmlStateMachineInfo::InvalidState);
145 QCOMPARE(info->transitionTargets(transitions.at(0)).size(), 0);
146 QCOMPARE(info->transitionEvents(transitions.at(0)).size(), 0);
147
148 // <anon>->next
149 QCOMPARE(info->transitionType(transitions.at(1)), QScxmlStateMachineInfo::ExternalTransition);
150 QCOMPARE(info->transitionSource(transitions.at(1)), states.at(0));
151 QCOMPARE(info->transitionTargets(transitions.at(1)).size(), 1);
152 QCOMPARE(info->transitionTargets(transitions.at(1)).at(0), states.at(1));
153 QCOMPARE(info->transitionEvents(transitions.at(1)).size(), 1);
154 QCOMPARE(info->transitionEvents(transitions.at(1)).at(0), QStringLiteral("step"));
155
156 // a->theEnd
157 QCOMPARE(info->transitionType(transitions.at(2)), QScxmlStateMachineInfo::ExternalTransition);
158 QCOMPARE(info->transitionSource(transitions.at(2)), states.at(2));
159 QCOMPARE(info->transitionTargets(transitions.at(2)).size(), 1);
160 QCOMPARE(info->transitionTargets(transitions.at(2)).at(0), states.at(4));
161 QCOMPARE(info->transitionEvents(transitions.at(2)).size(), 1);
162 QCOMPARE(info->transitionEvents(transitions.at(2)).at(0), QStringLiteral("step"));
163
164 // b->theEnd
165 QCOMPARE(info->transitionType(transitions.at(3)), QScxmlStateMachineInfo::InternalTransition);
166 QCOMPARE(info->transitionSource(transitions.at(3)), states.at(3));
167 QCOMPARE(info->transitionTargets(transitions.at(3)).size(), 1);
168 QCOMPARE(info->transitionTargets(transitions.at(3)).at(0), states.at(4));
169 QCOMPARE(info->transitionEvents(transitions.at(3)).size(), 1);
170 QCOMPARE(info->transitionEvents(transitions.at(3)).at(0), QStringLiteral("step"));
171
172 // initial transition that activates the first (anonymous) state
173 QCOMPARE(info->transitionType(transitions.at(4)), QScxmlStateMachineInfo::SyntheticTransition);
174 QCOMPARE(info->stateType(info->transitionSource(transitions.at(4))),
175 QScxmlStateMachineInfo::InvalidState);
176 QCOMPARE(info->transitionTargets(transitions.at(4)).size(), 1);
177 QCOMPARE(info->transitionTargets(transitions.at(4)).at(0), states.at(0));
178 QCOMPARE(info->transitionEvents(transitions.at(4)).size(), 0);
179
180 // "initial" transition in the next state that activates all sub-states
181 QCOMPARE(info->transitionType(transitions.at(5)), QScxmlStateMachineInfo::SyntheticTransition);
182 QCOMPARE(info->transitionSource(transitions.at(5)), states.at(1));
183 QCOMPARE(info->transitionTargets(transitions.at(5)).size(), 2);
184 QCOMPARE(info->transitionTargets(transitions.at(5)).at(0), states.at(2));
185 QCOMPARE(info->transitionTargets(transitions.at(5)).at(1), states.at(3));
186 QCOMPARE(info->transitionEvents(transitions.at(5)).size(), 0);
187
188 Recorder recorder;
189 QObject::connect(sender: info, signal: &QScxmlStateMachineInfo::statesEntered,
190 receiver: &recorder, slot: &Recorder::statesEntered);
191 QObject::connect(sender: info, signal: &QScxmlStateMachineInfo::statesExited,
192 receiver: &recorder, slot: &Recorder::statesExited);
193 QObject::connect(sender: info, signal: &QScxmlStateMachineInfo::transitionsTriggered,
194 receiver: &recorder, slot: &Recorder::transitionsTriggered);
195 QObject::connect(sender: stateMachine.data(), signal: &QScxmlStateMachine::reachedStableState,
196 receiver: &recorder, slot: &Recorder::reachedStableState);
197
198 // initial step into first anonymous state
199 stateMachine->start();
200 QVERIFY(recorder.finishMacroStep());
201 QCOMPARE(recorder.enterCount, 1);
202 QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 0);
203 QVERIFY(recorder.exited.isEmpty());
204
205 recorder.clear();
206
207 // step from anonymous state into the parallel state, which activates "a" and "b" (in THAT
208 // order!)
209 stateMachine->submitEvent(eventName: "step");
210 QVERIFY(recorder.finishMacroStep());
211 QCOMPARE(recorder.enterCount, 1);
212 QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 1 << 2 << 3);
213 QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 0);
214 QCOMPARE(recorder.transitionTriggerCount, 1);
215 QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 1);
216
217 recorder.clear();
218
219 // step from the state "b" into "theEnd", which exits "b", "a", and "next" in exactly that
220 // order
221 stateMachine->submitEvent(eventName: "step");
222 QVERIFY(recorder.finishMacroStep());
223 QCOMPARE(recorder.enterCount, 1);
224 QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 4);
225 QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 3 << 2 << 1);
226 QCOMPARE(recorder.transitionTriggerCount, 1);
227 QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 2);
228}
229
230
231QTEST_MAIN(tst_StateMachineInfo)
232
233#include "tst_statemachineinfo.moc"
234
235
236

source code of qtscxml/tests/auto/statemachineinfo/tst_statemachineinfo.cpp