2017-12-04 05:46:34 +00:00
/*
2021-04-05 01:21:05 +00:00
rex - An automation and testing framework .
2017-12-04 05:46:34 +00:00
© SURRO INDUSTRIES and Chris Punches , 2017.
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation , either version 3 of the
License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
along with this program . If not , see < https : //www.gnu.org/licenses/>.
*/
2017-04-22 12:07:11 +00:00
# include "Plan.h"
2017-06-24 21:24:11 +00:00
/// Plan_InvalidTaskIndex - Exception thrown when a Plan tries to access a contained Task's value by index not present
/// in the Unit.
class Plan_InvalidTaskIndex : public std : : runtime_error { public :
Plan_InvalidTaskIndex ( ) : std : : runtime_error ( " Plan: Attempted to access a Task using an invalid index. " ) { }
} ;
/// Plan_InvalidTaskName - Exception thrown when a Plan tries to access a contained Task's value by name not present
/// in the Unit.
class Plan_InvalidTaskName : public std : : runtime_error { public :
Plan_InvalidTaskName ( ) : std : : runtime_error ( " Plan: Attempted to access a Task using an invalid name. " ) { }
} ;
2017-12-03 19:10:09 +00:00
2017-12-04 02:33:58 +00:00
/// Plan_Task_GeneralExecutionException - Wrapper exception to catch exceptions thrown by the execution of Tasks.
class Plan_Task_GeneralExecutionException : public std : : exception
{
public :
/** Constructor (C strings).
* @ param message C - style string error message .
* The string contents are copied upon construction .
* Hence , responsibility for deleting the char * lies
* with the caller .
*/
explicit Plan_Task_GeneralExecutionException ( const char * message ) :
msg_ ( message )
{
}
/** Constructor (C++ STL strings).
* @ param message The error message .
*/
explicit Plan_Task_GeneralExecutionException ( const std : : string & message ) :
msg_ ( message )
{ }
/** Destructor.
* Virtual to allow for subclassing .
*/
virtual ~ Plan_Task_GeneralExecutionException ( ) throw ( ) { }
/** Returns a pointer to the (constant) error description.
* @ return A pointer to a const char * . The underlying memory
* is in posession of the Exception object . Callers must
* not attempt to free the memory .
*/
virtual const char * what ( ) const throw ( ) {
return msg_ . c_str ( ) ;
}
protected :
/** Error message.
*/
std : : string msg_ ;
} ;
2017-12-03 19:10:09 +00:00
/// Plan_Task_Missing_Dependency - Exception thrown when a Plan tries to access a contained Task's value by name not present
/// in the Unit.
2017-12-04 02:15:41 +00:00
class Plan_Task_Missing_Dependency : public std : : exception
{
public :
/** Constructor (C strings).
* @ param message C - style string error message .
* The string contents are copied upon construction .
* Hence , responsibility for deleting the char * lies
* with the caller .
*/
explicit Plan_Task_Missing_Dependency ( const char * message ) :
msg_ ( message )
{
}
2017-12-03 19:10:09 +00:00
2017-12-04 02:15:41 +00:00
/** Constructor (C++ STL strings).
* @ param message The error message .
*/
explicit Plan_Task_Missing_Dependency ( const std : : string & message ) :
msg_ ( message )
{ }
/** Destructor.
* Virtual to allow for subclassing .
*/
virtual ~ Plan_Task_Missing_Dependency ( ) throw ( ) { }
/** Returns a pointer to the (constant) error description.
* @ return A pointer to a const char * . The underlying memory
* is in posession of the Exception object . Callers must
* not attempt to free the memory .
*/
virtual const char * what ( ) const throw ( ) {
return msg_ . c_str ( ) ;
}
protected :
/** Error message.
*/
std : : string msg_ ;
} ;
2017-12-03 19:10:09 +00:00
2017-06-24 21:24:11 +00:00
/// 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.
2021-04-05 01:21:05 +00:00
Plan : : Plan ( Conf * configuration , int LOG_LEVEL ) : JSON_Loader ( LOG_LEVEL ) , slog ( LOG_LEVEL , " _plan_ " )
2017-12-08 03:37:42 +00:00
{
this - > configuration = configuration ;
2020-06-21 00:09:32 +00:00
this - > LOG_LEVEL = LOG_LEVEL ;
}
2017-06-23 20:13:44 +00:00
2017-06-24 21:24:11 +00:00
/// Plan::load_plan_file - Uses the json_root buffer on each run to append intact Units as they're deserialized from
/// the provided file.
///
/// \param filename - The filename to load the plan from.
/// \param verbose - Whether to print verbose output to STDOUT.
2020-06-21 00:09:32 +00:00
void Plan : : load_plan_file ( std : : string filename )
2017-04-29 21:35:03 +00:00
{
2017-06-23 20:13:44 +00:00
// plan always loads from file
2020-06-21 00:09:32 +00:00
this - > load_json_file ( filename ) ;
2017-06-23 20:13:44 +00:00
2017-06-24 21:24:11 +00:00
// staging buffer
Json : : Value jbuff ;
2017-06-23 20:13:44 +00:00
2017-06-24 21:24:11 +00:00
// fill the jbuff staging buffer wih a json::value object in the supplied filename
2020-06-21 00:09:32 +00:00
if ( this - > get_serialized ( jbuff , " plan " ) = = 0 )
2017-06-23 20:13:44 +00:00
{
2017-06-24 21:24:11 +00:00
this - > json_root = jbuff ;
2017-06-23 20:13:44 +00:00
}
2017-06-24 21:24:11 +00:00
// iterate through the json::value members that have been loaded. append to this->tasks vector
// buffer for tasks to append:
2020-06-21 00:09:32 +00:00
Task tmp_T = Task ( this - > LOG_LEVEL ) ;
2017-06-24 21:24:11 +00:00
for ( int index = 0 ; index < this - > json_root . size ( ) ; index + + )
{
2020-06-21 00:09:32 +00:00
tmp_T . load_root ( this - > json_root [ index ] ) ;
2017-06-24 21:24:11 +00:00
this - > tasks . push_back ( tmp_T ) ;
2020-06-21 00:09:32 +00:00
this - > slog . log ( LOG_INFO , " Added task \" " + tmp_T . get_name ( ) + " \" to Plan. " ) ;
2017-06-24 21:24:11 +00:00
}
2017-04-29 21:35:03 +00:00
}
2017-06-24 21:24:11 +00:00
/// Plan::get_task - Retrieves a task by index.
///
/// \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.
2017-12-03 19:10:09 +00:00
void Plan : : get_task ( Task & result , int index )
2017-04-30 00:32:09 +00:00
{
2017-06-24 21:24:11 +00:00
if ( index < = this - > tasks . size ( ) )
{
result = this - > tasks [ index ] ;
} else {
throw Plan_InvalidTaskIndex ( ) ;
}
2017-04-29 21:35:03 +00:00
}
2017-06-25 22:50:14 +00:00
/// Plan::load_definitions - Load the units corresponding to each task in plan from the given Suite.
2017-06-25 22:58:05 +00:00
///
/// \param unit_definitions - The Suite to load definitions from.
/// \param verbose - Whether to print verbose information to STDOUT.
2020-06-21 00:09:32 +00:00
void Plan : : load_definitions ( Suite unit_definitions )
2017-06-25 22:50:14 +00:00
{
// placeholder Unit
2020-06-21 00:09:32 +00:00
Unit tmp_U = Unit ( this - > LOG_LEVEL ) ;
2017-06-25 22:50:14 +00:00
// for every task in the plan:
for ( int i = 0 ; i < this - > tasks . size ( ) ; i + + )
{
// load the tmp_U corresponding to that task name
unit_definitions . get_unit ( tmp_U , this - > tasks [ i ] . get_name ( ) ) ;
// then have that task attach a copy of tmp_U
2020-06-21 00:09:32 +00:00
this - > tasks [ i ] . load_definition ( tmp_U ) ;
2017-06-25 22:50:14 +00:00
}
2017-06-27 02:27:24 +00:00
}
2017-12-03 19:10:09 +00:00
/// 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 )
{
2020-06-21 00:09:32 +00:00
this - > slog . log ( E_FATAL , " Task name \" " + provided_name + " \" was referenced but not defined! " ) ;
2017-12-03 19:10:09 +00:00
throw Plan_InvalidTaskName ( ) ;
}
}
2017-12-04 02:33:58 +00:00
/// Plan::all_dependencies_complete
///
/// \param name - The name of the task in the plan to check met dependencies for.
/// \return - boolean representation of whether all dependencies are complete or not.
2017-12-03 19:10:09 +00:00
bool Plan : : all_dependencies_complete ( std : : string name )
{
// get the task by name
2020-06-21 00:09:32 +00:00
Task named_task = Task ( this - > LOG_LEVEL ) ;
2017-12-03 19:10:09 +00:00
this - > get_task ( named_task , name ) ;
// get the dependencies of that task
std : : vector < std : : string > deps = named_task . get_dependencies ( ) ;
// create an empty task to assign values to during iteration
2020-06-21 00:09:32 +00:00
Task tmpTask = Task ( this - > LOG_LEVEL ) ;
2017-12-03 19:10:09 +00:00
// 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 ;
}
2017-06-27 02:27:24 +00:00
/// Plan::execute() - Iterates through all tasks in a plan and executes them.
///
/// \param verbose
2020-06-21 00:09:32 +00:00
void Plan : : execute ( )
2017-06-27 02:27:24 +00:00
{
// for each task in this plan
2017-12-03 03:01:09 +00:00
for ( int i = 0 ; i < this - > tasks . size ( ) ; i + + )
{
2017-12-03 19:10:09 +00:00
if ( this - > all_dependencies_complete ( this - > tasks [ i ] . get_name ( ) ) )
{
2020-06-21 00:09:32 +00:00
2021-02-14 00:41:08 +00:00
this - > slog . log ( E_INFO , " [ ' " + this - > tasks [ i ] . get_name ( ) + " ' ] Executing... " ) ;
2017-12-04 02:33:58 +00:00
try {
2020-06-21 00:09:32 +00:00
this - > tasks [ i ] . execute ( this - > configuration ) ;
2017-12-04 02:33:58 +00:00
}
catch ( std : : exception & e ) {
2021-02-14 00:41:08 +00:00
this - > slog . log ( E_FATAL , " [ ' " + this - > tasks [ i ] . get_name ( ) + " ' ] Report: " + e . what ( ) ) ;
2020-06-21 00:09:32 +00:00
throw Plan_Task_GeneralExecutionException ( " Could not execute task. " ) ;
2017-12-04 02:33:58 +00:00
}
2017-12-03 19:10:09 +00:00
} else {
2017-12-04 02:15:41 +00:00
// not all deps met for this task
2021-02-14 00:41:08 +00:00
this - > slog . log ( E_FATAL , " [ ' " + this - > tasks [ i ] . get_name ( ) + " ' ] This task was specified in the Plan but not executed due to missing dependencies. Please revise your plan. " ) ;
2020-06-21 00:09:32 +00:00
throw Plan_Task_Missing_Dependency ( " Unmet dependency for task. " ) ;
2017-12-03 03:01:09 +00:00
}
}
2017-06-27 02:27:24 +00:00
}