|
@@ -27,37 +27,21 @@
|
|
|
#include <ctkCmdLineModuleParameter.h>
|
|
|
#include <ctkCmdLineModuleRunException.h>
|
|
|
#include <ctkCmdLineModuleFuture.h>
|
|
|
+#include <ctkCmdLineModuleFutureWatcher.h>
|
|
|
|
|
|
#include "ctkCmdLineModuleSignalTester.h"
|
|
|
|
|
|
#include "ctkCmdLineModuleBackendLocalProcess.h"
|
|
|
|
|
|
+#include "ctkTest.h"
|
|
|
+
|
|
|
#include <QVariant>
|
|
|
#include <QCoreApplication>
|
|
|
#include <QDebug>
|
|
|
#include <QFutureWatcher>
|
|
|
|
|
|
-#include <cstdlib>
|
|
|
-
|
|
|
-
|
|
|
-#ifdef Q_OS_WIN
|
|
|
-#include <windows.h>
|
|
|
-#else
|
|
|
-#include <time.h>
|
|
|
-#endif
|
|
|
-
|
|
|
-void sleep_ms(int ms)
|
|
|
-{
|
|
|
-#ifdef Q_OS_WIN
|
|
|
- Sleep(ms);
|
|
|
-#else
|
|
|
- struct timespec nanostep;
|
|
|
- nanostep.tv_sec = ms / 1000;
|
|
|
- nanostep.tv_nsec = ((ms % 1000) * 1000.0 * 1000.0);
|
|
|
- nanosleep(&nanostep, NULL);
|
|
|
-#endif
|
|
|
-}
|
|
|
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
class ctkCmdLineModuleFrontendMockupFactory : public ctkCmdLineModuleFrontendFactory
|
|
|
{
|
|
|
public:
|
|
@@ -97,110 +81,167 @@ public:
|
|
|
virtual QString description() const { return "A mock-up factory for testing."; }
|
|
|
};
|
|
|
|
|
|
-bool futureTestStartFinish(ctkCmdLineModuleManager* manager, ctkCmdLineModuleFrontend* frontend)
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+class ctkCmdLineModuleFutureTester : public QObject
|
|
|
{
|
|
|
- qDebug() << "Testing ctkCmdLineModuleFuture start/finish signals.";
|
|
|
+ Q_OBJECT
|
|
|
|
|
|
- QList<QString> expectedSignals;
|
|
|
- expectedSignals.push_back("module.started");
|
|
|
- expectedSignals.push_back("module.finished");
|
|
|
+public Q_SLOTS:
|
|
|
|
|
|
- ctkCmdLineModuleSignalTester signalTester;
|
|
|
+ void ouputDataReady();
|
|
|
+ void errorDataReady();
|
|
|
|
|
|
- QFutureWatcher<ctkCmdLineModuleResult> watcher;
|
|
|
- QObject::connect(&watcher, SIGNAL(started()), &signalTester, SLOT(moduleStarted()));
|
|
|
- QObject::connect(&watcher, SIGNAL(finished()), &signalTester, SLOT(moduleFinished()));
|
|
|
+private Q_SLOTS:
|
|
|
|
|
|
- ctkCmdLineModuleFuture future = manager->run(frontend);
|
|
|
- watcher.setFuture(future);
|
|
|
+ void initTestCase();
|
|
|
|
|
|
- try
|
|
|
+ void init();
|
|
|
+ void cleanup();
|
|
|
+
|
|
|
+ void testStartFinish();
|
|
|
+ void testProgress();
|
|
|
+ void testPauseAndCancel();
|
|
|
+ void testOutput();
|
|
|
+ void testError();
|
|
|
+
|
|
|
+private:
|
|
|
+
|
|
|
+ QByteArray outputData;
|
|
|
+ QByteArray errorData;
|
|
|
+
|
|
|
+ ctkCmdLineModuleFutureWatcher* currentWatcher;
|
|
|
+
|
|
|
+ ctkCmdLineModuleFrontendMockupFactory factory;
|
|
|
+ ctkCmdLineModuleBackendLocalProcess backend;
|
|
|
+
|
|
|
+ ctkCmdLineModuleManager manager;
|
|
|
+
|
|
|
+ ctkCmdLineModuleReference moduleRef;
|
|
|
+ ctkCmdLineModuleFrontend* frontend;
|
|
|
+};
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::ouputDataReady()
|
|
|
+{
|
|
|
+ if (this->currentWatcher)
|
|
|
{
|
|
|
- future.waitForFinished();
|
|
|
+ outputData.append(currentWatcher->readPendingOutputData());
|
|
|
}
|
|
|
- catch (const ctkCmdLineModuleRunException& e)
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::errorDataReady()
|
|
|
+{
|
|
|
+ if (this->currentWatcher)
|
|
|
{
|
|
|
- qDebug() << e;
|
|
|
- return false;
|
|
|
+ errorData.append(currentWatcher->readPendingErrorData());
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- // process pending events
|
|
|
- QCoreApplication::processEvents();
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::initTestCase()
|
|
|
+{
|
|
|
+ manager.registerBackend(&backend);
|
|
|
+
|
|
|
+ QUrl moduleUrl = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/ctkCmdLineModuleTestBed");
|
|
|
+ moduleRef = manager.registerModule(moduleUrl);
|
|
|
+}
|
|
|
|
|
|
- return signalTester.checkSignals(expectedSignals);
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::init()
|
|
|
+{
|
|
|
+ currentWatcher = 0;
|
|
|
+ frontend = factory.create(moduleRef);
|
|
|
}
|
|
|
|
|
|
-bool futureTestProgress(ctkCmdLineModuleManager* manager, ctkCmdLineModuleFrontend* frontend)
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::cleanup()
|
|
|
{
|
|
|
- qDebug() << "Testing ctkCmdLineModuleFuture progress signals.";
|
|
|
+ delete frontend;
|
|
|
+ outputData.clear();
|
|
|
+ errorData.clear();
|
|
|
+}
|
|
|
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::testStartFinish()
|
|
|
+{
|
|
|
QList<QString> expectedSignals;
|
|
|
- expectedSignals.push_back("module.started");
|
|
|
- // this signal is send when connecting a QFutureWatcher to
|
|
|
- // an already started QFuture
|
|
|
- expectedSignals.push_back("module.progressValueChanged");
|
|
|
-
|
|
|
- // the following two signals are send when the module reports "filter start"
|
|
|
- expectedSignals.push_back("module.progressValueChanged");
|
|
|
- expectedSignals.push_back("module.progressTextChanged");
|
|
|
-
|
|
|
- // this signal is send when the module reports progress for "output1"
|
|
|
- expectedSignals.push_back("module.progressValueChanged");
|
|
|
-
|
|
|
- // the following two signal are sent at the end to report
|
|
|
- // completion and the full standard output text.
|
|
|
- expectedSignals.push_back("module.progressValueChanged");
|
|
|
- expectedSignals.push_back("module.progressTextChanged");
|
|
|
- expectedSignals.push_back("module.finished");
|
|
|
+ expectedSignals << "module.started"
|
|
|
+ << "module.progressRangeChanged(0,0)"
|
|
|
+ << "module.progressValueChanged(0)"
|
|
|
+ << "module.progressRangeChanged(0,1000)"
|
|
|
+ << "module.errorReady"
|
|
|
+ << "module.progressValueChanged(1000)"
|
|
|
+ << "module.finished";
|
|
|
|
|
|
ctkCmdLineModuleSignalTester signalTester;
|
|
|
|
|
|
- QFutureWatcher<ctkCmdLineModuleResult> watcher;
|
|
|
- QObject::connect(&watcher, SIGNAL(started()), &signalTester, SLOT(moduleStarted()));
|
|
|
- QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &signalTester, SLOT(moduleProgressValueChanged(int)));
|
|
|
- QObject::connect(&watcher, SIGNAL(progressTextChanged(QString)), &signalTester, SLOT(moduleProgressTextChanged(QString)));
|
|
|
- QObject::connect(&watcher, SIGNAL(finished()), &signalTester, SLOT(moduleFinished()));
|
|
|
+ ctkCmdLineModuleFuture future = manager.run(frontend);
|
|
|
+ signalTester.setFuture(future);
|
|
|
+ future.waitForFinished();
|
|
|
+
|
|
|
+ QCoreApplication::processEvents();
|
|
|
+ QVERIFY(signalTester.checkSignals(expectedSignals));
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::testProgress()
|
|
|
+{
|
|
|
+ QList<QString> expectedSignals;
|
|
|
+ expectedSignals << "module.started"
|
|
|
+ // the following signals are send when connecting a QFutureWatcher to
|
|
|
+ // an already started QFuture
|
|
|
+ << "module.progressRangeChanged(0,0)"
|
|
|
+ << "module.progressValueChanged(0)"
|
|
|
+ << "module.progressRangeChanged(0,1000)"
|
|
|
+
|
|
|
+ // the test module always reports error data when starting
|
|
|
+ << "module.errorReady"
|
|
|
+
|
|
|
+ // the following two signals are send when the module reports "filter start"
|
|
|
+ << "module.progressValueChanged(1)"
|
|
|
+ << "module.progressTextChanged(Test Filter)"
|
|
|
+
|
|
|
+ // this signal is send when the module reports progress for "output1"
|
|
|
+ << "module.progressValueChanged(999)"
|
|
|
+
|
|
|
+ // the output data (the order is not really deterministic here...)
|
|
|
+ << "module.outputReady"
|
|
|
+
|
|
|
+ // the following signal is sent at the end to report completion
|
|
|
+ << "module.progressValueChanged(1000)"
|
|
|
+ << "module.finished";
|
|
|
+
|
|
|
+ ctkCmdLineModuleSignalTester signalTester;
|
|
|
|
|
|
frontend->setValue("numOutputsVar", 1);
|
|
|
- ctkCmdLineModuleFuture future = manager->run(frontend);
|
|
|
- watcher.setFuture(future);
|
|
|
+ ctkCmdLineModuleFuture future = manager.run(frontend);
|
|
|
+ signalTester.setFuture(future);
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- future.waitForFinished();
|
|
|
- }
|
|
|
- catch (const ctkCmdLineModuleRunException& e)
|
|
|
- {
|
|
|
- qDebug() << e;
|
|
|
- return false;
|
|
|
- }
|
|
|
+ future.waitForFinished();
|
|
|
|
|
|
// process pending events
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
- return signalTester.checkSignals(expectedSignals);
|
|
|
+ QVERIFY(signalTester.checkSignals(expectedSignals));
|
|
|
}
|
|
|
|
|
|
-bool futureTestPauseAndCancel(ctkCmdLineModuleManager* manager, ctkCmdLineModuleFrontend* frontend)
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::testPauseAndCancel()
|
|
|
{
|
|
|
- qDebug() << "Testing ctkCmdLineModuleFuture pause and cancel capabilities";
|
|
|
-
|
|
|
-
|
|
|
ctkCmdLineModuleSignalTester signalTester;
|
|
|
|
|
|
- QFutureWatcher<ctkCmdLineModuleResult> watcher;
|
|
|
- QObject::connect(&watcher, SIGNAL(started()), &signalTester, SLOT(moduleStarted()));
|
|
|
- QObject::connect(&watcher, SIGNAL(paused()), &signalTester, SLOT(modulePaused()));
|
|
|
- QObject::connect(&watcher, SIGNAL(resumed()), &signalTester, SLOT(moduleResumed()));
|
|
|
- QObject::connect(&watcher, SIGNAL(canceled()), &signalTester, SLOT(moduleCanceled()));
|
|
|
- QObject::connect(&watcher, SIGNAL(finished()), &signalTester, SLOT(moduleFinished()));
|
|
|
-
|
|
|
frontend->setValue("runtimeVar", 60);
|
|
|
- ctkCmdLineModuleFuture future = manager->run(frontend);
|
|
|
- watcher.setFuture(future);
|
|
|
+ ctkCmdLineModuleFuture future = manager.run(frontend);
|
|
|
+ signalTester.setFuture(future);
|
|
|
|
|
|
QList<QString> expectedSignals;
|
|
|
- expectedSignals.push_back("module.started");
|
|
|
+ expectedSignals << "module.started"
|
|
|
+ << "module.progressRangeChanged(0,0)"
|
|
|
+ << "module.progressValueChanged(0)"
|
|
|
+ << "module.progressRangeChanged(0,1000)"
|
|
|
+ << "module.errorReady";
|
|
|
+
|
|
|
if (future.canPause())
|
|
|
{
|
|
|
// Due to Qt bug 12152, these two signals are reversed
|
|
@@ -214,154 +255,114 @@ bool futureTestPauseAndCancel(ctkCmdLineModuleManager* manager, ctkCmdLineModule
|
|
|
}
|
|
|
expectedSignals.push_back("module.finished");
|
|
|
|
|
|
- sleep_ms(500);
|
|
|
-
|
|
|
- QCoreApplication::processEvents();
|
|
|
- future.pause();
|
|
|
- sleep_ms(500);
|
|
|
- QCoreApplication::processEvents();
|
|
|
+ QTest::qWait(100);
|
|
|
|
|
|
if (future.canPause())
|
|
|
{
|
|
|
- if (!(future.isPaused() && future.isRunning()))
|
|
|
- {
|
|
|
- qDebug() << "Pause state wrong";
|
|
|
- future.setPaused(false);
|
|
|
- future.cancel();
|
|
|
- QCoreApplication::processEvents();
|
|
|
- future.waitForFinished();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ future.pause();
|
|
|
+ QTest::qWait(100);
|
|
|
+ QVERIFY(future.isPaused());
|
|
|
}
|
|
|
|
|
|
- future.togglePaused();
|
|
|
- QCoreApplication::processEvents();
|
|
|
-
|
|
|
- sleep_ms(500);
|
|
|
+ QVERIFY(future.isRunning());
|
|
|
|
|
|
- if (future.isPaused() && future.isRunning())
|
|
|
+ if (future.canPause())
|
|
|
{
|
|
|
- qDebug() << "Pause state wrong (module is paused, but it shouldn't be)";
|
|
|
- future.cancel();
|
|
|
- QCoreApplication::processEvents();
|
|
|
- future.waitForFinished();
|
|
|
- return false;
|
|
|
+ future.togglePaused();
|
|
|
+ QTest::qWait(100);
|
|
|
}
|
|
|
|
|
|
- try
|
|
|
+ QVERIFY(!future.isPaused());
|
|
|
+ QVERIFY(future.isRunning());
|
|
|
+
|
|
|
+ if (future.canCancel())
|
|
|
{
|
|
|
+ // give event processing a chance before killing the process
|
|
|
+ QTest::qWait(200);
|
|
|
future.cancel();
|
|
|
- QCoreApplication::processEvents();
|
|
|
- future.waitForFinished();
|
|
|
- }
|
|
|
- catch (const ctkCmdLineModuleRunException& e)
|
|
|
- {
|
|
|
- qDebug() << e;
|
|
|
- return false;
|
|
|
}
|
|
|
+ future.waitForFinished();
|
|
|
|
|
|
// process pending events
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
- if (!signalTester.checkSignals(expectedSignals))
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ QVERIFY(future.isCanceled());
|
|
|
+ QVERIFY(future.isFinished());
|
|
|
|
|
|
- if (!(future.isCanceled() && future.isFinished()))
|
|
|
- {
|
|
|
- qDebug() << "Cancel state wrong";
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
+ QVERIFY(signalTester.checkSignals(expectedSignals));
|
|
|
}
|
|
|
|
|
|
-bool futureTestError(ctkCmdLineModuleManager* manager, ctkCmdLineModuleFrontend* frontend)
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::testOutput()
|
|
|
{
|
|
|
- qDebug() << "Testing ctkCmdLineModuleFuture error reporting.";
|
|
|
+ ctkCmdLineModuleSignalTester signalTester;
|
|
|
|
|
|
- frontend->setValue("fileVar", "output1");
|
|
|
- frontend->setValue("exitCodeVar", 24);
|
|
|
- frontend->setValue("errorTextVar", "Some error occured\n");
|
|
|
+ connect(signalTester.watcher(), SIGNAL(outputDataReady()), SLOT(ouputDataReady()));
|
|
|
+ connect(signalTester.watcher(), SIGNAL(errorDataReady()), SLOT(errorDataReady()));
|
|
|
|
|
|
- QFutureWatcher<ctkCmdLineModuleResult> watcher;
|
|
|
- ctkCmdLineModuleFuture future = manager->run(frontend);
|
|
|
- watcher.setFuture(future);
|
|
|
+ this->currentWatcher = signalTester.watcher();
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- future.waitForFinished();
|
|
|
- return EXIT_FAILURE;
|
|
|
- }
|
|
|
- catch (const ctkCmdLineModuleRunException& e)
|
|
|
- {
|
|
|
- Q_ASSERT_X(e.errorCode() == 24, __FUNCTION__, "Error code mismatch");
|
|
|
- Q_ASSERT_X(e.errorString() == "Some error occured\n", __FUNCTION__, "Error text mismatch");
|
|
|
- }
|
|
|
+ frontend->setValue("numOutputsVar", 2);
|
|
|
+ frontend->setValue("errorTextVar", "Final error msg.");
|
|
|
+ ctkCmdLineModuleFuture future = manager.run(frontend);
|
|
|
+ signalTester.setFuture(future);
|
|
|
+
|
|
|
+ future.waitForFinished();
|
|
|
|
|
|
// process pending events
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
- return true;
|
|
|
+ QVERIFY(future.isFinished());
|
|
|
+
|
|
|
+ QList<QString> expectedSignals;
|
|
|
+ expectedSignals << "module.started"
|
|
|
+ << "module.progressRangeChanged(0,0)"
|
|
|
+ << "module.progressValueChanged(0)"
|
|
|
+ << "module.progressRangeChanged(0,1000)"
|
|
|
+ << "module.errorReady"
|
|
|
+ << "module.progressValueChanged(1)"
|
|
|
+ << "module.progressTextChanged(Test Filter)"
|
|
|
+ << "module.progressValueChanged(500)"
|
|
|
+ << "module.outputReady"
|
|
|
+ << "module.progressValueChanged(999)"
|
|
|
+ << "module.outputReady"
|
|
|
+ << "module.errorReady"
|
|
|
+ << "module.progressValueChanged(1000)"
|
|
|
+ << "module.finished";
|
|
|
+
|
|
|
+ QVERIFY(signalTester.checkSignals(expectedSignals));
|
|
|
+
|
|
|
+ const char* expectedOutput = "Output 1\nOutput 2\n";
|
|
|
+ const char* expectedError = "A superficial error message.\nFinal error msg.";
|
|
|
+
|
|
|
+ QCOMPARE(this->outputData.data(), expectedOutput);
|
|
|
+ QCOMPARE(this->errorData.data(), expectedError);
|
|
|
+
|
|
|
+ QCOMPARE(future.readAllOutputData().data(), expectedOutput);
|
|
|
+ QCOMPARE(future.readAllErrorData().data(), expectedError);
|
|
|
}
|
|
|
|
|
|
-int ctkCmdLineModuleFutureTest(int argc, char* argv[])
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+void ctkCmdLineModuleFutureTester::testError()
|
|
|
{
|
|
|
- QCoreApplication app(argc, argv);
|
|
|
-
|
|
|
- ctkCmdLineModuleFrontendMockupFactory factory;
|
|
|
- ctkCmdLineModuleBackendLocalProcess backend;
|
|
|
+ frontend->setValue("fileVar", "output1");
|
|
|
+ frontend->setValue("exitCodeVar", 24);
|
|
|
+ frontend->setValue("errorTextVar", "Some error occured\n");
|
|
|
|
|
|
- ctkCmdLineModuleManager manager;
|
|
|
- manager.registerBackend(&backend);
|
|
|
+ ctkCmdLineModuleFuture future = manager.run(frontend);
|
|
|
|
|
|
- QUrl moduleUrl = QUrl::fromLocalFile(app.applicationDirPath() + "/ctkCmdLineModuleTestBed");
|
|
|
- ctkCmdLineModuleReference moduleRef;
|
|
|
try
|
|
|
{
|
|
|
- moduleRef = manager.registerModule(moduleUrl);
|
|
|
- }
|
|
|
- catch (const ctkException& e)
|
|
|
- {
|
|
|
- qCritical() << "Module at" << moduleUrl << "could not be registered: " << e;
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- QScopedPointer<ctkCmdLineModuleFrontend> frontend(factory.create(moduleRef));
|
|
|
- if (!futureTestStartFinish(&manager, frontend.data()))
|
|
|
- {
|
|
|
- return EXIT_FAILURE;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- QScopedPointer<ctkCmdLineModuleFrontend> frontend(factory.create(moduleRef));
|
|
|
- if (!futureTestProgress(&manager, frontend.data()))
|
|
|
- {
|
|
|
- return EXIT_FAILURE;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- QScopedPointer<ctkCmdLineModuleFrontend> frontend(factory.create(moduleRef));
|
|
|
- if (!futureTestError(&manager, frontend.data()))
|
|
|
- {
|
|
|
- return EXIT_FAILURE;
|
|
|
- }
|
|
|
+ future.waitForFinished();
|
|
|
+ QFAIL("Expected exception not thrown.");
|
|
|
}
|
|
|
-
|
|
|
+ catch (const ctkCmdLineModuleRunException& e)
|
|
|
{
|
|
|
- QScopedPointer<ctkCmdLineModuleFrontend> frontend(factory.create(moduleRef));
|
|
|
- if (!futureTestPauseAndCancel(&manager, frontend.data()))
|
|
|
- {
|
|
|
- return EXIT_FAILURE;
|
|
|
- }
|
|
|
+ QVERIFY2(e.errorCode() == 24, "Test matching error code");
|
|
|
+ QCOMPARE(future.readAllErrorData().data(), "A superficial error message.\nSome error occured\n");
|
|
|
}
|
|
|
-
|
|
|
- // if (!futureTestResultReady(frontend.data()))
|
|
|
- // {
|
|
|
- // return EXIT_FAILURE;
|
|
|
- // }
|
|
|
-
|
|
|
- return EXIT_SUCCESS;
|
|
|
}
|
|
|
+
|
|
|
+// ----------------------------------------------------------------------------
|
|
|
+CTK_TEST_MAIN(ctkCmdLineModuleFutureTest)
|
|
|
+#include "moc_ctkCmdLineModuleFutureTest.cpp"
|