Przeglądaj źródła

Added unit tests for CLM timeout and caching handling.

Sascha Zelzer 11 lat temu
rodzic
commit
e8c8a0a884

+ 216 - 6
Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleManagerTest.cpp

@@ -24,7 +24,11 @@
 #include "ctkCmdLineModuleBackend.h"
 #include "ctkException.h"
 #include "ctkCmdLineModuleFuture.h"
+#include "ctkCmdLineModuleReferenceResult.h"
+#include <ctkCmdLineModuleConcurrentHelpers.h>
+#include <ctkCmdLineModuleTimeoutException.h>
 
+#include "ctkUtils.h"
 #include "ctkTest.h"
 
 #include <QCoreApplication>
@@ -43,18 +47,47 @@ class BackendMockUp : public ctkCmdLineModuleBackend
 
 public:
 
-  void addModule(const QUrl& location, const QByteArray& xml)
+  void addModule(const QUrl& location, const QByteArray& xml, int msDelay = 0)
   {
-    this->UrlToXml[location] = xml;
+    this->m_UrlToXmlRetrievalCount[location] = 0;
+    this->m_UrlToXml[location] = xml;
+    this->m_UrlToXmlOutputDelay[location] = msDelay;
+  }
+
+  void setTimestamp(const QUrl& location, qint64 timestamp)
+  {
+    this->m_UrlToTimestamp[location] = timestamp;
+  }
+
+  QList<QUrl> moduleLocations() const
+  {
+    return this->m_UrlToXml.keys();
+  }
+
+  int xmlRetrievalCount(const QUrl& location) const
+  {
+    QHash<QUrl,int>::ConstIterator iter = this->m_UrlToXmlRetrievalCount.find(location);
+    return iter == this->m_UrlToXmlRetrievalCount.end() ? 0 : iter.value();
   }
 
   virtual QString name() const { return "Mockup"; }
   virtual QString description() const { return "Test Mock-up"; }
   virtual QList<QString> schemes() const { return QList<QString>() << "test"; }
-  virtual qint64 timeStamp(const QUrl& /*location*/) const { return 0; }
-  virtual QByteArray rawXmlDescription(const QUrl& location, int /*timeout*/)
+
+  virtual qint64 timeStamp(const QUrl& location) const
+  {
+    QHash<QUrl,qint64>::ConstIterator iter = this->m_UrlToTimestamp.find(location);
+    return iter == this->m_UrlToTimestamp.end() ? 0 : iter.value();
+  }
+
+  virtual QByteArray rawXmlDescription(const QUrl& location, int timeout)
   {
-    return UrlToXml[location];
+    ++m_UrlToXmlRetrievalCount[location];
+    if (timeout < m_UrlToXmlOutputDelay[location])
+    {
+      throw ctkCmdLineModuleTimeoutException(location, "Timeout in BackendMockUp occurred");
+    }
+    return m_UrlToXml[location];
   }
 
 protected:
@@ -66,7 +99,10 @@ protected:
 
 private:
 
-  QHash<QUrl, QByteArray> UrlToXml;
+  QHash<QUrl, qint64> m_UrlToTimestamp;
+  QHash<QUrl, int> m_UrlToXmlRetrievalCount;
+  QHash<QUrl, int> m_UrlToXmlOutputDelay;
+  QHash<QUrl, QByteArray> m_UrlToXml;
 };
 
 }
@@ -79,15 +115,19 @@ class ctkCmdLineModuleManagerTester : public QObject
 private Q_SLOTS:
 
   void initTestCase();
+  void cleanup();
 
   void testStrictValidation();
   void testWeakValidation();
   void testSkipValidation();
+  void testTimeoutHandling();
+  void testCaching();
 
 private:
 
   QByteArray validXml;
   QByteArray invalidXml;
+  QString cachePath;
 };
 
 //-----------------------------------------------------------------------------
@@ -122,6 +162,14 @@ void ctkCmdLineModuleManagerTester::initTestCase()
                    "    </integer>\n"
                    "  </parameters>\n"
                    "</executable>\n";
+
+  cachePath = QDir::tempPath() + QDir::separator() + "ctkCmdLineModuleManagerTester_cache";
+}
+
+//-----------------------------------------------------------------------------
+void ctkCmdLineModuleManagerTester::cleanup()
+{
+  ctk::removeDirRecursively(cachePath);
 }
 
 //-----------------------------------------------------------------------------
@@ -186,6 +234,168 @@ void ctkCmdLineModuleManagerTester::testSkipValidation()
   QVERIFY(moduleRef2.xmlValidationErrorString().isEmpty());
 }
 
