-// 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>
#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)
{
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>
{
public:
Checker();
- void check(const WorkloadManager::Container& c, MyTask* t)override;
+ void check(const WorkloadManager::RunInfo& c, MyTask* t)override;
void globalCheck();
void reset();
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);
}
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;
}
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
}
}
{
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,
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();
}
+/**
+ * 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"