script/parallel-tests in moneta-1.0.0 vs script/parallel-tests in moneta-1.1.0

- old
+ new

@@ -1,106 +1,81 @@ #!/usr/bin/env ruby -def rspec(spec) - if system("rspec #{spec}") - true - elsif sig = $?.termsig - found = Signal.list.to_a.select {|name, id| id == sig }.first - puts "\e[31m########## SIG#{found ? found.first : sig} rspec #{spec} ##########\e[0m" - false - else - puts "rspec terminated with #{$?.exitstatus}" - false - end -end - ENV['PARALLEL_TESTS'] = 'yes' -specs = Dir['spec/*/*_spec.rb'].sort +require 'multi_json' -# Shuffle specs to ensure equal distribution over the test groups -# We have to shuffle with the same seed every time because rake is started -# multiple times! -old_seed = srand(43) -specs.shuffle! -srand(old_seed) +def tag_args tags + tags.flat_map{ |tag| ['--tag', tag] } +end -# FIXME: -# -# * QuickLZ segfaults because of an assertion -# QuickLZ is also not maintained on Github, but on Bitbucket -# and I don't know where the issue tracker is. -# -# * PStore increment/locking doesn't work correctly on JRuby -# -unstable = %w(quicklz riak cassandra) -unstable += %w(pstore) if defined?(JRUBY_VERSION) +def example_ids tags, specs + json = `bundle exec rspec -f j --dry-run #{tag_args(tags).join(' ')} -- #{specs.join(' ')}` + data = MultiJson.load(json) + data['examples'].map{ |example| example['id'] } +end -unstable_re = /#{unstable.join('|')}/ -unstable = specs.select {|s| s =~ unstable_re } -specs -= unstable +def run(*args) + pid = spawn(*args) + Signal.trap("INT") { Process.kill("INT", pid) } + Process.wait(pid) + $? == 0 +ensure + Signal.trap("INT", "DEFAULT") +end -group = ARGV.first || '1/1' -case group -when /^(\d+)\/(\d+)$/ - n = $1.to_i - max = $2.to_i - if n == max - specs = specs[(n-1)*(specs.size/max)..-1] +tags = ARGV.take_while { |arg| arg[0] != '-' } +ARGV.shift(tags.length) +opts = [] +files = nil +while arg = ARGV.shift + case arg + when '--' + files = ARGV + break + when '--remainder' + files = Dir['spec/**/*_spec.rb', 'test/**/*_test.rb'] + existing = File.open('.travis.yml').each_line.flat_map do |line| + next unless matches = line.match(%r{((?:test|spec)/(?:[\w\.]+/?)*)}) + path = matches[1] + path[-3..-1] == '.rb' ? path : path + '/**/*.rb' + end.compact + files -= Dir[*existing] else - specs = specs[(n-1)*(specs.size/max), specs.size/max] + opts << arg end -when 'unstable' - specs = unstable -else - puts "Invalid test group #{group}" - exit 1 end -puts "The following specs will be executed:\n\t#{specs.join "\n\t"}\n\n" +files ||= Dir['spec', 'test/**/*_test.rb'] +specs, tests = files.partition { |file| file.match /^spec/ } -# Memcached and Redis specs cannot be used in parallel -# because of flushing and lacking namespaces -parallel = [] -%w(memcached redis client shared riak tokyotyrant couch cassandra).each do |name| - serial = specs.select { |s| s.include?(name) } - unless serial.empty? - specs -= serial - parallel << serial - end -end +puts "The following specs will be executed:\n\t#{specs.join "\n\t"}\n\n" unless specs.empty? +puts "The following tests will be executed:\n\t#{tests.join "\n\t"}\n\n" unless tests.empty? -# The activesupport cache specs also use memcached -activesupport_cache_specs = specs.grep(/active_support.+cache/) -specs -= activesupport_cache_specs +results = [] +unless specs.empty? + # run all non :isolate examples in parallel + results << run(*%w{bundle exec parallel_rspec --}, + *opts, + *tag_args(tags | %w{~isolate}), + '--', + *specs) -if memcache_specs = parallel.find{ |serial| serial.all?{ |s| s.match /memcached/ } } - activesupport_cache_specs.each(&memcache_specs.method(:push)) -else - parallel += activesupport_cache_specs + # find the example IDs of the isolate examples to be run in serial + ids = example_ids(tags, specs) - example_ids(tags | %w{~isolate}, specs) + unless ids.empty? + results << run(*%w{bundle exec rspec}, + *opts, + '--', + *ids) + end end -# All the left-overs -parallel += specs.map {|s| [s] } - -threads = [] -failed = false -parallel.each do |serial| - threads << Thread.new do - begin - serial.each do |spec| - failed = true unless rspec(spec) - end - ensure - threads.delete Thread.current - end - end - sleep 0.1 - sleep 0.1 while threads.size >= 5 +tests.each do |test| + results << run(*%w{bundle exec ruby}, test) end -sleep 0.1 until threads.empty? -if failed +if results.any?{ |result| !result } puts "\e[31m########## MONETA TESTSUITE FAILED ##########\e[0m" exit 1 end puts "\e[32m########## MONETA TESTSUITE SUCCEDED ##########\e[0m"