@@ -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,10 +235,34 @@ 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? || 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 ]
@@ -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 = num_of_tests - 1; i > 0; i--)' )
440
+ output . puts ( ' {' )
441
+ output . puts ( ' int j = rand() % (i + 1);' )
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
@@ -439,18 +493,46 @@ def create_main(output, filename, tests, used_mocks)
439
493
else
440
494
output . puts ( " UnityBegin(\" #{ filename . gsub ( /\\ / , '\\\\\\' ) } \" );" )
441
495
end
496
+ if @options [ :shuffle_tests ]
497
+ output . puts
498
+ if @options [ :rng_seed ] == 0
499
+ output . puts ( ' srand(time(NULL));' )
500
+ else
501
+ output . puts ( " srand(#{ @options [ :rng_seed ] } );" )
502
+ end
503
+ end
504
+ output . puts
505
+ output . puts ( " int number_of_tests = #{ count_tests ( tests ) } ;" )
506
+ output . puts ( ' struct UnityRunTestParameters run_test_params_arr[number_of_tests];' )
507
+ output . puts
508
+ idx = 0
442
509
tests . each do |test |
443
510
if ( !@options [ :use_param_tests ] ) || test [ :args ] . nil? || test [ :args ] . empty?
444
- output . puts ( " run_test(#{ test [ :test ] } , \" #{ test [ :test ] } \" , #{ test [ :line_number ] } );" )
511
+ output . puts ( " run_test_params_arr[#{ idx } ].func = #{ test [ :test ] } ;" )
512
+ output . puts ( " run_test_params_arr[#{ idx } ].name = \" #{ test [ :test ] } \" ;" )
513
+ output . puts ( " run_test_params_arr[#{ idx } ].line_num = #{ test [ :line_number ] } ;" )
514
+ idx += 1
445
515
else
446
- test [ :args ] . each . with_index ( 1 ) do |args , idx |
447
- wrapper = "runner_args#{ idx } _#{ test [ :test ] } "
516
+ test [ :args ] . each . with_index ( 1 ) do |args , arg_idx |
517
+ wrapper = "runner_args#{ arg_idx } _#{ test [ :test ] } "
448
518
testname = "#{ test [ :test ] } (#{ args } )" . dump
449
- output . puts ( " run_test(#{ wrapper } , #{ testname } , #{ test [ :line_number ] } );" )
519
+ output . puts ( " run_test_params_arr[#{ idx } ].func = #{ wrapper } ;" )
520
+ output . puts ( " run_test_params_arr[#{ idx } ].name = #{ testname } ;" )
521
+ output . puts ( " run_test_params_arr[#{ idx } ].line_num = #{ test [ :line_number ] } ;" )
522
+ idx += 1
450
523
end
451
524
end
452
525
end
453
526
output . puts
527
+ if @options [ :shuffle_tests ]
528
+ output . puts ( ' shuffleTests(run_test_params_arr, number_of_tests);' )
529
+ output . puts
530
+ end
531
+ output . puts ( ' for (int i = 0; i < number_of_tests; i++)' )
532
+ output . puts ( ' {' )
533
+ output . puts ( ' run_test(run_test_params_arr[i].func, run_test_params_arr[i].name, run_test_params_arr[i].line_num);' )
534
+ output . puts ( ' }' )
535
+ output . puts
454
536
output . puts ( ' CMock_Guts_MemFreeFinal();' ) unless used_mocks . empty?
455
537
if @options [ :has_suite_teardown ]
456
538
if @options [ :omit_begin_end ]
@@ -536,7 +618,9 @@ def create_h_file(output, filename, tests, testfile_includes, used_mocks)
536
618
' --suite_teardown="" - code to execute for teardown of entire suite' ,
537
619
' --use_param_tests=1 - enable parameterized tests (disabled by default)' ,
538
620
' --omit_begin_end=1 - omit calls to UnityBegin and UNITY_END (disabled by default)' ,
539
- ' --header_file="" - path/name of test header file to generate too' ] . join ( "\n " )
621
+ ' --header_file="" - path/name of test header file to generate too' ,
622
+ ' --shuffle_tests=1 - enable shuffling of the test execution order (disabled by default)' ,
623
+ ' --rng_seed=1 - seed value for randomization of test execution order' ] . join ( "\n " )
540
624
exit 1
541
625
end
542
626
0 commit comments