parallel_tests 0.5.0 → 0.6.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.
data/Readme.md CHANGED
@@ -61,13 +61,12 @@ OR as plugin
61
61
  rake parallel:test --> got 4 CPUs? --> 26 seconds
62
62
  ...
63
63
 
64
- Test just a subfolder (e.g. use one integration server per subfolder)
64
+ Test by pattern (e.g. use one integration server per subfolder / see if you broke any user-related tests)
65
65
 
66
- rake parallel:test[models]
67
- rake parallel:test[something/else]
66
+ rake parallel:test[^unit] # everything in test/unit folder (every test file matching /^unit/)
67
+ rake parallel:test[user] # run users_controller + user_helper + user tests
68
+ rake parallel:test['user|product'] # run user and product related tests
68
69
 
69
- partial paths are OK too...
70
- rake parallel:test[functional] == rake parallel:test[fun]
71
70
 
72
71
  Example output
73
72
  --------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
data/bin/parallel_test CHANGED
@@ -17,7 +17,7 @@ Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2',
17
17
  Options are:
18
18
  BANNER
19
19
  opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs"){|n| options[:count] = n }
20
- opts.on("-p", '--path [PATH]', "run tests inside this path only"){|path| options[:path_prefix] = path }
20
+ opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern"){|pattern| options[:pattern] = pattern }
21
21
  opts.on("--no-sort", "do not sort files before running them"){ |no_sort| options[:no_sort] = no_sort }
22
22
  opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run"){ |multiply| options[:multiply] = multiply }
23
23
  opts.on("-r", '--root [PATH]', "execute test commands from this path"){|path| options[:root] = path }
@@ -60,10 +60,10 @@ else
60
60
 
61
61
  start = Time.now
62
62
 
63
- tests_folder = File.join(task, options[:path_prefix].to_s)
63
+ tests_folder = task
64
64
  tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
65
65
 
66
- groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, :no_sort => options[:no_sort])
66
+ groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, :no_sort => options[:no_sort], :pattern => options[:pattern])
67
67
  num_processes = groups.size
68
68
 
69
69
  #adjust processes to groups
@@ -5,26 +5,31 @@ require 'parallel_tests/railtie'
5
5
  class ParallelTests
6
6
  VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
7
7
 
8
- # parallel:spec[2,controller] <-> parallel:spec[controller]
8
+ # parallel:spec[:count, :pattern, :options]
9
9
  def self.parse_rake_args(args)
10
- num_processes = Parallel.processor_count
11
- options = ""
12
- if args[:count].to_s =~ /^\d*$/ # number or empty
13
- num_processes = args[:count] unless args[:count].to_s.empty?
14
- prefix = args[:path_prefix]
15
- options = args[:options] if args[:options]
16
- else # something stringy
17
- prefix = args[:count]
18
- end
19
- [num_processes.to_i, prefix.to_s, options]
10
+ # order as given by user
11
+ args = [args[:count], args[:pattern], args[:options]]
12
+
13
+ # count given or empty ?
14
+ # parallel:spec[2,models,options]
15
+ # parallel:spec[,models,options]
16
+ count = args.shift if args.first.to_s =~ /^\d*$/
17
+ num_processes = (count.to_s.empty? ? Parallel.processor_count : count.to_i)
18
+
19
+ pattern = args.shift
20
+ options = args.shift
21
+
22
+ [num_processes.to_i, pattern.to_s, options.to_s]
20
23
  end
21
24
 
22
25
  # finds all tests and partitions them into groups
23
26
  def self.tests_in_groups(root, num_groups, options={})
27
+ tests = find_tests(root, options)
24
28
  if options[:no_sort] == true
25
- Grouper.in_groups(find_tests(root), num_groups)
29
+ Grouper.in_groups(tests, num_groups)
26
30
  else
27
- Grouper.in_even_groups_by_size(tests_with_runtime(root), num_groups)
31
+ tests = with_runtime_info(tests)
32
+ Grouper.in_even_groups_by_size(tests, num_groups)
28
33
  end
29
34
  end
30
35
 
@@ -91,7 +96,7 @@ class ParallelTests
91
96
 
92
97
  # copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
93
98
  def self.bundler_enabled?
94
- return true if Object.const_defined?(:Bundler)
99
+ return true if Object.const_defined?(:Bundler)
95
100
 
96
101
  previous = nil
97
102
  current = File.expand_path(Dir.pwd)
