/** * AES module test fire automaton * $Id$ * $HeadURL$ * $Author$ */ #include #include #include #include #include #include #include #include #include #include "aes_test_fire_automaton.h" using namespace AreaEffectSimulation; void TestFireAutomaton::initTestCase() { } void TestFireAutomaton::init() { m_receivedFire.clear(); m_finishedTime = 0; } void TestFireAutomaton::fireReceived(int32_t id) { m_receivedFire.append({QDateTime::currentMSecsSinceEpoch(), id}); } /* Test basic pattern with fire slots = rounds */ void TestFireAutomaton::testSchedule_0() { int32_t effectDurationMSecs = 500; int32_t cadence = 100; int32_t numberOfRounds = 6; QVector effectorIds = {1}; QString pattern = QStringLiteral("all, next, all, next, all, next"); QVector expectedDelays = {0, 100, 200, 300, 400, 500}; QVector> expectedSchedule = {{1}, {1}, {1}, {1}, {1}, {1}}; testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule); } /* Test pattern with fill element */ void TestFireAutomaton::testSchedule_1() { int32_t effectDurationMSecs = 800; int32_t cadence = 100; int32_t numberOfRounds = 13; QVector effectorIds = {1, 2, 3}; QString pattern = QStringLiteral("all,next*,all"); QVector expectedDelays = {0, 0, 0, 100, 200, 300, 400, 500, 600, 700, 800, 800, 800}; QVector> expectedSchedule = {{1, 2, 3}, {1}, {2}, {3}, {1}, {2}, {3}, {1}, {1, 2, 3}}; testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule); } /* Test fill pattern element with 0 fill */ void TestFireAutomaton::testSchedule_2() { int32_t effectDurationMSecs = 200; int32_t cadence = 200; int32_t numberOfRounds = 6; QVector effectorIds = {1, 2, 3}; QString pattern = QStringLiteral("all,all*,all"); QVector expectedDelays = {0, 0, 0, 200, 200, 200}; QVector> expectedSchedule = {{1,2,3}, {1,2,3}}; testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule); } /* Test pattern with fire slots > rounds */ void TestFireAutomaton::testSchedule_3() { int32_t effectDurationMSecs = 200; int32_t cadence = 200; int32_t numberOfRounds = 5; QVector effectorIds = {1, 2, 3, 4}; QString pattern = QStringLiteral("all,all"); QVector expectedDelays = {0, 0, 0, 0, 200}; QVector> expectedSchedule = {{1, 2, 3, 4}, {1}}; testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule); testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule); } /* Test pattern with rounds > fire slots */ void TestFireAutomaton::testSchedule_4() { int32_t effectDurationMSecs = 100; int32_t numberOfRounds = 5; QVector effectorIds = {1}; QString pattern = QStringLiteral("next, next"); QVector expectedDelays = {0, 100}; QVector> expectedSchedule = {{1}, {1}}; FireAutomaton automaton; QVERIFY(!automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern)); } void TestFireAutomaton::testScheduling(int32_t time, FireAutomaton::TimeType type, int32_t numberOfRounds, const QVector& effectorIds, const QString& pattern, QVector expectedDelays, QVector> expectedSchedule) { FireAutomaton automaton; QVERIFY(automaton.init(time, type, numberOfRounds, effectorIds, pattern)); QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t))); QVERIFY(spyFire.isValid()); QSignalSpy spyFinished(&automaton, SIGNAL(finished())); QVERIFY(spyFinished.isValid()); connect(&automaton, &FireAutomaton::fire, this, &TestFireAutomaton::fireReceived); qint64 startTimeMSecs = QDateTime::currentMSecsSinceEpoch(); automaton.start(); QVERIFY(spyFinished.wait(1000)); QVERIFY(withinTolerance(QDateTime::currentMSecsSinceEpoch() - startTimeMSecs, expectedDelays.last(), 50)); int32_t expectedShots = 0; for(const auto& tick : expectedSchedule) expectedShots += tick.size(); QCOMPARE(spyFire.count(), expectedShots); for(int32_t i=0; i < numberOfRounds; i++) { withinTolerance(QDateTime::currentMSecsSinceEpoch() - startTimeMSecs, expectedDelays.last(), 50); } QVector> schedule; QVector tickIds; qint64 firstFireTime = m_receivedFire[0].time; if(expectedDelays.size() != m_receivedFire.size()) { QVERIFY(false); return; } // first fire time is basis for delays for(int i=0; i < m_receivedFire.size(); i++) { m_receivedFire[i].time = m_receivedFire[i].time - firstFireTime; QVERIFY(withinTolerance(m_receivedFire[i].time, expectedDelays[i], 50)); } tickIds.clear(); tickIds.append(m_receivedFire[0].id); for(int i=1; i < m_receivedFire.size(); i++) { if(withinTolerance(m_receivedFire[i - 1].time, m_receivedFire[i].time, 50)) { tickIds.append(m_receivedFire[i].id); } else { schedule.append(tickIds); tickIds.clear(); tickIds.append(m_receivedFire[i].id); } } schedule.append(tickIds); QVERIFY(schedule.size() == expectedSchedule.size()); for(int i=0; i < schedule.size(); i++) { QVERIFY(schedule[i] == expectedSchedule[i]); } m_receivedFire.clear(); } void TestFireAutomaton::testStepping(int32_t numberOfRounds, const QVector& effectorIds, const QString& pattern, QVector> expectedSchedule) { FireAutomaton automaton; QVERIFY(automaton.init(0, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern)); QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t))); QVERIFY(spyFire.isValid()); QSignalSpy spyFinished(&automaton, SIGNAL(finished())); QVERIFY(spyFinished.isValid()); connect(&automaton, &FireAutomaton::fire, this, &TestFireAutomaton::fireReceived); for(int i=0; i < expectedSchedule.size(); i++) { QVERIFY(automaton.performStep()); QTest::qWait(100); } QVERIFY(spyFinished.count() == 1); QVector> schedule; QVector tickIds; qint64 firstFireTime = m_receivedFire[0].time; // first fire time is basis for delays for(int i=0; i < m_receivedFire.size(); i++) { m_receivedFire[i].time = m_receivedFire[i].time - firstFireTime; } tickIds.clear(); tickIds.append(m_receivedFire[0].id); for(int i=1; i < m_receivedFire.size(); i++) { if(withinTolerance(m_receivedFire[i - 1].time, m_receivedFire[i].time, 50)) { tickIds.append(m_receivedFire[i].id); } else { schedule.append(tickIds); tickIds.clear(); tickIds.append(m_receivedFire[i].id); } } schedule.append(tickIds); QVERIFY(schedule.size() == expectedSchedule.size()); for(int i=0; i < schedule.size(); i++) { QVERIFY(schedule[i] == expectedSchedule[i]); } m_receivedFire.clear(); } void TestFireAutomaton::testResumePaused() { QVector effectorIds = {1}; int32_t effectDurationMSecs = 1000; int32_t numberOfRounds = 2; FireAutomaton automaton; QVERIFY(automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, QStringLiteral("all*"))); QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t))); QVERIFY(spyFire.isValid()); QSignalSpy spyFinished(&automaton, SIGNAL(finished())); QVERIFY(spyFinished.isValid()); qint64 startTimeMSecs = QDateTime::currentMSecsSinceEpoch(); QVERIFY(automaton.start()); QTest::qWait(100); automaton.pause(); QCOMPARE(automaton.getState(), FireAutomaton::State::Paused); qint64 pauseTimeMSecs = QDateTime::currentMSecsSinceEpoch(); QVERIFY(spyFire.count() == 1); QTest::qWait(100); qint64 resumeTimeMSecs = QDateTime::currentMSecsSinceEpoch(); automaton.resume(); QCOMPARE(automaton.getState(), FireAutomaton::State::Running); QVERIFY(spyFinished.wait(2000)); qint64 measuredDuration = (QDateTime::currentMSecsSinceEpoch() - startTimeMSecs) - resumeTimeMSecs + pauseTimeMSecs; QVERIFY(spyFinished.count() == 1); QVERIFY(withinTolerance(measuredDuration, effectDurationMSecs, 100)); QVERIFY(spyFire.count() == numberOfRounds); } void TestFireAutomaton::testStop() { QVector effectorIds = {1, 2, 3, 4, 5, 6}; int32_t effectDurationMSecs = 500; int32_t numberOfRounds = 10; FireAutomaton automaton; QVERIFY(automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, QStringLiteral("next*"))); QTest::qWait(10); QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t))); QVERIFY(spyFire.isValid()); QSignalSpy spyFinished(&automaton, SIGNAL(finished())); QVERIFY(spyFinished.isValid()); QVERIFY(automaton.start()); QTest::qWait(100); automaton.stop(); int32_t fireCount = spyFire.count(); QVERIFY(!spyFinished.wait(1000)); // expected to timeout QCOMPARE(spyFire.count(), fireCount); // expected to not have changed QVERIFY(spyFire.count() < numberOfRounds); // start to finish qint64 startTimeMSecs = QDateTime::currentMSecsSinceEpoch(); QVERIFY(automaton.start()); QVERIFY(spyFinished.wait(1000)); QVERIFY(spyFire.count() - fireCount == numberOfRounds); QVERIFY(withinTolerance(QDateTime::currentMSecsSinceEpoch() - startTimeMSecs, effectDurationMSecs, 50)); } void TestFireAutomaton::testStates() { QVector effectorIds = {11, 10}; int32_t effectDurationMSecs = 500; int32_t numberOfRounds = 10; FireAutomaton automaton; QSignalSpy spyFinished(&automaton, SIGNAL(finished())); QVERIFY(spyFinished.isValid()); // init QCOMPARE(automaton.getState(), FireAutomaton::State::Uninitialized); QVERIFY(!automaton.start()); QVERIFY(!automaton.pause()); QVERIFY(!automaton.resume()); QVERIFY(automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, QStringLiteral("all*"))); QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped); QVERIFY(!automaton.pause()); QVERIFY(!automaton.resume()); // start -> pause -> resume -> stop QVERIFY(automaton.start()); QCOMPARE(automaton.getState(), FireAutomaton::State::Running); QVERIFY(!automaton.start()); QVERIFY(!automaton.resume()); QVERIFY(automaton.pause()); QCOMPARE(automaton.getState(), FireAutomaton::State::Paused); QVERIFY(!automaton.start()); QVERIFY(!automaton.pause()); QVERIFY(automaton.resume()); QCOMPARE(automaton.getState(), FireAutomaton::State::Running); QVERIFY(spyFinished.wait(1000)); QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped); QVERIFY(!automaton.pause()); QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped); QVERIFY(!automaton.resume()); QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped); // start -> stop QVERIFY(automaton.start()); QCOMPARE(automaton.getState(), FireAutomaton::State::Running); QVERIFY(!automaton.start()); QVERIFY(!automaton.resume()); QVERIFY(spyFinished.wait(1000)); QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped); // start -> finish QVERIFY(automaton.start()); QCOMPARE(automaton.getState(), FireAutomaton::State::Running); QVERIFY(!automaton.start()); QVERIFY(!automaton.resume()); QVERIFY(spyFinished.wait(1000)); QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped); } int main(int argc, char *argv[]) { qRegisterMetaType("int32_t"); NGLCoreApplication app(argc, argv); app.createLogger("UnitTest_FireAutomaton", "log4cxx_aes.props"); TestFireAutomaton testCase; return QTest::qExec(&testCase, argc, argv); }