parallel_tests 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ *.sh
@@ -0,0 +1,119 @@
1
+ Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs.
2
+
3
+ Setup for Rails
4
+ ===============
5
+
6
+ sudo gem install parallel
7
+ script/plugin install git://github.com/grosser/parallel_tests.git
8
+
9
+ ### 1: Add to `config/database.yml`
10
+ test:
11
+ database: xxx_test<%= ENV['TEST_ENV_NUMBER'] %>
12
+
13
+ ### 2: Create additional database(s)
14
+ script/db_console
15
+ create database xxx_test2;
16
+ ...
17
+
18
+ ### 3: Copy development schema (repeat after migrations)
19
+ rake parallel:prepare
20
+
21
+ ### 4: Run!
22
+ rake parallel:test # Test::Unit
23
+ rake parallel:spec # RSpec
24
+ rake parallel:features # Cucumber
25
+
26
+ rake parallel:test[1] --> force 1 CPU --> 86 seconds
27
+ rake parallel:test --> got 2 CPUs? --> 47 seconds
28
+ rake parallel:test --> got 4 CPUs? --> 26 seconds
29
+ ...
30
+
31
+ Test just a subfolder (e.g. use one integration server per subfolder)
32
+ rake parallel:test[models]
33
+ rake parallel:test[something/else]
34
+
35
+ partial paths are OK too...
36
+ rake parallel:test[functional] == rake parallel:test[fun]
37
+
38
+ Example output
39
+ --------------
40
+ 2 processes for 210 specs, ~ 105 specs per process
41
+ ... test output ...
42
+
43
+ Results:
44
+ 877 examples, 0 failures, 11 pending
45
+ 843 examples, 0 failures, 1 pending
46
+
47
+ Took 29.925333 seconds
48
+
49
+ Even process runtimes (for specs only atm)
50
+ -----------------
51
+ Add to your `spec/parallel_specs.opts` (or `spec/spec.opts`) :
52
+ --format ParallelSpecs::SpecRuntimeLogger:tmp/parallel_profile.log
53
+ It will log test runtime and partition the test-load accordingly.
54
+
55
+ Setup for non-rails
56
+ ===================
57
+ sudo gem install parallel_tests
58
+ # go to your project dir
59
+ parallel_test OR parallel_spec OR parallel_cucumber
60
+ # [Optional] use ENV['TEST_ENV_NUMBER'] inside your tests for separate db/resources/etc.
61
+
62
+ Options are:
63
+ -n [PROCESSES] How many processes to use, default: available CPUs
64
+ -p, --path [PATH] run tests inside this path only
65
+ -r, --root [PATH] execute test commands from this path
66
+ -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUM']
67
+ -o, --test-options [SOMETHING] execute test commands with those options
68
+ -t, --type [TYPE] which type of tests to run? test, spec or features
69
+ -v, --version Show Version
70
+ -h, --help Show this.
71
+
72
+ You can run any kind of code with -e / --execute
73
+ parallel_test -n 5 -e 'ruby -e "puts %[hello from process #{ENV[:TEST_ENV_NUMBER.to_s].inspect}]"'
74
+ hello from process "2"
75
+ hello from process ""
76
+ hello from process "3"
77
+ hello from process "5"
78
+ hello from process "4"
79
+
80
+ <table>
81
+ <tr><td></td><td>1 Process</td><td>2 Processes</td><td>4 Processes</td></tr>
82
+ <tr><td>RSpec spec-suite</td><td>18</td><td>14</td><td>10</td></tr>
83
+ <tr><td>Rails-ActionPack</td><td>88</td><td>53</td><td>44</td></tr>
84
+ </table>
85
+
86
+ TIPS
87
+ ====
88
+ - [RSpec] add a `spec/parallel_spec.opts` to use different options, e.g. no --drb (default: `spec/spec.opts`)
89
+ - [RSpec] if something looks fishy try to delete `script/spec`
90
+ - [RSpec] if `script/spec` is missing parallel:spec uses just `spec` (which solves some issues with double-loaded environment.rb)
91
+ - [RSpec] 'script/spec_server' or [spork](http://github.com/timcharper/spork/tree/master) do not work in parallel
92
+ - [RSpec] `./script/generate rspec` if you are running rspec from gems (this plugin uses script/spec which may fail if rspec files are outdated)
93
+ - [Bundler] if you have a `.bundle/environment.rb` then `bundle exec xxx` will be used to run tests
94
+ - with zsh this would be `rake "parallel:prepare[3]"`
95
+
96
+ TODO
97
+ ====
98
+ - build parallel:bootstrap [idea/basics](http://github.com/garnierjm/parallel_specs/commit/dd8005a2639923dc5adc6400551c4dd4de82bf9a)
99
+ - make jRuby compatible [basics](http://yehudakatz.com/2009/07/01/new-rails-isolation-testing/)
100
+ - make windows compatible (does anyone care ?)
101
+
102
+ Authors
103
+ ====
104
+ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-parallelize-your-rspec-suite)
105
+
106
+ ###Contributors (alphabetical)
107
+ - [Charles Finkel](http://charlesfinkel.com/)
108
+ - [Jason Morrison](http://jayunit.net)
109
+ - [Joakim Kolsjö](http://www.rubyblocks.se)
110
+ - [Kpumuk](http://kpumuk.info/)
111
+ - [Maksim Horbu](http://github.com/mhorbul)
112
+ - [Rohan Deshpande](http://github.com/rdeshpande)
113
+ - [Tchandy](http://thiagopradi.net/)
114
+ - [Terence Lee](http://hone.heroku.com/)
115
+ - [Will Bryant](http://willbryant.net/)
116
+
117
+ [Michael Grosser](http://pragmatig.wordpress.com)
118
+ grosser.michael@gmail.com
119
+ Hereby placed under public domain, do what you want, just do not hold me accountable...
@@ -0,0 +1,19 @@
1
+ task :default => :spec
2
+ require 'spec/rake/spectask'
3
+ Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
4
+
5
+ begin
6
+ require 'jeweler'
7
+ project_name = 'parallel_tests'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = project_name
10
+ gem.summary = "Run tests / specs / features in parallel"
11
+ gem.email = "grosser.michael@gmail.com"
12
+ gem.homepage = "http://github.com/grosser/#{project_name}"
13
+ gem.authors = ["Michael Grosser"]
14
+ end
15
+
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
19
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec "#{File.join(File.dirname(__FILE__), 'parallel_test')} -t features #{ARGV * ' '}"
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec "#{File.join(File.dirname(__FILE__), 'parallel_test')} -t spec #{ARGV * ' '}"
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'optparse'
4
+ lib_folder = File.join(File.dirname(__FILE__), '..', 'lib')
5
+ require File.join(lib_folder, "parallel_tests")
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = <<BANNER
10
+ Run tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
11
+
12
+ Options are:
13
+ BANNER
14
+ opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs"){|n| options[:count] = n }
15
+ opts.on("-p", '--path [PATH]', "run tests inside this path only"){|path| options[:path_prefix] = path }
16
+ opts.on("-r", '--root [PATH]', "execute test commands from this path"){|path| options[:root] = path }
17
+ opts.on("-e", '--exec [COMMAND]', "execute this code parallel and with ENV['TEST_ENV_NUM']"){|path| options[:execute] = path }
18
+ opts.on("-o", '--test-options [SOMETHING]', "execute test commands with those options"){|arg| options[:test_options] = arg }
19
+ opts.on("-t", "--type [TYPE]", "which type of tests to run? test, spec or features"){|type| options[:type] = type }
20
+ opts.on('-v', '--version', 'Show Version'){ puts ParallelTests::VERSION; exit}
21
+ opts.on("-h", "--help", "Show this.") { puts opts; exit }
22
+ end.parse!
23
+
24
+ require 'parallel'
25
+ num_processes = options[:count] || Parallel.processor_count
26
+
27
+ if options[:execute]
28
+ require File.join(lib_folder, "parallel_tests")
29
+ Parallel.in_processes(num_processes) do |i|
30
+ ParallelTests.execute_command(options[:execute], i)
31
+ end
32
+ else
33
+ lib, name, task = {
34
+ 'test' => ["tests", "test", "test"],
35
+ 'spec' => ["specs", "spec", "spec"],
36
+ 'features' => ["cucumber", "feature", "features"]
37
+ }[options[:type]||'test']
38
+
39
+ require File.join(lib_folder, "parallel_#{lib}")
40
+ klass = eval("Parallel#{lib.capitalize}")
41
+
42
+ start = Time.now
43
+
44
+ tests_folder = File.join(task, options[:path_prefix].to_s)
45
+ tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
46
+
47
+ groups = klass.tests_in_groups(tests_folder, num_processes)
48
+ num_processes = groups.size
49
+
50
+ #adjust processes to groups
51
+ abort "no #{name}s found!" if groups.size == 0
52
+
53
+ num_tests = groups.inject(0){|sum,item| sum + item.size }
54
+ puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
55
+
56
+ output = Parallel.map(groups, :in_processes => num_processes) do |group|
57
+ klass.run_tests(group, groups.index(group), options[:test_options])
58
+ end
59
+
60
+ #parse and print results
61
+ results = klass.find_results(output*"")
62
+ puts ""
63
+ puts "Results:"
64
+ results.each{|r| puts r}
65
+
66
+ #report total time taken
67
+ puts ""
68
+ puts "Took #{Time.now - start} seconds"
69
+
70
+ #exit with correct status code
71
+ # - rake parallel:test && echo 123 ==> 123 should not show up when test failed
72
+ # - rake parallel:test db:reset ==> works when tests succeed
73
+ abort "#{name.capitalize}s Failed" if klass.failed?(results)
74
+ end
@@ -0,0 +1,33 @@
1
+ require File.join(File.dirname(__FILE__), 'parallel_tests')
2
+
3
+ class ParallelCucumber < ParallelTests
4
+ def self.run_tests(test_files, process_number, options)
5
+ color = ($stdout.tty? ? 'export AUTOTEST=1 ;' : '')#display color when we are in a terminal
6
+ cmd = "export RAILS_ENV=test ; #{color} #{executable} #{options} #{test_files*' '}"
7
+ execute_command(cmd, process_number)
8
+ end
9
+
10
+ def self.executable
11
+ if File.file?(".bundle/environment.rb")
12
+ "bundle exec cucumber"
13
+ elsif File.file?("script/cucumber")
14
+ "script/cucumber"
15
+ else
16
+ "cucumber"
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ def self.line_is_result?(line)
23
+ line =~ /^\d+ (steps|scenarios)/
24
+ end
25
+
26
+ def self.line_is_failure?(line)
27
+ line =~ /^\d+ (steps|scenarios).*(\d{2,}|[1-9]) failed/
28
+ end
29
+
30
+ def self.find_tests(root)
31
+ Dir["#{root}**/**/*.feature"]
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), 'parallel_tests')
2
+
3
+ class ParallelSpecs < ParallelTests
4
+ def self.run_tests(test_files, process_number, options)
5
+ spec_opts = ['spec/parallel_spec.opts', 'spec/spec.opts'].detect{|f| File.file?(f) }
6
+ spec_opts = (spec_opts ? "-O #{spec_opts}" : nil)
7
+ color = ($stdout.tty? ? 'export RSPEC_COLOR=1 ;' : '')#display color when we are in a terminal
8
+ cmd = "export RAILS_ENV=test ; #{color} #{executable} #{options} #{spec_opts} #{test_files*' '}"
9
+ execute_command(cmd, process_number)
10
+ end
11
+
12
+ def self.executable
13
+ if File.file?(".bundle/environment.rb")
14
+ "bundle exec spec"
15
+ elsif File.file?("script/spec")
16
+ "script/spec"
17
+ else
18
+ "spec"
19
+ end
20
+ end
21
+
22
+ protected
23
+
24
+ def self.find_tests(root)
25
+ Dir["#{root}**/**/*_spec.rb"]
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec/runner/formatter/progress_bar_formatter'
2
+
3
+ class ParallelSpecs::SpecRuntimeLogger < Spec::Runner::Formatter::BaseTextFormatter
4
+ def initialize(options, output)
5
+ if String === output
6
+ FileUtils.mkdir_p(File.dirname(output))
7
+ File.open(output,'w'){|f| f.write ''} # clean the file
8
+ @output = File.open(output, 'a+') #append so that multiple processes can write at once
9
+ else
10
+ @output = output
11
+ end
12
+ @example_times = Hash.new(0)
13
+ end
14
+
15
+ def example_started(*args)
16
+ @time = Time.now
17
+ end
18
+
19
+ def example_passed(example)
20
+ file = example.location.split(':').first
21
+ @example_times[file] += Time.now - @time
22
+ end
23
+
24
+ def start_dump(*args)
25
+ return unless ENV['TEST_ENV_NUMBER'] #only record when running in parallel
26
+ # TODO: Figure out why sometimes time can be less than 0
27
+ @output.puts @example_times.map { |file, time| "#{file}:#{time > 0 ? time : 0}" }
28
+ @output.flush
29
+ end
30
+
31
+ # stubs so that rspec doe not crash
32
+
33
+ def example_pending(*args)
34
+ end
35
+
36
+ def dump_summary(*args)
37
+ end
38
+
39
+ def dump_pending(*args)
40
+ end
41
+
42
+ def dump_failure(*args)
43
+ end
44
+
45
+ #stolen from Rspec
46
+ def close
47
+ @output.close if (IO === @output) & (@output != $stdout)
48
+ end
49
+ end
@@ -0,0 +1,119 @@
1
+ require 'parallel'
2
+
3
+ class ParallelTests
4
+ VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
5
+
6
+ # parallel:spec[2,controller] <-> parallel:spec[controller]
7
+ def self.parse_rake_args (args)
8
+ num_processes = Parallel.processor_count
9
+ options = ""
10
+ if args[:count].to_s =~ /^\d*$/ # number or empty
11
+ num_processes = args[:count] unless args[:count].to_s.empty?
12
+ prefix = args[:path_prefix]
13
+ options = args[:options] if args[:options]
14
+ else # something stringy
15
+ prefix = args[:count]
16
+ end
17
+ [num_processes.to_i, prefix.to_s, options]
18
+ end
19
+
20
+ # finds all tests and partitions them into groups
21
+ def self.tests_in_groups(root, num)
22
+ tests_with_sizes = slow_specs_first(find_tests_with_sizes(root))
23
+
24
+ groups = []
25
+ current_group = current_size = 0
26
+ tests_with_sizes.each do |test, size|
27
+ # inserts into next group if current is full and we are not in the last group
28
+ if (0.5*size + current_size) > group_size(tests_with_sizes, num) and num > current_group + 1
29
+ current_size = size
30
+ current_group += 1
31
+ else
32
+ current_size += size
33
+ end
34
+ groups[current_group] ||= []
35
+ groups[current_group] << test
36
+ end
37
+ groups.compact
38
+ end
39
+
40
+ def self.run_tests(test_files, process_number, options)
41
+ require_list = test_files.map { |filename| "\"#{filename}\"" }.join(",")
42
+ cmd = "export RAILS_ENV=test ; ruby -Itest #{options} -e '[#{require_list}].each {|f| require f }'"
43
+ execute_command(cmd, process_number)
44
+ end
45
+
46
+ def self.execute_command(cmd, process_number)
47
+ cmd = "export TEST_ENV_NUMBER=#{test_env_number(process_number)} ; #{cmd}"
48
+ f = open("|#{cmd}", 'r')
49
+ all = ''
50
+ while char = f.getc
51
+ char = (char.is_a?(Fixnum) ? char.chr : char) # 1.8 <-> 1.9
52
+ all << char
53
+ print char
54
+ STDOUT.flush
55
+ end
56
+ all
57
+ end
58
+
59
+ def self.find_results(test_output)
60
+ test_output.split("\n").map {|line|
61
+ line = line.gsub(/\.|F|\*/,'')
62
+ next unless line_is_result?(line)
63
+ line
64
+ }.compact
65
+ end
66
+
67
+ def self.failed?(results)
68
+ return true if results.empty?
69
+ !! results.detect{|line| line_is_failure?(line)}
70
+ end
71
+
72
+ def self.test_env_number(process_number)
73
+ process_number == 0 ? '' : process_number + 1
74
+ end
75
+
76
+ protected
77
+
78
+ def self.slow_specs_first(tests)
79
+ tests.sort_by{|test, size| size }.reverse
80
+ end
81
+
82
+ def self.line_is_result?(line)
83
+ line =~ /\d+ failure/
84
+ end
85
+
86
+ def self.line_is_failure?(line)
87
+ line =~ /(\d{2,}|[1-9]) (failure|error)/
88
+ end
89
+
90
+ def self.group_size(tests_with_sizes, num_groups)
91
+ total_size = tests_with_sizes.inject(0) { |sum, test| sum += test[1] }
92
+ total_size / num_groups.to_f
93
+ end
94
+
95
+ def self.find_tests_with_sizes(root)
96
+ tests = find_tests(root).sort
97
+
98
+ #TODO get the real root, atm this only works for complete runs when root point to e.g. real_root/spec
99
+ runtime_file = File.join(root,'..','tmp','parallel_profile.log')
100
+ lines = File.read(runtime_file).split("\n") rescue []
101
+
102
+ if lines.size * 1.5 > tests.size
103
+ # use recorded test runtime if we got enough data
104
+ times = Hash.new(1)
105
+ lines.each do |line|
106
+ test, time = line.split(":")
107
+ times[test] = time.to_f
108
+ end
109
+ tests.map { |test| [ test, times[test] ] }
110
+ else
111
+ # use file sizes
112
+ tests.map { |test| [ test, File.stat(test).size ] }
113
+ end
114
+ end
115
+
116
+ def self.find_tests(root)
117
+ Dir["#{root}**/**/*_test.rb"]
118
+ end
119
+ end
@@ -0,0 +1,61 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{parallel_tests}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Michael Grosser"]
12
+ s.date = %q{2010-03-02}
13
+ s.email = %q{grosser.michael@gmail.com}
14
+ s.executables = ["parallel_test", "parallel_spec", "parallel_cucumber"]
15
+ s.extra_rdoc_files = [
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.markdown",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "bin/parallel_cucumber",
24
+ "bin/parallel_spec",
25
+ "bin/parallel_test",
26
+ "lib/parallel_cucumber.rb",
27
+ "lib/parallel_specs.rb",
28
+ "lib/parallel_specs/spec_runtime_logger.rb",
29
+ "lib/parallel_tests.rb",
30
+ "parallel_tests.gemspec",
31
+ "spec/integration_spec.rb",
32
+ "spec/parallel_cucumber_spec.rb",
33
+ "spec/parallel_specs_spec.rb",
34
+ "spec/parallel_tests_spec.rb",
35
+ "spec/spec_helper.rb",
36
+ "tasks/parallel_specs.rake"
37
+ ]
38
+ s.homepage = %q{http://github.com/grosser/parallel_tests}
39
+ s.rdoc_options = ["--charset=UTF-8"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.3.6}
42
+ s.summary = %q{Run tests / specs / features in parallel}
43
+ s.test_files = [
44
+ "spec/spec_helper.rb",
45
+ "spec/parallel_tests_spec.rb",
46
+ "spec/parallel_specs_spec.rb",
47
+ "spec/parallel_cucumber_spec.rb",
48
+ "spec/integration_spec.rb"
49
+ ]
50
+
51
+ if s.respond_to? :specification_version then
52
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
56
+ else
57
+ end
58
+ else
59
+ end
60
+ end
61
+
@@ -0,0 +1,83 @@
1
+ describe 'CLI' do
2
+ before do
3
+ `rm -rf #{folder}`
4
+ end
5
+
6
+ after do
7
+ `rm -rf #{folder}`
8
+ end
9
+
10
+ def folder
11
+ "/tmp/parallel_tests_tests"
12
+ end
13
+
14
+ def write(file, content)
15
+ path = "#{folder}/spec/#{file}"
16
+ `mkdir -p #{File.dirname(path)}` unless File.exist?(File.dirname(path))
17
+ File.open(path, 'w'){|f| f.write content }
18
+ path
19
+ end
20
+
21
+ def bin_folder
22
+ "#{File.expand_path(File.dirname(__FILE__))}/../bin"
23
+ end
24
+
25
+ def executable
26
+ "#{bin_folder}/parallel_test"
27
+ end
28
+
29
+ def run_specs(options={})
30
+ `cd #{folder} && #{executable} -t spec -n #{options[:processes]||2} 2>&1 && echo 'i ran!'`
31
+ end
32
+
33
+ it "runs tests in parallel" do
34
+ write 'xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
35
+ write 'xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}'
36
+ result = run_specs
37
+
38
+ # test ran and gave their puts
39
+ result.should include('TEST1')
40
+ result.should include('TEST2')
41
+
42
+ # all results present
43
+ result.scan('1 example, 0 failure').size.should == 4 # 2 results + 2 result summary
44
+ result.scan(/Finished in \d+\.\d+ seconds/).size.should == 2
45
+ result.scan(/Took \d+\.\d+ seconds/).size.should == 1 # parallel summary
46
+
47
+ result.should include('i ran!')
48
+ end
49
+
50
+ it "fails when tests fail" do
51
+ write 'xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
52
+ write 'xxx2_spec.rb', 'describe("it"){it("should"){1.should == 2}}'
53
+ result = run_specs
54
+
55
+ result.scan('1 example, 1 failure').size.should == 2
56
+ result.scan('1 example, 0 failure').size.should == 2
57
+ result.should =~ /specs failed/i
58
+ result.should_not include('i ran!')
59
+ end
60
+
61
+ it "can exec given commands with ENV['TEST_ENV_NUM']" do
62
+ result = `#{executable} -e 'ruby -e "puts ENV[:TEST_ENV_NUMBER.to_s].inspect"' -n 4`
63
+ result.split("\n").sort.should == %w["" "2" "3" "4"]
64
+ end
65
+
66
+ it "can run through parallel_spec / parallel_cucumber" do
67
+ version = `#{executable} -v`
68
+ `#{bin_folder}/parallel_spec -v`.should == version
69
+ `#{bin_folder}/parallel_cucumber -v`.should == version
70
+ end
71
+
72
+ it "runs faster with more processes" do
73
+ write 'xxx_spec.rb', 'describe("it"){it("should"){sleep 2}}'
74
+ write 'xxx2_spec.rb', 'describe("it"){it("should"){sleep 2}}'
75
+ write 'xxx3_spec.rb', 'describe("it"){it("should"){sleep 2}}'
76
+ write 'xxx4_spec.rb', 'describe("it"){it("should"){sleep 2}}'
77
+ write 'xxx5_spec.rb', 'describe("it"){it("should"){sleep 2}}'
78
+ write 'xxx6_spec.rb', 'describe("it"){it("should"){sleep 2}}'
79
+ t = Time.now
80
+ run_specs :processes => 6
81
+ (Time.now - t).should < 5
82
+ end
83
+ end
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ParallelCucumber do
4
+ test_tests_in_groups(ParallelCucumber, 'features', ".feature")
5
+
6
+ describe :run_tests do
7
+ before(:each) do
8
+ File.stub!(:file?).with('.bundle/environment.rb').and_return false
9
+ File.stub!(:file?).with('script/cucumber').and_return true
10
+ end
11
+
12
+ it "uses TEST_ENV_NUMBER=blank when called for process 0" do
13
+ ParallelCucumber.should_receive(:open).with{|x,y| x=~/TEST_ENV_NUMBER= /}.and_return mock(:getc=>false)
14
+ ParallelCucumber.run_tests(['xxx'],0,'')
15
+ end
16
+
17
+ it "uses TEST_ENV_NUMBER=2 when called for process 1" do
18
+ ParallelCucumber.should_receive(:open).with{|x,y| x=~/TEST_ENV_NUMBER=2/}.and_return mock(:getc=>false)
19
+ ParallelCucumber.run_tests(['xxx'],1,'')
20
+ end
21
+
22
+ it "returns the output" do
23
+ io = open('spec/spec_helper.rb')
24
+ ParallelCucumber.stub!(:print)
25
+ ParallelCucumber.should_receive(:open).and_return io
26
+ ParallelCucumber.run_tests(['xxx'],1,'').should =~ /\$LOAD_PATH << File/
27
+ end
28
+
29
+ it "runs bundle exec cucumber when on bundler 0.9" do
30
+ File.stub!(:file?).with('.bundle/environment.rb').and_return true
31
+ ParallelCucumber.should_receive(:open).with{|x,y| x =~ %r{bundle exec cucumber}}.and_return mock(:getc=>false)
32
+ ParallelCucumber.run_tests(['xxx'],1,'')
33
+ end
34
+
35
+ it "runs script/cucumber when script/cucumber is found" do
36
+ ParallelCucumber.should_receive(:open).with{|x,y| x =~ %r{script/cucumber}}.and_return mock(:getc=>false)
37
+ ParallelCucumber.run_tests(['xxx'],1,'')
38
+ end
39
+
40
+ it "runs cucumber by default" do
41
+ File.stub!(:file?).with('script/cucumber').and_return false
42
+ ParallelCucumber.should_receive(:open).with{|x,y| x !~ %r{(script/cucumber)|(bundle exec cucumber)}}.and_return mock(:getc=>false)
43
+ ParallelCucumber.run_tests(['xxx'],1,'')
44
+ end
45
+
46
+ it "uses options passed in" do
47
+ ParallelCucumber.should_receive(:open).with{|x,y| x =~ %r{script/cucumber -p default}}.and_return mock(:getc=>false)
48
+ ParallelCucumber.run_tests(['xxx'],1,'-p default')
49
+ end
50
+ end
51
+
52
+ describe :find_results do
53
+ it "finds multiple results in test output" do
54
+ output = <<EOF
55
+ And I should not see "/en/" # features/step_definitions/webrat_steps.rb:87
56
+
57
+ 7 scenarios (3 failed, 4 passed)
58
+ 33 steps (3 failed, 2 skipped, 28 passed)
59
+ /apps/rs/features/signup.feature:2
60
+ Given I am on "/" # features/step_definitions/common_steps.rb:12
61
+ When I click "register" # features/step_definitions/common_steps.rb:6
62
+ And I should have "2" emails # features/step_definitions/user_steps.rb:25
63
+
64
+ 4 scenarios (4 passed)
65
+ 40 steps (40 passed)
66
+
67
+ EOF
68
+ ParallelCucumber.find_results(output).should == ["7 scenarios (3 failed, 4 passed)", "33 steps (3 failed, 2 skipped, 28 passed)", "4 scenarios (4 passed)", "40 steps (40 passed)"]
69
+ end
70
+ end
71
+
72
+ describe :failed do
73
+ it "fails with single failed" do
74
+ ParallelCucumber.failed?(['40 steps (40 passed)','33 steps (3 failed, 2 skipped, 28 passed)']).should == true
75
+ end
76
+
77
+ it "fails with multiple failed tests" do
78
+ ParallelCucumber.failed?(['33 steps (3 failed, 2 skipped, 28 passed)','33 steps (3 failed, 2 skipped, 28 passed)']).should == true
79
+ end
80
+
81
+ it "fails with a single scenario failure during setup phase" do
82
+ ParallelCucumber.failed?(['1 scenarios (1 failed)']).should == true
83
+ end
84
+
85
+ it "fails with scenario failures during setup phase when other steps pass" do
86
+ ParallelCucumber.failed?(['7 scenarios (3 failed, 4 passed)','40 steps (40 passed)']).should == true
87
+ end
88
+
89
+ it "does not fail with successful tests" do
90
+ ParallelCucumber.failed?(['4 scenarios (4 passed)','40 steps (40 passed)','4 scenarios (4 passed)','40 steps (40 passed)']).should == false
91
+ end
92
+
93
+ it "does not fail with 0 failures" do
94
+ ParallelCucumber.failed?(['4 scenarios (4 passed 0 failed)','40 steps (40 passed 0 failed)','4 scenarios (4 passed 0 failed)','40 steps (40 passed)']).should == false
95
+ end
96
+
97
+ it "does fail with 10 failures" do
98
+ ParallelCucumber.failed?(['40 steps (40 passed 10 failed)','40 steps (40 passed)']).should == true
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,134 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ParallelSpecs do
4
+ test_tests_in_groups(ParallelSpecs, 'spec', '_spec.rb')
5
+
6
+ describe :run_tests do
7
+ before do
8
+ File.stub!(:file?).with('.bundle/environment.rb').and_return false
9
+ File.stub!(:file?).with('script/spec').and_return true
10
+ File.stub!(:file?).with('spec/spec.opts').and_return true
11
+ File.stub!(:file?).with('spec/parallel_spec.opts').and_return false
12
+ end
13
+
14
+ it "uses TEST_ENV_NUMBER=blank when called for process 0" do
15
+ ParallelSpecs.should_receive(:open).with{|x,y|x=~/TEST_ENV_NUMBER= /}.and_return mock(:getc=>false)
16
+ ParallelSpecs.run_tests(['xxx'],0,'')
17
+ end
18
+
19
+ it "uses TEST_ENV_NUMBER=2 when called for process 1" do
20
+ ParallelSpecs.should_receive(:open).with{|x,y| x=~/TEST_ENV_NUMBER=2/}.and_return mock(:getc=>false)
21
+ ParallelSpecs.run_tests(['xxx'],1,'')
22
+ end
23
+
24
+ it "runs with color when called from cmdline" do
25
+ ParallelSpecs.should_receive(:open).with{|x,y| x=~/RSPEC_COLOR=1/}.and_return mock(:getc=>false)
26
+ $stdout.should_receive(:tty?).and_return true
27
+ ParallelSpecs.run_tests(['xxx'],1,'')
28
+ end
29
+
30
+ it "runs without color when not called from cmdline" do
31
+ ParallelSpecs.should_receive(:open).with{|x,y| x !~ /RSPEC_COLOR/}.and_return mock(:getc=>false)
32
+ $stdout.should_receive(:tty?).and_return false
33
+ ParallelSpecs.run_tests(['xxx'],1,'')
34
+ end
35
+
36
+ it "run bundle exec spec when on bundler 0.9" do
37
+ File.stub!(:file?).with('.bundle/environment.rb').and_return true
38
+ ParallelSpecs.should_receive(:open).with{|x,y| x =~ %r{bundle exec spec}}.and_return mock(:getc=>false)
39
+ ParallelSpecs.run_tests(['xxx'],1,'')
40
+ end
41
+
42
+ it "runs script/spec when script/spec can be found" do
43
+ File.should_receive(:file?).with('script/spec').and_return true
44
+ ParallelSpecs.should_receive(:open).with{|x,y| x =~ %r{script/spec}}.and_return mock(:getc=>false)
45
+ ParallelSpecs.run_tests(['xxx'],1,'')
46
+ end
47
+
48
+ it "runs spec when script/spec cannot be found" do
49
+ File.stub!(:file?).with('script/spec').and_return false
50
+ ParallelSpecs.should_receive(:open).with{|x,y| x !~ %r{(script/spec)|(bundle exec spec)}}.and_return mock(:getc=>false)
51
+ ParallelSpecs.run_tests(['xxx'],1,'')
52
+ end
53
+
54
+ it "uses no -O when no opts where found" do
55
+ File.stub!(:file?).with('spec/spec.opts').and_return false
56
+ ParallelSpecs.should_receive(:open).with{|x,y| x !~ %r{spec/spec.opts}}.and_return mock(:getc=>false)
57
+ ParallelSpecs.run_tests(['xxx'],1,'')
58
+ end
59
+
60
+ it "uses spec/spec.opts when found" do
61
+ ParallelSpecs.should_receive(:open).with{|x,y| x =~ %r{script/spec\s+-O spec/spec.opts}}.and_return mock(:getc=>false)
62
+ ParallelSpecs.run_tests(['xxx'],1,'')
63
+ end
64
+
65
+ it "uses spec/parallel_spec.opts when found" do
66
+ File.should_receive(:file?).with('spec/parallel_spec.opts').and_return true
67
+ ParallelSpecs.should_receive(:open).with{|x,y| x =~ %r{script/spec\s+-O spec/parallel_spec.opts}}.and_return mock(:getc=>false)
68
+ ParallelSpecs.run_tests(['xxx'],1,'')
69
+ end
70
+
71
+ it "uses options passed in" do
72
+ ParallelSpecs.should_receive(:open).with{|x,y| x =~ %r{script/spec -f n}}.and_return mock(:getc=>false)
73
+ ParallelSpecs.run_tests(['xxx'],1,'-f n')
74
+ end
75
+
76
+ it "returns the output" do
77
+ io = open('spec/spec_helper.rb')
78
+ ParallelSpecs.stub!(:print)
79
+ ParallelSpecs.should_receive(:open).and_return io
80
+ ParallelSpecs.run_tests(['xxx'],1,'').should =~ /\$LOAD_PATH << File/
81
+ end
82
+ end
83
+
84
+ describe :find_results do
85
+ it "finds multiple results in spec output" do
86
+ output = <<EOF
87
+ ....F...
88
+ ..
89
+ failute fsddsfsd
90
+ ...
91
+ ff.**..
92
+ 0 examples, 0 failures, 0 pending
93
+ ff.**..
94
+ 1 example, 1 failure, 1 pending
95
+ EOF
96
+
97
+ ParallelSpecs.find_results(output).should == ['0 examples, 0 failures, 0 pending','1 example, 1 failure, 1 pending']
98
+ end
99
+
100
+ it "is robust against scrambeled output" do
101
+ output = <<EOF
102
+ ....F...
103
+ ..
104
+ failute fsddsfsd
105
+ ...
106
+ ff.**..
107
+ 0 exFampl*es, 0 failures, 0 pend.ing
108
+ ff.**..
109
+ 1 exampF.les, 1 failures, 1 pend.ing
110
+ EOF
111
+
112
+ ParallelSpecs.find_results(output).should == ['0 examples, 0 failures, 0 pending','1 examples, 1 failures, 1 pending']
113
+ end
114
+ end
115
+
116
+ describe :failed do
117
+ it "fails with single failed specs" do
118
+ ParallelSpecs.failed?(['0 examples, 0 failures, 0 pending','1 examples, 1 failure, 1 pending']).should == true
119
+ end
120
+
121
+ it "fails with multiple failed specs" do
122
+ ParallelSpecs.failed?(['0 examples, 1 failure, 0 pending','1 examples, 111 failures, 1 pending']).should == true
123
+ end
124
+
125
+ it "does not fail with successful specs" do
126
+ ParallelSpecs.failed?(['0 examples, 0 failures, 0 pending','1 examples, 0 failures, 1 pending']).should == false
127
+ end
128
+
129
+ it "does fail with 10 failures" do
130
+ ParallelSpecs.failed?(['0 examples, 10 failures, 0 pending','1 examples, 0 failures, 1 pending']).should == true
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,130 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ParallelTests do
4
+ test_tests_in_groups(ParallelTests, 'test', '_test.rb')
5
+
6
+ describe :parse_rake_args do
7
+ it "should return the count" do
8
+ args = {:count => 2}
9
+ ParallelTests.parse_rake_args(args).should == [2, '', ""]
10
+ end
11
+
12
+ it "should default to the prefix" do
13
+ args = {:count => "models"}
14
+ ParallelTests.parse_rake_args(args).should == [2, "models", ""]
15
+ end
16
+
17
+ it "should return the count and prefix" do
18
+ args = {:count => 2, :path_prefix => "models"}
19
+ ParallelTests.parse_rake_args(args).should == [2, "models", ""]
20
+ end
21
+
22
+ it "should return the count, prefix, and options" do
23
+ args = {:count => 2, :path_prefix => "plain", :options => "-p default" }
24
+ ParallelTests.parse_rake_args(args).should == [2, "plain", "-p default"]
25
+ end
26
+ end
27
+
28
+ describe :run_tests do
29
+ it "uses TEST_ENV_NUMBER=blank when called for process 0" do
30
+ ParallelTests.should_receive(:open).with{|x,y|x=~/TEST_ENV_NUMBER= /}.and_return mock(:getc=>false)
31
+ ParallelTests.run_tests(['xxx'],0,'')
32
+ end
33
+
34
+ it "uses TEST_ENV_NUMBER=2 when called for process 1" do
35
+ ParallelTests.should_receive(:open).with{|x,y| x=~/TEST_ENV_NUMBER=2/}.and_return mock(:getc=>false)
36
+ ParallelTests.run_tests(['xxx'],1,'')
37
+ end
38
+
39
+ it "uses options" do
40
+ ParallelTests.should_receive(:open).with{|x,y| x=~ %r{ruby -Itest -v}}.and_return mock(:getc=>false)
41
+ ParallelTests.run_tests(['xxx'],1,'-v')
42
+ end
43
+
44
+ it "returns the output" do
45
+ io = open('spec/spec_helper.rb')
46
+ ParallelTests.stub!(:print)
47
+ ParallelTests.should_receive(:open).and_return io
48
+ ParallelTests.run_tests(['xxx'],1,'').should =~ /\$LOAD_PATH << File/
49
+ end
50
+ end
51
+
52
+ describe :find_results do
53
+ it "finds multiple results in test output" do
54
+ output = <<EOF
55
+ Loaded suite /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
56
+ Started
57
+ ..............
58
+ Finished in 0.145069 seconds.
59
+
60
+ 10 tests, 20 assertions, 0 failures, 0 errors
61
+ Loaded suite /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
62
+ Started
63
+ ..............
64
+ Finished in 0.145069 seconds.
65
+
66
+ 14 tests, 20 assertions, 0 failures, 0 errors
67
+
68
+ EOF
69
+
70
+ ParallelTests.find_results(output).should == ['10 tests, 20 assertions, 0 failures, 0 errors','14 tests, 20 assertions, 0 failures, 0 errors']
71
+ end
72
+
73
+ it "is robust against scrambeled output" do
74
+ output = <<EOF
75
+ Loaded suite /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
76
+ Started
77
+ ..............
78
+ Finished in 0.145069 seconds.
79
+
80
+ 10 tests, 20 assertions, 0 failures, 0 errors
81
+ Loaded suite /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
82
+ Started
83
+ ..............
84
+ Finished in 0.145069 seconds.
85
+
86
+ 14 te.dsts, 20 assertions, 0 failures, 0 errors
87
+ EOF
88
+
89
+ ParallelTests.find_results(output).should == ['10 tests, 20 assertions, 0 failures, 0 errors','14 tedsts, 20 assertions, 0 failures, 0 errors']
90
+ end
91
+ end
92
+
93
+ describe :failed do
94
+ it "fails with single failed" do
95
+ ParallelTests.failed?(['10 tests, 20 assertions, 0 failures, 0 errors','10 tests, 20 assertions, 1 failure, 0 errors']).should == true
96
+ end
97
+
98
+ it "fails with single error" do
99
+ ParallelTests.failed?(['10 tests, 20 assertions, 0 failures, 1 errors','10 tests, 20 assertions, 0 failures, 0 errors']).should == true
100
+ end
101
+
102
+ it "fails with failed and error" do
103
+ ParallelTests.failed?(['10 tests, 20 assertions, 0 failures, 1 errors','10 tests, 20 assertions, 1 failures, 1 errors']).should == true
104
+ end
105
+
106
+ it "fails with multiple failed tests" do
107
+ ParallelTests.failed?(['10 tests, 20 assertions, 2 failures, 0 errors','10 tests, 1 assertion, 1 failures, 0 errors']).should == true
108
+ end
109
+
110
+ it "does not fail with successful tests" do
111
+ ParallelTests.failed?(['10 tests, 20 assertions, 0 failures, 0 errors','10 tests, 20 assertions, 0 failures, 0 errors']).should == false
112
+ end
113
+
114
+ it "does fail with 10 failures" do
115
+ ParallelTests.failed?(['10 tests, 20 assertions, 10 failures, 0 errors','10 tests, 20 assertions, 0 failures, 0 errors']).should == true
116
+ end
117
+
118
+ it "is not failed with empty results" do
119
+ ParallelTests.failed?(['0 tests, 0 assertions, 0 failures, 0 errors']).should == false
120
+ end
121
+
122
+ it "is failed when there are no results" do
123
+ ParallelTests.failed?([]).should == true
124
+ end
125
+ end
126
+
127
+ it "has a version" do
128
+ ParallelTests::VERSION.should =~ /^\d+\.\d+\.\d+$/
129
+ end
130
+ end
@@ -0,0 +1,78 @@
1
+ # ---- requirements
2
+ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
3
+ require 'rubygems'
4
+
5
+ FAKE_RAILS_ROOT = '/tmp/pspecs/fixtures'
6
+
7
+ require 'parallel_specs'
8
+ require 'parallel_cucumber'
9
+
10
+ def size_of(group)
11
+ group.inject(0) { |sum, test| sum += File.stat(test).size }
12
+ end
13
+
14
+ def test_tests_in_groups(klass, folder, suffix)
15
+ test_root = "#{FAKE_RAILS_ROOT}/#{folder}"
16
+
17
+ describe :tests_in_groups do
18
+ before :all do
19
+ system "rm -rf #{FAKE_RAILS_ROOT}; mkdir -p #{test_root}/temp"
20
+
21
+ @files = [0,1,2,3,4,5,6,7].map do |i|
22
+ size = 99
23
+ file = "#{test_root}/temp/x#{i}#{suffix}"
24
+ File.open(file, 'w') { |f| f.puts 'x' * size }
25
+ file
26
+ end
27
+
28
+ @log = "#{FAKE_RAILS_ROOT}/tmp/parallel_profile.log"
29
+ `mkdir #{File.dirname(@log)}`
30
+ `rm -f #{@log}`
31
+ end
32
+
33
+ it "finds all tests" do
34
+ found = klass.tests_in_groups(test_root, 1)
35
+ all = [ Dir["#{test_root}/**/*#{suffix}"] ]
36
+ (found.flatten - all.flatten).should == []
37
+ end
38
+
39
+ it "partitions them into groups by equal size" do
40
+ groups = klass.tests_in_groups(test_root, 2)
41
+ groups.size.should == 2
42
+ size_of(groups[0]).should == 400
43
+ size_of(groups[1]).should == 400
44
+ end
45
+
46
+ it 'should partition correctly with a group size of 4' do
47
+ groups = klass.tests_in_groups(test_root, 4)
48
+ groups.size.should == 4
49
+ size_of(groups[0]).should == 200
50
+ size_of(groups[1]).should == 200
51
+ size_of(groups[2]).should == 200
52
+ size_of(groups[3]).should == 200
53
+ end
54
+
55
+ it 'should partition correctly with an uneven group size' do
56
+ groups = klass.tests_in_groups(test_root, 3)
57
+ groups.size.should == 3
58
+ size_of(groups[0]).should == 300
59
+ size_of(groups[1]).should == 300
60
+ size_of(groups[2]).should == 200
61
+ end
62
+
63
+ it "partitions by runtime when runtime-data is available" do
64
+ File.open(@log,'w') do |f|
65
+ @files[1..-1].each{|file| f.puts "#{file}:#{@files.index(file)}"}
66
+ f.puts "#{@files[0]}:10"
67
+ end
68
+
69
+ groups = klass.tests_in_groups(test_root, 2)
70
+ groups.size.should == 2
71
+ # 10 + 7 = 17
72
+ groups[0].should == [@files[0],@files[7]]
73
+ # 6+5+4+3+2+1 = 21
74
+ # still room for optimization...
75
+ groups[1].should == [@files[6],@files[5],@files[4],@files[3],@files[2],@files[1]]
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,56 @@
1
+ namespace :parallel do
2
+ desc "update test databases by running db:test:prepare for each --> parallel:prepare[num_cpus]"
3
+ task :prepare, :count do |t,args|
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', "parallel_tests")
5
+
6
+ Parallel.in_processes(args[:count] ? args[:count].to_i : nil) do |i|
7
+ puts "Preparing test database #{i + 1}"
8
+ `export TEST_ENV_NUMBER=#{ParallelTests.test_env_number(i)} ; rake db:test:prepare`
9
+ end
10
+ end
11
+
12
+ # Useful when dumping/resetting takes too long
13
+ desc "update test databases by running db:mgrate for each --> parallel:migrate[num_cpus]"
14
+ task :migrate, :count do |t,args|
15
+ require File.join(File.dirname(__FILE__), '..', 'lib', "parallel_tests")
16
+
17
+ Parallel.in_processes(args[:count] ? args[:count].to_i : nil) do |i|
18
+ puts "Migrating test database #{i + 1}"
19
+ `export TEST_ENV_NUMBER=#{ParallelTests.test_env_number(i)} ; rake db:migrate RAILS_ENV=test`
20
+ end
21
+ end
22
+
23
+ ['test', 'spec', 'features'].each do |type|
24
+ desc "run #{type} in parallel with parallel:#{type}[num_cpus]"
25
+ task type, :count, :path_prefix, :options do |t,args|
26
+ require File.join(File.dirname(__FILE__), '..', 'lib', "parallel_tests")
27
+ count, prefix, options = ParallelTests.parse_rake_args(args)
28
+ sh "#{File.join(File.dirname(__FILE__), '..', 'bin', 'parallel_test')} --type #{type} -n #{count} -p '#{prefix}' -r '#{RAILS_ROOT}' -o '#{options}'"
29
+ end
30
+ end
31
+ end
32
+
33
+ #backwards compatability
34
+ #spec:parallel:prepare
35
+ #spec:parallel
36
+ #test:parallel
37
+ namespace :spec do
38
+ namespace :parallel do
39
+ task :prepare, :count do |t,args|
40
+ $stderr.puts "WARNING -- Deprecated! use parallel:prepare"
41
+ Rake::Task['parallel:prepare'].invoke(args[:count])
42
+ end
43
+ end
44
+
45
+ task :parallel, :count, :path_prefix do |t,args|
46
+ $stderr.puts "WARNING -- Deprecated! use parallel:spec"
47
+ Rake::Task['parallel:spec'].invoke(args[:count], args[:path_prefix])
48
+ end
49
+ end
50
+
51
+ namespace :test do
52
+ task :parallel, :count, :path_prefix do |t,args|
53
+ $stderr.puts "WARNING -- Deprecated! use parallel:test"
54
+ Rake::Task['parallel:test'].invoke(args[:count], args[:path_prefix])
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parallel_tests
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
+ platform: ruby
11
+ authors:
12
+ - Michael Grosser
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-02 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: grosser.michael@gmail.com
23
+ executables:
24
+ - parallel_test
25
+ - parallel_spec
26
+ - parallel_cucumber
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.markdown
31
+ files:
32
+ - .gitignore
33
+ - README.markdown
34
+ - Rakefile
35
+ - VERSION
36
+ - bin/parallel_cucumber
37
+ - bin/parallel_spec
38
+ - bin/parallel_test
39
+ - lib/parallel_cucumber.rb
40
+ - lib/parallel_specs.rb
41
+ - lib/parallel_specs/spec_runtime_logger.rb
42
+ - lib/parallel_tests.rb
43
+ - parallel_tests.gemspec
44
+ - spec/integration_spec.rb
45
+ - spec/parallel_cucumber_spec.rb
46
+ - spec/parallel_specs_spec.rb
47
+ - spec/parallel_tests_spec.rb
48
+ - spec/spec_helper.rb
49
+ - tasks/parallel_specs.rake
50
+ has_rdoc: true
51
+ homepage: http://github.com/grosser/parallel_tests
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.6
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Run tests / specs / features in parallel
80
+ test_files:
81
+ - spec/spec_helper.rb
82
+ - spec/parallel_tests_spec.rb
83
+ - spec/parallel_specs_spec.rb
84
+ - spec/parallel_cucumber_spec.rb
85
+ - spec/integration_spec.rb