@@ -113,8 +118,7 @@ class ParallelTests
113
118
  "_test.rb"
114
119
  end
115
120
 
116
- def self.tests_with_runtime(root)
117
- tests = find_tests(root)
121
+ def self.with_runtime_info(tests)
118
122
  lines = File.read(runtime_log).split("\n") rescue []
119
123
 
120
124
  # use recorded test runtime if we got enough data
@@ -131,11 +135,16 @@ class ParallelTests
131
135
  end
132
136
  end
133
137
 
134
- def self.find_tests(root)
138
+ def self.find_tests(root, options={})
135
139
  if root.is_a?(Array)
136
140
  root
137
141
  else
138
- Dir["#{root}**/**/*#{self.test_suffix}"]
142
+ # follow one symlink and direct children
143
+ # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
144
+ files = Dir["#{root}/**{,/*/**}/*#{test_suffix}"].uniq
145
+ files = files.map{|f| f.sub(root+'/','') }
146
+ files = files.grep(/#{options[:pattern]}/)
147
+ files.map{|f| "#{root}/#{f}" }
139
148
  end
140
149
  end
141
150
  end
@@ -43,12 +43,12 @@ namespace :parallel do
43
43
 
44
44
  ['test', 'spec', 'features'].each do |type|
45
45
  desc "run #{type} in parallel with parallel:#{type}[num_cpus]"
46
- task type, :count, :path_prefix, :options do |t,args|
46
+ task type, :count, :pattern, :options do |t,args|
47
47
  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
48
48
  require "parallel_tests"
49
- count, prefix, options = ParallelTests.parse_rake_args(args)
49
+ count, pattern, options = ParallelTests.parse_rake_args(args)
50
50
  executable = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'parallel_test')
51
- command = "#{executable} --type #{type} -n #{count} -p '#{prefix}' -r '#{Rails.root}' -o '#{options}'"
51
+ command = "#{executable} --type #{type} -n #{count} -p '#{pattern}' -r '#{Rails.root}' -o '#{options}'"
52
52
  abort unless system(command) # allow to chain tasks e.g. rake parallel:spec parallel:features
53
53
  end
54
54
  end
@@ -66,15 +66,15 @@ namespace :spec do
66
66
  end
67
67
  end
68
68
 
69
- task :parallel, :count, :path_prefix do |t,args|
69
+ task :parallel, :count, :pattern do |t,args|
70
70
  $stderr.puts "WARNING -- Deprecated! use parallel:spec"
71
- Rake::Task['parallel:spec'].invoke(args[:count], args[:path_prefix])
71
+ Rake::Task['parallel:spec'].invoke(args[:count], args[:pattern])
72
72
  end
73
73
  end
74
74
 
75
75
  namespace :test do
76
- task :parallel, :count, :path_prefix do |t,args|
76
+ task :parallel, :count, :pattern do |t,args|
77
77
  $stderr.puts "WARNING -- Deprecated! use parallel:test"
78
- Rake::Task['parallel:test'].invoke(args[:count], args[:path_prefix])
78
+ Rake::Task['parallel:test'].invoke(args[:count], args[:pattern])
79
79
  end
80
80
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{parallel_tests}
8
- s.version = "0.5.0"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2011-07-03}
12
+ s.date = %q{2011-08-09}
13
13
  s.email = %q{grosser.michael@gmail.com}
14
14
  s.executables = ["parallel_cucumber", "parallel_test", "parallel_spec"]
