require'optparse'require'parallel_tests/test/runner'moduleParallelTestmoduleCLIdefself.run(argv)options=parse_options!(argv)test_results=nilnum_processes=ParallelTests.determine_number_of_processes(options[:count])num_processes=num_processes*(options[:multiply]||1)ifoptions[:execute]execute_shell_command_in_parallel(options[:execute],num_processes,options)elselib=options[:type]||'test'require"parallel_tests/#{lib}/runner"runner=eval("ParallelTests::#{lib.capitalize}::Runner")name=runner.test_file_namereport_time_takendogroups=runner.tests_in_groups(options[:files],num_processes,options)abort"no #{name}s found!"ifgroups.size==0num_processes=groups.sizenum_tests=groups.inject(0){|sum,item|sum+item.size}puts"#{num_processes} processes for #{num_tests}#{name}s, ~ #{num_tests/groups.size}#{name}s per process"test_results=Parallel.map(groups,:in_processes=>num_processes)do|group|ifgroup.empty?{:stdout=>'',:exit_status=>0}elserunner.run_tests(group,groups.index(group),options)endend#parse and print resultsresults=runner.find_results(test_results.map{|result|result[:stdout]}*"")puts""putsrunner.summarize_results(results)end#exit with correct status code so rake parallel:test && echo 123 worksfailed=test_results.any?{|result|result[:exit_status]!=0}abort"#{lib.capitalize}s Failed"iffailedendendprivatedefself.parse_options!(argv)options={}OptionParser.newdo|opts|opts.banner=<<BANNER
Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
[optional] Only run selected files & folders:
parallel_test test/bar test/baz/xxx_text.rb
Options are:
BANNERopts.on("-n [PROCESSES]",Integer,"How many processes to use, default: available CPUs"){|n|options[:count]=n}opts.on("-p",'--pattern [PATTERN]',"run tests matching this pattern"){|pattern|options[:pattern]=pattern}opts.on("--no-sort","do not sort files before running them"){|no_sort|options[:no_sort]=no_sort}opts.on("-m [FLOAT]","--multiply-processes [FLOAT]",Float,"use given number as a multiplier of processes to run"){|multiply|options[:multiply]=multiply}opts.on("-s [PATTERN]","--single [PATTERN]","Run all matching files in only one process")do|pattern|options[:single_process]||=[]options[:single_process]<</#{pattern}/endopts.on("-e",'--exec [COMMAND]',"execute this code parallel and with ENV['TEST_ENV_NUM']"){|path|options[:execute]=path}opts.on("-o","--test-options '[OPTIONS]'","execute test commands with those options"){|arg|options[:test_options]=arg}opts.on("-t","--type [TYPE]","test(default) / spec / cucumber"){|type|options[:type]=type}opts.on("--non-parallel","execute same commands but do not in parallel, needs --exec"){options[:non_parallel]=true}opts.on("--chunk-timeout [TIMEOUT]","timeout before re-printing the output of a child-process"){|timeout|options[:chunk_timeout]=timeout.to_f}opts.on('-v','--version','Show Version'){putsParallelTests::VERSION;exit}opts.on("-h","--help","Show this."){putsopts;exit}end.parse!(argv)raise"--no-sort and --single-process are not supported"ifoptions[:no_sort]andoptions[:single_process]options[:files]=argvoptionsenddefself.execute_shell_command_in_parallel(command,num_processes,options)runs=(0...num_processes).to_aresults=ifoptions[:non_parallel]runs.mapdo|i|ParallelTests::Test::Runner.execute_command(command,i,options)endelseParallel.map(runs,:in_processes=>num_processes)do|i|ParallelTests::Test::Runner.execute_command(command,i,options)endend.flattenabortifresults.any?{|r|r[:exit_status]!=0}enddefself.report_time_takenstart=Time.nowyieldputs""puts"Took #{Time.now-start} seconds"endendend