Skip to content

Commit b0bcdb5

Browse files
committed
Add support for randomizing test execution order
This commit reintroduces the option to shuffle the test execution order into the test runner. This has been tested with the temp_sensor example project in Ceedling. Unit tests have also been successfully executed. Signed-off-by: James Raphael Tiovalen <[email protected]>
1 parent cdf1d02 commit b0bcdb5

File tree

2 files changed

+106
-6
lines changed

2 files changed

+106
-6
lines changed

auto/generate_test_runner.rb

+90-6
Original file line numberDiff line numberDiff line change
@@ -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,10 +235,34 @@ 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? || 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]
@@ -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 = 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+
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
@@ -439,18 +493,46 @@ def create_main(output, filename, tests, used_mocks)
439493
else
440494
output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");")
441495
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
442509
tests.each do |test|
443510
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
445515
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]}"
448518
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
450523
end
451524
end
452525
end
453526
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
454536
output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty?
455537
if @options[:has_suite_teardown]
456538
if @options[:omit_begin_end]
@@ -536,7 +618,9 @@ def create_h_file(output, filename, tests, testfile_includes, used_mocks)
536618
' --suite_teardown="" - code to execute for teardown of entire suite',
537619
' --use_param_tests=1 - enable parameterized tests (disabled by default)',
538620
' --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")
540624
exit 1
541625
end
542626

docs/UnityHelperScriptsGuide.md

+16
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,22 @@ Unity test state setup and cleanup.
277277

278278
This option can also be specified at the command prompt as `--omit_begin_end`
279279

280+
##### `:shuffle_tests`
281+
282+
If `true`, the test execution order will be shuffled. Is `false` by default.
283+
284+
This option can also be specified at the command prompt as `--shuffle_tests`
285+
286+
##### `:rng_seed`
287+
288+
If set to some positive integer value, said value will be used as the seed value passed
289+
to the `srand` function. Otherwise, if not set to any value, `time(NULL)` will be used
290+
as the seed value.
291+
292+
Only applicable if `:shuffle_tests` is set to `true`.
293+
294+
This option can also be specified at the command prompt as `--rng_seed`
295+
280296
#### Parameterized tests provided macros
281297

282298
Unity provides support for few param tests generators, that can be combined

0 commit comments

Comments
 (0)