parallel_tests 0.13.3 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/Gemfile +1 -0
  2. data/Gemfile.lock +8 -2
  3. data/Rakefile +5 -1
  4. data/Readme.md +5 -2
  5. data/bin/parallel_spinach +5 -0
  6. data/lib/parallel_tests.rb +40 -38
  7. data/lib/parallel_tests/cli.rb +2 -2
  8. data/lib/parallel_tests/cucumber/failures_logger.rb +2 -2
  9. data/lib/parallel_tests/cucumber/runner.rb +5 -88
  10. data/lib/parallel_tests/{cucumber → gherkin}/io.rb +1 -1
  11. data/lib/parallel_tests/{cucumber/gherkin_listener.rb → gherkin/listener.rb} +2 -2
  12. data/lib/parallel_tests/gherkin/runner.rb +102 -0
  13. data/lib/parallel_tests/{cucumber → gherkin}/runtime_logger.rb +2 -2
  14. data/lib/parallel_tests/grouper.rb +41 -41
  15. data/lib/parallel_tests/rspec/failures_logger.rb +1 -1
  16. data/lib/parallel_tests/rspec/runner.rb +50 -48
  17. data/lib/parallel_tests/spinach/runner.rb +19 -0
  18. data/lib/parallel_tests/tasks.rb +7 -3
  19. data/lib/parallel_tests/test/runner.rb +125 -123
  20. data/lib/parallel_tests/test/runtime_logger.rb +57 -53
  21. data/lib/parallel_tests/version.rb +1 -1
  22. data/parallel_tests.gemspec +2 -2
  23. data/spec/integration_spec.rb +61 -0
  24. data/spec/parallel_tests/cucumber/failure_logger_spec.rb +1 -1
  25. data/spec/parallel_tests/cucumber/runner_spec.rb +5 -172
  26. data/spec/parallel_tests/{cucumber/gherkin_listener_spec.rb → gherkin/listener_spec.rb} +3 -3
  27. data/spec/parallel_tests/gherkin/runner_behaviour.rb +177 -0
  28. data/spec/parallel_tests/rspec/{failure_logger_spec.rb → failures_logger_spec.rb} +0 -0
  29. data/spec/parallel_tests/spinach/runner_spec.rb +12 -0
  30. data/spec/parallel_tests/test/runtime_logger_spec.rb +1 -1
  31. data/spec/parallel_tests_spec.rb +2 -2
  32. data/spec/spec_helper.rb +1 -1
  33. metadata +16 -10
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ gem 'bump'
5
5
  gem 'test-unit'
6
6
  gem 'rspec', '>=2.4'
7
7
  gem 'cucumber'
8
+ gem 'spinach'
8
9
  gem 'rake'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- parallel_tests (0.13.3)
4
+ parallel_tests (0.14.0)
5
5
  parallel
6
6
 
7
7
  GEM
@@ -9,6 +9,7 @@ GEM
9
9
  specs:
10
10
  builder (3.0.0)
11
11
  bump (0.3.8)
12
+ colorize (0.5.8)
12
13
  cucumber (1.1.4)
13
14
  builder (>= 2.1.2)
14
15
  diff-lcs (>= 1.1.2)
@@ -20,9 +21,10 @@ GEM
20
21
  json (>= 1.4.6)
21
22
  gherkin (2.7.6-java)
22
23
  json (>= 1.4.6)
24
+ gherkin-ruby (0.3.0)
23
25
  json (1.7.5)
24
26
  json (1.7.5-java)
25
- parallel (0.6.5)
27
+ parallel (0.7.0)
26
28
  rake (10.0.3)
27
29
  rspec (2.13.0)
28
30
  rspec-core (~> 2.13.0)
@@ -32,6 +34,9 @@ GEM
32
34
  rspec-expectations (2.13.0)
33
35
  diff-lcs (>= 1.1.3, < 2.0)
34
36
  rspec-mocks (2.13.1)
37
+ spinach (0.8.3)
38
+ colorize (= 0.5.8)
39
+ gherkin-ruby (~> 0.3.0)
35
40
  term-ansicolor (1.0.7)