15
15
  s.files = [
@@ -59,8 +59,8 @@ describe 'CLI' do
59
59
  end
60
60
 
61
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"]
62
+ result = `#{executable} -e 'ruby -e "print ENV[:TEST_ENV_NUMBER.to_s].to_i"' -n 4`
63
+ result.gsub('"','').split('').sort.should == %w[0 2 3 4]
64
64
  end
65
65
 
66
66
  it "can exec given command non-parallel" do
@@ -83,14 +83,11 @@ describe 'CLI' do
83
83
  end
84
84
 
85
85
  it "runs faster with more processes" do
86
- write 'xxx_spec.rb', 'describe("it"){it("should"){sleep 2}}'
87
- write 'xxx2_spec.rb', 'describe("it"){it("should"){sleep 2}}'
88
- write 'xxx3_spec.rb', 'describe("it"){it("should"){sleep 2}}'
89
- write 'xxx4_spec.rb', 'describe("it"){it("should"){sleep 2}}'
90
- write 'xxx5_spec.rb', 'describe("it"){it("should"){sleep 2}}'
91
- write 'xxx6_spec.rb', 'describe("it"){it("should"){sleep 2}}'
86
+ 2.times{|i|
87
+ write "xxx#{i}_spec.rb", 'describe("it"){it("should"){sleep 5}}; $stderr.puts ENV["TEST_ENV_NUMBER"]'
88
+ }
92
89
  t = Time.now
93
- run_specs :processes => 6
90
+ puts run_specs(:processes => 2)
94
91
  expected = 10
95
92
  (Time.now - t).should <= expected
96
93
  end
@@ -14,13 +14,13 @@ describe ParallelTests do
14
14
  ParallelTests.parse_rake_args(args).should == [Parallel.processor_count, "models", ""]
15
15
  end
16
16
 
17
- it "should return the count and prefix" do
18
- args = {:count => 2, :path_prefix => "models"}
17
+ it "should return the count and pattern" do
18
+ args = {:count => 2, :pattern => "models"}
19
19
  ParallelTests.parse_rake_args(args).should == [2, "models", ""]
20
20
  end
21
21
 
22
- it "should return the count, prefix, and options" do
23
- args = {:count => 2, :path_prefix => "plain", :options => "-p default" }
22
+ it "should return the count, pattern, and options" do
23
+ args = {:count => 2, :pattern => "plain", :options => "-p default" }
24
24
  ParallelTests.parse_rake_args(args).should == [2, "plain", "-p default"]
25
25
  end
26
26
  end
@@ -136,6 +136,54 @@ EOF
136
136
  end
137
137
  end
138
138
 
139
+ describe :find_tests do
140
+ it "returns if root is an array" do
141
+ ParallelTests.send(:find_tests, [1]).should == [1]
142
+ end
143
+
144
+ it "finds all test files" do
145
+ begin
146
+ root = "/tmp/test-find_tests-#{rand(999)}"
147
+ `mkdir #{root}`
148
+ `mkdir #{root}/a`
149
+ `mkdir #{root}/b`
150
+ `touch #{root}/x_test.rb`
151
+ `touch #{root}/a/x_test.rb`
152
+ `touch #{root}/a/test.rb`
153
+ `touch #{root}/b/y_test.rb`
154
+ `touch #{root}/b/test.rb`
155
+ `ln -s #{root}/b #{root}/c`
156
+ `ln -s #{root}/b #{root}/a/`
157
+ ParallelTests.send(:find_tests, root).sort.should == [
158
+ "#{root}/a/b/y_test.rb",
159
+ "#{root}/a/x_test.rb",
160
+ "#{root}/b/y_test.rb",
161
+ "#{root}/c/y_test.rb",
162
+ "#{root}/x_test.rb"
163
+ ]
164
+ ensure
165
+ `rm -rf #{root}`
166
+ end
167
+ end
168
+
169
+ it "finds files by pattern" do
170
+ begin
171
+ root = "/tmp/test-find_tests-#{rand(999)}"
172
+ `mkdir #{root}`
173
+ `mkdir #{root}/a`
174
+ `touch #{root}/a/x_test.rb`
175
+ `touch #{root}/a/y_test.rb`
176
+ `touch #{root}/a/z_test.rb`
177
+ ParallelTests.send(:find_tests, root, :pattern => '^a/(y|z)_test').sort.should == [
178
+ "#{root}/a/y_test.rb",
179
+ "#{root}/a/z_test.rb",
180
+ ]
181
+ ensure
182
+ `rm -rf #{root}`
183
+ end
184
+ end
185
+ end
186
+
139
187
  it "has a version" do
140
188
  ParallelTests::VERSION.should =~ /^\d+\.\d+\.\d+$/
141
189
  end
data/spec/spec_helper.rb CHANGED
@@ -64,7 +64,7 @@ def test_tests_in_groups(klass, folder, suffix)
64
64
 
65
65
  it "groups when given an array of files" do
66
66
  list_of_files = Dir["#{test_root}/**/*#{suffix}"]
67
- found = klass.tests_with_runtime(list_of_files)
67
+ found = klass.with_runtime_info(list_of_files)
68
68
  found.should =~ list_of_files.map{ |file| [file, File.stat(file).size]}
69
69
  end
70
70
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 5
8
+ - 6
9
9
  - 0
10
- version: 0.5.0
10
+ version: 0.6.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Grosser
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-03 00:00:00 +02:00
18
+ date: 2011-08-09 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency