#ifndef lib_scenario_scenario_h #define lib_scenario_scenario_h /* Cesar project {{{ * * Copyright (C) 2007 Spidcom * * <<>> * * }}} */ /** * \file lib/scenario/scenario.h * \brief Scenario system. * \ingroup lib * * Scenario test system * ==================== * * This scenario system is useful to test that given actions will trigger a * defined sequence of events. The tester defines a system where actions are * stimulus to which the unit under test must react. To test reactions, * functions that the unit calls out of the unit scope should be stubbed and * plugged onto the scenario system. * * Definitions * ----------- * * - \b action * This is a stimulus sent by the scenario system. This can correspond to * an interrupt, a user request, a report from an other layer... When an * action is encountered in the scenario, the system will call a function * depending on the action type. * - \b event * This is a reaction from the unit under test. Functions out of the * tested system should signal the scenario system when they are called. * - \b scenario * This is a sequence of actions and events with their associated * parameters. * - \b globals * This is a structure of associated variables which can be retrieved from * actions or events. * * Usage * ----- * * The user must provide in a definition header file: * * - the content of the globals structure, * - the list of actions, * - the parameters associated with each action, * - the action callbacks, * - the list of events, * - the parameters associated with each event. * * It must also place calls to the scenario system in the events functions and * check that the passed arguments are the expected one. * * To run a scenario, the user should: * * - construct a list of scenario entries (actions and events), * - construct the globals structure, * - call scenario_run(). * * If the scenario is not respected, the test will fail. * * The definition header file name is given by the module configuration. * * Examples * -------- * * Here is an example definition header file: * * \include test/inc/scenario_defs.h * * An example of event code: * * \dontinclude test/src/test_scenario.c * \skip functions out of UUT scope * \skip void * \until } * * An example of action code: * * \skip actions simulating * \skip void * \until } * * And an example of test code: * * \skip scenario ok * \until test_end */ #include "lib/test.h" #include "config/scenario.h" /* Forward declarations. */ typedef struct scenario_t scenario_t; typedef struct scenario_entry_t scenario_entry_t; typedef struct scenario_globals_t scenario_globals_t; typedef union scenario_params_t scenario_params_t; /** Callback for actions. */ typedef void (*scenario_action_cb_t) (scenario_globals_t *globals, scenario_params_t *params); /** Empty parameter list. */ typedef struct { } scenario_empty_t; #include "lib/scenario/inc/predefined.h" #include CONFIG_SCENARIO_DEFS /** Scenario event identifier, completed with the user provided event list. */ enum scenario_event_id_t { /** Event identifier for end of list. */ SCENARIO_NULL_ID, /** Event identifier for no operation. */ SCENARIO_NOP_ID, /** Event identifier for action. */ SCENARIO_ACTION_ID, #define SCENARIO_DEFS_EVENTS_ID(event) PASTE (SCENARIO_EVENT_, event), PREPROC_FOR_EACH (SCENARIO_DEFS_EVENTS_ID, SCENARIO_DEFS_EVENTS) #undef SCENARIO_DEFS_EVENTS_ID SCENARIO_NB_ID }; typedef enum scenario_event_id_t scenario_event_id_t; /** Scenario parameters. */ union scenario_params_t { #define SCENARIO_DEFS_PARAMS(event) \ scenario_event_ ## event ## _t event_ ## event; PREPROC_FOR_EACH (SCENARIO_DEFS_PARAMS, SCENARIO_DEFS_EVENTS) #undef SCENARIO_DEFS_PARAMS #define SCENARIO_DEFS_PARAMS(action) \ scenario_action_ ## action ## _t action_ ## action; PREPROC_FOR_EACH (SCENARIO_DEFS_PARAMS, SCENARIO_PREDEFINED_ACTIONS) PREPROC_FOR_EACH (SCENARIO_DEFS_PARAMS, SCENARIO_DEFS_ACTIONS) #undef SCENARIO_DEFS_PARAMS }; /** Scenario entry. */ struct scenario_entry_t { /** Event identifier. */ scenario_event_id_t event_id; /** Action callback if not an event. */ scenario_action_cb_t action_cb; /** Entry parameters. */ scenario_params_t params; }; /** Scenario globals. */ struct scenario_globals_t { SCENARIO_DEFS_GLOBALS }; /** Scenario context. */ struct scenario_t { /** Currently running test. */ struct test_t *t; /** Current entry. */ scenario_entry_t *current; /** Scenario entries. */ scenario_entry_t *entries; /** Scenario globals. */ scenario_globals_t *globals; }; /** Currently running scenario. */ extern scenario_t scenario; /** Event names. */ extern char *scenario_event_names[]; /** Use this at event entry. This macro is special and can not be included in * a do { } while (0). * * This macro will signal the event to the scenario system. * * If a second argument is given, this is the name of a variable which will be * declared and will contain event parameters. * * If a third argument is given, this is the name of a variable which will be * declared and will point to the globals structure. */ #define scenario_event(event, args...) \ scenario_event_ (event, ## args) #define scenario_event_(event, args...) \ PASTE_EXPAND (scenario_event_, PREPROC_NARG (events, ## args)) \ (event, ## args) #define scenario_event_1(event) \ dbg_assert (scenario.current); \ test_within (scenario.t); \ while (scenario.current->event_id == SCENARIO_NOP_ID) \ scenario.current++; \ dbg_assert (scenario.current->event_id < SCENARIO_NB_ID); \ test_fail_unless (scenario.current->event_id \ == SCENARIO_EVENT_ ## event, \ "unexpected <%s>, expecting <%s> at %d", #event, \ scenario_event_names[scenario.current->event_id], \ scenario.current - scenario.entries); \ scenario.current++ #define scenario_event_2(event, params_var) \ scenario_event_1 (event); \ scenario_event_ ## event ## _t *params_var = \ &scenario.current[-1].params.event_ ## event #define scenario_event_3(event, params_var, globals_var) \ scenario_event_2 (event, params_var); \ scenario_globals_t *globals_var = scenario.globals /** Use this to define an event in a scenario list of entries. The first * parameter is the event name. The following parameters are fields * assignments for the event parameters. */ #define SCENARIO_EVENT(event, params_def...) \ { SCENARIO_EVENT_ ## event, NULL, \ { .event_ ## event = { params_def } } } /** Use this to define an action in a scenario list of entries. */ #define SCENARIO_ACTION(action, params_def...) \ { SCENARIO_ACTION_ID, scenario_action_ ## action ## _cb, \ { .action_ ## action = { params_def } } } /** Use this to define a sublist of entries. The sublist will be used as the * main list, and processing will return to the main list once the sub list is * ended. */ #define SCENARIO_SUB(entries_) \ { SCENARIO_ACTION_ID, scenario_action_predefined_sub_cb, \ { .action_predefined_sub = { .entries = entries_ } } } /** Use this for a no operation in a scenario list of entries. */ #define SCENARIO_NOP \ { SCENARIO_NOP_ID, NULL, { } } /** Use this to mark end of scenario list of entries. */ #define SCENARIO_END \ { SCENARIO_NULL_ID, NULL, { } } BEGIN_DECLS /** * Run a scenario until completion. * \param t test context * \param entries list of entries for this scenario * \param globals pointer to globals structure for this scenario */ void scenario_run (test_t t, scenario_entry_t *entries, scenario_globals_t *globals); END_DECLS #endif /* lib_scenario_scenario_h */