summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>2009-06-23 09:20:34 (GMT)
committerEskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>2009-06-23 09:23:45 (GMT)
commit6769af5510b963e10dc045630e1ab07fd16ba6d1 (patch)
treea421e6f74a6e2f327225ad7177c680cca874d5a2
parentce8eaf11405f8acd607f29af5b7daeb57b4abb6d (diff)
downloadQt-6769af5510b963e10dc045630e1ab07fd16ba6d1.zip
Qt-6769af5510b963e10dc045630e1ab07fd16ba6d1.tar.gz
Qt-6769af5510b963e10dc045630e1ab07fd16ba6d1.tar.bz2
Remove default error state
Having an implicit default error state in the graph which the user has not added is unintuitive and ugly. Rather than have a default error state, we stop execution of the machine and print an error message when the machine has run-time errors. If a user wishes to prevent errors from stopping the machine, you can set one or more error states explicitly.
-rw-r--r--src/corelib/statemachine/qstate.cpp6
-rw-r--r--src/corelib/statemachine/qstatemachine.cpp92
-rw-r--r--src/corelib/statemachine/qstatemachine_p.h1
-rw-r--r--tests/auto/qstatemachine/tst_qstatemachine.cpp86
4 files changed, 82 insertions, 103 deletions
diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp
index fd7ddef..83dd869 100644
--- a/src/corelib/statemachine/qstate.cpp
+++ b/src/corelib/statemachine/qstate.cpp
@@ -240,7 +240,7 @@ void QState::assignProperty(QObject *object, const char *name,
}
/*!
- Returns this state group's error state.
+ Returns this state's error state.
\sa QStateMachine::errorState(), QStateMachine::setErrorState()
*/
@@ -253,7 +253,9 @@ QAbstractState *QState::errorState() const
/*!
Sets this state's error state to be the given \a state. If the error state
is not set, or if it is set to 0, the state will inherit its parent's error
- state recursively.
+ state recursively. If no error state is set for the state itself or any of
+ its ancestors, an error will cause the machine to stop executing and an error
+ will be printed to the console.
\sa QStateMachine::setErrorState(), QStateMachine::errorState()
*/
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index 682dd97..bf3ee31 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -156,16 +156,14 @@ QT_BEGIN_NAMESPACE
transitions, e.g., \l{QSignalTransition}s as in this example. See
the QState class description for further details.
- If an error is encountered, the machine will enter the
- \l{errorState}{error state}, which is a special state created by
- the machine. The types of errors possible are described by the
+ If an error is encountered, the machine will look for an
+ \l{errorState}{error state}, and if one is available, it will
+ enter this state. The types of errors possible are described by the
\l{QStateMachine::}{Error} enum. After the error state is entered,
the type of the error can be retrieved with error(). The execution
- of the state graph will not stop when the error state is entered.
- So it is possible to handle the error, for instance, by connecting
- to the \l{QAbstractState::}{entered()} signal of the error state.
- It is also possible to set a custom error state with
- setErrorState().
+ of the state graph will not stop when the error state is entered. If
+ no error state applies to the erroneous state, the machine will stop
+ executing and an error message will be printed to the console.
\omit This stuff will be moved elsewhere
This is
@@ -238,7 +236,6 @@ QStateMachinePrivate::QStateMachinePrivate()
error = QStateMachine::NoError;
globalRestorePolicy = QStateMachine::DoNotRestoreProperties;
rootState = 0;
- initialErrorStateForRoot = 0;
signalEventGenerator = 0;
#ifndef QT_NO_ANIMATION
animationsEnabled = true;
@@ -984,27 +981,25 @@ void QStateMachinePrivate::unregisterRestorable(QObject *object, const QByteArra
QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context)
{
- // If the user sets the root state's error state to 0, we return the initial error state
- if (context == 0)
- return initialErrorStateForRoot;
-
// Find error state recursively in parent hierarchy if not set explicitly for context state
QAbstractState *errorState = 0;
-
- QState *s = qobject_cast<QState*>(context);
- if (s)
- errorState = s->errorState();
+ if (context != 0) {
+ QState *s = qobject_cast<QState*>(context);
+ if (s != 0)
+ errorState = s->errorState();
- if (!errorState)
- errorState = findErrorState(context->parentState());
+ if (errorState == 0)
+ errorState = findErrorState(context->parentState());
+ }
return errorState;
}
void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext)
{
+ Q_Q(QStateMachine);
+
error = errorCode;
-
switch (errorCode) {
case QStateMachine::NoInitialStateError:
Q_ASSERT(currentContext != 0);
@@ -1036,16 +1031,19 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta
QAbstractState *currentErrorState = findErrorState(currentContext);
// Avoid infinite loop if the error state itself has an error
- if (currentContext == currentErrorState) {
- Q_ASSERT(currentContext != initialErrorStateForRoot); // RootErrorState is broken
- currentErrorState = initialErrorStateForRoot;
- }
+ if (currentContext == currentErrorState)
+ currentErrorState = 0;
- Q_ASSERT(currentErrorState != 0);
Q_ASSERT(currentErrorState != rootState);
-
- QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
- addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry);
+
+ if (currentErrorState != 0) {
+ QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
+ addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry);
+ } else {
+ qWarning("Unrecoverable error detected in running state machine: %s",
+ qPrintable(errorString));
+ q->stop();
+ }
}
#ifndef QT_NO_ANIMATION
@@ -1148,9 +1146,6 @@ void QStateMachinePrivate::_q_start()
return;
}
QAbstractState *initial = rootState->initialState();
- if (initial == 0)
- setError(QStateMachine::NoInitialStateError, rootState);
-
configuration.clear();
qDeleteAll(internalEventQueue);
internalEventQueue.clear();
@@ -1483,27 +1478,6 @@ QStateMachine::~QStateMachine()
namespace {
-class RootErrorState : public QAbstractState
-{
-public:
- RootErrorState(QState *parent)
- : QAbstractState(parent)
- {
- setObjectName(QString::fromLatin1("DefaultErrorState"));
- }
-
- void onEntry(QEvent *)
- {
- QAbstractStatePrivate *d = QAbstractStatePrivate::get(this);
- QStateMachine *machine = d->machine();
-
- qWarning("Unrecoverable error detected in running state machine: %s",
- qPrintable(machine->errorString()));
- }
-
- void onExit(QEvent *) {}
-};
-
class RootState : public QState
{
public:
@@ -1526,9 +1500,7 @@ QState *QStateMachine::rootState() const
Q_D(const QStateMachine);
if (!d->rootState) {
const_cast<QStateMachinePrivate*>(d)->rootState = new RootState(0);
- const_cast<QStateMachinePrivate*>(d)->initialErrorStateForRoot = new RootErrorState(d->rootState);
d->rootState->setParent(const_cast<QStateMachine*>(this));
- d->rootState->setErrorState(d->initialErrorStateForRoot);
}
return d->rootState;
}
@@ -1552,17 +1524,13 @@ QAbstractState *QStateMachine::errorState() const
If the erroneous state has an error state set, this will be entered by the machine. If no error
state has been set, the state machine will search the parent hierarchy recursively for an
error state. The error state of the root state can thus be seen as a global error state that
- applies for the states for which a more specific error state has not been set.
+ applies for all states for which a more specific error state has not been set.
Before entering the error state, the state machine will set the error code returned by error() and
- error message returned by errorString().
-
- The default error state will print a warning to the console containing the information returned by
- errorString(). By setting a new error state on either the state machine itself, or on specific
- states, you can fine tune error handling in the state machine.
+ error message returned by errorString().
- If the root state's error state is set to 0, or if the error state selected by the machine itself
- contains an error, the default error state will be used.
+ If there is no error state available for the erroneous state, the state machine will print a
+ warning message on the console and stop executing.
\sa QState::setErrorState(), rootState()
*/
diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h
index f3c6a27..1335b93 100644
--- a/src/corelib/statemachine/qstatemachine_p.h
+++ b/src/corelib/statemachine/qstatemachine_p.h
@@ -175,7 +175,6 @@ public:
QString errorString;
QSet<QAbstractState *> pendingErrorStates;
QSet<QAbstractState *> pendingErrorStatesForDefaultEntry;
- QAbstractState *initialErrorStateForRoot;
#ifndef QT_NO_ANIMATION
bool animationsEnabled;
diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp
index 81f0370..a859866 100644
--- a/tests/auto/qstatemachine/tst_qstatemachine.cpp
+++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp
@@ -151,6 +151,8 @@ private slots:
void defaultGlobalRestorePolicy();
void globalRestorePolicySetToRestore();
void globalRestorePolicySetToDoNotRestore();
+
+ void noInitialStateForInitialState();
//void restorePolicyNotInherited();
//void mixedRestoreProperties();
@@ -345,7 +347,7 @@ void tst_QStateMachine::transitionEntersParent()
void tst_QStateMachine::defaultErrorState()
{
QStateMachine machine;
- QVERIFY(machine.errorState() != 0);
+ QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
QState *brokenState = new QState();
brokenState->setObjectName("MyInitialState");
@@ -364,9 +366,7 @@ void tst_QStateMachine::defaultErrorState()
QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'MyInitialState'"));
-
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(machine.errorState()));
+ QCOMPARE(machine.isRunning(), false);
}
class CustomErrorState: public QState
@@ -424,6 +424,7 @@ void tst_QStateMachine::customGlobalErrorState()
QCoreApplication::processEvents();
+ QCOMPARE(machine.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(customErrorState));
QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
@@ -459,6 +460,7 @@ void tst_QStateMachine::customLocalErrorStateInBrokenState()
machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
+ QCOMPARE(machine.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(customErrorState));
QCOMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
@@ -494,8 +496,7 @@ void tst_QStateMachine::customLocalErrorStateInOtherState()
machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(machine.errorState()));
+ QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::customLocalErrorStateInParentOfBrokenState()
@@ -529,6 +530,7 @@ void tst_QStateMachine::customLocalErrorStateInParentOfBrokenState()
machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
+ QCOMPARE(machine.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(customErrorState));
}
@@ -607,6 +609,7 @@ void tst_QStateMachine::errorStateHasChildren()
machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
+ QCOMPARE(machine.isRunning(), true);
QCOMPARE(machine.configuration().count(), 2);
QVERIFY(machine.configuration().contains(customErrorState));
QVERIFY(machine.configuration().contains(childOfErrorState));
@@ -620,7 +623,6 @@ void tst_QStateMachine::errorStateHasErrors()
customErrorState->setObjectName("customErrorState");
machine.addState(customErrorState);
- QAbstractState *oldErrorState = machine.errorState();
machine.setErrorState(customErrorState);
QState *childOfErrorState = new QState(customErrorState);
@@ -647,8 +649,7 @@ void tst_QStateMachine::errorStateHasErrors()
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'customErrorState'");
QCoreApplication::processEvents();
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(oldErrorState)); // Fall back to default
+ QCOMPARE(machine.isRunning(), false);
QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'customErrorState'"));
}
@@ -680,8 +681,7 @@ void tst_QStateMachine::errorStateIsRootState()
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
QCoreApplication::processEvents();
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(machine.errorState()));
+ QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::errorStateEntersParentFirst()
@@ -759,7 +759,6 @@ void tst_QStateMachine::errorStateEntersParentFirst()
void tst_QStateMachine::customErrorStateIsNull()
{
QStateMachine machine;
- QAbstractState *oldErrorState = machine.errorState();
machine.rootState()->setErrorState(0);
QState *initialState = new QState();
@@ -780,8 +779,7 @@ void tst_QStateMachine::customErrorStateIsNull()
QCoreApplication::processEvents();
QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(oldErrorState));
+ QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::clearError()
@@ -797,6 +795,7 @@ void tst_QStateMachine::clearError()
machine.start();
QCoreApplication::processEvents();
+ QCOMPARE(machine.isRunning(), true);
QCOMPARE(machine.error(), QStateMachine::NoInitialStateError);
QCOMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
@@ -862,6 +861,7 @@ void tst_QStateMachine::historyStateHasNowhereToGo()
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
+ QCOMPARE(machine.isRunning(), true);
QCOMPARE(machine.configuration().count(), 1);
QVERIFY(machine.configuration().contains(machine.errorState()));
QCOMPARE(machine.error(), QStateMachine::NoDefaultStateInHistoryStateError);
@@ -920,8 +920,7 @@ void tst_QStateMachine::transitionToStateNotInGraph()
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: No common ancestor for targets and source of transition from state 'initialState'");
QCoreApplication::processEvents();
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(qobject_cast<QState*>(machine.rootState())->errorState()));
+ QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::customErrorStateNotInGraph()
@@ -932,7 +931,7 @@ void tst_QStateMachine::customErrorStateNotInGraph()
errorState.setObjectName("errorState");
QTest::ignoreMessage(QtWarningMsg, "QState::setErrorState: error state cannot belong to a different state machine");
machine.setErrorState(&errorState);
- QVERIFY(&errorState != machine.errorState());
+ QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
QState *initialBrokenState = new QState(machine.rootState());
initialBrokenState->setObjectName("initialBrokenState");
@@ -942,9 +941,8 @@ void tst_QStateMachine::customErrorStateNotInGraph()
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'initialBrokenState'");
QCoreApplication::processEvents();
-
- QCOMPARE(machine.configuration().count(), 1);
- QVERIFY(machine.configuration().contains(machine.errorState()));
+
+ QCOMPARE(machine.isRunning(), false);
}
void tst_QStateMachine::restoreProperties()
@@ -1019,8 +1017,7 @@ void tst_QStateMachine::addAndRemoveState()
{
QStateMachine machine;
QStatePrivate *root_d = QStatePrivate::get(machine.rootState());
- QCOMPARE(root_d->childStates().size(), 1); // the error state
- QCOMPARE(root_d->childStates().at(0), (QAbstractState*)machine.errorState());
+ QCOMPARE(root_d->childStates().size(), 0);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: cannot add null state");
machine.addState(0);
@@ -1031,9 +1028,8 @@ void tst_QStateMachine::addAndRemoveState()
machine.addState(s1);
QCOMPARE(s1->machine(), &machine);
QCOMPARE(s1->parentState(), machine.rootState());
- QCOMPARE(root_d->childStates().size(), 2);
- QCOMPARE(root_d->childStates().at(0), (QAbstractState*)machine.errorState());
- QCOMPARE(root_d->childStates().at(1), (QAbstractState*)s1);
+ QCOMPARE(root_d->childStates().size(), 1);
+ QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine");
machine.addState(s1);
@@ -1042,24 +1038,21 @@ void tst_QStateMachine::addAndRemoveState()
QCOMPARE(s2->parentState(), (QState*)0);
machine.addState(s2);
QCOMPARE(s2->parentState(), machine.rootState());
- QCOMPARE(root_d->childStates().size(), 3);
- QCOMPARE(root_d->childStates().at(0), (QAbstractState*)machine.errorState());
- QCOMPARE(root_d->childStates().at(1), (QAbstractState*)s1);
- QCOMPARE(root_d->childStates().at(2), (QAbstractState*)s2);
+ QCOMPARE(root_d->childStates().size(), 2);
+ QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
+ QCOMPARE(root_d->childStates().at(1), (QAbstractState*)s2);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine");
machine.addState(s2);
machine.removeState(s1);
QCOMPARE(s1->parentState(), (QState*)0);
- QCOMPARE(root_d->childStates().size(), 2);
- QCOMPARE(root_d->childStates().at(0), (QAbstractState*)machine.errorState());
- QCOMPARE(root_d->childStates().at(1), (QAbstractState*)s2);
+ QCOMPARE(root_d->childStates().size(), 1);
+ QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s2);
machine.removeState(s2);
QCOMPARE(s2->parentState(), (QState*)0);
- QCOMPARE(root_d->childStates().size(), 1);
- QCOMPARE(root_d->childStates().at(0), (QAbstractState*)machine.errorState());
+ QCOMPARE(root_d->childStates().size(), 0);
QTest::ignoreMessage(QtWarningMsg, "QStateMachine::removeState: cannot remove null state");
machine.removeState(0);
@@ -2393,12 +2386,10 @@ void tst_QStateMachine::targetStateWithNoParent()
QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
machine.start();
QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: No common ancestor for targets and source of transition from state 's1'");
- QTRY_COMPARE(machine.isRunning(), true);
QTRY_COMPARE(startedSpy.count(), 1);
- QCOMPARE(stoppedSpy.count(), 0);
+ QCOMPARE(machine.isRunning(), false);
+ QCOMPARE(stoppedSpy.count(), 1);
QCOMPARE(finishedSpy.count(), 0);
- QCOMPARE(machine.configuration().size(), 1);
- QVERIFY(machine.configuration().contains(machine.errorState()));
QCOMPARE(machine.error(), QStateMachine::NoCommonAncestorForTransitionError);
}
@@ -2453,6 +2444,25 @@ void tst_QStateMachine::defaultGlobalRestorePolicy()
QCOMPARE(propertyHolder->property("b").toInt(), 4);
}
+void tst_QStateMachine::noInitialStateForInitialState()
+{
+ QStateMachine machine;
+
+ QState *initialState = new QState(machine.rootState());
+ initialState->setObjectName("initialState");
+ machine.setInitialState(initialState);
+
+ QState *childState = new QState(initialState);
+
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
+ "Missing initial state in compound state 'initialState'");
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QCOMPARE(machine.isRunning(), false);
+ QCOMPARE(int(machine.error()), int(QStateMachine::NoInitialStateError));
+}
+
/*
void tst_QStateMachine::restorePolicyNotInherited()
{