summaryrefslogtreecommitdiff
path: root/cesar/lib/scenario/scenario.h
blob: 919e7a80351165fb9c53d699493f68f963d8a10b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#ifndef lib_scenario_scenario_h
#define lib_scenario_scenario_h
/* Cesar project {{{
 *
 * Copyright (C) 2007 Spidcom
 *
 * <<<Licence>>>
 *
 * }}} */
/**
 * \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 */