From 5f85185e8d157e8148e329700d40d017a3604b7b Mon Sep 17 00:00:00 2001 From: Phanes Date: Sun, 3 Dec 2017 14:10:09 -0500 Subject: [PATCH] dependency management implemented -- this should be polished later to say the task name in the exception message. last feature to implement is logging. --- conf/config.json | 3 +- conf/plans/test.plan | 2 +- conf/units/all_test.units | 8 +-- src/loaders/Plan.cpp | 109 ++++++++++++++++++++++++++------------ src/loaders/Plan.h | 6 ++- src/loaders/Task.cpp | 19 ++++++- src/loaders/Task.h | 5 ++ 7 files changed, 109 insertions(+), 43 deletions(-) diff --git a/conf/config.json b/conf/config.json index 072a6cb..0a0a052 100644 --- a/conf/config.json +++ b/conf/config.json @@ -1,4 +1,5 @@ { "units_path": "/home/phanes/development/internal/Examplar/conf/units/all_test.units", - "plan_path": "/home/phanes/development/internal/Examplar/conf/plans/test.plan" + "plan_path": "/home/phanes/development/internal/Examplar/conf/plans/test.plan", + "config_version": "1" } diff --git a/conf/plans/test.plan b/conf/plans/test.plan index 53ab21b..66eb2fe 100644 --- a/conf/plans/test.plan +++ b/conf/plans/test.plan @@ -2,6 +2,6 @@ "plan": [ { "name": "independent test 1", "dependencies": [ null ] }, { "name": "independent test 2", "dependencies": [ null ] }, - { "name": "dependent test", "dependencies": [ "independent test 1", null, null ] } + { "name": "dependent test", "dependencies": [ "independent test 1" ] } ] } diff --git a/conf/units/all_test.units b/conf/units/all_test.units index 7a4e4e6..2fb5ac4 100644 --- a/conf/units/all_test.units +++ b/conf/units/all_test.units @@ -5,13 +5,13 @@ "target": "/usr/bin/false", "rectifier": "/usr/bin/true", "active": true, - "required": true, + "required": false, "rectify": true }, { "name": "independent test 2", - "target": "/usr/bin/false", - "rectifier": "/usr/bin/false", + "target": "/usr/bin/true", + "rectifier": "/usr/bin/true", "active": true, "required": false, "rectify": false @@ -26,7 +26,7 @@ }, { "name": "dependent test", - "target": "ssh root@phanes.silogroup.org", + "target": "/usr/bin/true", "rectifier": "/usr/bin/false", "active": true, "required": false, diff --git a/src/loaders/Plan.cpp b/src/loaders/Plan.cpp index f7959cc..351e8c8 100644 --- a/src/loaders/Plan.cpp +++ b/src/loaders/Plan.cpp @@ -11,6 +11,14 @@ class Plan_InvalidTaskName: public std::runtime_error { public: Plan_InvalidTaskName(): std::runtime_error("Plan: Attempted to access a Task using an invalid name.") {} }; + +/// Plan_Task_Missing_Dependency - Exception thrown when a Plan tries to access a contained Task's value by name not present +/// in the Unit. +class Plan_Task_Missing_Dependency: public std::runtime_error { public: + Plan_Task_Missing_Dependency(): std::runtime_error("Plan: Attempted to execute a task that had unmet dependencies.") {} +}; + + /// Plan::Plan() - Constructor for Plan class. A Plan is a managed container for a Task vector. These tasks reference /// Units that are defined in the Units files (Suite). If Units are definitions, Tasks are selections of those /// definitions to execute, and if Units together form a Suite, Tasks together form a Plan. @@ -53,7 +61,7 @@ void Plan::load_plan_file(std::string filename, bool verbose) /// \param result - The variable receiving the value. /// \param index - The numerical index in the Task vector to retrieve a value for. /// \param verbose - Whether to print verbose output to STDOUT. -void Plan::get_task(Task & result, int index, bool verbose) +void Plan::get_task(Task & result, int index ) { if ( index <= this->tasks.size() ) { @@ -63,30 +71,6 @@ void Plan::get_task(Task & result, int index, bool verbose) } } -/// Plan::get_task - Retrieves a task by name. -/// -/// \param result - The variable receiving the value. -/// \param provided_name - The name to find a task by. -/// \param verbose - Whether to print verbose output to STDOUT. -void Plan::get_task(Task & result, std::string provided_name, bool verbose) -{ - bool foundMatch = false; - - for ( int i = 0; i < this->tasks.size(); i++ ) - { - if ( this->tasks[i].get_name() == provided_name ) - { - result = this->tasks[i]; - foundMatch = true; - break; - } - } - if (! foundMatch ) - { - std::cerr << "Task name \"" << provided_name << "\" was referenced but not defined!" << std::endl; - throw Plan_InvalidTaskName(); - } -} /// Plan::load_definitions - Load the units corresponding to each task in plan from the given Suite. /// @@ -108,6 +92,63 @@ void Plan::load_definitions( Suite unit_definitions, bool verbose ) } } +/// Plan::get_task - Retrieves a task by name. +/// +/// \param result - The variable receiving the value. +/// \param provided_name - The name to find a task by. +/// \param verbose - Whether to print verbose output to STDOUT. +void Plan::get_task(Task & result, std::string provided_name ) +{ + bool foundMatch = false; + + for ( int i = 0; i < this->tasks.size(); i++ ) + { + if ( this->tasks[i].get_name() == provided_name ) + { + result = this->tasks[i]; + foundMatch = true; + break; + } + } + if (! foundMatch ) + { + std::cerr << "Task name \"" << provided_name << "\" was referenced but not defined!" << std::endl; + throw Plan_InvalidTaskName(); + } +} + + +// TODO dependency check goes here +// This should check to see if there are unmet dependencies. +// done -- add a "completed" attribute to Task +// Iterate through Task::dependencies, a vector attached to tasks[i] containing task names +// use Plan::get_task( name ) method to retrieve that task +// check if it is in a ready state +// return ready/not ready +bool Plan::all_dependencies_complete(std::string name) +{ + // get the task by name + Task named_task; + this->get_task( named_task, name ); + + // get the dependencies of that task + std::vector deps = named_task.get_dependencies(); + + // create an empty task to assign values to during iteration + Task tmpTask; + // iterate through its dependencies + for ( int i = 0; i < deps.size(); i++ ) + { + this->get_task( tmpTask, deps[i]); + if (! tmpTask.is_complete() ) + { + // error message? + return false; + } + } + return true; +} + /// Plan::execute() - Iterates through all tasks in a plan and executes them. /// /// \param verbose @@ -116,15 +157,17 @@ void Plan::execute( bool verbose ) // for each task in this plan for ( int i = 0; i < this->tasks.size(); i++ ) { - if ( verbose ) { - std::cout << "Executing task \"" << this->tasks[i].get_name() << "\"." << std::endl; -// std::cout << "Executing task \"" << this->tasks[0].get_name() << "\"." << std::endl; + if (this->all_dependencies_complete(this->tasks[i].get_name()) ) + { + if ( verbose ) + { + std::cout << "Executing task \"" << this->tasks[i].get_name() << "\"." << std::endl; + } + this->tasks[i].execute( verbose ); + } else { + throw Plan_Task_Missing_Dependency(); + // not all deps met for this task } - this->tasks[i].execute( verbose ); -// this->tasks[0].execute( verbose ); - - // for testing a logic issue in Task.execute(), remove when done -// throw Plan_InvalidTaskIndex(); } } diff --git a/src/loaders/Plan.h b/src/loaders/Plan.h index 97e55a4..dd25115 100644 --- a/src/loaders/Plan.h +++ b/src/loaders/Plan.h @@ -19,10 +19,10 @@ class Plan: public JSON_Loader void load_plan_file( std::string filename, bool verbose ); // fetch a task from this->tasks - void get_task( Task & result, std::string provided_name, bool verbose ); + void get_task( Task & result, std::string provided_name ); // fetch a task from this->tasks - void get_task( Task & result, int index, bool verbose ); + void get_task( Task & result, int index ); // load unit definitions from a provided suite and import them into individual tasks void load_definitions( Suite unit_definitions, bool verbose ); @@ -32,6 +32,8 @@ class Plan: public JSON_Loader // execute all tasks in this plan void execute( bool verbose ); + + bool all_dependencies_complete(std::string name); }; #endif //FTESTS_PLAN_H diff --git a/src/loaders/Task.cpp b/src/loaders/Task.cpp index 686a4c2..5a824ca 100644 --- a/src/loaders/Task.cpp +++ b/src/loaders/Task.cpp @@ -91,6 +91,20 @@ bool Task::is_complete() return this->complete; } + +/// Task::mark_complete - Marks the task complete.. +void Task::mark_complete() +{ + this->complete = true; +} + + +/// Task::get_dependencies - returns a pointer to the dependencies vector. +std::vector Task::get_dependencies() +{ + return this->dependencies; +} + /// Task::has_definition - Indicator if the task has attached its definition from a Suite. bool Task::has_definition() { @@ -102,10 +116,10 @@ bool Task::has_definition() /// \param verbose - Verbosity level - not implemented yet. void Task::execute( bool verbose ) { - // DUFFING + // DUFFING - If Examplar is broken it's probably going to be in this block. // PREWORK - // throw if unit not coupled to all necessary values since Task is stateful (stateful is okay) + // throw if unit not coupled to all necessary values since Task is stateful (yes, stateful is okay) if (! this->has_definition() ) { throw Task_NotReady(); } @@ -133,6 +147,7 @@ void Task::execute( bool verbose ) if ( verbose ) { std::cout << "\tTarget " << task_name << " succeeded." << std::endl; } + this->mark_complete(); // next } else { // Non-Zero d[0] from initial target execution, get to d[1] diff --git a/src/loaders/Task.h b/src/loaders/Task.h index e9aee91..6fc30e6 100644 --- a/src/loaders/Task.h +++ b/src/loaders/Task.h @@ -42,6 +42,11 @@ class Task // execute this task's definition void execute( bool verbose ); + + void mark_complete(); + + // returns a pointer to the dependencies vector + std::vector get_dependencies(); }; #endif //FTESTS_TASK_H