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"