diff options
Diffstat (limited to 'fakegen.rb')
-rw-r--r-- | fakegen.rb | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/fakegen.rb b/fakegen.rb new file mode 100644 index 0000000..b070183 --- /dev/null +++ b/fakegen.rb @@ -0,0 +1,612 @@ + +# fakegen.rb +# A simple code generator to create some C macros for defining test fake functions + + +$cpp_output = true +$MAX_ARGS = 20 +$DEFAULT_ARG_HISTORY = 50 +$MAX_CALL_HISTORY = 50 + +def include_dependencies + putd "#include <stdarg.h>" + putd "#include <string.h> /* For memset and memcpy */" + puts +end + +def output_constants + putd "#define FFF_MAX_ARGS (#{$MAX_ARGS}u)" + putd "#ifndef FFF_ARG_HISTORY_LEN" + indent { + putd "#define FFF_ARG_HISTORY_LEN (#{$DEFAULT_ARG_HISTORY}u)" + } + putd "#endif" + putd "#ifndef FFF_CALL_HISTORY_LEN" + indent { + putd "#define FFF_CALL_HISTORY_LEN (#{$MAX_CALL_HISTORY}u)" + } + putd "#endif" +end + + + + + +# ------ Helper macros to use internally ------ # +def output_internal_helper_macros + putd "/* -- INTERNAL HELPER MACROS -- */" + + define_return_sequence_helper + define_custom_fake_sequence_helper + define_reset_fake_macro + define_declare_arg_helper + define_declare_all_func_common_helper + define_declare_return_value_history + define_save_arg_helper + define_room_for_more_history + define_save_ret_history_helper + define_save_arg_history_helper + define_history_dropped_helper + define_value_function_variables_helper + define_custom_fake_seq_variables_helper + define_increment_call_count_helper + define_return_fake_result_helper + define_extern_c_helper + define_reset_fake_helper + + putd "/* -- END INTERNAL HELPER MACROS -- */" + puts +end + +def define_return_sequence_helper + putd_backslash "#define SET_RETURN_SEQ(FUNCNAME, ARRAY_POINTER, ARRAY_LEN)" + indent { + putd_backslash "FUNCNAME##_fake.return_val_seq = ARRAY_POINTER;" + putd "FUNCNAME##_fake.return_val_seq_len = ARRAY_LEN;" + } +end + +def define_custom_fake_sequence_helper + putd_backslash "#define SET_CUSTOM_FAKE_SEQ(FUNCNAME, ARRAY_POINTER, ARRAY_LEN)" + indent { + putd_backslash "FUNCNAME##_fake.custom_fake_seq = ARRAY_POINTER;" + putd "FUNCNAME##_fake.custom_fake_seq_len = ARRAY_LEN;" + } +end + +def define_reset_fake_macro + puts + putd "/* Defining a function to reset a fake function */" + putd_backslash "#define RESET_FAKE(FUNCNAME) {" + indent { + putd_backslash "FUNCNAME##_reset();" + } + putd_backslash "}" + puts +end + +def define_declare_arg_helper + puts + putd_backslash "#define DECLARE_ARG(type, n, FUNCNAME)" + indent { + putd_backslash "type arg##n##_val;" + putd "type arg##n##_history[FFF_ARG_HISTORY_LEN];" + } +end + +def define_declare_all_func_common_helper + puts + putd_backslash "#define DECLARE_ALL_FUNC_COMMON" + indent { + putd_backslash "unsigned int call_count;" + putd_backslash "unsigned int arg_history_len;" + putd_backslash "unsigned int arg_histories_dropped;" + } +end + +def define_declare_return_value_history + putd "" + putd_backslash "#define DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)" + indent { + putd "RETURN_TYPE return_val_history[FFF_ARG_HISTORY_LEN];" + } +end + +def define_save_arg_helper + puts + putd_backslash "#define SAVE_ARG(FUNCNAME, n)" + indent { + putd "memcpy((void*)&FUNCNAME##_fake.arg##n##_val, (void*)&arg##n, sizeof(arg##n));" + } +end + +def define_save_ret_history_helper + putd "" + putd_backslash "#define SAVE_RET_HISTORY(FUNCNAME, RETVAL)" + indent { + putd_backslash "if ((FUNCNAME##_fake.call_count - 1) < FFF_ARG_HISTORY_LEN)" + indent { + putd_backslash "memcpy((void *)&FUNCNAME##_fake.return_val_history[FUNCNAME##_fake.call_count - 1], (const void *) &RETVAL, sizeof(RETVAL));" + } + } +end + +def define_room_for_more_history + puts + putd_backslash "#define ROOM_FOR_MORE_HISTORY(FUNCNAME)" + indent { + putd "FUNCNAME##_fake.call_count < FFF_ARG_HISTORY_LEN" + } +end + +def define_save_arg_history_helper + puts + putd_backslash "#define SAVE_ARG_HISTORY(FUNCNAME, ARGN)" + indent { + putd "memcpy((void*)&FUNCNAME##_fake.arg##ARGN##_history[FUNCNAME##_fake.call_count], (void*)&arg##ARGN, sizeof(arg##ARGN));" + } +end + +def define_history_dropped_helper + puts + putd_backslash "#define HISTORY_DROPPED(FUNCNAME)" + indent { + putd "FUNCNAME##_fake.arg_histories_dropped++" + } +end + +def define_value_function_variables_helper + puts + putd_backslash "#define DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)" + indent { + putd_backslash "RETURN_TYPE return_val;" + putd_backslash "int return_val_seq_len;" + putd_backslash "int return_val_seq_idx;" + putd_backslash "RETURN_TYPE * return_val_seq;" + } +end + +def define_custom_fake_seq_variables_helper + puts + putd_backslash "#define DECLARE_CUSTOM_FAKE_SEQ_VARIABLES" + indent { + putd_backslash "int custom_fake_seq_len;" + putd_backslash "int custom_fake_seq_idx;" + } +end + +def define_increment_call_count_helper + puts + putd_backslash "#define INCREMENT_CALL_COUNT(FUNCNAME)" + indent { + putd "FUNCNAME##_fake.call_count++" + } +end + +def define_return_fake_result_helper + puts + putd_backslash "#define RETURN_FAKE_RESULT(FUNCNAME)" + indent { + putd_backslash "if (FUNCNAME##_fake.return_val_seq_len){ /* then its a sequence */" + indent { + putd_backslash "if(FUNCNAME##_fake.return_val_seq_idx < FUNCNAME##_fake.return_val_seq_len) {" + indent { + putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_idx])" + putd_backslash "return FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_idx++];" + } + putd_backslash "}" + putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_len-1])" + putd_backslash "return FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_len-1]; /* return last element */" + } + putd_backslash "}" + putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val)" + putd_backslash "return FUNCNAME##_fake.return_val;" + } +end + +def define_extern_c_helper + puts + putd "#ifdef __cplusplus" + indent { + putd "#define FFF_EXTERN_C extern \"C\"{" + putd "#define FFF_END_EXTERN_C } " + } + putd "#else /* ansi c */" + indent { + putd "#define FFF_EXTERN_C " + putd "#define FFF_END_EXTERN_C " + } + putd "#endif /* cpp/ansi c */" +end + +def define_reset_fake_helper + puts + putd_backslash "#define DEFINE_RESET_FUNCTION(FUNCNAME)" + indent { + putd_backslash "void FUNCNAME##_reset(void){" + indent { + putd_backslash "memset(&FUNCNAME##_fake, 0, sizeof(FUNCNAME##_fake));" + putd_backslash "FUNCNAME##_fake.arg_history_len = FFF_ARG_HISTORY_LEN;" + } + putd "}" + } +end +# ------ End Helper macros ------ # + +#fakegen helpers to print at levels of indentation +$current_depth = 0 +def putd(str) + $current_depth.times {|not_used| print " "} + puts str +end + +#multiline putd which adds a \ at the end of the generated macro +def putd_backslash str + putd(str + " \\") +end + +def pushd + $current_depth = $current_depth + 4 +end + +def popd + $current_depth = $current_depth - 4 +end + +def indent + pushd + yield + popd +end + +def output_macro(arg_count, has_varargs, is_value_function) + + vararg_name = has_varargs ? "_VARARG" : "" + fake_macro_name = is_value_function ? "FAKE_VALUE_FUNC#{arg_count}#{vararg_name}" : "FAKE_VOID_FUNC#{arg_count}#{vararg_name}" + declare_macro_name = "DECLARE_#{fake_macro_name}" + define_macro_name = "DEFINE_#{fake_macro_name}" + saved_arg_count = arg_count - (has_varargs ? 1 : 0) + return_type = is_value_function ? "RETURN_TYPE" : "" + + puts + output_macro_header(declare_macro_name, saved_arg_count, has_varargs, return_type) + indent { + extern_c { # define argument capture variables + output_variables(saved_arg_count, has_varargs, is_value_function) + } + } + + puts + output_macro_header(define_macro_name, saved_arg_count, has_varargs, return_type) + indent { + extern_c { + putd_backslash "FUNCNAME##_Fake FUNCNAME##_fake;" + putd_backslash function_signature(saved_arg_count, has_varargs, is_value_function) + "{" + indent { + output_function_body(saved_arg_count, has_varargs, is_value_function) + } + putd_backslash "}" + putd_backslash "DEFINE_RESET_FUNCTION(FUNCNAME)" + } + } + + puts + + output_macro_header(fake_macro_name, saved_arg_count, has_varargs, return_type) + indent { + putd macro_signature_for(declare_macro_name, saved_arg_count, has_varargs, return_type) + putd macro_signature_for(define_macro_name, saved_arg_count, has_varargs, return_type) + puts + } +end + +def output_macro_header(macro_name, arg_count, has_varargs, return_type) + output_macro_name(macro_name, arg_count, has_varargs, return_type) +end + +# #define #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...) +def output_macro_name(macro_name, arg_count, has_varargs, return_type) + putd "#define " + macro_signature_for(macro_name, arg_count, has_varargs, return_type) +end + +# #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...) \ +def macro_signature_for(macro_name, arg_count, has_varargs, return_type) + parameter_list = "#{macro_name}(" + if return_type != "" + parameter_list += return_type + parameter_list += ", " + end + parameter_list += "FUNCNAME" + + arg_count.times { |i| parameter_list += ", ARG#{i}_TYPE" } + + parameter_list += ", ..." if has_varargs + + parameter_list += ") \\" + + parameter_list +end + +def output_variables(arg_count, has_varargs, is_value_function) + in_struct{ + arg_count.times { |argN| + putd_backslash "DECLARE_ARG(ARG#{argN}_TYPE, #{argN}, FUNCNAME)" + } + putd_backslash "DECLARE_ALL_FUNC_COMMON" + putd_backslash "DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)" unless not is_value_function + putd_backslash "DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)" unless not is_value_function + putd_backslash "DECLARE_CUSTOM_FAKE_SEQ_VARIABLES" + output_custom_function_signature(arg_count, has_varargs, is_value_function) + output_custom_function_array(arg_count, has_varargs, is_value_function) + } + putd_backslash "extern FUNCNAME##_Fake FUNCNAME##_fake;" + putd_backslash "void FUNCNAME##_reset(void);" + putd_backslash function_signature(arg_count, has_varargs, is_value_function) + ";" +end + +#example: ARG0_TYPE arg0, ARG1_TYPE arg1 +def arg_val_list(args_count) + return "void" if (args_count == 0) + arguments = [] + args_count.times { |i| arguments << "ARG#{i}_TYPE arg#{i}" } + arguments.join(", ") +end + +#example: arg0, arg1 +def arg_list(args_count) + arguments = [] + args_count.times { |i| arguments << "arg#{i}" } + arguments.join(", ") +end + +# RETURN_TYPE (*custom_fake)(ARG0_TYPE arg0);\ +# void (*custom_fake)(ARG0_TYPE arg0, ARG1_TYPE arg1, ARG2_TYPE arg2);\ +def output_custom_function_signature(arg_count, has_varargs, is_value_function) + return_type = is_value_function ? "RETURN_TYPE" : "void" + ap_list = has_varargs ? ", va_list ap" : "" + signature = "(*custom_fake)(#{arg_val_list(arg_count)}#{ap_list});" + putd_backslash return_type + signature +end + +def output_custom_function_array(arg_count, has_varargs, is_value_function) + return_type = is_value_function ? "RETURN_TYPE" : "void" + ap_list = has_varargs ? ", va_list ap" : "" + custom_array = "(**custom_fake_seq)(#{arg_val_list(arg_count)}#{ap_list});" + putd_backslash return_type + custom_array +end + +# example: RETURN_TYPE FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1) +def function_signature(arg_count, has_varargs, is_value_function) + return_type = is_value_function ? "RETURN_TYPE" : "void" + varargs = has_varargs ? ", ..." : "" + "#{return_type} FUNCNAME(#{arg_val_list(arg_count)}#{varargs})" +end + +def output_function_body(arg_count, has_varargs, is_value_function) + arg_count.times { |i| putd_backslash "SAVE_ARG(FUNCNAME, #{i});" } + putd_backslash "if(ROOM_FOR_MORE_HISTORY(FUNCNAME)){" + indent { + arg_count.times { |i| putd_backslash "SAVE_ARG_HISTORY(FUNCNAME, #{i});" } + } + putd_backslash "}" + putd_backslash "else{" + indent { + putd_backslash "HISTORY_DROPPED(FUNCNAME);" + } + putd_backslash "}" + putd_backslash "INCREMENT_CALL_COUNT(FUNCNAME);" + putd_backslash "REGISTER_CALL(FUNCNAME);" + + if has_varargs + putd_backslash "if(FUNCNAME##_fake.custom_fake){" + indent { + putd_backslash "RETURN_TYPE ret;" if is_value_function + putd_backslash "va_list ap;" + putd_backslash "va_start(ap, arg#{arg_count-1});" + } + custom_fake_call = "FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)}, ap);" + indent { + if is_value_function + putd_backslash "ret = #{custom_fake_call}" + else + putd_backslash "#{custom_fake_call}" + end + putd_backslash "va_end(ap);" + putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function + putd_backslash "return ret;" if is_value_function + } + putd_backslash "}" + else + return_type = is_value_function ? "return " : "" + putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_len){ /* a sequence of custom fakes */" + indent { + putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_idx < FUNCNAME##_fake.custom_fake_seq_len){" + indent { + putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)});" unless not is_value_function + putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function + putd_backslash "return ret;" unless not is_value_function + putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)});" unless is_value_function + } + putd_backslash "}" + putd_backslash "else{" + indent { + putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)});" unless not is_value_function + putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function + putd_backslash "return ret;" unless not is_value_function + putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)});" + } + putd_backslash "}" + } + putd_backslash "}" + putd_backslash "if (FUNCNAME##_fake.custom_fake) #{return_type}FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});" + end + + putd_backslash "RETURN_FAKE_RESULT(FUNCNAME)" if is_value_function +end + +def define_fff_globals + putd "typedef void (*fff_function_t)(void);" + putd "typedef struct { " + indent { + putd "fff_function_t call_history[FFF_CALL_HISTORY_LEN];" + putd "unsigned int call_history_idx;" + } + putd "} fff_globals_t;" + puts + putd_backslash "FFF_EXTERN_C" + putd "extern fff_globals_t fff;" + putd_backslash "FFF_END_EXTERN_C" + puts + putd_backslash "#define DEFINE_FFF_GLOBALS" + indent { + putd_backslash "FFF_EXTERN_C" + indent { + putd_backslash "fff_globals_t fff;" + } + putd "FFF_END_EXTERN_C" + } + puts + putd_backslash "#define FFF_RESET_HISTORY()" + indent { + putd_backslash "fff.call_history_idx = 0;" + putd "memset(fff.call_history, 0, sizeof(fff.call_history));" + } + puts + putd_backslash "#define REGISTER_CALL(function)" + indent { + putd_backslash "if(fff.call_history_idx < FFF_CALL_HISTORY_LEN)" + indent { + putd "fff.call_history[fff.call_history_idx++] = (fff_function_t)function;" + } + } +end + +def extern_c + putd_backslash "FFF_EXTERN_C" + indent { + yield + } + putd_backslash "FFF_END_EXTERN_C" +end + +def in_struct + putd_backslash "typedef struct FUNCNAME##_Fake {" + indent { + yield + } + putd_backslash "} FUNCNAME##_Fake;" +end + +def include_guard + putd "#ifndef FAKE_FUNCTIONS" + putd "#define FAKE_FUNCTIONS" + puts + + yield + + puts + putd "#endif /* FAKE_FUNCTIONS */" +end + +def msvc_expand_macro_fix + putd "/* MSVC expand macro fix */" + putd "#define EXPAND(x) x" +end + +def generate_arg_sequence(args, prefix, do_reverse, joinstr) + fmap = (0..args).flat_map {|i| [prefix + i.to_s]} + if do_reverse then fmap.reverse.join(joinstr) else fmap.join(", ") end +end + +def counting_macro_instance(type, vararg = :non_vararg, prefix = "") + appendix = (vararg == :vararg) ? "_VARARG" : "" + minus_count = (type == :VOID) ? 1 : 2 + + <<-MACRO_COUNTING_INSTANCE +#define #{prefix}FAKE_#{type.to_s}_FUNC#{appendix}(...) \ + EXPAND(#{prefix}FUNC_#{type.to_s}#{appendix}_(PP_NARG_MINUS#{minus_count}(__VA_ARGS__), __VA_ARGS__)) + +#define #{prefix}FUNC_#{type.to_s}#{appendix}_(N,...) \ + EXPAND(#{prefix}FUNC_#{type.to_s}#{appendix}_N(N,__VA_ARGS__)) + +#define #{prefix}FUNC_#{type.to_s}#{appendix}_N(N,...) \ + EXPAND(#{prefix}FAKE_#{type.to_s}_FUNC ## N#{" ## _VARARG" if vararg == :vararg}(__VA_ARGS__)) + + MACRO_COUNTING_INSTANCE +end + +def output_macro_counting_shortcuts + msvc_expand_macro_fix + putd <<-MACRO_COUNTING + +#define PP_NARG_MINUS2(...) \ + EXPAND(PP_NARG_MINUS2_(__VA_ARGS__, PP_RSEQ_N_MINUS2())) + +#define PP_NARG_MINUS2_(...) \ + EXPAND(PP_ARG_MINUS2_N(__VA_ARGS__)) + +#define PP_ARG_MINUS2_N(returnVal, #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N + +#define PP_RSEQ_N_MINUS2() \ + #{generate_arg_sequence($MAX_ARGS, '', true, ',')} + + +#define PP_NARG_MINUS1(...) \ + EXPAND(PP_NARG_MINUS1_(__VA_ARGS__, PP_RSEQ_N_MINUS1())) + +#define PP_NARG_MINUS1_(...) \ + EXPAND(PP_ARG_MINUS1_N(__VA_ARGS__)) + +#define PP_ARG_MINUS1_N(#{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N + +#define PP_RSEQ_N_MINUS1() \ + #{generate_arg_sequence($MAX_ARGS, '', true, ',')} + + + +/* DECLARE AND DEFINE FAKE FUNCTIONS - PLACE IN TEST FILES */ + +#{counting_macro_instance(:VALUE)} +#{counting_macro_instance(:VOID)} +#{counting_macro_instance(:VALUE, :vararg)} +#{counting_macro_instance(:VOID, :vararg)} + +/* DECLARE FAKE FUNCTIONS - PLACE IN HEADER FILES */ + +#{counting_macro_instance(:VALUE, :non_vararg, "DECLARE_")} +#{counting_macro_instance(:VOID, :non_vararg, "DECLARE_")} +#{counting_macro_instance(:VALUE, :vararg, "DECLARE_")} +#{counting_macro_instance(:VOID, :vararg, "DECLARE_")} + +/* DEFINE FAKE FUNCTIONS - PLACE IN SOURCE FILES */ + +#{counting_macro_instance(:VALUE, :non_vararg, "DEFINE_")} +#{counting_macro_instance(:VOID, :non_vararg, "DEFINE_")} +#{counting_macro_instance(:VALUE, :vararg, "DEFINE_")} +#{counting_macro_instance(:VOID, :vararg, "DEFINE_")} + + MACRO_COUNTING +end + +def output_c_and_cpp + + include_guard { + include_dependencies + output_constants + output_internal_helper_macros + yield + output_macro_counting_shortcuts + } +end + +# lets generate!! +output_c_and_cpp{ + define_fff_globals + # Create fake generators for 0..MAX_ARGS + num_fake_generators = $MAX_ARGS + 1 + num_fake_generators.times {|arg_count| output_macro(arg_count, false, false)} + num_fake_generators.times {|arg_count| output_macro(arg_count, false, true)} + # generate the varargs variants + (2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, false)} + (2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, true)} +} |