36
41
  test-unit (2.4.4)
37
42
 
@@ -45,4 +50,5 @@ DEPENDENCIES
45
50
  parallel_tests!
46
51
  rake
47
52
  rspec (>= 2.4)
53
+ spinach
48
54
  test-unit
data/Rakefile CHANGED
@@ -2,5 +2,9 @@ require 'bump/tasks'
2
2
  require 'bundler/gem_tasks'
3
3
 
4
4
  task :default do
5
- sh "rspec spec/"
5
+ if RUBY_VERSION < "1.9.0"
6
+ sh "rspec --tag ~fails_on_ruby_187 spec/"
7
+ else
8
+ sh "rspec spec/"
9
+ end
6
10
  end
data/Readme.md CHANGED
@@ -9,7 +9,7 @@ Setup for Rails
9
9
  [still using Rails 2?](https://github.com/grosser/parallel_tests/blob/master/ReadmeRails2.md)
10
10
 
11
11
  ### Install
12
- If you use RSpec: ensure you got >= 2.4
12
+ If you use RSpec: ensure you have >= 2.4
13
13
 
14
14
  As gem
15
15
 
@@ -48,6 +48,7 @@ test:
48
48
  rake parallel:test # Test::Unit
49
49
  rake parallel:spec # RSpec
50
50
  rake parallel:features # Cucumber
51
+ rake parallel:features-spinach # Spinach
51
52
 
52
53
  rake parallel:test[1] --> force 1 CPU --> 86 seconds
53
54
  rake parallel:test --> got 2 CPUs? --> 47 seconds
@@ -164,6 +165,7 @@ Setup for non-rails
164
165
  parallel_test test/
165
166
  parallel_rspec spec/
166
167
  parallel_cucumber features/
168
+ parallel_spinach features/
167
169
 
168
170
  - use ENV['TEST_ENV_NUMBER'] inside your tests to select separate db/memcache/etc.
169
171
  - Only run selected files & folders:
@@ -183,7 +185,7 @@ Options are:
183
185
  -i, --isolate Do not run any other tests in the group used by --single(-s)
184
186
  -e, --exec [COMMAND] execute this code parallel and with ENV['TEST_ENV_NUM']
185
187
  -o, --test-options '[OPTIONS]' execute test commands with those options
186
- -t, --type [TYPE] test(default) / rspec / cucumber
188
+ -t, --type [TYPE] test(default) / rspec / cucumber / spinach
187
189
  --serialize-stdout Serialize stdout output, nothing will be written until everything is done
188
190
  --non-parallel execute same commands but do not in parallel, needs --exec
189
191
  --no-symlinks Do not traverse symbolic links to find test files
@@ -286,6 +288,7 @@ inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-
286
288
  - [Iain Beeston](https://github.com/iainbeeston)
287
289
  - [Alejandro Pulver](https://github.com/alepulver)
288
290
  - [Felix Clack](https://github.com/felixclack)
291
+ - [Izaak Alpert](https://github.com/karlhungus)
289
292
 
290
293
  [Michael Grosser](http://grosser.it)<br/>
291
294
  michael@grosser.it<br/>
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+ require "parallel_tests"
4
+
5
+ ParallelTests::CLI.new.run(["--type", "spinach"] + ARGV)
@@ -8,52 +8,54 @@ module ParallelTests
8
8
  autoload :VERSION, "parallel_tests/version"
9
9
  autoload :Grouper, "parallel_tests/grouper"
10
10
 
11
- def self.determine_number_of_processes(count)
12
- [
13
- count,
14
- ENV["PARALLEL_TEST_PROCESSORS"],
15
- Parallel.processor_count
16
- ].detect{|c| not c.to_s.strip.empty? }.to_i
17
- end
11
+ class << self
12
+ def determine_number_of_processes(count)
13
+ [
14
+ count,
15
+ ENV["PARALLEL_TEST_PROCESSORS"],
16
+ Parallel.processor_count
17
+ ].detect{|c| not c.to_s.strip.empty? }.to_i
18
+ end
18
19
 
19
- # copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
20
- def self.bundler_enabled?
21
- return true if Object.const_defined?(:Bundler)
20
+ # copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
21
+ def bundler_enabled?
22
+ return true if Object.const_defined?(:Bundler)
22
23
 
23
- previous = nil
24
- current = File.expand_path(Dir.pwd)
24
+ previous = nil
25
+ current = File.expand_path(Dir.pwd)
25
26
 
26
- until !File.directory?(current) || current == previous
27
- filename = File.join(current, "Gemfile")
28
- return true if File.exists?(filename)
29
- current, previous = File.expand_path("..", current), current
30
- end
27
+ until !File.directory?(current) || current == previous
28
+ filename = File.join(current, "Gemfile")
29
+ return true if File.exists?(filename)
30
+ current, previous = File.expand_path("..", current), current
31
+ end
31
32
 
32
- false
33
- end
33
+ false
34
+ end
34
35
 
35
- def self.first_process?
36
- !ENV["TEST_ENV_NUMBER"] || ENV["TEST_ENV_NUMBER"].to_i == 0
37
- end
36
+ def first_process?
37
+ !ENV["TEST_ENV_NUMBER"] || ENV["TEST_ENV_NUMBER"].to_i == 0
38
+ end
38
39
 
39
- def self.wait_for_other_processes_to_finish
40
- return unless ENV["TEST_ENV_NUMBER"]
41
- sleep 1 until number_of_running_processes <= 1
42
- end
40
+ def wait_for_other_processes_to_finish
41
+ return unless ENV["TEST_ENV_NUMBER"]
42
+ sleep 1 until number_of_running_processes <= 1
43
+ end
43
44
 
44
- # Fun fact: this includes the current process if it's run via parallel_tests
45
- def self.number_of_running_processes
46
- result = `#{GREP_PROCESSES_COMMAND}`
47
- raise "Could not grep for processes -> #{result}" if result.strip != "" && !$?.success?
48
- result.split("\n").size
49
- end
45
+ # Fun fact: this includes the current process if it's run via parallel_tests
46
+ def number_of_running_processes
47
+ result = `#{GREP_PROCESSES_COMMAND}`
48
+ raise "Could not grep for processes -> #{result}" if result.strip != "" && !$?.success?
49
+ result.split("\n").size
50
+ end
50
51
 
51
- # real time even if someone messed with timecop in tests
52
- def self.now
53
- if Time.respond_to?(:now_without_mock_time) # Timecop
54
- Time.now_without_mock_time
55
- else
56
- Time.now
52
+ # real time even if someone messed with timecop in tests
53
+ def now
54
+ if Time.respond_to?(:now_without_mock_time) # Timecop
55
+ Time.now_without_mock_time
56
+ else
57
+ Time.now
58
+ end
57
59
  end
58
60
  end
59
61
  end
@@ -96,7 +96,7 @@ BANNER
96
96
  opts.on("--group-by [TYPE]", <<-TEXT
97
97
  group tests by:
98
98
  found - order of finding files
99
- steps - number of cucumber steps
99
+ steps - number of cucumber/spinach steps
100
100
  default - runtime or filesize
101
101
  TEXT
102
102
  ) { |type| options[:group_by] = type.to_sym }
@@ -117,7 +117,7 @@ TEXT
117
117
 
118
118
  opts.on("-e", "--exec [COMMAND]", "execute this code parallel and with ENV['TEST_ENV_NUM']") { |path| options[:execute] = path }
119
119
  opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options") { |arg| options[:test_options] = arg }
120
- opts.on("-t", "--type [TYPE]", "test(default) / rspec / cucumber") do |type|
120
+ opts.on("-t", "--type [TYPE]", "test(default) / rspec / cucumber / spinach") do |type|
121
121
  begin
122
122
  @runner = load_runner(type)
123
123
  rescue NameError, LoadError => e
@@ -1,10 +1,10 @@
1
1
  require 'cucumber/formatter/rerun'
2
- require 'parallel_tests/cucumber/io'
2
+ require 'parallel_tests/gherkin/io'
3
3
 
4
4
  module ParallelTests
5
5
  module Cucumber
6
6
  class FailuresLogger < ::Cucumber::Formatter::Rerun
7
- include Io
7
+ include ParallelTests::Gherkin::Io
8
8
 
9
9
  def initialize(runtime, path_or_io, options)
10
10
  @io = prepare_io(path_or_io)
@@ -1,94 +1,11 @@
1
- require "parallel_tests/test/runner"
2
- require 'shellwords'
1
+ require "parallel_tests/gherkin/runner"
3
2
 
4
3
  module ParallelTests
5
4
  module Cucumber
6
- class Runner < ParallelTests::Test::Runner
7
- NAME = 'Cucumber'
8
-
9
- def self.run_tests(test_files, process_number, num_processes, options)
10
- sanitized_test_files = test_files.map { |val| Shellwords.escape(val) }
11
- options = options.merge(:env => {"AUTOTEST" => "1"}) if $stdout.tty? # display color when we are in a terminal
12
- runtime_logging = " --format ParallelTests::Cucumber::RuntimeLogger --out #{runtime_log}"
13
- cmd = [
14
- executable,
15
- (runtime_logging if File.directory?(File.dirname(runtime_log))),
16
- cucumber_opts(options[:test_options]),
17
- *sanitized_test_files
18
- ].compact.join(" ")
19
- execute_command(cmd, process_number, num_processes, options)
20
- end
21
-
22
- def self.determine_executable
23
- case
24
- when File.exists?("bin/cucumber")
25
- "bin/cucumber"
26
- when ParallelTests.bundler_enabled?
27
- "bundle exec cucumber"
28
- when File.file?("script/cucumber")
29
- "script/cucumber"
30
- else
31
- "cucumber"
32
- end
33
- end
34
-
35
- def self.runtime_log
36
- 'tmp/parallel_runtime_cucumber.log'
37
- end
38
-
39
- def self.test_file_name
40
- "feature"
41
- end
42
-
43
- def self.test_suffix
44
- ".feature"
45
- end
46
-
47
- def self.line_is_result?(line)
48
- line =~ /^\d+ (steps?|scenarios?)/
49
- end
50
-
51
- # cucumber has 2 result lines per test run, that cannot be added
52
- # 1 scenario (1 failed)
53
- # 1 step (1 failed)
54
- def self.summarize_results(results)
55
- sort_order = %w[scenario step failed undefined skipped pending passed]
56
-
57
- %w[scenario step].map do |group|
58
- group_results = results.grep /^\d+ #{group}/
59
- next if group_results.empty?
60
-
61
- sums = sum_up_results(group_results)
62
- sums = sums.sort_by { |word, _| sort_order.index(word) || 999 }
63
- sums.map! do |word, number|
64
- plural = "s" if word == group and number != 1
65
- "#{number} #{word}#{plural}"
66
- end
67
- "#{sums[0]} (#{sums[1..-1].join(", ")})"
68
- end.compact.join("\n")
69
- end
70
-
71
- def self.cucumber_opts(given)
72
- if given =~ /--profile/ or given =~ /(^|\s)-p /
73
- given
74
- else
75
- [given, profile_from_config].compact.join(" ")
76
- end
77
- end
78
-
79
- def self.profile_from_config
80
- # copied from https://github.com/cucumber/cucumber/blob/master/lib/cucumber/cli/profile_loader.rb#L85
81
- config = Dir.glob('{,.config/,config/}cucumber{.yml,.yaml}').first
82
- if config && File.read(config) =~ /^parallel:/
83
- "--profile parallel"
84
- end
85
- end
86
-
87
- def self.tests_in_groups(tests, num_groups, options={})
88
- if options[:group_by] == :steps
89
- Grouper.by_steps(find_tests(tests, options), num_groups, options)
90
- else
91
- super
5
+ class Runner < ParallelTests::Gherkin::Runner
6
+ class << self
7
+ def name
8
+ 'cucumber'
92
9
  end
93
10
  end
94
11
  end
@@ -1,7 +1,7 @@
1
1
  require 'parallel_tests'
2
2
 
3
3
  module ParallelTests
4
- module Cucumber
4
+ module Gherkin
5
5
  module Io
6
6
 
7
7
  def prepare_io(path_or_io)
@@ -1,8 +1,8 @@
1
1
  require 'gherkin'
2
2
 
3
3
  module ParallelTests
4
- module Cucumber
5
- class GherkinListener
4
+ module Gherkin
5
+ class Listener
6
6
  attr_reader :collect
7
7
 
8
8
  attr_writer :ignore_tag_pattern
@@ -0,0 +1,102 @@
1
+ require "parallel_tests/test/runner"
2
+ require 'shellwords'
3
+
4
+ module ParallelTests
5
+ module Gherkin
6
+ class Runner < ParallelTests::Test::Runner
7
+
8
+ class << self
9
+ def run_tests(test_files, process_number, num_processes, options)
10
+ sanitized_test_files = test_files.map { |val| Shellwords.escape(val) }
11
+ options = options.merge(:env => {"AUTOTEST" => "1"}) if $stdout.tty? # display color when we are in a terminal
12
+ cmd = [
13
+ executable,
14
+ (runtime_logging if File.directory?(File.dirname(runtime_log))),
15
+ cucumber_opts(options[:test_options]),
16
+ *sanitized_test_files
17
+ ].compact.join(" ")
18
+ execute_command(cmd, process_number, num_processes, options)
19
+ end
20
+
21
+ def test_file_name
22
+ "feature"
23
+ end
24
+
25
+ def test_suffix
26
+ ".feature"
27
+ end
28
+
29
+ def line_is_result?(line)
30
+ line =~ /^\d+ (steps?|scenarios?)/
31
+ end
32
+
33
+ # cucumber has 2 result lines per test run, that cannot be added
34
+ # 1 scenario (1 failed)
35
+ # 1 step (1 failed)
36
+ def summarize_results(results)
37
+ sort_order = %w[scenario step failed undefined skipped pending passed]
38
+
39
+ %w[scenario step].map do |group|
40
+ group_results = results.grep /^\d+ #{group}/
41
+ next if group_results.empty?
42
+
43
+ sums = sum_up_results(group_results)
44
+ sums = sums.sort_by { |word, _| sort_order.index(word) || 999 }
45
+ sums.map! do |word, number|
46
+ plural = "s" if word == group and number != 1
47
+ "#{number} #{word}#{plural}"
48
+ end
49
+ "#{sums[0]} (#{sums[1..-1].join(", ")})"
50
+ end.compact.join("\n")
51
+ end
52
+
53
+ def cucumber_opts(given)
54
+ if given =~ /--profile/ or given =~ /(^|\s)-p /
55
+ given
56
+ else
57
+ [given, profile_from_config].compact.join(" ")
58
+ end
59
+ end
60
+
61
+ def profile_from_config
62
+ # copied from https://github.com/cucumber/cucumber/blob/master/lib/cucumber/cli/profile_loader.rb#L85
63
+ config = Dir.glob("{,.config/,config/}#{name}{.yml,.yaml}").first
64
+ if config && File.read(config) =~ /^parallel:/
65
+ "--profile parallel"
66
+ end
67
+ end
68
+
69
+ def tests_in_groups(tests, num_groups, options={})
70
+ if options[:group_by] == :steps
71
+ Grouper.by_steps(find_tests(tests, options), num_groups, options)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+
78
+ def runtime_logging
79
+ " --format ParallelTests::Gherkin::RuntimeLogger --out #{runtime_log}"
80
+ end
81
+
82
+ def runtime_log
83
+ "tmp/parallel_runtime_#{name}.log"
84
+ end
85
+
86
+ def determine_executable
87
+ case
88
+ when File.exists?("bin/#{name}")
89
+ "bin/#{name}"
90
+ when ParallelTests.bundler_enabled?
91
+ "bundle exec #{name}"
92
+ when File.file?("script/#{name}")
93
+ "script/#{name}"
94
+ else
95
+ "#{name}"
96
+ end
97
+ end
98
+
99
+ end
100
+ end
101
+ end
102
+ end