parallel_tests 0.16.3 → 0.16.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +14 -15
- data/Readme.md +1 -1
- data/lib/parallel_tests/cli.rb +1 -0
- data/lib/parallel_tests/cucumber/scenario_line_logger.rb +51 -0
- data/lib/parallel_tests/cucumber/scenarios.rb +36 -0
- data/lib/parallel_tests/gherkin/runner.rb +16 -12
- data/lib/parallel_tests/grouper.rb +26 -8
- data/lib/parallel_tests/version.rb +1 -1
- data/spec/integration_spec.rb +30 -0
- data/spec/parallel_tests/cucumber/scenarios_spec.rb +59 -0
- data/spec/parallel_tests/grouper_spec.rb +9 -0
- data/spec/parallel_tests/tasks_spec.rb +9 -0
- data/spec/parallel_tests/test/runner_spec.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1e3fb69bc5868e4a5321edbbc4f4527aaa7d277
|
4
|
+
data.tar.gz: f6dc34a502ef7a65f565ecff44bf0990154d2ed3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92e953b15c06a785571f9c1aafc3fcdfb4ae6adf03cd9810215079485a9780a6cc280e2052aa857ee211697930c3b0530f1c1366ac5e560052a87c7f6ab008e8
|
7
|
+
data.tar.gz: df03a89e62a972b117a4e8b2ac9be3ebdd296ae6031c9ff8b392d8743a95ee96eb06e001e2da41b00e58cc2d7b6c1f53367fe41ce359840fefe2d450d18ea0ee
|
data/Gemfile.lock
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
parallel_tests (0.16.
|
4
|
+
parallel_tests (0.16.4)
|
5
5
|
parallel
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
builder (3.
|
10
|
+
builder (3.2.2)
|
11
11
|
bump (0.3.8)
|
12
12
|
colorize (0.5.8)
|
13
|
-
cucumber (1.
|
13
|
+
cucumber (1.3.10)
|
14
14
|
builder (>= 2.1.2)
|
15
|
-
diff-lcs (>= 1.1.
|
16
|
-
gherkin (~> 2.
|
17
|
-
|
18
|
-
|
19
|
-
diff-lcs (1.2.
|
20
|
-
gherkin (2.
|
21
|
-
|
22
|
-
gherkin (2.
|
23
|
-
|
15
|
+
diff-lcs (>= 1.1.3)
|
16
|
+
gherkin (~> 2.12)
|
17
|
+
multi_json (>= 1.7.5, < 2.0)
|
18
|
+
multi_test (>= 0.0.2)
|
19
|
+
diff-lcs (1.2.5)
|
20
|
+
gherkin (2.12.2)
|
21
|
+
multi_json (~> 1.3)
|
22
|
+
gherkin (2.12.2-java)
|
23
|
+
multi_json (~> 1.3)
|
24
24
|
gherkin-ruby (0.3.0)
|
25
|
-
|
26
|
-
|
25
|
+
multi_json (1.8.2)
|
26
|
+
multi_test (0.0.2)
|
27
27
|
parallel (0.9.0)
|
28
28
|
rake (10.0.3)
|
29
29
|
rspec (2.13.0)
|
@@ -37,7 +37,6 @@ GEM
|
|
37
37
|
spinach (0.8.3)
|
38
38
|
colorize (= 0.5.8)
|
39
39
|
gherkin-ruby (~> 0.3.0)
|
40
|
-
term-ansicolor (1.0.7)
|
41
40
|
test-unit (2.4.4)
|
42
41
|
|
43
42
|
PLATFORMS
|
data/Readme.md
CHANGED
@@ -213,7 +213,7 @@ TIPS
|
|
213
213
|
- [RSpec] Instantly see failures (instead of just a red F) with [rspec-instafail](https://github.com/grosser/rspec-instafail)
|
214
214
|
- [Bundler] if you have a `Gemfile` then `bundle exec` will be used to run tests
|
215
215
|
- [Cucumber] add a `parallel: foo` profile to your `config/cucumber.yml` and it will be used to run parallel tests
|
216
|
-
- [Cucumber] Pass in cucumber options by not giving the options an identifier ex: parallel:features[
|
216
|
+
- [Cucumber] Pass in cucumber options by not giving the options an identifier ex: `rake parallel:features[,,'cucumber_opts']`
|
217
217
|
- [Capybara setup](https://github.com/grosser/parallel_tests/wiki)
|
218
218
|
- [Sphinx setup](https://github.com/grosser/parallel_tests/wiki)
|
219
219
|
- [Capistrano setup](https://github.com/grosser/parallel_tests/wiki/Remotely-with-capistrano) let your tests run on a big box instead of your laptop
|
data/lib/parallel_tests/cli.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'gherkin/tag_expression'
|
2
|
+
|
3
|
+
module ParallelTests
|
4
|
+
module Cucumber
|
5
|
+
module Formatters
|
6
|
+
class ScenarioLineLogger
|
7
|
+
attr_reader :scenarios
|
8
|
+
|
9
|
+
def initialize(tag_expression = ::Gherkin::TagExpression.new([]))
|
10
|
+
@scenarios = []
|
11
|
+
@tag_expression = tag_expression
|
12
|
+
end
|
13
|
+
|
14
|
+
def visit_feature_element(feature_element)
|
15
|
+
return unless @tag_expression.evaluate(feature_element.source_tags)
|
16
|
+
|
17
|
+
case feature_element
|
18
|
+
when ::Cucumber::Ast::Scenario
|
19
|
+
line = if feature_element.respond_to?(:line)
|
20
|
+
feature_element.line
|
21
|
+
else
|
22
|
+
feature_element.instance_variable_get(:@line)
|
23
|
+
end
|
24
|
+
@scenarios << [feature_element.feature.file, line].join(":")
|
25
|
+
when ::Cucumber::Ast::ScenarioOutline
|
26
|
+
sections = feature_element.instance_variable_get(:@example_sections)
|
27
|
+
sections.each { |section|
|
28
|
+
rows = if section[1].respond_to?(:rows)
|
29
|
+
section[1].rows
|
30
|
+
else
|
31
|
+
section[1].instance_variable_get(:@rows)
|
32
|
+
end
|
33
|
+
rows.each_with_index { |row, index|
|
34
|
+
next if index == 0 # slices didn't work with jruby data structure
|
35
|
+
line = if row.respond_to?(:line)
|
36
|
+
row.line
|
37
|
+
else
|
38
|
+
row.instance_variable_get(:@line)
|
39
|
+
end
|
40
|
+
@scenarios << [feature_element.feature.file, line].join(":")
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def method_missing(*args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'gherkin/tag_expression'
|
2
|
+
require 'cucumber/runtime'
|
3
|
+
require 'cucumber'
|
4
|
+
require 'parallel_tests/cucumber/scenario_line_logger'
|
5
|
+
require 'parallel_tests/gherkin/listener'
|
6
|
+
|
7
|
+
module ParallelTests
|
8
|
+
module Cucumber
|
9
|
+
class Scenarios
|
10
|
+
class << self
|
11
|
+
def all(files, options={})
|
12
|
+
tag_expressions = if options[:ignore_tag_pattern]
|
13
|
+
options[:ignore_tag_pattern].split(/\s*,\s*/).map {|tag| "~#{tag}" }
|
14
|
+
else
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
split_into_scenarios files, tag_expressions
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def split_into_scenarios(files, tags=[])
|
23
|
+
tag_expression = ::Gherkin::TagExpression.new(tags)
|
24
|
+
scenario_line_logger = ParallelTests::Cucumber::Formatters::ScenarioLineLogger.new(tag_expression)
|
25
|
+
loader = ::Cucumber::Runtime::FeaturesLoader.new(files, [], tag_expression)
|
26
|
+
|
27
|
+
loader.features.each do |feature|
|
28
|
+
feature.accept(scenario_line_logger)
|
29
|
+
end
|
30
|
+
|
31
|
+
scenario_line_logger.scenarios
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -19,7 +19,7 @@ module ParallelTests
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_file_name
|
22
|
-
"feature"
|
22
|
+
@test_file_name || "feature"
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_suffix
|
@@ -37,7 +37,7 @@ module ParallelTests
|
|
37
37
|
sort_order = %w[scenario step failed undefined skipped pending passed]
|
38
38
|
|
39
39
|
%w[scenario step].map do |group|
|
40
|
-
group_results = results.grep
|
40
|
+
group_results = results.grep(/^\d+ #{group}/)
|
41
41
|
next if group_results.empty?
|
42
42
|
|
43
43
|
sums = sum_up_results(group_results)
|
@@ -67,8 +67,12 @@ module ParallelTests
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def tests_in_groups(tests, num_groups, options={})
|
70
|
-
if options[:group_by] == :
|
71
|
-
|
70
|
+
if options[:group_by] == :scenarios
|
71
|
+
@test_file_name = "scenario"
|
72
|
+
end
|
73
|
+
method = "by_#{options[:group_by]}"
|
74
|
+
if Grouper.respond_to?(method)
|
75
|
+
Grouper.send(method, find_tests(tests, options), num_groups, options)
|
72
76
|
else
|
73
77
|
super
|
74
78
|
end
|
@@ -85,14 +89,14 @@ module ParallelTests
|
|
85
89
|
|
86
90
|
def determine_executable
|
87
91
|
case
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
when File.exists?("bin/#{name}")
|
93
|
+
"bin/#{name}"
|
94
|
+
when ParallelTests.bundler_enabled?
|
95
|
+
"bundle exec #{name}"
|
96
|
+
when File.file?("script/#{name}")
|
97
|
+
"script/#{name}"
|
98
|
+
else
|
99
|
+
"#{name}"
|
96
100
|
end
|
97
101
|
end
|
98
102
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'parallel_tests/cucumber/scenarios'
|
2
|
+
|
1
3
|
module ParallelTests
|
2
4
|
class Grouper
|
3
5
|
class << self
|
@@ -6,22 +8,22 @@ module ParallelTests
|
|
6
8
|
in_even_groups_by_size(features_with_steps, num_groups)
|
7
9
|
end
|
8
10
|
|
9
|
-
def
|
11
|
+
def by_scenarios(tests, num_groups, options={})
|
12
|
+
scenarios = group_by_scenarios(tests, options)
|
13
|
+
in_even_groups_by_size(scenarios, num_groups)
|
14
|
+
end
|
15
|
+
|
16
|
+
def in_even_groups_by_size(items, num_groups, options= {})
|
10
17
|
groups = Array.new(num_groups) { {:items => [], :size => 0} }
|
11
18
|
|
12
19
|
# add all files that should run in a single process to one group
|
13
20
|
(options[:single_process] || []).each do |pattern|
|
14
|
-
matched,
|
21
|
+
matched, items = items.partition { |item, size| item =~ pattern }
|
15
22
|
matched.each { |item, size| add_to_group(groups.first, item, size) }
|
16
23
|
end
|
17
24
|
|
18
25
|
groups_to_fill = (options[:isolate] ? groups[1..-1] : groups)
|
19
|
-
|
20
|
-
# add all other files
|
21
|
-
largest_first(items_with_sizes).each do |item, size|
|
22
|
-
smallest = smallest_group(groups_to_fill)
|
23
|
-
add_to_group(smallest, item, size)
|
24
|
-
end
|
26
|
+
group_features_by_size(items_to_group(items), groups_to_fill)
|
25
27
|
|
26
28
|
groups.map!{|g| g[:items].sort }
|
27
29
|
end
|
@@ -51,6 +53,22 @@ module ParallelTests
|
|
51
53
|
}
|
52
54
|
listener.collect.sort_by{|_,value| -value }
|
53
55
|
end
|
56
|
+
|
57
|
+
def group_by_scenarios(tests, options={})
|
58
|
+
ParallelTests::Cucumber::Scenarios.all(tests, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def group_features_by_size(items, groups_to_fill)
|
62
|
+
items.each do |item, size|
|
63
|
+
size ||= 1
|
64
|
+
smallest = smallest_group(groups_to_fill)
|
65
|
+
add_to_group(smallest, item, size)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def items_to_group(items)
|
70
|
+
items.first && items.first.size == 2 ? largest_first(items) : items
|
71
|
+
end
|
54
72
|
end
|
55
73
|
end
|
56
74
|
end
|
data/spec/integration_spec.rb
CHANGED
@@ -304,6 +304,36 @@ cucumber features/fail1.feature:2 # Scenario: xxx
|
|
304
304
|
3 steps (2 failed, 1 passed)
|
305
305
|
"""
|
306
306
|
end
|
307
|
+
|
308
|
+
it "groups by scenario" do
|
309
|
+
write "features/long.feature", <<-EOS
|
310
|
+
Feature: xxx
|
311
|
+
Scenario: xxx
|
312
|
+
Given I print TEST_ENV_NUMBER
|
313
|
+
|
314
|
+
Scenario: xxx
|
315
|
+
Given I print TEST_ENV_NUMBER
|
316
|
+
|
317
|
+
Scenario Outline: xxx
|
318
|
+
Given I print TEST_ENV_NUMBER
|
319
|
+
|
320
|
+
Examples:
|
321
|
+
| num |
|
322
|
+
| one |
|
323
|
+
| two |
|
324
|
+
EOS
|
325
|
+
result = run_tests "features", :type => "cucumber", :add => "--group-by scenarios"
|
326
|
+
result.should include("2 processes for 4 scenarios")
|
327
|
+
end
|
328
|
+
|
329
|
+
it "groups by step" do
|
330
|
+
write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
331
|
+
write "features/good2.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
332
|
+
|
333
|
+
result = run_tests "features", :type => "cucumber", :add => '--group-by steps'
|
334
|
+
|
335
|
+
result.should include("2 processes for 2 features")
|
336
|
+
end
|
307
337
|
end
|
308
338
|
|
309
339
|
context "Spinach", :fails_on_ruby_187 => true do
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'parallel_tests/cucumber/scenarios'
|
3
|
+
|
4
|
+
module ParallelTests
|
5
|
+
module Cucumber
|
6
|
+
describe Scenarios do
|
7
|
+
describe '.all' do
|
8
|
+
context 'by default' do
|
9
|
+
let(:feature_file) do
|
10
|
+
Tempfile.new('grouper.feature').tap do |feature|
|
11
|
+
feature.write <<-EOS
|
12
|
+
Feature: Grouping by scenario
|
13
|
+
|
14
|
+
Scenario: First
|
15
|
+
Given I do nothing
|
16
|
+
|
17
|
+
Scenario: Second
|
18
|
+
Given I don't do anything
|
19
|
+
EOS
|
20
|
+
feature.rewind
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns all the scenarios' do
|
25
|
+
scenarios = Scenarios.all([feature_file.path])
|
26
|
+
scenarios.should eq %W(#{feature_file.path}:3 #{feature_file.path}:6)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with tags' do
|
31
|
+
let(:feature_file) do
|
32
|
+
Tempfile.new('grouper.feature').tap do |feature|
|
33
|
+
feature.write <<-EOS
|
34
|
+
Feature: Grouping by scenario
|
35
|
+
|
36
|
+
@wip
|
37
|
+
Scenario: First
|
38
|
+
Given I do nothing
|
39
|
+
|
40
|
+
Scenario: Second
|
41
|
+
Given I don't do anything
|
42
|
+
|
43
|
+
@ignore
|
44
|
+
Scenario: Third
|
45
|
+
Given I am ignored
|
46
|
+
EOS
|
47
|
+
feature.rewind
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'ignores those scenarios' do
|
52
|
+
scenarios = Scenarios.all([feature_file.path], :ignore_tag_pattern => '@ignore, @wip')
|
53
|
+
scenarios.should eq %W(#{feature_file.path}:7)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -49,4 +49,13 @@ describe ParallelTests::Grouper do
|
|
49
49
|
call(6).should == [["5"], ["4"], ["3"], ["2"], ["1"], []]
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
describe :by_scenarios do
|
54
|
+
let(:feature_file) { double 'file' }
|
55
|
+
|
56
|
+
it 'splits a feature into individual scenarios' do
|
57
|
+
ParallelTests::Cucumber::Scenarios.should_receive(:all).and_return({ 'feature_file:3' => 1 })
|
58
|
+
ParallelTests::Grouper.by_scenarios([feature_file], 1)
|
59
|
+
end
|
60
|
+
end
|
52
61
|
end
|
@@ -22,6 +22,15 @@ describe ParallelTests::Tasks do
|
|
22
22
|
args = {:count => 2, :pattern => "plain", :options => "-p default"}
|
23
23
|
ParallelTests::Tasks.parse_args(args).should == [2, "plain", "-p default"]
|
24
24
|
end
|
25
|
+
|
26
|
+
it "should return the count, pattern, and options" do
|
27
|
+
args = {
|
28
|
+
:count => 2,
|
29
|
+
:pattern => "plain",
|
30
|
+
:options => "-p default --group-by steps",
|
31
|
+
}
|
32
|
+
ParallelTests::Tasks.parse_args(args).should == [2, "plain", "-p default --group-by steps"]
|
33
|
+
end
|
25
34
|
end
|
26
35
|
|
27
36
|
describe ".rails_env" do
|
@@ -43,7 +43,7 @@ describe ParallelTests::Test::Runner do
|
|
43
43
|
|
44
44
|
it "does sort when not passed do_sort option" do
|
45
45
|
ParallelTests::Test::Runner.stub!(:tests_with_runtime).and_return([])
|
46
|
-
ParallelTests::Grouper.should_receive(:
|
46
|
+
ParallelTests::Grouper.should_receive(:group_features_by_size).and_return([])
|
47
47
|
call([], 1)
|
48
48
|
end
|
49
49
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parallel_tests
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parallel
|
@@ -50,6 +50,8 @@ files:
|
|
50
50
|
- lib/parallel_tests/cli.rb
|
51
51
|
- lib/parallel_tests/cucumber/failures_logger.rb
|
52
52
|
- lib/parallel_tests/cucumber/runner.rb
|
53
|
+
- lib/parallel_tests/cucumber/scenario_line_logger.rb
|
54
|
+
- lib/parallel_tests/cucumber/scenarios.rb
|
53
55
|
- lib/parallel_tests/gherkin/io.rb
|
54
56
|
- lib/parallel_tests/gherkin/listener.rb
|
55
57
|
- lib/parallel_tests/gherkin/runner.rb
|
@@ -71,6 +73,7 @@ files:
|
|
71
73
|
- spec/parallel_tests/cli_spec.rb
|
72
74
|
- spec/parallel_tests/cucumber/failure_logger_spec.rb
|
73
75
|
- spec/parallel_tests/cucumber/runner_spec.rb
|
76
|
+
- spec/parallel_tests/cucumber/scenarios_spec.rb
|
74
77
|
- spec/parallel_tests/gherkin/listener_spec.rb
|
75
78
|
- spec/parallel_tests/gherkin/runner_behaviour.rb
|
76
79
|
- spec/parallel_tests/grouper_spec.rb
|
@@ -104,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
107
|
version: '0'
|
105
108
|
requirements: []
|
106
109
|
rubyforge_project:
|
107
|
-
rubygems_version: 2.0.
|
110
|
+
rubygems_version: 2.0.3
|
108
111
|
signing_key:
|
109
112
|
specification_version: 4
|
110
113
|
summary: Run Test::Unit / RSpec / Cucumber / Spinach in parallel
|