1 /*
2  * Copyright (c) 2011 Tony Bai <bigwhite.cn@gmail.com>
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * @file cbehave.h
19  *
20  */
21 #pragma once
22 
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26 
27 #ifndef __cplusplus
28 #include <stdbool.h>
29 #endif
30 
31 #include "apr_ring.h"
32 
33 #define CBEHAVE_LOGO \
34     "*******************************************************************\n\
35 \tCBEHAVE -- A Behavior Driven Development Framework for C\n\
36 \t\t\t By Tony Bai\n\
37 *******************************************************************"
38 
39 #define CBEHAVE_MAX_NAME_LEN 128
40 
41 typedef struct cbehave_state {
42     int total_features;
43     int failed_features;
44     int total_scenarios;
45     int failed_scenarios;
46 } cbehave_state;
47 
48 typedef enum {
49 	CBEHAVE_SCOPE_NONE,
50 	CBEHAVE_SCOPE_GIVEN,
51 	CBEHAVE_SCOPE_WHEN,
52 	CBEHAVE_SCOPE_THEN
53 } cbehave_scope_e;
54 extern cbehave_scope_e cbehave_scope;
55 
56 // Main BDD keywords, GIVEN-WHEN-THEN
57 #define END_SCOPE \
58     if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
59         cbehave_given_exit(_state); \
60     } else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
61         cbehave_when_exit(_state); \
62     } else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
63         cbehave_then_exit(_state); \
64     }
65 #define GIVEN_IMPL(x, _prompt) \
66     END_SCOPE \
67     cbehave_scope = CBEHAVE_SCOPE_GIVEN; \
68     cbehave_given_entry(_prompt, x, _state);
69 #define GIVEN(x) GIVEN_IMPL(x, "Given")
70 #define GIVEN_END
71 
72 #define WHEN_IMPL(x, _prompt) \
73     END_SCOPE \
74     cbehave_scope = CBEHAVE_SCOPE_WHEN; \
75     cbehave_when_entry(_prompt, x, _state);
76 #define WHEN(x) WHEN_IMPL(x, "When")
77 #define WHEN_END
78 
79 #define THEN_IMPL(x, _prompt) \
80     END_SCOPE \
81     cbehave_scope = CBEHAVE_SCOPE_THEN; \
82     cbehave_then_entry(_prompt, x, _state);
83 #define THEN(x) THEN_IMPL(x, "Then")
84 #define THEN_END
85 
86 // AND and friends
87 #define AND_IMPL(x, _prompt) \
88     if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
89         GIVEN_IMPL(x, _prompt) \
90     } else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
91         WHEN_IMPL(x, _prompt) \
92     } else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
93         THEN_IMPL(x, _prompt) \
94     }
95 
96 #define AND(x) AND_IMPL(x, "And")
97 #define BUT(x) AND_IMPL(x, "But")
98 
99 #define SCENARIO(x) { \
100     int _scenario_state = 0; \
101     cbehave_scenario_entry(x, _state); \
102     cbehave_scope = CBEHAVE_SCOPE_NONE;
103 
104 #define SCENARIO_END \
105     END_SCOPE \
106     cbehave_scenario_exit(&_scenario_state, _state); \
107 }
108 
109 #define FEATURE(idx, x) static void _cbehave_feature_##idx(void *_state) { \
110     cbehave_state _old_state; \
111     cbehave_feature_entry(x, &_old_state, _state);
112 
113 #define FEATURE_END \
114 _feature_over: \
115     cbehave_feature_exit(&_old_state, _state); \
116 }
117 
118 #define ASSERT(cond, ret) \
119 if (!(cond)) {\
120     cbehave_feature_return(__FILE__, __LINE__, ret, _state); \
121     goto _feature_over; \
122 }\
123 
124 #define TEST_FEATURE(name) {_cbehave_feature_##name}
125 
126 #define SHOULD_INT_EQUAL(actual, expected) do { \
127     should_int_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
128 } while(0)
129 
130 #define SHOULD_INT_GT(val1, val2) do { \
131     should_int_gt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
132 } while(0)
133 
134 #define SHOULD_INT_LT(val1, val2) do { \
135     should_int_lt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
136 } while(0)
137 
138 #define SHOULD_INT_GE(val1, val2) do { \
139     should_int_ge((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
140 } while(0)
141 
142 #define SHOULD_INT_LE(val1, val2) do { \
143     should_int_le((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
144 } while(0)
145 
146 #define SHOULD_STR_EQUAL(actual, expected) do { \
147     should_str_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
148 } while(0)
149 
150 #define SHOULD_MEM_EQUAL(actual, expected, size) do { \
151     should_mem_equal((actual), (expected), (size), &_scenario_state, __FILE__, __LINE__); \
152 } while(0)
153 
154 #define SHOULD_BE_TRUE(actual) do { \
155     should_be_bool((actual), true, &_scenario_state, __FILE__, __LINE__); \
156 } while(0)
157 
158 #define SHOULD_BE_FALSE(actual) do { \
159     should_be_bool((actual), false, &_scenario_state, __FILE__, __LINE__); \
160 } while(0)
161 
162 #define CBEHAVE_RUN(_description, ...)\
163 int main(void) {\
164 	cbehave_feature _cfeatures[] = {__VA_ARGS__};\
165 	return cbehave_runner(_description, _cfeatures);\
166 }
167 
168 #define cbehave_runner(str, features) \
169     _cbehave_runner(str, features, sizeof(features)/sizeof(features[0]))
170 
171 typedef struct cbehave_feature {
172     void (*func)(void *state);
173 } cbehave_feature;
174 
175 int _cbehave_runner(const char *description, const cbehave_feature *features, int count);
176 void should_int_equal(int actual, int expected,
177                       void *state,
178                       const char *file, int line);
179 void should_int_gt(int val1, int val2,
180                    void *state,
181                    const char *file, int line);
182 void should_int_lt(int val1, int val2,
183                    void *state,
184                    const char *file, int line);
185 void should_int_ge(int val1, int val2,
186                    void *state,
187                    const char *file, int line);
188 void should_int_le(int val1, int val2,
189                    void *state,
190                    const char *file, int line);
191 void should_str_equal(const char *actual, const char *expected, void *state,
192                       const char *file, int line);
193 void should_mem_equal(const void *actual, const void *expected, size_t size, void *state,
194                       const char *file, int line);
195 void should_be_bool(bool actual, bool expected, void *state, const char *file, int line);
196 
197 void cbehave_given_entry(const char *prompt, const char *str, void *state);
198 void cbehave_when_entry(const char *prompt, const char *str, void *state);
199 void cbehave_then_entry(const char *prompt, const char *str, void *state);
200 void cbehave_scenario_entry(const char *str, void *state);
201 void cbehave_feature_entry(const char *str, void *old_state, void *state);
202 
203 void cbehave_given_exit(void *state);
204 void cbehave_when_exit(void *state);
205 void cbehave_then_exit(void *state);
206 void cbehave_scenario_exit(void *scenario_state, void *state);
207 void cbehave_feature_exit(void *old_state, void *state);
208 void cbehave_feature_return(const char *file, int line, int ret, void *state);
209 
210 /*
211  * mock symbol list
212  *
213  * ------------
214  * | symbol-#0|-> value_list
215  * ------------
216  * | symbol-#1|-> value_list
217  * ------------
218  * | symbol-#2|-> value_list
219  * ------------
220  * | ... ...  |-> value_list
221  * ------------
222  * | symbol-#n|-> value_list
223  * ------------
224  */
225 
226 typedef struct cbehave_value_t {
227     APR_RING_ENTRY(cbehave_value_t)    link;
228     void                               *value;
229 } cbehave_value_t;
230 typedef APR_RING_HEAD(cbehave_value_head_t, cbehave_value_t) cbehave_value_head_t;
231 
232 typedef struct cbehave_symbol_t {
233     APR_RING_ENTRY(cbehave_symbol_t)   link;
234     char                               desc[CBEHAVE_MAX_NAME_LEN];
235     int                                obj_type;
236     int                                always_return_flag; /* 1: always return the same value; 0(default) */
237     void*                              value;
238     cbehave_value_head_t               value_list;
239 } cbehave_symbol_t;
240 typedef APR_RING_HEAD(cbehave_symbol_head_t, cbehave_symbol_t) cbehave_symbol_head_t;
241 
242 void* cbehave_mock_obj(const char *fcname, int lineno, const char *fname, int obj_type);
243 void cbehave_mock_obj_return(const char *symbol_name, void *value, const char *fcname,
244                              int lineno, const char *fname, int obj_type, int count);
245 
246 #define MOCK_ARG     0x0
247 #define MOCK_RETV    0x1
248 
249 #define CBEHAVE_MOCK_ARG() cbehave_mock_obj(__func__, __LINE__, __FILE__, MOCK_ARG)
250 #define CBEHAVE_MOCK_RETV() cbehave_mock_obj(__func__, __LINE__, __FILE__, MOCK_RETV)
251 
252 #define CBEHAVE_ARG_RETURN(fcname, value) do { \
253     cbehave_mock_obj_return(#fcname, (void*)value, __func__, __LINE__, __FILE__, MOCK_ARG, 1); \
254 } while(0);
255 
256 #define CBEHAVE_ARG_RETURN_COUNT(fcname, value, count) do { \
257     cbehave_mock_obj_return(#fcname, (void*)value, __func__, __LINE__, __FILE__, MOCK_ARG, count); \
258 } while(0);
259 
260 #define CBEHAVE_RETV_RETURN(fcname, value) do { \
261     cbehave_mock_obj_return(#fcname, (void*)value, __func__, __LINE__, __FILE__, MOCK_RETV, 1); \
262 } while(0);
263 
264 #define CBEHAVE_RETV_RETURN_COUNT(fcname, value, count) do { \
265     cbehave_mock_obj_return(#fcname, (void*)value, __func__, __LINE__, __FILE__, MOCK_RETV, count); \
266 } while(0);
267 
268 
269 #ifdef __cplusplus
270 }
271 #endif
272