2025-08-08 12:40:03 +02:00

271 lines
8.6 KiB
C

/*
* Copyright (c) 2011 Tony Bai <bigwhite.cn@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @file cbehave.h
*
*/
#ifndef _CBEHAVE_H
#define _CBEHAVE_H
#ifdef _cplusplus
extern "C" {
#endif
#ifndef _cplusplus
#include <stdbool.h>
#endif
#include "apr_ring.h"
#define CBEHAVE_LOGO \
"*******************************************************************\n\
\tCBEHAVE -- A Behavior Driven Development Framework for C\n\
\t\t\t By Tony Bai\n\
*******************************************************************"
#define CBEHAVE_MAX_NAME_LEN 128
typedef struct cbehave_state {
int total_features;
int failed_features;
int total_scenarios;
int failed_scenarios;
} cbehave_state;
typedef enum {
CBEHAVE_SCOPE_NONE,
CBEHAVE_SCOPE_GIVEN,
CBEHAVE_SCOPE_WHEN,
CBEHAVE_SCOPE_THEN
} cbehave_scope_e;
extern cbehave_scope_e cbehave_scope;
#define END_SCOPE \
if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
cbehave_given_exit(_state); \
} else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
cbehave_when_exit(_state); \
} else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
cbehave_then_exit(_state); \
}
#define GIVEN_IMPL(x, _prompt) \
END_SCOPE \
cbehave_scope = CBEHAVE_SCOPE_GIVEN; \
cbehave_given_entry(_prompt, x, _state);
#define GIVEN(x) GIVEN_IMPL(x, "Given")
#define GIVEN_END
#define WHEN_IMPL(x, _prompt) \
END_SCOPE \
cbehave_scope = CBEHAVE_SCOPE_WHEN; \
cbehave_when_entry(_prompt, x, _state);
#define WHEN(x) WHEN_IMPL(x, "When")
#define WHEN_END
#define THEN_IMPL(x, _prompt) \
END_SCOPE \
cbehave_scope = CBEHAVE_SCOPE_THEN; \
cbehave_then_entry(_prompt, x, _state);
#define THEN(x) THEN_IMPL(x, "Then")
#define THEN_END
#define AND(x) \
if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
GIVEN_IMPL(x, "And") \
} else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
WHEN_IMPL(x, "And") \
} else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
THEN_IMPL(x, "And") \
}
#define SCENARIO(x) { \
int _scenario_state = 0; \
cbehave_scenario_entry(x, _state); \
cbehave_scope = CBEHAVE_SCOPE_NONE;
#define SCENARIO_END \
END_SCOPE \
cbehave_scenario_exit(&_scenario_state, _state); \
}
#define FEATURE(idx, x) static void _cbehave_feature_##idx(void *_state) { \
cbehave_state _old_state; \
cbehave_feature_entry(x, &_old_state, _state);
#define FEATURE_END \
_feature_over: \
cbehave_feature_exit(&_old_state, _state); \
}
#define ASSERT(cond, ret) \
if (!(cond)) {\
cbehave_feature_return(__FILE__, __LINE__, ret, _state); \
goto _feature_over; \
}\
#define TEST_FEATURE(name) {_cbehave_feature_##name}
#define SHOULD_INT_EQUAL(actual, expected) do { \
should_int_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_INT_GT(val1, val2) do { \
should_int_gt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_INT_LT(val1, val2) do { \
should_int_lt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_INT_GE(val1, val2) do { \
should_int_ge((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_INT_LE(val1, val2) do { \
should_int_le((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_STR_EQUAL(actual, expected) do { \
should_str_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_MEM_EQUAL(actual, expected, size) do { \
should_mem_equal((actual), (expected), (size), &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_BE_TRUE(actual) do { \
should_be_bool((actual), true, &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define SHOULD_BE_FALSE(actual) do { \
should_be_bool((actual), false, &_scenario_state, __FILE__, __LINE__); \
} while(0)
#define CBEHAVE_RUN(_description, ...)\
int main(void) {\
cbehave_feature _cfeatures[] = {__VA_ARGS__};\
return cbehave_runner(_description, _cfeatures);\
}
#define cbehave_runner(str, features) \
_cbehave_runner(str, features, sizeof(features)/sizeof(features[0]))
typedef struct cbehave_feature {
void (*func)(void *state);
} cbehave_feature;
int _cbehave_runner(const char *description, const cbehave_feature *features, int count);
void should_int_equal(int actual, int expected,
void *state,
const char *file, int line);
void should_int_gt(int val1, int val2,
void *state,
const char *file, int line);
void should_int_lt(int val1, int val2,
void *state,
const char *file, int line);
void should_int_ge(int val1, int val2,
void *state,
const char *file, int line);
void should_int_le(int val1, int val2,
void *state,
const char *file, int line);
void should_str_equal(const char *actual, const char *expected, void *state,
const char *file, int line);
void should_mem_equal(const void *actual, const void *expected, size_t size, void *state,
const char *file, int line);
void should_be_bool(bool actual, bool expected, void *state, const char *file, int line);
void cbehave_given_entry(const char *prompt, const char *str, void *state);
void cbehave_when_entry(const char *prompt, const char *str, void *state);
void cbehave_then_entry(const char *prompt, const char *str, void *state);
void cbehave_scenario_entry(const char *str, void *state);
void cbehave_feature_entry(const char *str, void *old_state, void *state);
void cbehave_given_exit(void *state);
void cbehave_when_exit(void *state);
void cbehave_then_exit(void *state);
void cbehave_scenario_exit(void *scenario_state, void *state);
void cbehave_feature_exit(void *old_state, void *state);
void cbehave_feature_return(const char *file, int line, int ret, void *state);
/*
* mock symbol list
*
* ------------
* | symbol-#0|-> value_list
* ------------
* | symbol-#1|-> value_list
* ------------
* | symbol-#2|-> value_list
* ------------
* | ... ... |-> value_list
* ------------
* | symbol-#n|-> value_list
* ------------
*/
typedef struct cbehave_value_t {
APR_RING_ENTRY(cbehave_value_t) link;
void *value;
} cbehave_value_t;
typedef APR_RING_HEAD(cbehave_value_head_t, cbehave_value_t) cbehave_value_head_t;
typedef struct cbehave_symbol_t {
APR_RING_ENTRY(cbehave_symbol_t) link;
char desc[CBEHAVE_MAX_NAME_LEN];
int obj_type;
int always_return_flag; /* 1: always return the same value; 0(default) */
void* value;
cbehave_value_head_t value_list;
} cbehave_symbol_t;
typedef APR_RING_HEAD(cbehave_symbol_head_t, cbehave_symbol_t) cbehave_symbol_head_t;
void* cbehave_mock_obj(const char *fcname, int lineno, const char *fname, int obj_type);
void cbehave_mock_obj_return(const char *symbol_name, void *value, const char *fcname,
int lineno, const char *fname, int obj_type, int count);
#define MOCK_ARG 0x0
#define MOCK_RETV 0x1
#define CBEHAVE_MOCK_ARG() cbehave_mock_obj(__FUNCTION__, __LINE__, __FILE__, MOCK_ARG)
#define CBEHAVE_MOCK_RETV() cbehave_mock_obj(__FUNCTION__, __LINE__, __FILE__, MOCK_RETV)
#define CBEHAVE_ARG_RETURN(fcname, value) do { \
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, 1); \
} while(0);
#define CBEHAVE_ARG_RETURN_COUNT(fcname, value, count) do { \
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, count); \
} while(0);
#define CBEHAVE_RETV_RETURN(fcname, value) do { \
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, 1); \
} while(0);
#define CBEHAVE_RETV_RETURN_COUNT(fcname, value, count) do { \
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, count); \
} while(0);
#ifdef _cplusplus
}
#endif
#endif /* _CBEHAVE_H */