parallelized_specs 0.0.8 → 0.1.6

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.
data/Gemfile CHANGED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gem 'parallel'
4
+
5
+ group :dev do
6
+ gem 'test-unit', :platform => :ruby_19
7
+ gem 'rspec', '>=2.4'
8
+ gem 'rake'
9
+ gem 'jeweler'
10
+ end
data/Gemfile.lock CHANGED
@@ -1,30 +1,7 @@
1
1
  GEM
2
- remote: http://rubygems.org/
3
2
  specs:
4
- diff-lcs (1.1.2)
5
- git (1.2.5)
6
- jeweler (1.6.3)
7
- bundler (~> 1.0)
8
- git (>= 1.2.5)
9
- rake
10
- parallel (0.5.1)
11
- rake (0.8.7)
12
- rspec (2.6.0)
13
- rspec-core (~> 2.6.0)
14
- rspec-expectations (~> 2.6.0)
15
- rspec-mocks (~> 2.6.0)
16
- rspec-core (2.6.4)
17
- rspec-expectations (2.6.0)
18
- diff-lcs (~> 1.1.2)
19
- rspec-mocks (2.6.0)
20
- test-unit (2.4.4)
21
3
 
22
4
  PLATFORMS
23
5
  ruby
24
6
 
25
7
  DEPENDENCIES
26
- jeweler
27
- parallel
28
- rake
29
- rspec (>= 2.4)
30
- test-unit
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ begin
12
12
  gem.email = "jake@instructure.com"
13
13
  gem.homepage = "http://github.com/jakesorce/#{gem.name}"
14
14
  gem.authors = "Jake Sorce, Bryan Madsen"
15
- gem.version = "0.0.8"
15
+ gem.version = "0.1.6"
16
16
  end
17
17
 
18
18
  Jeweler::GemcutterTasks.new
data/Readme.md CHANGED
@@ -56,9 +56,7 @@ ParallelizedSpecs uses 1 database per test-process, 2 processes will use `*_test
56
56
  rake parallel:prepare
57
57
 
58
58
  ### 4: Run!
59
- rake parallel:test # Test::Unit
60
59
  rake parallel:spec # RSpec
61
- rake parallel:features # Cucumber
62
60
 
63
61
  rake parallel:test[1] --> force 1 CPU --> 86 seconds
64
62
  rake parallel:test --> got 2 CPUs? --> 47 seconds
@@ -67,7 +65,7 @@ ParallelizedSpecs uses 1 database per test-process, 2 processes will use `*_test
67
65
 
68
66
  Test by pattern (e.g. use one integration server per subfolder / see if you broke any 'user'-related tests)
69
67
 
70
- rake parallel:test[^unit] # everything in test/unit folder (every test file matching /^unit/)
68
+ rake parallel:test[^unit] # everything in test/spec folder (every test file matching /^spec/)
71
69
  rake parallel:test[user] # run users_controller + user_helper + user tests
72
70
  rake parallel:test['user|product'] # run user and product related tests
73
71
 
@@ -151,35 +149,6 @@ Setup for non-rails
151
149
 
152
150
  parallelized_specs test/bar test/baz/xxx_text_spec.rb
153
151
 
154
- Options are:
155
-
156
- -n [PROCESSES] How many processes to use, default: available CPUs
157
- -p, --path [PATH] run tests inside this path only
158
- --no-sort do not sort files before running them
159
- -m, --multiply-processes [FLOAT] use given number as a multiplier of processes to run
160
- -r, --root [PATH] execute test commands from this path
161
- -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUM']
162
- -o, --test-options '[OPTIONS]' execute test commands with those options
163
- -t, --type [TYPE] which type of tests to run? test, spec or features
164
- --non-parallel execute same commands but do not in parallel, needs --exec
165
- -v, --version Show Version
166
- -h, --help Show this.
167
-
168
- You can run any kind of code with -e / --execute
169
-
170
- parallel_test -n 5 -e 'ruby -e "puts %[hello from process #{ENV[:TEST_ENV_NUMBER.to_s].inspect}]"'
171
- hello from process "2"
172
- hello from process ""
173
- hello from process "3"
174
- hello from process "5"
175
- hello from process "4"
176
-
177
- <table>
178
- <tr><td></td><td>1 Process</td><td>2 Processes</td><td>4 Processes</td></tr>
179
- <tr><td>RSpec spec-suite</td><td>18s</td><td>14s</td><td>10s</td></tr>
180
- <tr><td>Rails-ActionPack</td><td>88s</td><td>53s</td><td>44s</td></tr>
181
- </table>
182
-
183
152
  TIPS
