From b39781b214e2502e0884bce88aa3ac324f2d0b12 Mon Sep 17 00:00:00 2001
From: Eskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>
Date: Thu, 7 May 2009 13:03:59 +0200
Subject: Make it impossible to have root state as source or target of
 transition or as error state

Since the root state has no ancestors, it cannot be source or target in
transitions since there will be no LCA for the transition, which is required for
the algorithm of enterStates and exitStates. In SCXML the root state cannot be
target or source of a transition. By the same logic, it cannot be an error
state. The root state will always have a valid machine, since it's added to
a machine immediately, which makes this code possible.
---
 src/corelib/statemachine/qabstracttransition.cpp | 15 ++++++++++++---
 src/corelib/statemachine/qstate.cpp              | 13 ++++++++++++-
 src/corelib/statemachine/qstatemachine.cpp       |  2 ++
 tests/auto/qstatemachine/tst_qstatemachine.cpp   |  7 +++++--
 4 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp
index 1897aa6..c6a261d 100644
--- a/src/corelib/statemachine/qabstracttransition.cpp
+++ b/src/corelib/statemachine/qabstracttransition.cpp
@@ -180,7 +180,7 @@ QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets,
     d_ptr->q_ptr = this;
 #endif
     Q_D(QAbstractTransition);
-    d->targetStates = targets;
+    setTargetStates(targets);
 }
 
 /*!
@@ -221,7 +221,7 @@ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd,
     d_ptr->q_ptr = this;
 #endif
     Q_D(QAbstractTransition);
-    d->targetStates = targets;
+    setTargetStates(targets);
 }
 
 /*!
@@ -265,7 +265,7 @@ void QAbstractTransition::setTargetState(QAbstractState* target)
     if (!target)
         d->targetStates.clear();
     else
-        d->targetStates = QList<QAbstractState*>() << target;
+        setTargetStates(QList<QAbstractState*>() << target);
 }
 
 /*!
@@ -284,6 +284,15 @@ QList<QAbstractState*> QAbstractTransition::targetStates() const
 void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets)
 {
     Q_D(QAbstractTransition);
+
+    for (int i=0; i<targets.size(); ++i) {
+        QAbstractState *target = targets.at(i);
+        if (target->machine() != 0 && target->machine()->rootState() == target) {
+            qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition");
+            return;
+        }
+    }
+
     d->targetStates = targets;
 }
 
diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp
index 4c9e033..acee27d 100644
--- a/src/corelib/statemachine/qstate.cpp
+++ b/src/corelib/statemachine/qstate.cpp
@@ -280,11 +280,15 @@ QAbstractState *QState::errorState() const
 void QState::setErrorState(QAbstractState *state)
 {
     Q_D(QState);
-    if (state != 0 && QAbstractStatePrivate::get(state)->machine() != d->machine()) {
+    if (state != 0 && state->machine() != machine()) {
         qWarning("QState::setErrorState: error state cannot belong "
                  "to a different state machine");
         return;
     }
+    if (state->machine() != 0 && state->machine()->rootState() == state) {
+        qWarning("QStateMachine::setErrorState: root state cannot be error state");
+        return;
+    }
 
     d->errorState = state;
 }
@@ -301,6 +305,13 @@ QAbstractTransition *QState::addTransition(QAbstractTransition *transition)
         qWarning("QState::addTransition: cannot add null transition");
         return 0;
     }
+
+    // machine() will always be non-null for root state
+    if (machine() != 0 && machine()->rootState() == this) {
+        qWarning("QState::addTransition: cannot add transition from root state");
+        return 0;
+    }
+
     const QList<QAbstractState*> &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
     for (int i = 0; i < targets.size(); ++i) {
         QAbstractState *t = targets.at(i);
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index 21e564c..110a4f8 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -1011,6 +1011,8 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta
     }
 
     Q_ASSERT(currentErrorState != 0);
+    Q_ASSERT(currentErrorState != rootState);
+    
     QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
     addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry);
 }
diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp
index edd6459..9b7bff3 100644
--- a/tests/auto/qstatemachine/tst_qstatemachine.cpp
+++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp
@@ -254,6 +254,8 @@ private:
 
 void tst_QStateMachine::transitionToRootState()
 {
+    s_countWarnings = false;
+
     QStateMachine machine;
 
     QState *initialState = new QState();
@@ -668,6 +670,8 @@ void tst_QStateMachine::errorStateHasErrors()
 
 void tst_QStateMachine::errorStateIsRootState()
 {
+    s_countWarnings = false;
+
     QStateMachine machine;
     machine.setErrorState(machine.rootState());
 
@@ -691,9 +695,8 @@ void tst_QStateMachine::errorStateIsRootState()
     machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
     QCoreApplication::processEvents();
 
-    QEXPECT_FAIL("", "Covered by transitionToRootState test. Remove this when that test passes", Continue);
     QCOMPARE(machine.configuration().count(), 1);    
-    QVERIFY(machine.configuration().contains(initialState));
+    QVERIFY(machine.configuration().contains(machine.errorState()));
 }
 
 void tst_QStateMachine::errorStateEntersParentFirst()
-- 
cgit v0.12