summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@qt.io>2016-09-19 12:33:25 +0200
committerErik Verbruggen <erik.verbruggen@qt.io>2016-10-07 12:00:16 +0000
commitbc0d3869e409b40faa5e152a3e38e8ed97938efd (patch)
tree4ca4dda2f6e4e9e71122f54f6d3edd30d8f465ab
parent70a4f65a9e182f1e06bb1483ff3b311f10ed119b (diff)
State machine interrogation interface
This interface can be used to get all states and transitions from an SCXML state machine. It will also signal which states are entered and exited for each microstep, and which transitions are taken by the microstep. Change-Id: I4bb936add6b3fd87a322093b8aee66521bb294a4 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/scxml/qscxmlstatemachine.cpp31
-rw-r--r--src/scxml/qscxmlstatemachine_p.h21
-rw-r--r--src/scxml/qscxmlstatemachineinfo.cpp201
-rw-r--r--src/scxml/qscxmlstatemachineinfo_p.h109
-rw-r--r--src/scxml/scxml.pro6
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/statemachineinfo/statemachine.scxml45
-rw-r--r--tests/auto/statemachineinfo/statemachineinfo.pro13
-rw-r--r--tests/auto/statemachineinfo/tst_statemachineinfo.cpp205
-rw-r--r--tests/auto/statemachineinfo/tst_statemachineinfo.qrc5
10 files changed, 636 insertions, 3 deletions
diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp
index 28ca6dd..e6137e0 100644
--- a/src/scxml/qscxmlstatemachine.cpp
+++ b/src/scxml/qscxmlstatemachine.cpp
@@ -430,6 +430,7 @@ QScxmlStateMachinePrivate::QScxmlStateMachinePrivate(const QMetaObject *metaObje
, m_parentStateMachine(Q_NULLPTR)
, m_eventLoopHook(this)
, m_metaObject(metaObject)
+ , m_infoSignalProxy(nullptr)
{}
QScxmlStateMachinePrivate::~QScxmlStateMachinePrivate()
@@ -715,6 +716,21 @@ void QScxmlStateMachinePrivate::emitInvokedServicesChanged()
emit q->invokedServicesChanged(q->invokedServices());
}
+void QScxmlStateMachinePrivate::attach(QScxmlStateMachineInfo *info)
+{
+ Q_Q(QScxmlStateMachine);
+
+ if (!m_infoSignalProxy)
+ m_infoSignalProxy = new QScxmlInternal::StateMachineInfoProxy(q);
+
+ QObject::connect(m_infoSignalProxy, &QScxmlInternal::StateMachineInfoProxy::statesEntered,
+ info, &QScxmlStateMachineInfo::statesEntered);
+ QObject::connect(m_infoSignalProxy, &QScxmlInternal::StateMachineInfoProxy::statesExited,
+ info, &QScxmlStateMachineInfo::statesExited);
+ QObject::connect(m_infoSignalProxy,&QScxmlInternal::StateMachineInfoProxy::transitionsTriggered,
+ info, &QScxmlStateMachineInfo::transitionsTriggered);
+}
+
QStringList QScxmlStateMachinePrivate::stateNames(const std::vector<int> &stateIndexes) const
{
QStringList names;
@@ -1022,6 +1038,11 @@ void QScxmlStateMachinePrivate::exitStates(const OrderedSet &enabledTransitions)
emitStateActive(s, false);
removeService(s);
}
+
+ if (m_infoSignalProxy) {
+ emit m_infoSignalProxy->statesExited(
+ QVector<QScxmlStateMachineInfo::StateId>::fromStdVector(statesToExitSorted));
+ }
}
void QScxmlStateMachinePrivate::computeExitSet(const OrderedSet &enabledTransitions,
@@ -1048,6 +1069,12 @@ void QScxmlStateMachinePrivate::executeTransitionContent(const OrderedSet &enabl
if (transition.transitionInstructions != StateTable::InvalidIndex)
m_executionEngine->execute(transition.transitionInstructions);
}
+
+ if (m_infoSignalProxy) {
+ emit m_infoSignalProxy->transitionsTriggered(
+ QVector<QScxmlStateMachineInfo::TransitionId>::fromStdVector(
+ enabledTransitions.list()));
+ }
}
void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions)
@@ -1104,6 +1131,10 @@ void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions
}
for (int s : sortedStates)
emitStateActive(s, true);
+ if (m_infoSignalProxy) {
+ emit m_infoSignalProxy->statesEntered(
+ QVector<QScxmlStateMachineInfo::StateId>::fromStdVector(sortedStates));
+ }
}
void QScxmlStateMachinePrivate::computeEntrySet(const OrderedSet &enabledTransitions,
diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h
index 3ca0688..8ffee02 100644
--- a/src/scxml/qscxmlstatemachine_p.h
+++ b/src/scxml/qscxmlstatemachine_p.h
@@ -53,6 +53,7 @@
#include <QtScxml/private/qscxmlexecutablecontent_p.h>
#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
#include <QtCore/private/qobject_p.h>
#include <QtCore/private/qmetaobject_p.h>
#include <QtCore/qmetaobject.h>
@@ -101,6 +102,21 @@ private:
void disconnectNotify(const QMetaMethod &signal) override;
};
+
+class StateMachineInfoProxy: public QObject
+{
+ Q_OBJECT
+
+public:
+ StateMachineInfoProxy(QObject *parent)
+ : QObject(parent)
+ {}
+
+Q_SIGNALS:
+ void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
+};
} // QScxmlInternal namespace
class QScxmlInvokableService;
@@ -264,6 +280,9 @@ public:
void emitInvokedServicesChanged();
void emitSignalForEvent(int signalIndex, const QVariant &data);
+ void attach(QScxmlStateMachineInfo *info);
+ const OrderedSet &configuration() const { return m_configuration; }
+
private:
QStringList stateNames(const std::vector<int> &stateIndexes) const;
std::vector<int> historyStates(int stateIdx) const;
@@ -358,6 +377,8 @@ private:
}
bool isPaused() const { return m_runningState == Paused; }
+
+ QScxmlInternal::StateMachineInfoProxy *m_infoSignalProxy;
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachineinfo.cpp b/src/scxml/qscxmlstatemachineinfo.cpp
new file mode 100644
index 0000000..d3a23f9
--- /dev/null
+++ b/src/scxml/qscxmlstatemachineinfo.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscxmlstatemachineinfo_p.h"
+#include "qscxmlstatemachine_p.h"
+#include "qscxmlexecutablecontent_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlStateMachineInfoPrivate: public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QScxmlStateMachineInfo)
+
+public:
+ QScxmlStateMachine *stateMachine() const
+ { return qobject_cast<QScxmlStateMachine *>(q_func()->parent()); }
+
+ QScxmlStateMachinePrivate *stateMachinePrivate() const
+ { return QScxmlStateMachinePrivate::get(stateMachine()); }
+
+ const QScxmlExecutableContent::StateTable *stateTable() const
+ { return stateMachinePrivate()->m_stateTable; }
+};
+
+QScxmlStateMachineInfo::QScxmlStateMachineInfo(QScxmlStateMachine *stateMachine)
+ : QObject(*new QScxmlStateMachineInfoPrivate, stateMachine)
+{
+ QScxmlStateMachinePrivate::get(stateMachine)->attach(this);
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::allStates() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::StateId> all;
+ for (int i = 0, ei = d->stateTable()->stateCount; i < ei; ++i) {
+ all.append(i);
+ }
+ return all;
+}
+
+QVector<QScxmlStateMachineInfo::TransitionId> QScxmlStateMachineInfo::allTransitions() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::TransitionId> all;
+ for (int i = 0, ei = d->stateTable()->transitionCount; i < ei; ++i) {
+ all.append(i);
+ }
+ return all;
+}
+
+QString QScxmlStateMachineInfo::stateName(int stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (stateId < StateMachineRootState && stateId >= d->stateTable()->stateCount)
+ return QString();
+
+ if (stateId == StateMachineRootState) {
+ if (d->stateTable()->name < 0)
+ return QString();
+ else
+ return d->stateMachinePrivate()->m_tableData->string(d->stateTable()->name);
+ }
+
+ auto state = d->stateTable()->state(stateId);
+ if (state.name >= 0)
+ return d->stateMachinePrivate()->m_tableData->string(state.name);
+ else
+ return QString();
+}
+
+QScxmlStateMachineInfo::StateType QScxmlStateMachineInfo::stateType(StateId stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (stateId < StateMachineRootState || stateId >= d->stateTable()->stateCount)
+ return InvalidState;
+
+ if (stateId == -1)
+ return StateMachineRootState;
+
+ auto state = d->stateTable()->state(stateId);
+ switch (state.type) {
+ default: return InvalidState;
+ case QScxmlExecutableContent::StateTable::State::Normal: return NormalState;
+ case QScxmlExecutableContent::StateTable::State::Parallel: return ParallelState;
+ case QScxmlExecutableContent::StateTable::State::Final: return FinalState;
+ case QScxmlExecutableContent::StateTable::State::ShallowHistory: return ShallowHistoryState;
+ case QScxmlExecutableContent::StateTable::State::DeepHistory: return DeepHistoryState;
+ }
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::stateChildren(StateId stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::StateId> all;
+ auto state = d->stateTable()->state(stateId);
+ if (state.childStates == QScxmlExecutableContent::StateTable::InvalidIndex)
+ return all;
+
+ auto kids = d->stateTable()->array(state.childStates);
+ for (auto childId : kids) {
+ all.append(childId);
+ }
+ return all;
+}
+
+QScxmlStateMachineInfo::TransitionType QScxmlStateMachineInfo::transitionType(QScxmlStateMachineInfo::TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return InvalidTransition;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ switch (transition.type) {
+ default: return InvalidTransition;
+ case QScxmlExecutableContent::StateTable::Transition::Invalid: return InvalidTransition;
+ case QScxmlExecutableContent::StateTable::Transition::Internal: return InternalTransition;
+ case QScxmlExecutableContent::StateTable::Transition::External: return ExternalTransition;
+ case QScxmlExecutableContent::StateTable::Transition::Synthetic: return SyntheticTransition;
+ }
+}
+
+QScxmlStateMachineInfo::StateId QScxmlStateMachineInfo::transitionSource(TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return InvalidState;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ return transition.source;
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::transitionTargets(TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::StateId> targets;
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return targets;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ if (transition.targets == QScxmlExecutableContent::StateTable::InvalidIndex)
+ return targets;
+
+ for (int target : d->stateTable()->array(transition.targets)) {
+ targets.append(target);
+ }
+
+ return targets;
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::configuration() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ return QVector<StateId>::fromStdVector(d->stateMachinePrivate()->configuration().list());
+}
+
+QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachineinfo_p.h b/src/scxml/qscxmlstatemachineinfo_p.h
new file mode 100644
index 0000000..a80ef35
--- /dev/null
+++ b/src/scxml/qscxmlstatemachineinfo_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCXMLSTATEMACHINEINFO_H
+#define QSCXMLSTATEMACHINEINFO_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScxml/qscxmlglobals.h>
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlStateMachine;
+class QScxmlStateMachineInfoPrivate;
+
+class Q_SCXML_EXPORT QScxmlStateMachineInfo: public QObject
+{
+ Q_OBJECT
+
+public: // types
+ typedef int StateId;
+ typedef int TransitionId;
+ enum StateType : int {
+ InvalidState = -2,
+ StateMachineRootState = -1,
+ NormalState = 0,
+ ParallelState = 1,
+ FinalState = 2,
+ ShallowHistoryState = 3,
+ DeepHistoryState = 4
+ };
+ enum TransitionType : int {
+ InvalidTransition = -1,
+ InternalTransition = 0,
+ ExternalTransition = 1,
+ SyntheticTransition = 2
+ };
+
+public: // methods
+ QScxmlStateMachineInfo(QScxmlStateMachine *stateMachine);
+
+ QVector<StateId> allStates() const;
+ QVector<TransitionId> allTransitions() const;
+ QString stateName(int stateId) const;
+ StateType stateType(int stateId) const;
+ QVector<StateId> stateChildren(StateId stateId) const;
+ TransitionType transitionType(TransitionId transitionId) const;
+ StateId transitionSource(TransitionId transitionId) const;
+ QVector<StateId> transitionTargets(TransitionId transitionId) const;
+ QVector<StateId> configuration() const;
+
+Q_SIGNALS:
+ void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
+
+private:
+ Q_DECLARE_PRIVATE(QScxmlStateMachineInfo)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLSTATEMACHINEINFO_H
diff --git a/src/scxml/scxml.pro b/src/scxml/scxml.pro
index 8722ea3..fa96789 100644
--- a/src/scxml/scxml.pro
+++ b/src/scxml/scxml.pro
@@ -30,7 +30,8 @@ HEADERS += \
qscxmlerror.h \
qscxmlinvokableservice.h \
qscxmltabledata.h \
- qscxmltabledata_p.h
+ qscxmltabledata_p.h \
+ qscxmlstatemachineinfo_p.h
SOURCES += \
qscxmlparser.cpp \
@@ -44,7 +45,8 @@ SOURCES += \
qscxmlcppdatamodel.cpp \
qscxmlerror.cpp \
qscxmlinvokableservice.cpp \
- qscxmltabledata.cpp
+ qscxmltabledata.cpp \
+ qscxmlstatemachineinfo.cpp
FEATURES += ../../mkspecs/features/qscxmlc.prf
features.files = $$FEATURES
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 776a54a..60576d5 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -4,4 +4,5 @@ SUBDIRS = cmake\
dynamicmetaobject\
parser\
scion\
- statemachine
+ statemachine \
+ statemachineinfo
diff --git a/tests/auto/statemachineinfo/statemachine.scxml b/tests/auto/statemachineinfo/statemachine.scxml
new file mode 100644
index 0000000..94f06b1
--- /dev/null
+++ b/tests/auto/statemachineinfo/statemachine.scxml
@@ -0,0 +1,45 @@
+<?xml version="1.0" ?>
+<!--
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="InfoTest">
+ <transition/>
+ <state>
+ <transition target="next" event="step"/>
+ </state>
+ <parallel id="next">
+ <state id="a">
+ <transition target="theEnd" event="step"/>
+ </state>
+ <state id="b">
+ <transition target="theEnd" event="step" type="internal"/>
+ </state>
+ </parallel>
+ <final id="theEnd"/>
+</scxml>
diff --git a/tests/auto/statemachineinfo/statemachineinfo.pro b/tests/auto/statemachineinfo/statemachineinfo.pro
new file mode 100644
index 0000000..f8d5b5d
--- /dev/null
+++ b/tests/auto/statemachineinfo/statemachineinfo.pro
@@ -0,0 +1,13 @@
+QT = core gui qml testlib scxml-private
+CONFIG += testcase
+
+TARGET = tst_statemachineinfo
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+RESOURCES += tst_statemachineinfo.qrc
+
+SOURCES += \
+ tst_statemachineinfo.cpp
diff --git a/tests/auto/statemachineinfo/tst_statemachineinfo.cpp b/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
new file mode 100644
index 0000000..1c33847
--- /dev/null
+++ b/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
+
+Q_DECLARE_METATYPE(QScxmlError);
+
+class tst_StateMachineInfo: public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void checkInfo();
+};
+
+class Recorder: public QObject
+{
+ Q_OBJECT
+
+public:
+ void clear()
+ {
+ enterCount = 0;
+ entered.clear();
+ exitCount = 0;
+ exited.clear();
+ transitionTriggerCount = 0;
+ transitions.clear();
+ macroStepDone = false;
+ }
+
+public slots:
+ void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states)
+ { entered = states; ++enterCount; }
+
+ void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states)
+ { exited = states; ++exitCount; }
+
+ void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions)
+ { this->transitions = transitions; ++transitionTriggerCount; }
+
+ void reachedStableState()
+ { macroStepDone = true; }
+
+ bool finishMacroStep() const
+ {
+ for (int i = 0; i < 100; ++i) {
+ if (!macroStepDone)
+ QCoreApplication::processEvents();
+ }
+
+ return macroStepDone;
+ }
+
+public:
+ int enterCount = 0;
+ QVector<QScxmlStateMachineInfo::StateId> entered;
+ int exitCount = 0;
+ QVector<QScxmlStateMachineInfo::StateId> exited;
+ int transitionTriggerCount = 0;
+ QVector<QScxmlStateMachineInfo::TransitionId> transitions;
+ bool macroStepDone = false;
+};
+
+void tst_StateMachineInfo::checkInfo()
+{
+ QScopedPointer<QScxmlStateMachine> stateMachine(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachineinfo/statemachine.scxml")));
+ QVERIFY(!stateMachine.isNull());
+ QVERIFY(stateMachine->parseErrors().isEmpty());
+ auto info = new QScxmlStateMachineInfo(stateMachine.data());
+
+ const QString machineName = QLatin1String("InfoTest");
+ QCOMPARE(info->stateName(QScxmlStateMachineInfo::StateMachineRootState), machineName);
+
+ auto states = info->allStates();
+ QCOMPARE(states.size(), 5);
+ QCOMPARE(info->stateName(states.at(0)), QLatin1String(""));
+ QCOMPARE(info->stateName(states.at(1)), QLatin1String("next"));
+ QCOMPARE(info->stateName(states.at(2)), QLatin1String("a"));
+ QCOMPARE(info->stateName(states.at(3)), QLatin1String("b"));
+ QCOMPARE(info->stateName(states.at(4)), QLatin1String("theEnd"));
+
+ QCOMPARE(info->stateType(states.at(0)), QScxmlStateMachineInfo::NormalState);
+ QCOMPARE(info->stateType(states.at(1)), QScxmlStateMachineInfo::ParallelState);
+ QCOMPARE(info->stateType(states.at(2)), QScxmlStateMachineInfo::NormalState);
+ QCOMPARE(info->stateType(states.at(3)), QScxmlStateMachineInfo::NormalState);
+ QCOMPARE(info->stateType(states.at(4)), QScxmlStateMachineInfo::FinalState);
+
+ auto transitions = info->allTransitions();
+ QCOMPARE(transitions.size(), 6);
+
+ // targetless transition on top level
+ QCOMPARE(info->transitionType(transitions.at(0)), QScxmlStateMachineInfo::ExternalTransition);
+ QCOMPARE(info->stateType(info->transitionSource(transitions.at(0))),
+ QScxmlStateMachineInfo::StateMachineRootState);
+ QCOMPARE(info->transitionTargets(transitions.at(0)).size(), 0);
+
+ // <anon>->next
+ QCOMPARE(info->transitionType(transitions.at(1)), QScxmlStateMachineInfo::ExternalTransition);
+ QCOMPARE(info->transitionSource(transitions.at(1)), states.at(0));
+ QCOMPARE(info->transitionTargets(transitions.at(1)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(1)).at(0), states.at(1));
+
+ // a->theEnd
+ QCOMPARE(info->transitionType(transitions.at(2)), QScxmlStateMachineInfo::ExternalTransition);
+ QCOMPARE(info->transitionSource(transitions.at(2)), states.at(2));
+ QCOMPARE(info->transitionTargets(transitions.at(2)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(2)).at(0), states.at(4));
+
+ // b->theEnd
+ QCOMPARE(info->transitionType(transitions.at(3)), QScxmlStateMachineInfo::InternalTransition);
+ QCOMPARE(info->transitionSource(transitions.at(3)), states.at(3));
+ QCOMPARE(info->transitionTargets(transitions.at(3)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(3)).at(0), states.at(4));
+
+ // initial transition that activates the first (anonymous) state
+ QCOMPARE(info->transitionType(transitions.at(4)), QScxmlStateMachineInfo::SyntheticTransition);
+ QCOMPARE(info->stateType(info->transitionSource(transitions.at(4))),
+ QScxmlStateMachineInfo::StateMachineRootState);
+ QCOMPARE(info->transitionTargets(transitions.at(4)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(4)).at(0), states.at(0));
+
+ // "initial" transition in the next state that activates all sub-states
+ QCOMPARE(info->transitionType(transitions.at(5)), QScxmlStateMachineInfo::SyntheticTransition);
+ QCOMPARE(info->transitionSource(transitions.at(5)), states.at(1));
+ QCOMPARE(info->transitionTargets(transitions.at(5)).size(), 2);
+ QCOMPARE(info->transitionTargets(transitions.at(5)).at(0), states.at(2));
+ QCOMPARE(info->transitionTargets(transitions.at(5)).at(1), states.at(3));
+
+ Recorder recorder;
+ QObject::connect(info, &QScxmlStateMachineInfo::statesEntered,
+ &recorder, &Recorder::statesEntered);
+ QObject::connect(info, &QScxmlStateMachineInfo::statesExited,
+ &recorder, &Recorder::statesExited);
+ QObject::connect(info, &QScxmlStateMachineInfo::transitionsTriggered,
+ &recorder, &Recorder::transitionsTriggered);
+ QObject::connect(stateMachine.data(), &QScxmlStateMachine::reachedStableState,
+ &recorder, &Recorder::reachedStableState);
+
+ // initial step into first anonymous state
+ stateMachine->start();
+ QVERIFY(recorder.finishMacroStep());
+ QCOMPARE(recorder.enterCount, 1);
+ QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 0);
+ QVERIFY(recorder.exited.isEmpty());
+
+ recorder.clear();
+
+ // step from anonymous state into the parallel state, which activates "a" and "b" (in THAT
+ // order!)
+ stateMachine->submitEvent("step");
+ QVERIFY(recorder.finishMacroStep());
+ QCOMPARE(recorder.enterCount, 1);
+ QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 1 << 2 << 3);
+ QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 0);
+ QCOMPARE(recorder.transitionTriggerCount, 1);
+ QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 1);
+
+ recorder.clear();
+
+ // step from the state "b" into "theEnd", which exits "b", "a", and "next" in exactly that
+ // order
+ stateMachine->submitEvent("step");
+ QVERIFY(recorder.finishMacroStep());
+ QCOMPARE(recorder.enterCount, 1);
+ QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 4);
+ QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 3 << 2 << 1);
+ QCOMPARE(recorder.transitionTriggerCount, 1);
+ QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 2);
+}
+
+
+QTEST_MAIN(tst_StateMachineInfo)
+
+#include "tst_statemachineinfo.moc"
+
+
diff --git a/tests/auto/statemachineinfo/tst_statemachineinfo.qrc b/tests/auto/statemachineinfo/tst_statemachineinfo.qrc
new file mode 100644
index 0000000..0e464d4
--- /dev/null
+++ b/tests/auto/statemachineinfo/tst_statemachineinfo.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/tst_statemachineinfo">
+ <file>statemachine.scxml</file>
+ </qresource>
+</RCC>