@@ -47,7 +47,9 @@ def self.default_options
4747 use_param_tests : false ,
4848 use_system_files : true ,
4949 include_extensions : '(?:hpp|hh|H|h)' ,
50- source_extensions : '(?:cpp|cc|ino|C|c)'
50+ source_extensions : '(?:cpp|cc|ino|C|c)' ,
51+ shuffle_tests : false ,
52+ rng_seed : 0
5153 }
5254 end
5355
@@ -90,6 +92,7 @@ def run(input_file, output_file, options = nil)
9092 def generate ( input_file , output_file , tests , used_mocks , testfile_includes )
9193 File . open ( output_file , 'w' ) do |output |
9294 create_header ( output , used_mocks , testfile_includes )
95+ create_run_test_params_struct ( output )
9396 create_externs ( output , tests , used_mocks )
9497 create_mock_management ( output , used_mocks )
9598 create_setup ( output )
@@ -99,6 +102,7 @@ def generate(input_file, output_file, tests, used_mocks, testfile_includes)
99102 create_reset ( output )
100103 create_run_test ( output ) unless tests . empty?
101104 create_args_wrappers ( output , tests )
105+ create_shuffle_tests ( output ) if @options [ :shuffle_tests ]
102106 create_main ( output , input_file , tests , used_mocks )
103107 end
104108
@@ -231,16 +235,40 @@ def find_setup_and_teardown(source)
231235 @options [ :has_suite_teardown ] ||= ( source =~ /int\s +suiteTearDown\s *\( int\s +([a-zA-Z0-9_])+\s *\) / )
232236 end
233237
238+ def count_tests ( tests )
239+ if ( @options [ :use_param_tests ] )
240+ idx = 0
241+ tests . each do |test |
242+ if ( ( test [ :args ] . nil? ) or ( test [ :args ] . empty? ) )
243+ idx += 1
244+ else
245+ test [ :args ] . each do |args |
246+ idx += 1
247+ end
248+ end
249+ end
250+ return idx
251+ else
252+ return tests . size
253+ end
254+ end
255+
234256 def create_header ( output , mocks , testfile_includes = [ ] )
235257 output . puts ( '/* AUTOGENERATED FILE. DO NOT EDIT. */' )
236258 output . puts ( "\n /*=======Automagically Detected Files To Include=====*/" )
237259 output . puts ( 'extern "C" {' ) if @options [ :externcincludes ]
260+ if @options [ :shuffle_tests ]
261+ output . puts ( '#include <stdlib.h>' )
262+ if @options [ :rng_seed ] == 0
263+ output . puts ( '#include <time.h>' )
264+ end
265+ end
238266 output . puts ( "#include \" #{ @options [ :framework ] } .h\" " )
239267 output . puts ( '#include "cmock.h"' ) unless mocks . empty?
240268 output . puts ( '}' ) if @options [ :externcincludes ]
241269 if @options [ :defines ] && !@options [ :defines ] . empty?
242270 output . puts ( "/* injected defines for unity settings, etc */" )
243- @options [ :defines ] . each do |d |
271+ @options [ :defines ] . each do |d |
244272 def_only = d . match ( /(\w +).*/ ) [ 1 ]
245273 output . puts ( "#ifndef #{ def_only } \n #define #{ d } \n #endif /* #{ def_only } */" )
246274 end
@@ -270,6 +298,16 @@ def create_header(output, mocks, testfile_includes = [])
270298 output . puts ( 'char* GlobalOrderError;' )
271299 end
272300
301+ def create_run_test_params_struct ( output )
302+ output . puts ( "\n /*=======Structure Used By Test Runner=====*/" )
303+ output . puts ( 'struct UnityRunTestParameters' )
304+ output . puts ( '{' )
305+ output . puts ( ' UnityTestFunction func;' )
306+ output . puts ( ' const char* name;' )
307+ output . puts ( ' UNITY_LINE_TYPE line_num;' )
308+ output . puts ( '};' )
309+ end
310+
273311 def create_externs ( output , tests , _mocks )
274312 output . puts ( "\n /*=======External Functions This Runner Calls=====*/" )
275313 output . puts ( "extern void #{ @options [ :setup_name ] } (void);" )
@@ -392,6 +430,22 @@ def create_args_wrappers(output, tests)
392430 end
393431 end
394432
433+ def create_shuffle_tests ( output )
434+ output . puts ( "\n /*=======Shuffle Test Order=====*/" )
435+ output . puts ( 'static void shuffleTests(struct UnityRunTestParameters run_test_params_arr[], int num_of_tests)' )
436+ output . puts ( '{' )
437+
438+ # Use Fisher-Yates shuffle algorithm
439+ output . puts ( ' for (int i = 0; i < num_of_tests - 1; i++)' )
440+ output . puts ( ' {' )
441+ output . puts ( ' int j = (rand() % (num_of_tests - i)) + i;' )
442+ output . puts ( ' struct UnityRunTestParameters temp = run_test_params_arr[i];' )
443+ output . puts ( ' run_test_params_arr[i] = run_test_params_arr[j];' )
444+ output . puts ( ' run_test_params_arr[j] = temp;' )
445+ output . puts ( ' }' )
446+ output . puts ( '}' )
447+ end
448+
395449 def create_main ( output , filename , tests , used_mocks )
396450 output . puts ( "\n /*=======MAIN=====*/" )
397451 main_name = @options [ :main_name ] . to_sym == :auto ? "main_#{ filename . gsub ( '.c' , '' ) } " : ( @options [ :main_name ] ) . to_s
@@ -437,18 +491,43 @@ def create_main(output, filename, tests, used_mocks)
437491 else
438492 output . puts ( " UnityBegin(\" #{ filename . gsub ( /\\ / , '\\\\\\' ) } \" );" )
439493 end
440- tests . each do |test |
494+ if @options [ :shuffle_tests ]
495+ output . puts
496+ if @options [ :rng_seed ] == 0
497+ output . puts ( ' srand(time(NULL));' )
498+ else
499+ output . puts ( " srand(#{ @options [ :rng_seed ] } );" )
500+ end
501+ end
502+ output . puts
503+ output . puts ( " int number_of_tests = #{ count_tests ( tests ) } ;" )
504+ output . puts ( ' struct UnityRunTestParameters run_test_params_arr[number_of_tests];' )
505+ output . puts
506+ tests . each . with_index ( ) do |test , idx |
441507 if ( !@options [ :use_param_tests ] ) || test [ :args ] . nil? || test [ :args ] . empty?
442- output . puts ( " run_test(#{ test [ :test ] } , \" #{ test [ :test ] } \" , #{ test [ :line_number ] } );" )
508+ output . puts ( " run_test_params_arr[#{ idx } ].func = #{ test [ :test ] } ;" )
509+ output . puts ( " run_test_params_arr[#{ idx } ].name = \" #{ test [ :test ] } \" ;" )
510+ output . puts ( " run_test_params_arr[#{ idx } ].line_num = #{ test [ :line_number ] } ;" )
443511 else
444- test [ :args ] . each . with_index ( 1 ) do |args , idx |
445- wrapper = "runner_args#{ idx } _#{ test [ :test ] } "
512+ test [ :args ] . each . with_index ( 1 ) do |args , arg_idx |
513+ wrapper = "runner_args#{ arg_idx } _#{ test [ :test ] } "
446514 testname = "#{ test [ :test ] } (#{ args } )" . dump
447- output . puts ( " run_test(#{ wrapper } , #{ testname } , #{ test [ :line_number ] } );" )
515+ output . puts ( " run_test_params_arr[#{ idx } ].func = #{ wrapper } ;" )
516+ output . puts ( " run_test_params_arr[#{ idx } ].name = #{ testname } ;" )
517+ output . puts ( " run_test_params_arr[#{ idx } ].line_num = #{ test [ :line_number ] } ;" )
448518 end
449519 end
450520 end
451521 output . puts
522+ if @options [ :shuffle_tests ]
523+ output . puts ( ' shuffleTests(run_test_params_arr, number_of_tests);' )
524+ output . puts
525+ end
526+ output . puts ( ' for (int i = 0; i < number_of_tests; i++)' )
527+ output . puts ( ' {' )
528+ output . puts ( ' run_test(run_test_params_arr[i].func, run_test_params_arr[i].name, run_test_params_arr[i].line_num);' )
529+ output . puts ( ' }' )
530+ output . puts
452531 output . puts ( ' CMock_Guts_MemFreeFinal();' ) unless used_mocks . empty?
453532 if @options [ :has_suite_teardown ]
454533 if @options [ :omit_begin_end ]
@@ -534,7 +613,9 @@ def create_h_file(output, filename, tests, testfile_includes, used_mocks)
534613 ' --suite_teardown="" - code to execute for teardown of entire suite' ,
535614 ' --use_param_tests=1 - enable parameterized tests (disabled by default)' ,
536615 ' --omit_begin_end=1 - omit calls to UnityBegin and UnityEnd (disabled by default)' ,
537- ' --header_file="" - path/name of test header file to generate too' ] . join ( "\n " )
616+ ' --header_file="" - path/name of test header file to generate too' ,
617+ ' --shuffle_tests=1 - enable shuffling of the test execution order (disabled by default)' ,
618+ ' --rng_seed=1 - seed value for randomization of test execution order' ] . join ( "\n " )
538619 exit 1
539620 end
540621
0 commit comments