QStateMachine: Fix transition ordering.
When there are conflicting transitions, a transition that is nested deeper (i.e. more specific) has priority. If two transitions have the same nesting level, the one that comes first in the document order gets priority. Before this patch, only the document order was considered. Change-Id: I58f188c270cabe2c386a783ceef7a0a955105425 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@theqtcompany.com>bb10
parent
e46b6db986
commit
eb4bf7df60
|
|
@ -299,6 +299,17 @@ static bool containsDecendantOf(const QSet<QAbstractState *> &states, const QAbs
|
|||
return false;
|
||||
}
|
||||
|
||||
static int descendantDepth(const QAbstractState *state, const QAbstractState *ancestor)
|
||||
{
|
||||
int depth = 0;
|
||||
for (const QAbstractState *it = state; it != 0; it = it->parentState()) {
|
||||
if (it == ancestor)
|
||||
break;
|
||||
++depth;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
|
||||
|
||||
function getProperAncestors(state1, state2)
|
||||
|
|
@ -451,10 +462,25 @@ static int indexOfDescendant(QState *s, QAbstractState *desc)
|
|||
bool QStateMachinePrivate::transitionStateEntryLessThan(QAbstractTransition *t1, QAbstractTransition *t2)
|
||||
{
|
||||
QState *s1 = t1->sourceState(), *s2 = t2->sourceState();
|
||||
if (s1 == s2)
|
||||
return QStatePrivate::get(s1)->transitions().indexOf(t1) < QStatePrivate::get(s2)->transitions().indexOf(t2);
|
||||
else
|
||||
return stateEntryLessThan(t1->sourceState(), t2->sourceState());
|
||||
if (s1 == s2) {
|
||||
QList<QAbstractTransition*> transitions = QStatePrivate::get(s1)->transitions();
|
||||
return transitions.indexOf(t1) < transitions.indexOf(t2);
|
||||
} else if (isDescendant(s1, s2)) {
|
||||
return true;
|
||||
} else if (isDescendant(s2, s1)) {
|
||||
return false;
|
||||
} else {
|
||||
Q_ASSERT(s1->machine() != 0);
|
||||
QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
|
||||
QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
|
||||
Q_ASSERT(lca != 0);
|
||||
int s1Depth = descendantDepth(s1, lca);
|
||||
int s2Depth = descendantDepth(s2, lca);
|
||||
if (s1Depth == s2Depth)
|
||||
return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
|
||||
else
|
||||
return s1Depth > s2Depth;
|
||||
}
|
||||
}
|
||||
|
||||
bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2)
|
||||
|
|
@ -594,7 +620,7 @@ void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransitio
|
|||
{
|
||||
Q_ASSERT(cache);
|
||||
|
||||
if (enabledTransitions.size() == 1)
|
||||
if (enabledTransitions.size() < 2)
|
||||
return; // There is no transition to conflict with.
|
||||
|
||||
QList<QAbstractTransition*> filteredTransitions;
|
||||
|
|
@ -2081,6 +2107,7 @@ void QStateMachinePrivate::emitStateFinished(QState *forState, QFinalState *guil
|
|||
Q_ASSERT(guiltyState);
|
||||
|
||||
#ifdef QSTATEMACHINE_DEBUG
|
||||
Q_Q(QStateMachine);
|
||||
qDebug() << q << ": emitting finished signal for" << forState;
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ private slots:
|
|||
void qtbug_44963();
|
||||
void qtbug_44783();
|
||||
void internalTransition();
|
||||
void conflictingTransition();
|
||||
};
|
||||
|
||||
class TestState : public QState
|
||||
|
|
@ -6382,5 +6383,68 @@ void tst_QStateMachine::internalTransition()
|
|||
TEST_ACTIVE_CHANGED(s, 1);
|
||||
}
|
||||
|
||||
void tst_QStateMachine::conflictingTransition()
|
||||
{
|
||||
SignalEmitter emitter;
|
||||
|
||||
QStateMachine machine;
|
||||
QState b(QState::ParallelStates, &machine);
|
||||
QState c(&b);
|
||||
QState d(QState::ParallelStates, &b);
|
||||
QState e(&d);
|
||||
QState e1(&e);
|
||||
QState e2(&e);
|
||||
QState f(&d);
|
||||
QState f1(&f);
|
||||
QState f2(&f);
|
||||
QState a1(&machine);
|
||||
|
||||
machine.setInitialState(&b);
|
||||
e.setInitialState(&e1);
|
||||
f.setInitialState(&f1);
|
||||
c.addTransition(&emitter, SIGNAL(signalWithNoArg()), &a1)->setObjectName("c->a1");
|
||||
e1.addTransition(&emitter, SIGNAL(signalWithNoArg()), &e2)->setObjectName("e1->e2");
|
||||
f1.addTransition(&emitter, SIGNAL(signalWithNoArg()), &f2)->setObjectName("f1->f2");
|
||||
|
||||
b.setObjectName("b");
|
||||
c.setObjectName("c");
|
||||
d.setObjectName("d");
|
||||
e.setObjectName("e");
|
||||
e1.setObjectName("e1");
|
||||
e2.setObjectName("e2");
|
||||
f.setObjectName("f");
|
||||
f1.setObjectName("f1");
|
||||
f2.setObjectName("f2");
|
||||
a1.setObjectName("a1");
|
||||
|
||||
machine.start();
|
||||
|
||||
QTRY_COMPARE(machine.configuration().contains(&b), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&c), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&d), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&e), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&e1), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&e2), false);
|
||||
QTRY_COMPARE(machine.configuration().contains(&f), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&f1), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&f2), false);
|
||||
QTRY_COMPARE(machine.configuration().contains(&a1), false);
|
||||
|
||||
emitter.emitSignalWithNoArg();
|
||||
|
||||
QTRY_COMPARE(machine.configuration().contains(&b), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&c), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&d), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&e), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&e1), false);
|
||||
QTRY_COMPARE(machine.configuration().contains(&e2), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&f), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&f1), false);
|
||||
QTRY_COMPARE(machine.configuration().contains(&f2), true);
|
||||
QTRY_COMPARE(machine.configuration().contains(&a1), false);
|
||||
|
||||
QVERIFY(machine.isRunning());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QStateMachine)
|
||||
#include "tst_qstatemachine.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue