parallel_tests 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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