184
153
  ====
185
154
  - [RSpec] add a `spec/parallel_spec.opts` to use different options, e.g. no --drb (default: `spec/spec.opts`)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
1
+ 0.1.6
@@ -12,9 +12,8 @@ class ParallelizedSpecs
12
12
  groups.map!(&:sort!)
13
13
  end
14
14
 
15
- def self.in_even_groups_by_size(items_with_sizes, num_groups, options={})
15
+ def self.in_even_groups_by_size(items_with_sizes, num_groups, options)
16
16
  groups = Array.new(num_groups){{:items => [], :size => 0}}
17
-
18
17
  # add all files that should run in a single process to one group
19
18
  (options[:single_process]||[]).each do |pattern|
20
19
  matched, items_with_sizes = items_with_sizes.partition{|item, size| item =~ pattern }
@@ -1,19 +1,19 @@
1
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+ require "parallelized_specs"
3
+
1
4
  namespace :parallel do
2
- def run_in_parallel(cmd, options)
3
- count = (options[:count] ? options[:count].to_i : nil)
4
- executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallelized_spec')
5
- command = "#{executable} --exec '#{cmd}' -n #{count} #{'--non-parallel' if options[:non_parallel]}"
6
- abort unless system(command)
5
+ def run_db_in_parallel(cmd, options={})
6
+ ParallelizedSpecs.execute_parallel_db(cmd, options)
7
7
  end
8
8
 
9
9
  desc "create test databases via db:create --> parallel:create[num_cpus]"
10
10
  task :create, :count do |t, args|
11
- run_in_parallel('rake db:create RAILS_ENV=test', args)
11
+ run_db_in_parallel('rake db:create RAILS_ENV=test', args)
12
12
  end
13
13
 
14
14
  desc "drop test databases via db:drop --> parallel:drop[num_cpus]"
15
15
  task :drop, :count do |t, args|
16
- run_in_parallel('rake db:drop RAILS_ENV=test', args)
16
+ run_db_in_parallel('rake db:drop RAILS_ENV=test', args)
17
17
  end
18
18
 
19
19
  desc "update test databases by dumping and loading --> parallel:prepare[num_cpus]"
@@ -25,46 +25,27 @@ namespace :parallel do
25
25
  else
26
26
  # there is no separate dump / load for schema_format :sql -> do it safe and slow
27
27
  args = args.to_hash.merge(:non_parallel => true) # normal merge returns nil
28
- run_in_parallel('rake db:test:prepare --trace', args)
28
+ run_db_in_parallel('rake db:test:prepare --trace', args)
29
29
  end
30
30
  end
31
31
 
32
32
  # when dumping/resetting takes too long
33
33
  desc "update test databases via db:migrate --> parallel:migrate[num_cpus]"
34
34
  task :migrate, :count do |t, args|
35
- run_in_parallel('rake db:migrate RAILS_ENV=test', args)
35
+ run_db_in_parallel('rake db:migrate RAILS_ENV=test', args)
36
36
  end
37
37
 
38
38
  # just load the schema (good for integration server <-> no development db)
39
39
  desc "load dumped schema for test databases via db:schema:load --> parallel:load_schema[num_cpus]"
40
40
  task :load_schema, :count do |t, args|
41
- run_in_parallel('rake db:test:load', args)
41
+ run_db_in_parallel('rake db:test:load', args)
42
42
  end
43
43
 
