Salome HOME
YACS in SSL mode
[modules/yacs.git] / src / workloadmanager / Test / TestMain.cxx
index b3b53c9d981339ac7aedaf27d64e0d2ff3574934..a79956943f2c83c781807fc81392f6282b3e6002 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2020  EDF R&D
+// Copyright (C) 2020-2021  CEA/DEN, EDF R&D
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 // License along with this library; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 //
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
-#include <cppunit/CompilerOutputter.h>
-#include <cppunit/TestResult.h>
-#include <cppunit/TestResultCollector.h>
-#include <cppunit/TextTestProgressListener.h>
-#include <cppunit/BriefTestProgressListener.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
-#include <cppunit/TestRunner.h>
-#include <cppunit/TextTestRunner.h>
+
+#include <cppunit/TestFixture.h>
 #include <stdexcept>
 
 #include <iostream>
@@ -40,7 +35,7 @@
 #include "../WorkloadManager.hxx"
 #include "../DefaultAlgorithm.hxx"
 
-constexpr bool ACTIVATE_DEBUG_LOG = true;
+constexpr bool ACTIVATE_DEBUG_LOG = false;
 template<typename... Ts>
 void DEBUG_LOG(Ts... args)
 {
@@ -61,7 +56,7 @@ class MyTask;
 class AbstractChecker
 {
 public:
-  virtual void check(const WorkloadManager::Container& c, MyTask* t)=0;
+  virtual void check(const WorkloadManager::RunInfo& c, MyTask* t)=0;
 };
 
 template <std::size_t size_R, std::size_t size_T>
@@ -69,7 +64,7 @@ class Checker : public AbstractChecker
 {
 public:
   Checker();
-  void check(const WorkloadManager::Container& c, MyTask* t)override;
+  void check(const WorkloadManager::RunInfo& c, MyTask* t)override;
   void globalCheck();
   void reset();
 
@@ -83,12 +78,12 @@ private:
 class MyTask : public WorkloadManager::Task
 {
 public:
-  const WorkloadManager::ContainerType* type()const override {return _type;}
-  void run(const WorkloadManager::Container& c)override
+  const WorkloadManager::ContainerType& type()const override {return *_type;}
+  void run(const WorkloadManager::RunInfo& c)override
   {
     _check->check(c, this);
 
-    DEBUG_LOG("Running task ", _id, " on ", c.resource->name, "-", c.type->name,
+    DEBUG_LOG("Running task ", _id, " on ", c.resource.name, "-", c.type.name,
               "-", c.index);
     std::this_thread::sleep_for(std::chrono::seconds(_sleep));
     DEBUG_LOG("Finish task ", _id);
@@ -137,11 +132,11 @@ Checker<size_R, size_T>::Checker()
 }
 
 template <std::size_t size_R, std::size_t size_T>
-void Checker<size_R, size_T>::check(const WorkloadManager::Container& c,
+void Checker<size_R, size_T>::check(const WorkloadManager::RunInfo& c,
                                     MyTask* t)
 {
   std::unique_lock<std::mutex> lock(_mutex);
-  int& max = _maxContainersForResource[c.resource->id][c.type->id];
+  int& max = _maxContainersForResource[c.resource.id][c.type.id];
   if( max < c.index)
     max = c.index;
 }
@@ -155,11 +150,13 @@ void Checker<size_R, size_T>::globalCheck()
     for(std::size_t j=0; j < size_T; j++)
     {
       int max = _maxContainersForResource[i][j];
-      DEBUG_LOG(resources[i].name, ", ", types[j].name, ":", max+1);
+      DEBUG_LOG(resources[i].name, ", ", types[j].name,
+                " max simultaneous runs:", max+1);
       CPPUNIT_ASSERT( (max+1) * types[j].neededCores <= resources[i].nbCores );
       global_max += types[j].neededCores * float(max+1);
     }
-    DEBUG_LOG(resources[i].name, " global: ", global_max);
+    DEBUG_LOG(resources[i].name, " max cores added for evry type: ", global_max);
+    // This assertion may be false if there are more resources than needed.
     CPPUNIT_ASSERT(global_max >= resources[i].nbCores); // cores fully used
   }
 }
@@ -176,20 +173,33 @@ class MyTest: public CppUnit::TestFixture
 {
   CPPUNIT_TEST_SUITE(MyTest);
   CPPUNIT_TEST(atest);
+  CPPUNIT_TEST(btest);
+  CPPUNIT_TEST(ctest);
   CPPUNIT_TEST_SUITE_END();
 public:
   void atest();
+  void btest(); // ignore resources
+  void ctest(); // no available resource
 };
 
+/**
+ * General test with 150 tasks of 3 types:
+ *   - 50 tasks which need 4 cores for 2s each
+ *   - 50 tasks which need 1 core for 1s each
+ *   - 50 tasks which need no core but take 2s each
+ * We use 2 resources: 10 cores and 18 cores
+ * We verify the global time of execution.
+ */
 void MyTest::atest()
 {
   constexpr std::size_t resourcesNumber = 2;
-  constexpr std::size_t typesNumber = 2;
+  constexpr std::size_t typesNumber = 3;
   Checker<resourcesNumber, typesNumber> check;
   check.resources[0].nbCores = 10;
   check.resources[1].nbCores = 18;
   check.types[0].neededCores = 4.0;
   check.types[1].neededCores = 1.0;
+  check.types[2].neededCores = 0.0; // tasks to be run with no cost
 
   for(std::size_t i=0; i < resourcesNumber; i ++)
     DEBUG_LOG(check.resources[i].name, " has ", check.resources[i].nbCores,
@@ -198,22 +208,25 @@ void MyTest::atest()
     DEBUG_LOG(check.types[i].name, " needs ", check.types[i].neededCores,
               " cores.");
 
-  constexpr std::size_t tasksNumber = 100;
+  constexpr std::size_t tasksNumber = 150;
   MyTask tasks[tasksNumber];
-  for(std::size_t i = 0; i < tasksNumber / 2; i++)
-    tasks[i].reset(i, &check.types[0], 2, &check);
-  for(std::size_t i = tasksNumber / 2; i < tasksNumber; i++)
-    tasks[i].reset(i, &check.types[1], 1, &check);
+  for(int type_id = 0; type_id < typesNumber; type_id++)
+    for(int j = type_id * tasksNumber / typesNumber;
+        j < (type_id + 1) * tasksNumber / typesNumber;
+        j++)
+        //            id,  ContainerType,       sleep (1|2s)
+        tasks[j].reset(j, &check.types[type_id], 2-type_id%2, &check);
 
   DEBUG_LOG("Number of tasks: ", tasksNumber);
-  DEBUG_LOG("Tasks from 0 to ", tasksNumber/2, " are ", tasks[0].type()->name);
-  DEBUG_LOG("Tasks from ", tasksNumber/2, " to ", tasksNumber, " are ",
-            tasks[tasksNumber / 2].type()->name);
+  for(int type_id = 0; type_id < typesNumber; type_id++)
+    DEBUG_LOG("Tasks from ", type_id * tasksNumber / typesNumber,
+              " to ", (type_id + 1) * tasksNumber / typesNumber,
+              " are of type ", check.types[type_id].name);
 
   WorkloadManager::DefaultAlgorithm algo;
   WorkloadManager::WorkloadManager wlm(algo);
   for(std::size_t i=0; i < resourcesNumber; i ++)
-    wlm.addResource(&check.resources[i]);
+    wlm.addResource(check.resources[i]);
 
   // Add 4 core tasks first
   check.reset();
@@ -264,6 +277,101 @@ void MyTest::atest()
 
 }
 
+/**
+ * Test the case of tasks which need no resources and can be run whithout
+ * waiting.
+ */
+void MyTest::btest()
+{
+  Checker<1, 1> check;
+  WorkloadManager::ContainerType ctype;
+  ctype.ignoreResources = true;
+  constexpr std::size_t tasksNumber = 20;
+  MyTask tasks[tasksNumber];
+  for(std::size_t i = 0; i < tasksNumber; i++)
+    tasks[i].reset(i, &ctype, 1, &check);
+  WorkloadManager::DefaultAlgorithm algo;
+  WorkloadManager::WorkloadManager wlm(algo);
+  // no resource needed
+  std::chrono::steady_clock::time_point start_time;
+  std::chrono::steady_clock::time_point end_time;
+  std::chrono::seconds duration;
+  start_time = std::chrono::steady_clock::now();
+  wlm.start();
+  for(std::size_t i = 0; i < tasksNumber; i++)
+    wlm.addTask(&tasks[i]);
+  wlm.stop();
+  end_time = std::chrono::steady_clock::now();
+  duration = std::chrono::duration_cast<std::chrono::seconds>
+             (end_time - start_time);
+  std::chrono::seconds maxExpectedDuration(2);
+  CPPUNIT_ASSERT( duration <= maxExpectedDuration);
+}
+
+/**
+ * Test the case of a task which need more cores than any resource has.
+ */
+class ErrorTask : public WorkloadManager::Task
+{
+public:
+  ErrorTask(int nb_cores): WorkloadManager::Task(), _type(), _ok(), _message()
+  {
+    _type.ignoreResources = false;
+    _type.neededCores = nb_cores;
+  }
+
+  const WorkloadManager::ContainerType& type()const override {return _type;}
+
+  void run(const WorkloadManager::RunInfo& c)override
+  {
+    _ok = c.isOk;
+    _message = c.error_message;
+  }
+
+  bool checkState(bool ok, const std::string& message)
+  {
+    return (ok == _ok) && (message == _message);
+  }
+
+private:
+  WorkloadManager::ContainerType _type;
+  bool _ok;
+  std::string _message;
+};
+
+void MyTest::ctest()
+{
+  WorkloadManager::Resource r;
+  r.id = 1;
+  r.name = "r1";
+  r.nbCores = 1;
+  ErrorTask t1(1), t2(10);
+  WorkloadManager::DefaultAlgorithm algo;
+  WorkloadManager::WorkloadManager wlm(algo);
+  wlm.addResource(r);
+  wlm.addTask(&t1);
+  wlm.addTask(&t2);
+  wlm.start();
+  wlm.stop();
+  CPPUNIT_ASSERT(t1.checkState(true, ""));
+  CPPUNIT_ASSERT(t2.checkState(false, "No resource can run this task."));
+  // no error mode: wait for a resource to be added
+  WorkloadManager::DefaultAlgorithm algo_noerror;
+  WorkloadManager::WorkloadManager wlm2(algo_noerror);
+  wlm2.addResource(r);
+  wlm2.addTask(&t1);
+  wlm2.addTask(&t2);
+  wlm2.start();
+  std::this_thread::sleep_for(std::chrono::seconds(1));
+  r.id = 2;
+  r.name = "r2";
+  r.nbCores = 20;
+  wlm2.addResource(r);
+  wlm2.stop();
+  CPPUNIT_ASSERT(t1.checkState(true, ""));
+  CPPUNIT_ASSERT(t2.checkState(true, ""));
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(MyTest);
 
 #include "BasicMainTest.hxx"