@@ -47,7 +47,9 @@ def self.default_options
47
47
use_param_tests : false ,
48
48
use_system_files : true ,
49
49
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
51
53
}
52
54
end
53
55
@@ -90,6 +92,7 @@ def run(input_file, output_file, options = nil)
90
92
def generate ( input_file , output_file , tests , used_mocks , testfile_includes )
91
93
File . open ( output_file , 'w' ) do |output |
92
94
create_header ( output , used_mocks , testfile_includes )
95
+ create_run_test_params_struct ( output )
93
96
create_externs ( output , tests , used_mocks )
94
97
create_mock_management ( output , used_mocks )
95
98
create_setup ( output )
@@ -99,6 +102,7 @@ def generate(input_file, output_file, tests, used_mocks, testfile_includes)
99
102
create_reset ( output )
100
103
create_run_test ( output ) unless tests . empty?
101
104
create_args_wrappers ( output , tests )
105
+ create_shuffle_tests ( output ) if @options [ :shuffle_tests ]
102
106
create_main ( output , input_file , tests , used_mocks )
103
107
end
104
108
@@ -231,16 +235,40 @@ def find_setup_and_teardown(source)
231
235
@options [ :has_suite_teardown ] ||= ( source =~ /int\s +suiteTearDown\s *\( int\s +([a-zA-Z0-9_])+\s *\) / )
232
236
end
233
237
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
+
234
256
def create_header ( output , mocks , testfile_includes = [ ] )
235
257
output . puts ( '/* AUTOGENERATED FILE. DO NOT EDIT. */' )
236
258
output . puts ( "\n /*=======Automagically Detected Files To Include=====*/" )
237
259
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
238
266
output . puts ( "#include \" #{ @options [ :framework ] } .h\" " )
239
267
output . puts ( '#include "cmock.h"' ) unless mocks . empty?
240
268
output . puts ( '}' ) if @options [ :externcincludes ]
241
269
if @options [ :defines ] && !@options [ :defines ] . empty?
242
270
output . puts ( "/* injected defines for unity settings, etc */" )
243
- @options [ :defines ] . each do |d |
271
+ @options [ :defines ] . each do |d |
244
272
def_only = d . match ( /(\w +).*/ ) [ 1 ]
245
273
output . puts ( "#ifndef #{ def_only } \n #define #{ d } \n #endif /* #{ def_only } */" )
246
274
end
@@ -270,6 +298,16 @@ def create_header(output, mocks, testfile_includes = [])
270
298
output . puts ( 'char* GlobalOrderError;' )
271
299
end
272
300
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
+
273
311
def create_externs ( output , tests , _mocks )
274
312
output . puts ( "\n /*=======External Functions This Runner Calls=====*/" )
275
313
output . puts ( "extern void #{ @options [ :setup_name ] } (void);" )
@@ -392,6 +430,22 @@ def create_args_wrappers(output, tests)
392
430
end
393
431
end
394
432
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
+
395
449
def create_main ( output , filename , tests , used_mocks )
396
450
output . puts ( "\n /*=======MAIN=====*/" )
397
451
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)
437
491
else
438
492
output . puts ( " UnityBegin(\" #{ filename . gsub ( /\\ / , '\\\\\\' ) } \" );" )
439
493
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 |
441
507
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 ] } ;" )
443
511
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 ] } "
446
514
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 ] } ;" )
448
518
end
449
519
end
450
520
end
451
521
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
452
531
output . puts ( ' CMock_Guts_MemFreeFinal();' ) unless used_mocks . empty?
453
532
if @options [ :has_suite_teardown ]
454
533
if @options [ :omit_begin_end ]
@@ -534,7 +613,9 @@ def create_h_file(output, filename, tests, testfile_includes, used_mocks)
534
613
' --suite_teardown="" - code to execute for teardown of entire suite' ,
535
614
' --use_param_tests=1 - enable parameterized tests (disabled by default)' ,
536
615
' --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 " )
538
619
exit 1
539
620
end
540
621
0 commit comments