44
44
  desc "run spec in parallel with parallel:spec[num_cpus]"
45
45
  task 'spec', :count, :pattern, :options, :arguments do |t, args|
46
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
47
- require "parallelized_specs"
48
46
  count, pattern, options = ParallelizedSpecs.parse_rake_args(args)
49
- executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallelized_spec')
50
- command = "#{executable} --type 'spec' -n #{count} -p '#{pattern}' -r '#{Rails.root}' -o '#{options}' #{args[:arguments]}"
51
- abort unless system(command) # allow to chain tasks e.g. rake parallel:spec
47
+ #command = "-n #{count} -p '#{pattern}' -r '#{Rails.root}' -o '#{options}' #{args[:arguments]}"
48
+ opts = Hash[:count => count, :pattern => pattern, :options => options, :root => Rails.root, :files => args[:arguments]]
49
+ ParallelizedSpecs.execute_parallel_specs(opts)
52
50
  end
53
51
  end
54
-
55
- #backwards compatability
56
- #spec:parallel:prepare
57
- #spec:parallel
58
- namespace :spec do
59
- namespace :parallel do
60
- task :prepare, :count do |t, args|
61
- $stderr.puts "WARNING -- Deprecated! use parallel:prepare"
62
- Rake::Task['parallel:prepare'].invoke(args[:count])
63
- end
64
- end
65
-
66
- task :parallel, :count, :pattern do |t, args|
67
- $stderr.puts "WARNING -- Deprecated! use parallel:spec"
68
- Rake::Task['parallel:spec'].invoke(args[:count], args[:pattern])
69
- end
70
- end
@@ -1,4 +1,6 @@
1
1
  require 'parallel'
2
+ raise "please ' gem install parallel '" if Gem::Version.new(Parallel::VERSION) < Gem::Version.new('0.4.2')
3
+ require 'optparse'
2
4
  require 'parallelized_specs/grouper'
3
5
  require 'parallelized_specs/railtie'
4
6
  require 'parallelized_specs/spec_error_logger'
@@ -51,6 +53,64 @@ class ParallelizedSpecs
51
53
  "_spec.rb"
52
54
  end
53
55
 
56
+ def self.execute_parallel_db(cmd, options={})
57
+ count = options[:count].to_i
58
+ count = Parallel.processor_count if count == 0
59
+ runs = (0...count).to_a
60
+ results = if options[:non_parallel]
61
+ runs.map do |i|
62
+ execute_command(cmd, i, options)
63
+ end
64
+ else
65
+ Parallel.map(runs, :in_processes => count) do |i|
66
+ execute_command(cmd, i, options)
67
+ end
68
+ end.flatten
69
+ abort if results.any? { |r| r[:exit_status] != 0 }
70
+ end
71
+
72
+ def self.execute_parallel_specs(options)
73
+ num_processes = options[:count] || Parallel.processor_count
74
+ lib, name, task = {
75
+ 'spec' => %w(specs spec spec),
76
+ }[options[:type]||'spec']
77
+
78
+ start = Time.now
79
+
80
+ tests_folder = task
81
+ tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
82
+ if options[:files].is_a?(Array)
83
+ groups = tests_in_groups(options[:files] || tests_folder, num_processes, options)
84
+ else
85
+ files_array = options[:files].split(/ /)
86
+ groups = tests_in_groups(files_array || tests_folder, num_processes, options)
87
+ end
88
+ num_processes = groups.size
89
+
90
+ #adjust processes to groups
91
+ abort "no #{name}s found!" if groups.size == 0
92
+
93
+ num_tests = groups.inject(0) { |sum, item| sum + item.size }
94
+ puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
95
+
96
+ test_results = Parallel.map(groups, :in_processes => num_processes) do |group|
97
+ run_tests(group, groups.index(group), options)
98
+ end
99
+
100
+ #parse and print results
101
+ results = find_results(test_results.map { |result| result[:stdout] }*"")
102
+ puts ""
103
+ puts summarize_results(results)
104
+
105
+ #report total time taken
106
+ puts ""
107
+ puts "Took #{Time.now - start} seconds"
108
+
109
+ #exit with correct status code so rake parallel:test && echo 123 works
110
+ failed = test_results.any? { |result| result[:exit_status] != 0 }
111
+ abort "#{name.capitalize}s Failed" if failed
112
+ end
113
+
54
114
  # parallel:spec[:count, :pattern, :options]
