blob: bbc3f890bd8d05eaf0d14a5046b203e977064a5d [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite 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/QtTest>
#include "qstate.h"
#include "qstatemachine.h"
#include "qsignaltransition.h"
class tst_QState : public QObject
{
Q_OBJECT
private slots:
void assignProperty();
void assignPropertyTwice();
void historyInitialState();
void transitions();
void privateSignals();
void parallelStateAndInitialState();
};
class TestClass: public QObject
{
Q_OBJECT
public:
TestClass() : called(false) {}
bool called;
public slots:
void slot() { called = true; }
};
void tst_QState::assignProperty()
{
QStateMachine machine;
QObject object;
object.setProperty("fooBar", 10);
QState *s1 = new QState(&machine);
s1->assignProperty(&object, "fooBar", 20);
machine.setInitialState(s1);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(object.property("fooBar").toInt(), 20);
}
void tst_QState::assignPropertyTwice()
{
QStateMachine machine;
QObject object;
object.setProperty("fooBar", 10);
QState *s1 = new QState(&machine);
s1->assignProperty(&object, "fooBar", 20);
s1->assignProperty(&object, "fooBar", 30);
machine.setInitialState(s1);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(object.property("fooBar").toInt(), 30);
}
class EventTestTransition: public QAbstractTransition
{
public:
EventTestTransition(QEvent::Type type, QState *targetState)
: QAbstractTransition(), m_type(type)
{
setTargetState(targetState);
}
protected:
bool eventTest(QEvent *e)
{
return e->type() == m_type;
}
void onTransition(QEvent *) {}
private:
QEvent::Type m_type;
};
void tst_QState::historyInitialState()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QHistoryState *h1 = new QHistoryState(s2);
s2->setInitialState(h1);
QState *s3 = new QState(s2);
h1->setDefaultState(s3);
QState *s4 = new QState(s2);
s1->addTransition(new EventTestTransition(QEvent::User, s2));
s2->addTransition(new EventTestTransition(QEvent::User, s1));
s3->addTransition(new EventTestTransition(QEvent::Type(QEvent::User+1), s4));
machine.setInitialState(s1);
machine.start();
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s3));
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s3));
machine.postEvent(new QEvent(QEvent::Type(QEvent::User+1)));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s4));
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 1);
QVERIFY(machine.configuration().contains(s1));
machine.postEvent(new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(machine.configuration().size(), 2);
QVERIFY(machine.configuration().contains(s2));
QVERIFY(machine.configuration().contains(s4));
}
void tst_QState::transitions()
{
QState s1;
QState s2;
QVERIFY(s1.transitions().isEmpty());
QAbstractTransition *t1 = s1.addTransition(this, SIGNAL(destroyed()), &s2);
QAbstractTransition *t1_1 = s1.addTransition(this, &tst_QState::destroyed, &s2);
QVERIFY(t1 != 0);
QVERIFY(t1_1 != 0);
QCOMPARE(s1.transitions().count(), 2);
QCOMPARE(s1.transitions().first(), t1);
QCOMPARE(s1.transitions().last(), t1_1);
QVERIFY(s2.transitions().isEmpty());
s1.removeTransition(t1);
s1.removeTransition(t1_1);
QVERIFY(s1.transitions().isEmpty());
s1.addTransition(t1);
QCOMPARE(s1.transitions().count(), 1);
QCOMPARE(s1.transitions().first(), t1);
QAbstractTransition *t2 = new QEventTransition(&s1);
QCOMPARE(s1.transitions().count(), 2);
QVERIFY(s1.transitions().contains(t1));
QVERIFY(s1.transitions().contains(t2));
// Transitions from child states should not be reported.
QState *s21 = new QState(&s2);
QAbstractTransition *t3 = s21->addTransition(this, SIGNAL(destroyed()), &s2);
QVERIFY(s2.transitions().isEmpty());
QCOMPARE(s21->transitions().count(), 1);
QCOMPARE(s21->transitions().first(), t3);
}
class MyState : public QState
{
Q_OBJECT
public:
MyState(QState *parent = 0)
: QState(parent)
{
}
void emitPrivateSignals()
{
// These deliberately do not compile
// emit entered();
// emit exited();
//
// emit entered(QPrivateSignal());
// emit exited(QPrivateSignal());
//
// emit entered(QAbstractState::QPrivateSignal());
// emit exited(QAbstractState::QPrivateSignal());
}
};
class MyTransition : public QSignalTransition
{
Q_OBJECT
public:
MyTransition(QObject * sender, const char * signal, QState *sourceState = 0)
: QSignalTransition(sender, signal, sourceState)
{
}
void emitPrivateSignals()
{
// These deliberately do not compile
// emit triggered();
//
// emit triggered(QPrivateSignal());
//
// emit triggered(QAbstractTransition::QPrivateSignal());
}
};
class SignalConnectionTester : public QObject
{
Q_OBJECT
public:
SignalConnectionTester(QObject *parent = 0)
: QObject(parent), testPassed(false)
{
}
public Q_SLOTS:
void testSlot()
{
testPassed = true;
}
public:
bool testPassed;
};
class TestTrigger : public QObject
{
Q_OBJECT
public:
TestTrigger(QObject *parent = 0)
: QObject(parent)
{
}
void emitTrigger()
{
emit trigger();
}
signals:
void trigger();
};
void tst_QState::privateSignals()
{
QStateMachine machine;
QState *s1 = new QState(&machine);
MyState *s2 = new MyState(&machine);
TestTrigger testTrigger;
MyTransition *t1 = new MyTransition(&testTrigger, SIGNAL(trigger()), s1);
s1->addTransition(t1);
t1->setTargetState(s2);
machine.setInitialState(s1);
machine.start();
QCoreApplication::processEvents();
SignalConnectionTester s1Tester;
SignalConnectionTester s2Tester;
SignalConnectionTester t1Tester;
QObject::connect(s1, &QState::exited, &s1Tester, &SignalConnectionTester::testSlot);
QObject::connect(s2, &QState::entered, &s2Tester, &SignalConnectionTester::testSlot);
QObject::connect(t1, &QSignalTransition::triggered, &t1Tester, &SignalConnectionTester::testSlot);
testTrigger.emitTrigger();
QCoreApplication::processEvents();
QVERIFY(s1Tester.testPassed);
QVERIFY(s2Tester.testPassed);
QVERIFY(t1Tester.testPassed);
}
void tst_QState::parallelStateAndInitialState()
{
QStateMachine machine;
{ // setting an initial state on a parallel state:
QState a(QState::ParallelStates, &machine);
QState b(&a);
QVERIFY(!a.initialState());
const QString warning
= QString::asprintf("QState::setInitialState: ignoring attempt to set initial state of parallel state group %p", &a);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
a.setInitialState(&b); // should produce a warning and do nothing.
QVERIFY(!a.initialState());
}
{ // setting the child-mode from ExclusiveStates to ParallelStates should remove the initial state:
QState a(QState::ExclusiveStates, &machine);
QState b(&a);
a.setInitialState(&b);
QCOMPARE(a.initialState(), &b);
const QString warning
= QString::asprintf("QState::setChildMode: setting the child-mode of state %p to "
"parallel removes the initial state", &a);
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
a.setChildMode(QState::ParallelStates); // should produce a warning and remove the initial state
QVERIFY(!a.initialState());
QCOMPARE(a.childMode(), QState::ParallelStates);
}
}
QTEST_MAIN(tst_QState)
#include "tst_qstate.moc"