vinted-parallel_tests 0.13.3
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +48 -0
- data/Rakefile +6 -0
- data/Readme.md +293 -0
- data/ReadmeRails2.md +48 -0
- data/bin/parallel_cucumber +5 -0
- data/bin/parallel_rspec +5 -0
- data/bin/parallel_test +5 -0
- data/lib/parallel_tests/cli.rb +187 -0
- data/lib/parallel_tests/cucumber/failures_logger.rb +25 -0
- data/lib/parallel_tests/cucumber/gherkin_listener.rb +82 -0
- data/lib/parallel_tests/cucumber/io.rb +41 -0
- data/lib/parallel_tests/cucumber/runner.rb +98 -0
- data/lib/parallel_tests/cucumber/runtime_logger.rb +28 -0
- data/lib/parallel_tests/grouper.rb +56 -0
- data/lib/parallel_tests/railtie.rb +8 -0
- data/lib/parallel_tests/rspec/failures_logger.rb +44 -0
- data/lib/parallel_tests/rspec/logger_base.rb +52 -0
- data/lib/parallel_tests/rspec/runner.rb +72 -0
- data/lib/parallel_tests/rspec/runtime_logger.rb +54 -0
- data/lib/parallel_tests/rspec/summary_logger.rb +19 -0
- data/lib/parallel_tests/tasks.rb +139 -0
- data/lib/parallel_tests/test/runner.rb +168 -0
- data/lib/parallel_tests/test/runtime_logger.rb +97 -0
- data/lib/parallel_tests/version.rb +3 -0
- data/lib/parallel_tests.rb +61 -0
- data/parallel_tests.gemspec +14 -0
- data/spec/integration_spec.rb +285 -0
- data/spec/parallel_tests/cli_spec.rb +71 -0
- data/spec/parallel_tests/cucumber/failure_logger_spec.rb +43 -0
- data/spec/parallel_tests/cucumber/gherkin_listener_spec.rb +97 -0
- data/spec/parallel_tests/cucumber/runner_spec.rb +179 -0
- data/spec/parallel_tests/grouper_spec.rb +52 -0
- data/spec/parallel_tests/rspec/failures_logger_spec.rb +82 -0
- data/spec/parallel_tests/rspec/runner_spec.rb +187 -0
- data/spec/parallel_tests/rspec/runtime_logger_spec.rb +126 -0
- data/spec/parallel_tests/rspec/summary_logger_spec.rb +37 -0
- data/spec/parallel_tests/tasks_spec.rb +151 -0
- data/spec/parallel_tests/test/runner_spec.rb +413 -0
- data/spec/parallel_tests/test/runtime_logger_spec.rb +90 -0
- data/spec/parallel_tests_spec.rb +137 -0
- data/spec/spec_helper.rb +157 -0
- metadata +110 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe 'CLI' do
|
|
6
|
+
before do
|
|
7
|
+
`rm -rf #{folder}`
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after do
|
|
11
|
+
`rm -rf #{folder}`
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def folder
|
|
15
|
+
"/tmp/parallel_tests_tests"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def write(file, content)
|
|
19
|
+
path = "#{folder}/#{file}"
|
|
20
|
+
ensure_folder File.dirname(path)
|
|
21
|
+
File.open(path, 'w'){|f| f.write content }
|
|
22
|
+
path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def read(file)
|
|
26
|
+
File.read "#{folder}/#{file}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def bin_folder
|
|
30
|
+
"#{File.expand_path(File.dirname(__FILE__))}/../bin"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def executable(options={})
|
|
34
|
+
"#{bin_folder}/parallel_#{options[:type] || 'test'}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ensure_folder(folder)
|
|
38
|
+
`mkdir -p #{folder}` unless File.exist?(folder)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def run_tests(test_folder, options={})
|
|
42
|
+
ensure_folder folder
|
|
43
|
+
processes = "-n #{options[:processes]||2}" unless options[:processes] == false
|
|
44
|
+
command = "cd #{folder} && #{options[:export]} #{executable(options)} #{test_folder} #{processes} #{options[:add]} 2>&1"
|
|
45
|
+
result = `#{command}`
|
|
46
|
+
raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail]
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "runs tests in parallel" do
|
|
51
|
+
write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
|
|
52
|
+
write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){puts "TEST2"}}'
|
|
53
|
+
result = run_tests "spec", :type => 'rspec'
|
|
54
|
+
|
|
55
|
+
# test ran and gave their puts
|
|
56
|
+
result.should include('TEST1')
|
|
57
|
+
result.should include('TEST2')
|
|
58
|
+
|
|
59
|
+
# all results present
|
|
60
|
+
result.scan('1 example, 0 failure').size.should == 2 # 2 results
|
|
61
|
+
result.scan('2 examples, 0 failures').size.should == 1 # 1 summary
|
|
62
|
+
result.scan(/Finished in \d+\.\d+ seconds/).size.should == 2
|
|
63
|
+
result.scan(/Took \d+\.\d+ seconds/).size.should == 1 # parallel summary
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "runs tests which outputs accented characters" do
|
|
67
|
+
write "spec/xxx_spec.rb", "#encoding: utf-8\ndescribe('it'){it('should'){puts 'Byłem tu'}}"
|
|
68
|
+
result = run_tests "spec", :type => 'rspec'
|
|
69
|
+
# test ran and gave their puts
|
|
70
|
+
result.should include('Byłem tu')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "does not run any tests if there are none" do
|
|
74
|
+
write 'spec/xxx_spec.rb', '1'
|
|
75
|
+
result = run_tests "spec", :type => 'rspec'
|
|
76
|
+
result.should include('No examples found')
|
|
77
|
+
result.should include('Took')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "fails when tests fail" do
|
|
81
|
+
write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
|
|
82
|
+
write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){1.should == 2}}'
|
|
83
|
+
result = run_tests "spec", :fail => true, :type => 'rspec'
|
|
84
|
+
|
|
85
|
+
result.scan('1 example, 1 failure').size.should == 1
|
|
86
|
+
result.scan('1 example, 0 failure').size.should == 1
|
|
87
|
+
result.scan('2 examples, 1 failure').size.should == 1
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "can serialize stdout" do
|
|
91
|
+
write 'spec/xxx_spec.rb', '5.times{describe("it"){it("should"){sleep 0.01; puts "TEST1"}}}'
|
|
92
|
+
write 'spec/xxx2_spec.rb', 'sleep 0.01; 5.times{describe("it"){it("should"){sleep 0.01; puts "TEST2"}}}'
|
|
93
|
+
result = run_tests "spec", :type => 'rspec', :add => "--serialize-stdout"
|
|
94
|
+
|
|
95
|
+
result.should_not =~ /TEST1.*TEST2.*TEST1/m
|
|
96
|
+
result.should_not =~ /TEST2.*TEST1.*TEST2/m
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context "with given commands" do
|
|
100
|
+
it "can exec given commands with ENV['TEST_ENV_NUM']" do
|
|
101
|
+
result = `#{executable} -e 'ruby -e "print ENV[:TEST_ENV_NUMBER.to_s].to_i"' -n 4`
|
|
102
|
+
result.gsub('"','').split('').sort.should == %w[0 2 3 4]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "can exec given command non-parallel" do
|
|
106
|
+
result = `#{executable} -e 'ruby -e "sleep(rand(10)/100.0); puts ENV[:TEST_ENV_NUMBER.to_s].inspect"' -n 4 --non-parallel`
|
|
107
|
+
result.split("\n").should == %w["" "2" "3" "4"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "can serialize stdout" do
|
|
111
|
+
result = `#{executable} -e 'ruby -e "5.times{sleep 0.01;puts ENV[:TEST_ENV_NUMBER.to_s].to_i;STDOUT.flush}"' -n 2 --serialize-stdout`
|
|
112
|
+
result.should_not =~ /0.*2.*0/m
|
|
113
|
+
result.should_not =~ /2.*0.*2/m
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "exists with success if all sub-processes returned success" do
|
|
117
|
+
system("#{executable} -e 'cat /dev/null' -n 4").should == true
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "exists with failure if any sub-processes returned failure" do
|
|
121
|
+
system("#{executable} -e 'test -e xxxx' -n 4").should == false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "runs through parallel_rspec" do
|
|
126
|
+
version = `#{executable} -v`
|
|
127
|
+
`#{bin_folder}/parallel_rspec -v`.should == version
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "runs through parallel_cucumber" do
|
|
131
|
+
version = `#{executable} -v`
|
|
132
|
+
`#{bin_folder}/parallel_cucumber -v`.should == version
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "runs with --group-by found" do
|
|
136
|
+
# it only tests that it does not blow up, as it did before fixing...
|
|
137
|
+
write "spec/x1_spec.rb", "puts '111'"
|
|
138
|
+
run_tests "spec", :type => 'rspec', :add => '--group-by found'
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "runs faster with more processes" do
|
|
142
|
+
pending if RUBY_PLATFORM == "java" # just too slow ...
|
|
143
|
+
2.times{|i|
|
|
144
|
+
write "spec/xxx#{i}_spec.rb", 'describe("it"){it("should"){sleep 5}}; $stderr.puts ENV["TEST_ENV_NUMBER"]'
|
|
145
|
+
}
|
|
146
|
+
t = Time.now
|
|
147
|
+
run_tests("spec", :processes => 2, :type => 'rspec')
|
|
148
|
+
expected = 10
|
|
149
|
+
(Time.now - t).should <= expected
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "can run with given files" do
|
|
153
|
+
write "spec/x1_spec.rb", "puts '111'"
|
|
154
|
+
write "spec/x2_spec.rb", "puts '222'"
|
|
155
|
+
write "spec/x3_spec.rb", "puts '333'"
|
|
156
|
+
result = run_tests "spec/x1_spec.rb spec/x3_spec.rb", :type => 'rspec'
|
|
157
|
+
result.should include('111')
|
|
158
|
+
result.should include('333')
|
|
159
|
+
result.should_not include('222')
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "runs successfully without any files" do
|
|
163
|
+
results = run_tests "", :type => 'rspec'
|
|
164
|
+
results.should include("2 processes for 0 specs")
|
|
165
|
+
results.should include("Took")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "can run with test-options" do
|
|
169
|
+
write "spec/x1_spec.rb", "111"
|
|
170
|
+
write "spec/x2_spec.rb", "111"
|
|
171
|
+
result = run_tests "spec",
|
|
172
|
+
:add => "--test-options ' --version'",
|
|
173
|
+
:processes => 2,
|
|
174
|
+
:type => 'rspec'
|
|
175
|
+
result.should =~ /\d+\.\d+\.\d+.*\d+\.\d+\.\d+/m # prints version twice
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "runs with PARALLEL_TEST_PROCESSORS processes" do
|
|
179
|
+
processes = 5
|
|
180
|
+
processes.times{|i|
|
|
181
|
+
write "spec/x#{i}_spec.rb", "puts %{ENV-\#{ENV['TEST_ENV_NUMBER']}-}"
|
|
182
|
+
}
|
|
183
|
+
result = run_tests "spec",
|
|
184
|
+
:export => "PARALLEL_TEST_PROCESSORS=#{processes}",
|
|
185
|
+
:processes => processes,
|
|
186
|
+
:type => 'rspec'
|
|
187
|
+
result.scan(/ENV-.?-/).should =~ ["ENV--", "ENV-2-", "ENV-3-", "ENV-4-", "ENV-5-"]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "filters test by given pattern and relative paths" do
|
|
191
|
+
write "spec/x_spec.rb", "puts 'XXX'"
|
|
192
|
+
write "spec/y_spec.rb", "puts 'YYY'"
|
|
193
|
+
write "spec/z_spec.rb", "puts 'ZZZ'"
|
|
194
|
+
result = run_tests "spec", :add => "-p '^spec/(x|z)'", :type => "rspec"
|
|
195
|
+
result.should include('XXX')
|
|
196
|
+
result.should_not include('YYY')
|
|
197
|
+
result.should include('ZZZ')
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "can wait_for_other_processes_to_finish" do
|
|
201
|
+
pending if RUBY_PLATFORM == "java" # just too slow ...
|
|
202
|
+
write "test/a_test.rb", "require 'parallel_tests'; sleep 0.5 ; ParallelTests.wait_for_other_processes_to_finish; puts 'a'"
|
|
203
|
+
write "test/b_test.rb", "sleep 1; puts 'b'"
|
|
204
|
+
write "test/c_test.rb", "sleep 1.5; puts 'c'"
|
|
205
|
+
write "test/d_test.rb", "sleep 2; puts 'd'"
|
|
206
|
+
run_tests("test", :processes => 4).should include("b\nc\nd\na\n")
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context "Test::Unit" do
|
|
210
|
+
it "runs" do
|
|
211
|
+
write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
|
|
212
|
+
result = run_tests("test")
|
|
213
|
+
result.should include('1 test')
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it "passes test options" do
|
|
217
|
+
write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
|
|
218
|
+
result = run_tests("test", :add => '--test-options "-v"')
|
|
219
|
+
result.should include('test_xxx') # verbose output of every test
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "runs successfully without any files" do
|
|
223
|
+
results = run_tests("")
|
|
224
|
+
results.should include("2 processes for 0 tests")
|
|
225
|
+
results.should include("Took")
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
context "Cucumber" do
|
|
230
|
+
before do
|
|
231
|
+
write "features/steps/a.rb", "
|
|
232
|
+
Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }
|
|
233
|
+
And('I sleep a bit'){ sleep 0.2 }
|
|
234
|
+
"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it "runs tests which outputs accented characters" do
|
|
238
|
+
write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print accented characters"
|
|
239
|
+
write "features/steps/a.rb", "#encoding: utf-8\nGiven('I print accented characters'){ puts \"I tu też\" }"
|
|
240
|
+
result = run_tests "features", :type => "cucumber", :add => '--pattern good'
|
|
241
|
+
result.should include('I tu też')
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "passes TEST_ENV_NUMBER when running with pattern (issue #86)" do
|
|
245
|
+
write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
|
246
|
+
write "features/good2.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
|
247
|
+
write "features/b.feature", "Feature: xxx\n Scenario: xxx\n Given I FAIL"
|
|
248
|
+
write "features/steps/a.rb", "Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }"
|
|
249
|
+
|
|
250
|
+
result = run_tests "features", :type => "cucumber", :add => '--pattern good'
|
|
251
|
+
|
|
252
|
+
result.should include('YOUR TEST ENV IS 2!')
|
|
253
|
+
result.should include('YOUR TEST ENV IS !')
|
|
254
|
+
result.should_not include('I FAIL')
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it "writes a runtime log" do
|
|
258
|
+
log = "tmp/parallel_runtime_cucumber.log"
|
|
259
|
+
write(log, "x")
|
|
260
|
+
2.times{|i|
|
|
261
|
+
# needs sleep so that runtime loggers dont overwrite each other initially
|
|
262
|
+
write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n And I sleep a bit"
|
|
263
|
+
}
|
|
264
|
+
run_tests "features", :type => "cucumber"
|
|
265
|
+
read(log).gsub(/\.\d+/,'').split("\n").should =~ [
|
|
266
|
+
"features/good0.feature:0",
|
|
267
|
+
"features/good1.feature:0"
|
|
268
|
+
]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "runs each feature once when there are more processes then features (issue #89)" do
|
|
272
|
+
2.times{|i|
|
|
273
|
+
write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
|
274
|
+
}
|
|
275
|
+
result = run_tests "features", :type => "cucumber", :add => '-n 3'
|
|
276
|
+
result.scan(/YOUR TEST ENV IS \d?!/).sort.should == ["YOUR TEST ENV IS !", "YOUR TEST ENV IS 2!"]
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
it "runs successfully without any files" do
|
|
280
|
+
results = run_tests("", :type => "cucumber")
|
|
281
|
+
results.should include("2 processes for 0 features")
|
|
282
|
+
results.should include("Took")
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "parallel_tests/cli"
|
|
3
|
+
require "parallel_tests/rspec/runner"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe ParallelTests::CLI do
|
|
7
|
+
subject { ParallelTests::CLI.new }
|
|
8
|
+
|
|
9
|
+
describe "#parse_options" do
|
|
10
|
+
let(:defaults){ {:files => []} }
|
|
11
|
+
|
|
12
|
+
def call(*args)
|
|
13
|
+
subject.send(:parse_options!, *args)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "parses regular count" do
|
|
17
|
+
call(["-n3"]).should == defaults.merge(:count => 3)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "parses count 0 as non-parallel" do
|
|
21
|
+
call(["-n0"]).should == defaults.merge(:non_parallel => true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "parses non-parallel as non-parallel" do
|
|
25
|
+
call(["--non-parallel"]).should == defaults.merge(:non_parallel => true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "finds the correct type when multiple are given" do
|
|
29
|
+
call(["--type", "test", "-t", "rspec"])
|
|
30
|
+
subject.instance_variable_get(:@runner).should == ParallelTests::RSpec::Runner
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "parses nice as nice" do
|
|
34
|
+
call(["--nice"]).should == defaults.merge(:nice => true)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "#load_runner" do
|
|
39
|
+
it "requires and loads default runner" do
|
|
40
|
+
subject.should_receive(:require).with("parallel_tests/test/runner")
|
|
41
|
+
subject.send(:load_runner, "test").should == ParallelTests::Test::Runner
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "requires and loads rspec runner" do
|
|
45
|
+
subject.should_receive(:require).with("parallel_tests/rspec/runner")
|
|
46
|
+
subject.send(:load_runner, "rspec").should == ParallelTests::RSpec::Runner
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "fails to load unfindable runner" do
|
|
50
|
+
expect{
|
|
51
|
+
subject.send(:load_runner, "foo").should == ParallelTests::RSpec::Runner
|
|
52
|
+
}.to raise_error(LoadError)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "#final_fail_message" do
|
|
57
|
+
before do
|
|
58
|
+
subject.instance_variable_set(:@runner, ParallelTests::Test::Runner)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'returns a plain fail message if colors are nor supported' do
|
|
62
|
+
subject.should_receive(:use_colors?).and_return(false)
|
|
63
|
+
subject.send(:final_fail_message).should == "Tests Failed"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'returns a colorized fail message if colors are supported' do
|
|
67
|
+
subject.should_receive(:use_colors?).and_return(true)
|
|
68
|
+
subject.send(:final_fail_message).should == "\e[31mTests Failed\e[0m"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'parallel_tests/cucumber/io'
|
|
3
|
+
require 'parallel_tests/cucumber/failures_logger'
|
|
4
|
+
|
|
5
|
+
describe ParallelTests::Cucumber::FailuresLogger do
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
@output = OutputLogger.new([])
|
|
9
|
+
@output.stub(:write)
|
|
10
|
+
|
|
11
|
+
@logger1 = ParallelTests::Cucumber::FailuresLogger.new(nil, @output, nil)
|
|
12
|
+
@logger2 = ParallelTests::Cucumber::FailuresLogger.new(nil, @output, nil)
|
|
13
|
+
@logger3 = ParallelTests::Cucumber::FailuresLogger.new(nil, @output, nil)
|
|
14
|
+
|
|
15
|
+
@feature1 = mock('feature', :file => "feature/path/to/feature1.feature")
|
|
16
|
+
@feature2 = mock('feature', :file => "feature/path/to/feature2.feature")
|
|
17
|
+
@feature3 = mock('feature', :file => "feature/path/to/feature3.feature")
|
|
18
|
+
|
|
19
|
+
@logger1.instance_variable_set("@lines", [1, 2, 3])
|
|
20
|
+
@logger2.instance_variable_set("@lines", [2, 4, 6])
|
|
21
|
+
@logger3.instance_variable_set("@lines", [3, 6, 9])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should produce a list of lines for failing scenarios" do
|
|
25
|
+
@logger1.after_feature(@feature1)
|
|
26
|
+
@logger2.after_feature(@feature2)
|
|
27
|
+
@logger3.after_feature(@feature3)
|
|
28
|
+
|
|
29
|
+
output_file_contents = @output.output.join("\n").concat("\n")
|
|
30
|
+
|
|
31
|
+
output_file_contents.should == <<END
|
|
32
|
+
feature/path/to/feature1.feature:1
|
|
33
|
+
feature/path/to/feature1.feature:2
|
|
34
|
+
feature/path/to/feature1.feature:3
|
|
35
|
+
feature/path/to/feature2.feature:2
|
|
36
|
+
feature/path/to/feature2.feature:4
|
|
37
|
+
feature/path/to/feature2.feature:6
|
|
38
|
+
feature/path/to/feature3.feature:3
|
|
39
|
+
feature/path/to/feature3.feature:6
|
|
40
|
+
feature/path/to/feature3.feature:9
|
|
41
|
+
END
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'parallel_tests/cucumber/gherkin_listener'
|
|
2
|
+
|
|
3
|
+
describe ParallelTests::Cucumber::GherkinListener do
|
|
4
|
+
describe :collect do
|
|
5
|
+
before(:each) do
|
|
6
|
+
@listener = ParallelTests::Cucumber::GherkinListener.new
|
|
7
|
+
@listener.uri("feature_file")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "returns steps count" do
|
|
11
|
+
3.times {@listener.step(nil)}
|
|
12
|
+
@listener.collect.should == {"feature_file" => 3}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "counts background steps separately" do
|
|
16
|
+
@listener.background("background")
|
|
17
|
+
5.times {@listener.step(nil)}
|
|
18
|
+
@listener.collect.should == {"feature_file" => 0}
|
|
19
|
+
|
|
20
|
+
@listener.scenario("scenario")
|
|
21
|
+
2.times {@listener.step(nil)}
|
|
22
|
+
@listener.collect.should == {"feature_file" => 2}
|
|
23
|
+
|
|
24
|
+
@listener.scenario("scenario")
|
|
25
|
+
@listener.collect.should == {"feature_file" => 2}
|
|
26
|
+
|
|
27
|
+
@listener.eof
|
|
28
|
+
@listener.collect.should == {"feature_file" => 12}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "counts scenario outlines steps separately" do
|
|
32
|
+
@listener.scenario_outline("outline")
|
|
33
|
+
5.times {@listener.step(nil)}
|
|
34
|
+
@listener.collect.should == {"feature_file" => 0}
|
|
35
|
+
|
|
36
|
+
@listener.scenario("scenario")
|
|
37
|
+
2.times {@listener.step(nil)}
|
|
38
|
+
@listener.collect.should == {"feature_file" => 2}
|
|
39
|
+
|
|
40
|
+
@listener.scenario("scenario")
|
|
41
|
+
@listener.collect.should == {"feature_file" => 2}
|
|
42
|
+
|
|
43
|
+
3.times {@listener.examples}
|
|
44
|
+
@listener.eof
|
|
45
|
+
@listener.collect.should == {"feature_file" => 17}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'counts scenarios that should not be ignored' do
|
|
49
|
+
@listener.ignore_tag_pattern = nil
|
|
50
|
+
@listener.scenario( stub('scenario', :tags =>[ stub('tag', :name => '@WIP' )]) )
|
|
51
|
+
@listener.step(nil)
|
|
52
|
+
@listener.eof
|
|
53
|
+
@listener.collect.should == {"feature_file" => 1}
|
|
54
|
+
|
|
55
|
+
@listener.ignore_tag_pattern = /@something_other_than_WIP/
|
|
56
|
+
@listener.scenario( stub('scenario', :tags =>[ stub('tag', :name => '@WIP' )]) )
|
|
57
|
+
@listener.step(nil)
|
|
58
|
+
@listener.eof
|
|
59
|
+
@listener.collect.should == {"feature_file" => 2}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'does not count scenarios that should be ignored' do
|
|
63
|
+
@listener.ignore_tag_pattern = /@WIP/
|
|
64
|
+
@listener.scenario( stub('scenario', :tags =>[ stub('tag', :name => '@WIP' )]))
|
|
65
|
+
@listener.step(nil)
|
|
66
|
+
@listener.eof
|
|
67
|
+
@listener.collect.should == {"feature_file" => 0}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'counts outlines that should not be ignored' do
|
|
71
|
+
@listener.ignore_tag_pattern = nil
|
|
72
|
+
@listener.scenario_outline( stub('scenario', :tags =>[ stub('tag', :name => '@WIP' )]) )
|
|
73
|
+
@listener.step(nil)
|
|
74
|
+
3.times {@listener.examples}
|
|
75
|
+
@listener.eof
|
|
76
|
+
@listener.collect.should == {"feature_file" => 3}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@listener.ignore_tag_pattern = /@something_other_than_WIP/
|
|
80
|
+
@listener.scenario_outline( stub('scenario', :tags =>[ stub('tag', :name => '@WIP' )]) )
|
|
81
|
+
@listener.step(nil)
|
|
82
|
+
3.times {@listener.examples}
|
|
83
|
+
@listener.eof
|
|
84
|
+
@listener.collect.should == {"feature_file" => 6}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'does not count outlines that should be ignored' do
|
|
88
|
+
@listener.ignore_tag_pattern = /@WIP/
|
|
89
|
+
@listener.scenario_outline( stub('scenario', :tags =>[ stub('tag', :name => '@WIP' )]) )
|
|
90
|
+
@listener.step(nil)
|
|
91
|
+
3.times {@listener.examples}
|
|
92
|
+
@listener.eof
|
|
93
|
+
@listener.collect.should == {"feature_file" => 0}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "parallel_tests/cucumber/runner"
|
|
3
|
+
|
|
4
|
+
describe ParallelTests::Cucumber do
|
|
5
|
+
test_tests_in_groups(ParallelTests::Cucumber::Runner, 'features', ".feature")
|
|
6
|
+
|
|
7
|
+
describe :run_tests do
|
|
8
|
+
before do
|
|
9
|
+
ParallelTests.stub!(:bundler_enabled?).and_return false
|
|
10
|
+
File.stub!(:file?).with('.bundle/environment.rb').and_return false
|
|
11
|
+
File.stub!(:file?).with('script/cucumber').and_return true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(*args)
|
|
15
|
+
ParallelTests::Cucumber::Runner.run_tests(*args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def should_run_with(regex)
|
|
19
|
+
ParallelTests::Test::Runner.should_receive(:execute_command).with{|a,b,c,d| a =~ regex}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "allows to override runner executable via PARALLEL_TESTS_EXECUTABLE" do
|
|
23
|
+
ENV['PARALLEL_TESTS_EXECUTABLE'] = 'script/custom_rspec'
|
|
24
|
+
should_run_with /script\/custom_rspec/
|
|
25
|
+
call(['xxx'],1,22,{})
|
|
26
|
+
ENV.delete('PARALLEL_TESTS_EXECUTABLE')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "runs bundle exec cucumber when on bundler 0.9" do
|
|
30
|
+
ParallelTests.stub!(:bundler_enabled?).and_return true
|
|
31
|
+
should_run_with %r{bundle exec cucumber}
|
|
32
|
+
call(['xxx'],1,22,{})
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "runs script/cucumber when script/cucumber is found" do
|
|
36
|
+
should_run_with %r{script/cucumber}
|
|
37
|
+
call(['xxx'],1,22,{})
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "runs cucumber by default" do
|
|
41
|
+
File.stub!(:file?).with('script/cucumber').and_return false
|
|
42
|
+
should_run_with %r{^cucumber}
|
|
43
|
+
call(['xxx'],1,22,{})
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "uses bin/cucumber when present" do
|
|
47
|
+
File.stub(:exists?).with("bin/cucumber").and_return true
|
|
48
|
+
should_run_with %r{bin/cucumber}
|
|
49
|
+
call(['xxx'],1,22,{})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "uses options passed in" do
|
|
53
|
+
should_run_with %r{script/cucumber .* -p default}
|
|
54
|
+
call(['xxx'],1,22,:test_options => '-p default')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "sanitizes dangerous file names" do
|
|
58
|
+
should_run_with %r{xx\\ x}
|
|
59
|
+
call(['xx x'],1,22,{})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "with parallel profile in config/cucumber.yml" do
|
|
63
|
+
before do
|
|
64
|
+
file_contents = 'parallel: -f progress'
|
|
65
|
+
Dir.stub(:glob).and_return ['config/cucumber.yml']
|
|
66
|
+
File.stub(:read).with('config/cucumber.yml').and_return file_contents
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "uses parallel profile" do
|
|
70
|
+
should_run_with %r{script/cucumber .* foo bar --profile parallel xxx}
|
|
71
|
+
call(['xxx'],1,22, :test_options => 'foo bar')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "uses given profile via --profile" do
|
|
75
|
+
should_run_with %r{script/cucumber .* --profile foo xxx$}
|
|
76
|
+
call(['xxx'],1,22, :test_options => '--profile foo')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "uses given profile via -p" do
|
|
80
|
+
should_run_with %r{script/cucumber .* -p foo xxx$}
|
|
81
|
+
call(['xxx'],1,22, :test_options => '-p foo')
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "does not use parallel profile if config/cucumber.yml does not contain it" do
|
|
86
|
+
file_contents = 'blob: -f progress'
|
|
87
|
+
should_run_with %r{script/cucumber .* foo bar}
|
|
88
|
+
Dir.should_receive(:glob).and_return ['config/cucumber.yml']
|
|
89
|
+
File.should_receive(:read).with('config/cucumber.yml').and_return file_contents
|
|
90
|
+
call(['xxx'],1,22,:test_options => 'foo bar')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "does not use the parallel profile if config/cucumber.yml does not exist" do
|
|
94
|
+
should_run_with %r{script/cucumber} # TODO this test looks useless...
|
|
95
|
+
Dir.should_receive(:glob).and_return []
|
|
96
|
+
call(['xxx'],1,22,{})
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe :line_is_result? do
|
|
101
|
+
it "should match lines with only one scenario" do
|
|
102
|
+
line = "1 scenario (1 failed)"
|
|
103
|
+
ParallelTests::Cucumber::Runner.line_is_result?(line).should be_true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should match lines with multiple scenarios" do
|
|
107
|
+
line = "2 scenarios (1 failed, 1 passed)"
|
|
108
|
+
ParallelTests::Cucumber::Runner.line_is_result?(line).should be_true
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should match lines with only one step" do
|
|
112
|
+
line = "1 step (1 failed)"
|
|
113
|
+
ParallelTests::Cucumber::Runner.line_is_result?(line).should be_true
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "should match lines with multiple steps" do
|
|
117
|
+
line = "5 steps (1 failed, 4 passed)"
|
|
118
|
+
ParallelTests::Cucumber::Runner.line_is_result?(line).should be_true
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should not match other lines" do
|
|
122
|
+
line = ' And I should have "2" emails # features/step_definitions/user_steps.rb:25'
|
|
123
|
+
ParallelTests::Cucumber::Runner.line_is_result?(line).should be_false
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe :find_results do
|
|
128
|
+
it "finds multiple results in test output" do
|
|
129
|
+
output = <<EOF
|
|
130
|
+
And I should not see "/en/" # features/step_definitions/webrat_steps.rb:87
|
|
131
|
+
|
|
132
|
+
7 scenarios (3 failed, 4 passed)
|
|
133
|
+
33 steps (3 failed, 2 skipped, 28 passed)
|
|
134
|
+
/apps/rs/features/signup.feature:2
|
|
135
|
+
Given I am on "/" # features/step_definitions/common_steps.rb:12
|
|
136
|
+
When I click "register" # features/step_definitions/common_steps.rb:6
|
|
137
|
+
And I should have "2" emails # features/step_definitions/user_steps.rb:25
|
|
138
|
+
|
|
139
|
+
4 scenarios (4 passed)
|
|
140
|
+
40 steps (40 passed)
|
|
141
|
+
|
|
142
|
+
And I should not see "foo" # features/step_definitions/webrat_steps.rb:87
|
|
143
|
+
|
|
144
|
+
1 scenario (1 passed)
|
|
145
|
+
1 step (1 passed)
|
|
146
|
+
|
|
147
|
+
EOF
|
|
148
|
+
ParallelTests::Cucumber::Runner.find_results(output).should == ["7 scenarios (3 failed, 4 passed)", "33 steps (3 failed, 2 skipped, 28 passed)", "4 scenarios (4 passed)", "40 steps (40 passed)", "1 scenario (1 passed)", "1 step (1 passed)"]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
describe :summarize_results do
|
|
153
|
+
def call(*args)
|
|
154
|
+
ParallelTests::Cucumber::Runner.summarize_results(*args)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "sums up results for scenarios and steps separately from each other" do
|
|
158
|
+
results = ["7 scenarios (3 failed, 4 passed)", "33 steps (3 failed, 2 skipped, 28 passed)", "4 scenarios (4 passed)",
|
|
159
|
+
"40 steps (40 passed)", "1 scenario (1 passed)", "1 step (1 passed)"]
|
|
160
|
+
call(results).should == "12 scenarios (3 failed, 9 passed)\n74 steps (3 failed, 2 skipped, 69 passed)"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "adds same results with plurals" do
|
|
164
|
+
results = ["1 scenario (1 passed)", "2 steps (2 passed)",
|
|
165
|
+
"2 scenarios (2 passed)", "7 steps (7 passed)"]
|
|
166
|
+
call(results).should == "3 scenarios (3 passed)\n9 steps (9 passed)"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "adds non-similar results" do
|
|
170
|
+
results = ["1 scenario (1 passed)", "1 step (1 passed)",
|
|
171
|
+
"2 scenarios (1 failed, 1 pending)", "2 steps (1 failed, 1 pending)"]
|
|
172
|
+
call(results).should == "3 scenarios (1 failed, 1 pending, 1 passed)\n3 steps (1 failed, 1 pending, 1 passed)"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "does not pluralize 1" do
|
|
176
|
+
call(["1 scenario (1 passed)", "1 step (1 passed)"]).should == "1 scenario (1 passed)\n1 step (1 passed)"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|