55
115
  def self.parse_rake_args(args)
56
116
  # order as given by user
@@ -71,7 +131,7 @@ class ParallelizedSpecs
71
131
  end
72
132
 
73
133
  # finds all tests and partitions them into groups
74
- def self.tests_in_groups(root, num_groups, options={})
134
+ def self.tests_in_groups(root, num_groups, options)
75
135
  tests = find_tests(root, options)
76
136
  if options[:no_sort]
77
137
  Grouper.in_groups(tests, num_groups)
@@ -184,15 +244,15 @@ class ParallelizedSpecs
184
244
  end
185
245
  end
186
246
 
187
- def self.find_tests(root, options={})
247
+ def self.find_tests(root, options)
188
248
  if root.is_a?(Array)
189
249
  root
190
250
  else
191
251
  # follow one symlink and direct children
192
252
  # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
193
- files = Dir["#{root}/**{,/*/**}/*#{test_suffix}"].uniq
253
+ files = Dir["#{Rails.root}/**{,/*/**}/*#{test_suffix}"].uniq
194
254
  files = files.map { |f| f.sub(root+'/', '') }
195
- files = files.grep(/#{options[:pattern]}/)
255
+ files = files.grep(/#{options['pattern']}/)
196
256
  files.map { |f| "#{root}/#{f}" }
197
257
  end
198
258
  end
@@ -5,20 +5,18 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "parallelized_specs"
8
- s.version = "0.0.8"
8
+ s.version = "0.1.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jake Sorce, Bryan Madsen"]
12
- s.date = "2012-04-23"
12
+ s.date = "2012-04-27"
13
13
  s.email = "jake@instructure.com"
14
- s.executables = ["parallelized_spec"]
15
14
  s.files = [
16
15
  "Gemfile",
17
16
  "Gemfile.lock",
18
17
  "Rakefile",
19
18
  "Readme.md",
20
19
  "VERSION",
21
- "bin/parallelized_spec",
22
20
  "lib/parallelized_specs.rb",
23
21
  "lib/parallelized_specs/grouper.rb",
24
22
  "lib/parallelized_specs/railtie.rb",
@@ -48,9 +46,12 @@ Gem::Specification.new do |s|
48
46
  s.specification_version = 3
49
47
 
50
48
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<parallel>, [">= 0"])
51
50
  else
51
+ s.add_dependency(%q<parallel>, [">= 0"])
52
52
  end
53
53
  else
54
+ s.add_dependency(%q<parallel>, [">= 0"])
54
55
  end
55
56
  end
56
57
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallelized_specs
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 0
9
- - 8
10
- version: 0.0.8
8
+ - 1
9
+ - 6
10
+ version: 0.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jake Sorce, Bryan Madsen
@@ -15,13 +15,26 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-04-23 00:00:00 Z
19
- dependencies: []
20
-
18
+ date: 2012-04-27 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: parallel
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
21
34
  description:
22
35
  email: jake@instructure.com
23
- executables:
24
- - parallelized_spec
36
+ executables: []
37
+
25
38
  extensions: []
26
39
 
27
40
  extra_rdoc_files: []
@@ -32,7 +45,6 @@ files:
32
45
  - Rakefile
33
46
  - Readme.md
34
47
  - VERSION
35
- - bin/parallelized_spec
36
48
  - lib/parallelized_specs.rb
37
49
  - lib/parallelized_specs/grouper.rb
38
50
  - lib/parallelized_specs/railtie.rb
@@ -1,95 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'optparse'
4
- require 'parallel'
5
- raise "please ' gem install parallel '" if Gem::Version.new(Parallel::VERSION) < Gem::Version.new('0.4.2')
6
- $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
7
- require "parallelized_specs"
8
-
9
- options = {}
10
- OptionParser.new do |opts|
11
- opts.banner = <<BANNER
12
- Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
13
-
14
- [optional] Only run selected files & folders:
15
- parallelized_spec test/bar test/baz/xxx_text_spec.rb
16
-
17
- Options are:
18
- BANNER
19
- opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs"){|n| options[:count] = n }
20
- opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern"){|pattern| options[:pattern] = pattern }
21
- opts.on("--no-sort", "do not sort files before running them"){ |no_sort| options[:no_sort] = no_sort }
22
- opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run"){ |multiply| options[:multiply] = multiply }
23
- opts.on("-r", '--root [PATH]', "execute test commands from this path"){|path| options[:root] = path }
24
- opts.on("-s [PATTERN]", "--single [PATTERN]", "Run all matching files in only one process") do |pattern|
25
- options[:single_process] ||= []
26
- options[:single_process] << /#{pattern}/
27
- end
28
- opts.on("-e", '--exec [COMMAND]', "execute this code parallel and with ENV['TEST_ENV_NUM']"){|path| options[:execute] = path }
29
- opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options"){|arg| options[:test_options] = arg }
30
- opts.on("-t", "--type [TYPE]", "which type of tests to run? test, spec or features"){|type| options[:type] = type }
31
- opts.on("--non-parallel", "execute same commands but do not in parallel, needs --exec"){ options[:non_parallel] = true }
32
- opts.on("--chunk-timeout [TIMEOUT]", "timeout before re-printing the output of a child-process"){|timeout| options[:chunk_timeout] = timeout.to_f }
33
- opts.on('-v', '--version', 'Show Version'){ puts ParallelizedSpecs::VERSION; exit}
34
- opts.on("-h", "--help", "Show this.") { puts opts; exit }
35
- end.parse!
36
-
37
- raise "--no-sort and --single-process are not supported" if options[:no_sort] and options[:single_process]
38
-
39
- # get files to run from arguments
40
- options[:files] = ARGV if ARGV.size > 0
41
-
42
- num_processes = options[:count] || Parallel.processor_count
43
- num_processes = num_processes * (options[:multiply] || 1)
44
-
45
- if options[:execute]
46
- runs = (0...num_processes).to_a
47
- results = if options[:non_parallel]
48
- runs.map do |i|
49
- ParallelizedSpecs.execute_command(options[:execute], i, options)
50
- end
51
- else
52
- Parallel.map(runs, :in_processes => num_processes) do |i|
53
- ParallelizedSpecs.execute_command(options[:execute], i, options)
54
- end
55
- end.flatten
56
- abort if results.any?{|r| r[:exit_status] != 0 }
57
- else
58
- lib, name, task = {
59
- 'spec' => %w(specs spec spec),
60
- }[options[:type]||'spec']
61
-
62
- require "parallelized_#{lib}"
63
- klass = eval("Parallelized#{lib.capitalize}")
64
-
65
- start = Time.now
66
-
67
- tests_folder = task
68
- tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
69
-
70
- groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, options)
71
- num_processes = groups.size
72
-
73
- #adjust processes to groups
74
- abort "no #{name}s found!" if groups.size == 0
75
-
76
- num_tests = groups.inject(0){|sum,item| sum + item.size }
77
- puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
78
-
79
- test_results = Parallel.map(groups, :in_processes => num_processes) do |group|
80
- klass.run_tests(group, groups.index(group), options)
81
- end
82
-
83
- #parse and print results
84
- results = klass.find_results(test_results.map{|result| result[:stdout] }*"")
85
- puts ""
86
- puts klass.summarize_results(results)
87
-
88
- #report total time taken
89
- puts ""
90
- puts "Took #{Time.now - start} seconds"
91
-
92
- #exit with correct status code so rake parallel:test && echo 123 works
93
- failed = test_results.any?{|result| result[:exit_status] != 0 }
94
- abort "#{name.capitalize}s Failed" if failed
95
- end