parallel_tests 0.13.3 → 0.14.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/Gemfile +1 -0
- data/Gemfile.lock +8 -2
- data/Rakefile +5 -1
- data/Readme.md +5 -2
- data/bin/parallel_spinach +5 -0
- data/lib/parallel_tests.rb +40 -38
- data/lib/parallel_tests/cli.rb +2 -2
- data/lib/parallel_tests/cucumber/failures_logger.rb +2 -2
- data/lib/parallel_tests/cucumber/runner.rb +5 -88
- data/lib/parallel_tests/{cucumber → gherkin}/io.rb +1 -1
- data/lib/parallel_tests/{cucumber/gherkin_listener.rb → gherkin/listener.rb} +2 -2
- data/lib/parallel_tests/gherkin/runner.rb +102 -0
- data/lib/parallel_tests/{cucumber → gherkin}/runtime_logger.rb +2 -2
- data/lib/parallel_tests/grouper.rb +41 -41
- data/lib/parallel_tests/rspec/failures_logger.rb +1 -1
- data/lib/parallel_tests/rspec/runner.rb +50 -48
- data/lib/parallel_tests/spinach/runner.rb +19 -0
- data/lib/parallel_tests/tasks.rb +7 -3
- data/lib/parallel_tests/test/runner.rb +125 -123
- data/lib/parallel_tests/test/runtime_logger.rb +57 -53
- data/lib/parallel_tests/version.rb +1 -1
- data/parallel_tests.gemspec +2 -2
- data/spec/integration_spec.rb +61 -0
- data/spec/parallel_tests/cucumber/failure_logger_spec.rb +1 -1
- data/spec/parallel_tests/cucumber/runner_spec.rb +5 -172
- data/spec/parallel_tests/{cucumber/gherkin_listener_spec.rb → gherkin/listener_spec.rb} +3 -3
- data/spec/parallel_tests/gherkin/runner_behaviour.rb +177 -0
- data/spec/parallel_tests/rspec/{failure_logger_spec.rb → failures_logger_spec.rb} +0 -0
- data/spec/parallel_tests/spinach/runner_spec.rb +12 -0
- data/spec/parallel_tests/test/runtime_logger_spec.rb +1 -1
- data/spec/parallel_tests_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- metadata +16 -10
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
parallel_tests (0.
|
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.
|
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
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
|
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/>
|
data/lib/parallel_tests.rb
CHANGED
@@ -8,52 +8,54 @@ module ParallelTests
|
|
8
8
|
autoload :VERSION, "parallel_tests/version"
|
9
9
|
autoload :Grouper, "parallel_tests/grouper"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
24
|
+
previous = nil
|
25
|
+
current = File.expand_path(Dir.pwd)
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
33
|
+
false
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def first_process?
|
37
|
+
!ENV["TEST_ENV_NUMBER"] || ENV["TEST_ENV_NUMBER"].to_i == 0
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
data/lib/parallel_tests/cli.rb
CHANGED
@@ -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/
|
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/
|
2
|
-
require 'shellwords'
|
1
|
+
require "parallel_tests/gherkin/runner"
|
3
2
|
|
4
3
|
module ParallelTests
|
5
4
|
module Cucumber
|
6
|
-
class Runner < ParallelTests::
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
@@ -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
|