+//-----------------------------------------------------------------------------
+void ctkCmdLineModuleManagerTester::testTimeoutHandling()
+{
+  BackendMockUp backend;
+  backend.addModule(QUrl("test://validXml"), validXml, 1000);
+  backend.addModule(QUrl("test://validXml2"), validXml);
+
+  ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
+  manager.setTimeOutForXMLRetrieval(2000);
+
+  manager.registerBackend(&backend);
+
+  // register modules with a sufficient large timeout value
+  QList<ctkCmdLineModuleReferenceResult> results =
+      QtConcurrent::blockingMapped(backend.moduleLocations(),
+                                   ctkCmdLineModuleConcurrentRegister(&manager, true));
+
+  QVERIFY(results.size() == 2);
+  QVERIFY(results[0].m_Reference && results[0].m_RuntimeError.isEmpty());
+  QVERIFY(results[1].m_Reference && results[1].m_RuntimeError.isEmpty());
+
+  // unregister the modules
+  QList<bool> unregisterResults =
+      QtConcurrent::blockingMapped(backend.moduleLocations(),
+                                   ctkCmdLineModuleConcurrentUnRegister(&manager));
+  QVERIFY(unregisterResults.size() == 2);
+  QVERIFY(unregisterResults[0] && unregisterResults[1]);
+
+  // register modules where one runs into a timeout
+  manager.setTimeOutForXMLRetrieval(500);
+  results = QtConcurrent::blockingMapped(backend.moduleLocations(),
+                                         ctkCmdLineModuleConcurrentRegister(&manager, true));
+
+  QVERIFY(results.size() == 2);
+  QVERIFY(!results[0].m_Reference && !results[0].m_RuntimeError.isEmpty());
+  QVERIFY(results[1].m_Reference && results[1].m_RuntimeError.isEmpty());
+
+}
+
+//-----------------------------------------------------------------------------
+void ctkCmdLineModuleManagerTester::testCaching()
+{
+  QUrl location("test://validXml");
+  QUrl location2("test://validXml2");
+
+  // The code below is inside a local scopes so the manager is destroyed
+  // but the cache is still available (unregistering the module would also
+  // remove the cache entry, but we need to test the entry)
+
+  {
+    BackendMockUp backend;
+    backend.addModule(location, validXml, 1000);
+
+    ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
+    manager.setTimeOutForXMLRetrieval(500);
+
+    manager.registerBackend(&backend);
+
+    QVERIFY(backend.xmlRetrievalCount(location) == 0);
+
+    // module runs into a timeout
+    try
+    {
+      manager.registerModule(location);
+      QFAIL("ctkCmdLineModuleTimeoutException expected");
+    }
+    catch (const ctkCmdLineModuleTimeoutException&)
+    {}
+    QVERIFY(backend.xmlRetrievalCount(location) == 1);
+
+    // Increase the timeout and register the module again. It should
+    // not have been cached and the manager is supposed to try to
+    // retrieve the XML description again
+    manager.setTimeOutForXMLRetrieval(2000);
+    QVERIFY(manager.registerModule(location));
+    QVERIFY(backend.xmlRetrievalCount(location) == 2);
+
+    // Registering the same module again should just return the already
+    // created module reference
+    QVERIFY(manager.registerModule(location));
+    QVERIFY(backend.xmlRetrievalCount(location) == 2);
+  }
+
+  {
+    BackendMockUp backend;
+    backend.addModule(location, validXml);
+    backend.setTimestamp(location, 1);
+    backend.addModule(location2, invalidXml);
+    backend.setTimestamp(location2, 1);
+
+    ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
+    manager.registerBackend(&backend);
+
+    QVERIFY(manager.registerModule(location));
+    QVERIFY(backend.xmlRetrievalCount(location) == 1);
+
+    try
+    {
+      manager.registerModule(location2);
+      QFAIL("ctkInvalidArgumentException (invalid XML) expected");
+    }
+    catch (const ctkInvalidArgumentException&)
+    {}
+    QVERIFY(backend.xmlRetrievalCount(location2) == 1);
+  }
+
+  // Do the same again but now the cache entries should be returned
+  {
+    BackendMockUp backend;
+    backend.addModule(location, validXml);
+    backend.setTimestamp(location, 1);
+    backend.addModule(location2, invalidXml);
+    backend.setTimestamp(location2, 1);
+
+    ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
+    manager.registerBackend(&backend);
+
+    QVERIFY(manager.registerModule(location));
+    QVERIFY(backend.xmlRetrievalCount(location) == 0);
+
+    try
+    {
+      manager.registerModule(location2);
+      QFAIL("ctkInvalidArgumentException (invalid XML) expected");
+    }
+    catch (const ctkInvalidArgumentException&)
+    {}
+    QVERIFY(backend.xmlRetrievalCount(location2) == 0);
+  }
+
+  // Now test updated timestamps
+  {
+    BackendMockUp backend;
+    backend.addModule(location, validXml);
+    backend.setTimestamp(location, 2);
+    // use valid XML now but keep the previous timestamp
+    backend.addModule(location2, validXml);
+    backend.setTimestamp(location2, 1);
+
+    ctkCmdLineModuleManager manager(ctkCmdLineModuleManager::STRICT_VALIDATION, cachePath);
+    manager.registerBackend(&backend);
+
+    QVERIFY(manager.registerModule(location));
+    QVERIFY(backend.xmlRetrievalCount(location) == 1);
+
+    // should still throw an exception due to the cache entry
+    try
+    {
+      manager.registerModule(location2);
+      QFAIL("ctkInvalidArgumentException (invalid XML) expected");
+    }
+    catch (const ctkInvalidArgumentException&)
+    {}
+    QVERIFY(backend.xmlRetrievalCount(location2) == 0);
+
+    // now update the timestamp and check that the valid XML is retrieved
+    backend.setTimestamp(location, 2);
+    QVERIFY(manager.registerModule(location));
+    QVERIFY(backend.xmlRetrievalCount(location) == 1);
+  }
+}
+
 // ----------------------------------------------------------------------------
 CTK_TEST_MAIN(ctkCmdLineModuleManagerTest)
 #include "moc_ctkCmdLineModuleManagerTest.cpp"