parallel_tests 1.0.9 → 1.1.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.
- checksums.yaml +4 -4
- data/Readme.md +7 -3
- data/bin/parallel_cucumber +5 -1
- data/bin/parallel_rspec +5 -1
- data/bin/parallel_spinach +5 -1
- data/bin/parallel_test +5 -2
- data/lib/parallel_tests.rb +0 -1
- metadata +3 -51
- data/.gitignore +0 -4
- data/.rspec +0 -2
- data/.travis.yml +0 -10
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -53
- data/Rakefile +0 -10
- data/ReadmeRails2.md +0 -48
- data/lib/parallel_tests/cli.rb +0 -206
- data/lib/parallel_tests/cucumber/failures_logger.rb +0 -25
- data/lib/parallel_tests/cucumber/runner.rb +0 -37
- data/lib/parallel_tests/cucumber/scenario_line_logger.rb +0 -51
- data/lib/parallel_tests/cucumber/scenarios.rb +0 -34
- data/lib/parallel_tests/gherkin/io.rb +0 -41
- data/lib/parallel_tests/gherkin/listener.rb +0 -87
- data/lib/parallel_tests/gherkin/runner.rb +0 -116
- data/lib/parallel_tests/gherkin/runtime_logger.rb +0 -28
- data/lib/parallel_tests/grouper.rb +0 -73
- data/lib/parallel_tests/railtie.rb +0 -8
- data/lib/parallel_tests/rspec/failures_logger.rb +0 -54
- data/lib/parallel_tests/rspec/logger_base.rb +0 -55
- data/lib/parallel_tests/rspec/runner.rb +0 -73
- data/lib/parallel_tests/rspec/runtime_logger.rb +0 -59
- data/lib/parallel_tests/rspec/summary_logger.rb +0 -19
- data/lib/parallel_tests/spinach/runner.rb +0 -19
- data/lib/parallel_tests/tasks.rb +0 -157
- data/lib/parallel_tests/test/runner.rb +0 -186
- data/lib/parallel_tests/test/runtime_logger.rb +0 -98
- data/lib/parallel_tests/version.rb +0 -3
- data/parallel_tests.gemspec +0 -14
- data/spec/integration_spec.rb +0 -437
- data/spec/parallel_tests/cli_spec.rb +0 -149
- data/spec/parallel_tests/cucumber/failure_logger_spec.rb +0 -43
- data/spec/parallel_tests/cucumber/runner_spec.rb +0 -25
- data/spec/parallel_tests/cucumber/scenarios_spec.rb +0 -69
- data/spec/parallel_tests/gherkin/listener_spec.rb +0 -96
- data/spec/parallel_tests/gherkin/runner_behaviour.rb +0 -216
- data/spec/parallel_tests/grouper_spec.rb +0 -61
- data/spec/parallel_tests/rspec/failures_logger_spec.rb +0 -82
- data/spec/parallel_tests/rspec/logger_base_spec.rb +0 -35
- data/spec/parallel_tests/rspec/runner_spec.rb +0 -201
- data/spec/parallel_tests/rspec/runtime_logger_spec.rb +0 -131
- data/spec/parallel_tests/rspec/summary_logger_spec.rb +0 -37
- data/spec/parallel_tests/spinach/runner_spec.rb +0 -12
- data/spec/parallel_tests/tasks_spec.rb +0 -178
- data/spec/parallel_tests/test/runner_spec.rb +0 -407
- data/spec/parallel_tests/test/runtime_logger_spec.rb +0 -112
- data/spec/parallel_tests_spec.rb +0 -137
- data/spec/spec_helper.rb +0 -182
@@ -1,186 +0,0 @@
|
|
1
|
-
require 'parallel_tests'
|
2
|
-
|
3
|
-
module ParallelTests
|
4
|
-
module Test
|
5
|
-
class Runner
|
6
|
-
NAME = 'Test'
|
7
|
-
|
8
|
-
class << self
|
9
|
-
# --- usually overwritten by other runners
|
10
|
-
|
11
|
-
def name
|
12
|
-
NAME
|
13
|
-
end
|
14
|
-
|
15
|
-
def runtime_log
|
16
|
-
'tmp/parallel_runtime_test.log'
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_suffix
|
20
|
-
/_(test|spec).rb$/
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_file_name
|
24
|
-
"test"
|
25
|
-
end
|
26
|
-
|
27
|
-
def run_tests(test_files, process_number, num_processes, options)
|
28
|
-
require_list = test_files.map { |filename| %{"#{File.expand_path filename}"} }.join(",")
|
29
|
-
cmd = "#{executable} -Itest -e '[#{require_list}].each {|f| require f }' -- #{options[:test_options]}"
|
30
|
-
execute_command(cmd, process_number, num_processes, options)
|
31
|
-
end
|
32
|
-
|
33
|
-
def line_is_result?(line)
|
34
|
-
line.gsub!(/[.F*]/,'')
|
35
|
-
line =~ /\d+ failure/
|
36
|
-
end
|
37
|
-
|
38
|
-
# --- usually used by other runners
|
39
|
-
|
40
|
-
# finds all tests and partitions them into groups
|
41
|
-
def tests_in_groups(tests, num_groups, options={})
|
42
|
-
tests = find_tests(tests, options)
|
43
|
-
|
44
|
-
tests = if options[:group_by] == :found
|
45
|
-
tests.map { |t| [t, 1] }
|
46
|
-
elsif options[:group_by] == :filesize
|
47
|
-
with_filesize_info(tests)
|
48
|
-
else
|
49
|
-
with_runtime_info(tests, options)
|
50
|
-
end
|
51
|
-
Grouper.in_even_groups_by_size(tests, num_groups, options)
|
52
|
-
end
|
53
|
-
|
54
|
-
def execute_command(cmd, process_number, num_processes, options)
|
55
|
-
env = (options[:env] || {}).merge(
|
56
|
-
"TEST_ENV_NUMBER" => test_env_number(process_number),
|
57
|
-
"PARALLEL_TEST_GROUPS" => num_processes
|
58
|
-
)
|
59
|
-
cmd = "nice #{cmd}" if options[:nice]
|
60
|
-
cmd = "#{cmd} 2>&1" if options[:combine_stderr]
|
61
|
-
|
62
|
-
execute_command_and_capture_output(env, cmd, options[:serialize_stdout])
|
63
|
-
end
|
64
|
-
|
65
|
-
def execute_command_and_capture_output(env, cmd, silence)
|
66
|
-
# make processes descriptive / visible in ps -ef
|
67
|
-
separator = (WINDOWS ? ' & ' : ';')
|
68
|
-
exports = env.map do |k,v|
|
69
|
-
if WINDOWS
|
70
|
-
"(SET \"#{k}=#{v}\")"
|
71
|
-
else
|
72
|
-
"#{k}=#{v};export #{k}"
|
73
|
-
end
|
74
|
-
end.join(separator)
|
75
|
-
cmd = "#{exports}#{separator}#{cmd}"
|
76
|
-
|
77
|
-
output = open("|#{cmd}", "r") { |output| capture_output(output, silence) }
|
78
|
-
exitstatus = $?.exitstatus
|
79
|
-
|
80
|
-
{:stdout => output, :exit_status => exitstatus}
|
81
|
-
end
|
82
|
-
|
83
|
-
def find_results(test_output)
|
84
|
-
test_output.split("\n").map {|line|
|
85
|
-
line.gsub!(/\e\[\d+m/,'')
|
86
|
-
next unless line_is_result?(line)
|
87
|
-
line
|
88
|
-
}.compact
|
89
|
-
end
|
90
|
-
|
91
|
-
def test_env_number(process_number)
|
92
|
-
process_number == 0 ? '' : process_number + 1
|
93
|
-
end
|
94
|
-
|
95
|
-
def summarize_results(results)
|
96
|
-
sums = sum_up_results(results)
|
97
|
-
sums.sort.map{|word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
|
98
|
-
end
|
99
|
-
|
100
|
-
protected
|
101
|
-
|
102
|
-
def executable
|
103
|
-
ENV['PARALLEL_TESTS_EXECUTABLE'] || determine_executable
|
104
|
-
end
|
105
|
-
|
106
|
-
def determine_executable
|
107
|
-
"ruby"
|
108
|
-
end
|
109
|
-
|
110
|
-
def sum_up_results(results)
|
111
|
-
results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
|
112
|
-
counts = results.scan(/(\d+) (\w+)/)
|
113
|
-
counts.inject(Hash.new(0)) do |sum, (number, word)|
|
114
|
-
sum[word] += number.to_i
|
115
|
-
sum
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# read output of the process and print it in chunks
|
120
|
-
def capture_output(out, silence)
|
121
|
-
result = ""
|
122
|
-
loop do
|
123
|
-
begin
|
124
|
-
read = out.readpartial(1000000) # read whatever chunk we can get
|
125
|
-
if RUBY_VERSION >= "1.9" && Encoding.default_internal
|
126
|
-
read = read.force_encoding(Encoding.default_internal)
|
127
|
-
end
|
128
|
-
result << read
|
129
|
-
unless silence
|
130
|
-
$stdout.print read
|
131
|
-
$stdout.flush
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end rescue EOFError
|
135
|
-
result
|
136
|
-
end
|
137
|
-
|
138
|
-
def with_runtime_info(tests, options = {})
|
139
|
-
log = options[:runtime_log] || runtime_log
|
140
|
-
lines = File.read(log).split("\n") rescue []
|
141
|
-
|
142
|
-
# use recorded test runtime if we got enough data
|
143
|
-
if lines.size * 1.5 > tests.size
|
144
|
-
puts "Using recorded test runtime: #{log}"
|
145
|
-
times = Hash.new(1)
|
146
|
-
lines.each do |line|
|
147
|
-
test, time = line.split(":")
|
148
|
-
next unless test and time
|
149
|
-
times[File.expand_path(test)] = time.to_f
|
150
|
-
end
|
151
|
-
tests.sort.map{|test| [test, times[File.expand_path(test)]] }
|
152
|
-
else # use file sizes
|
153
|
-
with_filesize_info(tests)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def with_filesize_info(tests)
|
158
|
-
# use filesize to group files
|
159
|
-
tests.sort.map { |test| [test, File.stat(test).size] }
|
160
|
-
end
|
161
|
-
|
162
|
-
def find_tests(tests, options = {})
|
163
|
-
(tests || []).map do |file_or_folder|
|
164
|
-
if File.directory?(file_or_folder)
|
165
|
-
files = files_in_folder(file_or_folder, options)
|
166
|
-
files.grep(test_suffix).grep(options[:pattern]||//)
|
167
|
-
else
|
168
|
-
file_or_folder
|
169
|
-
end
|
170
|
-
end.flatten.uniq
|
171
|
-
end
|
172
|
-
|
173
|
-
def files_in_folder(folder, options={})
|
174
|
-
pattern = if options[:symlinks] == false # not nil or true
|
175
|
-
"**/*"
|
176
|
-
else
|
177
|
-
# follow one symlink and direct children
|
178
|
-
# http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
|
179
|
-
"**{,/*/**}/*"
|
180
|
-
end
|
181
|
-
Dir[File.join(folder, pattern)].uniq
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
require 'parallel_tests'
|
2
|
-
require 'parallel_tests/test/runner'
|
3
|
-
|
4
|
-
module ParallelTests
|
5
|
-
module Test
|
6
|
-
class RuntimeLogger
|
7
|
-
@@has_started = false
|
8
|
-
|
9
|
-
class << self
|
10
|
-
def log(test, start_time, end_time)
|
11
|
-
return if test.is_a? ::Test::Unit::TestSuite # don't log for suites-of-suites
|
12
|
-
|
13
|
-
if !@@has_started # make empty log file
|
14
|
-
File.open(logfile, 'w'){}
|
15
|
-
@@has_started = true
|
16
|
-
end
|
17
|
-
|
18
|
-
locked_appending_to(logfile) do |file|
|
19
|
-
file.puts(message(test, start_time, end_time))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def message(test, start_time, end_time)
|
26
|
-
delta = "%.2f" % (end_time.to_f-start_time.to_f)
|
27
|
-
filename = class_directory(test.class) + class_to_filename(test.class) + ".rb"
|
28
|
-
"#{filename}:#{delta}"
|
29
|
-
end
|
30
|
-
|
31
|
-
# Note: this is a best guess at conventional test directory structure, and may need
|
32
|
-
# tweaking / post-processing to match correctly for any given project
|
33
|
-
def class_directory(suspect)
|
34
|
-
result = "test/"
|
35
|
-
|
36
|
-
if defined?(Rails)
|
37
|
-
result += case suspect.superclass.name
|
38
|
-
when "ActionDispatch::IntegrationTest"
|
39
|
-
"integration/"
|
40
|
-
when "ActionDispatch::PerformanceTest"
|
41
|
-
"performance/"
|
42
|
-
when "ActionController::TestCase"
|
43
|
-
"functional/"
|
44
|
-
when "ActionView::TestCase"
|
45
|
-
"unit/helpers/"
|
46
|
-
else
|
47
|
-
"unit/"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
result
|
51
|
-
end
|
52
|
-
|
53
|
-
# based on https://github.com/grosser/single_test/blob/master/lib/single_test.rb#L117
|
54
|
-
def class_to_filename(suspect)
|
55
|
-
word = suspect.to_s.dup
|
56
|
-
return word unless word.match /^[A-Z]/ and not word.match %r{/[a-z]}
|
57
|
-
|
58
|
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
59
|
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
60
|
-
word.gsub!(/\:\:/, '/')
|
61
|
-
word.tr!("-", "_")
|
62
|
-
word.downcase!
|
63
|
-
word
|
64
|
-
end
|
65
|
-
|
66
|
-
def locked_appending_to(file)
|
67
|
-
File.open(file, 'a') do |f|
|
68
|
-
begin
|
69
|
-
f.flock File::LOCK_EX
|
70
|
-
yield f
|
71
|
-
ensure
|
72
|
-
f.flock File::LOCK_UN
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def logfile
|
78
|
-
ParallelTests::Test::Runner.runtime_log
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
require 'test/unit/testsuite'
|
86
|
-
class ::Test::Unit::TestSuite
|
87
|
-
alias :run_without_timing :run unless defined? @@timing_installed
|
88
|
-
|
89
|
-
def run(result, &progress_block)
|
90
|
-
first_test = self.tests.first
|
91
|
-
start_time = ParallelTests.now
|
92
|
-
run_without_timing(result, &progress_block)
|
93
|
-
end_time = ParallelTests.now
|
94
|
-
ParallelTests::Test::RuntimeLogger.log(first_test, start_time, end_time)
|
95
|
-
end
|
96
|
-
|
97
|
-
@@timing_installed = true
|
98
|
-
end
|
data/parallel_tests.gemspec
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
-
name = "parallel_tests"
|
3
|
-
require "#{name}/version"
|
4
|
-
|
5
|
-
Gem::Specification.new name, ParallelTests::VERSION do |s|
|
6
|
-
s.summary = "Run Test::Unit / RSpec / Cucumber / Spinach in parallel"
|
7
|
-
s.authors = ["Michael Grosser"]
|
8
|
-
s.email = "michael@grosser.it"
|
9
|
-
s.homepage = "http://github.com/grosser/#{name}"
|
10
|
-
s.files = `git ls-files`.split("\n")
|
11
|
-
s.license = "MIT"
|
12
|
-
s.executables = ["parallel_spinach", "parallel_cucumber", "parallel_rspec", "parallel_test"]
|
13
|
-
s.add_runtime_dependency "parallel"
|
14
|
-
end
|
data/spec/integration_spec.rb
DELETED
@@ -1,437 +0,0 @@
|
|
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 "respects default encoding when reading child stdout", :encoding => true do
|
74
|
-
write 'test/xxx_test.rb', <<-EOF
|
75
|
-
# encoding: utf-8
|
76
|
-
require 'test/unit'
|
77
|
-
class XTest < Test::Unit::TestCase
|
78
|
-
def test_unicode
|
79
|
-
raise '¯\\_(ツ)_/¯'
|
80
|
-
end
|
81
|
-
end
|
82
|
-
EOF
|
83
|
-
# Need to tell Ruby to default to utf-8 to simulate environments where
|
84
|
-
# this is set. (Otherwise, it defaults to nil and the undefined conversion
|
85
|
-
# issue doesn't come up.)
|
86
|
-
result = run_tests('test', :fail => true,
|
87
|
-
:export => 'RUBYOPT=-Eutf-8:utf-8')
|
88
|
-
result.should include('¯\_(ツ)_/¯')
|
89
|
-
end
|
90
|
-
|
91
|
-
it "does not run any tests if there are none" do
|
92
|
-
write 'spec/xxx_spec.rb', '1'
|
93
|
-
result = run_tests "spec", :type => 'rspec'
|
94
|
-
result.should include('No examples found')
|
95
|
-
result.should include('Took')
|
96
|
-
end
|
97
|
-
|
98
|
-
it "fails when tests fail" do
|
99
|
-
write 'spec/xxx_spec.rb', 'describe("it"){it("should"){puts "TEST1"}}'
|
100
|
-
write 'spec/xxx2_spec.rb', 'describe("it"){it("should"){1.should == 2}}'
|
101
|
-
result = run_tests "spec", :fail => true, :type => 'rspec'
|
102
|
-
|
103
|
-
result.scan('1 example, 1 failure').size.should == 1
|
104
|
-
result.scan('1 example, 0 failure').size.should == 1
|
105
|
-
result.scan('2 examples, 1 failure').size.should == 1
|
106
|
-
end
|
107
|
-
|
108
|
-
it "can serialize stdout" do
|
109
|
-
write 'spec/xxx_spec.rb', '5.times{describe("it"){it("should"){sleep 0.01; puts "TEST1"}}}'
|
110
|
-
write 'spec/xxx2_spec.rb', 'sleep 0.01; 5.times{describe("it"){it("should"){sleep 0.01; puts "TEST2"}}}'
|
111
|
-
result = run_tests "spec", :type => 'rspec', :add => "--serialize-stdout"
|
112
|
-
|
113
|
-
result.should_not =~ /TEST1.*TEST2.*TEST1/m
|
114
|
-
result.should_not =~ /TEST2.*TEST1.*TEST2/m
|
115
|
-
end
|
116
|
-
|
117
|
-
it "can serialize stdout and stderr" do
|
118
|
-
write 'spec/xxx_spec.rb', '5.times{describe("it"){it("should"){sleep 0.01; $stderr.puts "errTEST1"; puts "TEST1"}}}'
|
119
|
-
write 'spec/xxx2_spec.rb', 'sleep 0.01; 5.times{describe("it"){it("should"){sleep 0.01; $stderr.puts "errTEST2"; puts "TEST2"}}}'
|
120
|
-
result = run_tests "spec", :type => 'rspec', :add => "--serialize-stdout --combine-stderr"
|
121
|
-
|
122
|
-
result.should_not =~ /TEST1.*TEST2.*TEST1/m
|
123
|
-
result.should_not =~ /TEST2.*TEST1.*TEST2/m
|
124
|
-
end
|
125
|
-
|
126
|
-
context "with given commands" do
|
127
|
-
it "can exec given commands with ENV['TEST_ENV_NUM']" do
|
128
|
-
result = `#{executable} -e 'ruby -e "print ENV[:TEST_ENV_NUMBER.to_s].to_i"' -n 4`
|
129
|
-
result.gsub('"','').split('').sort.should == %w[0 2 3 4]
|
130
|
-
end
|
131
|
-
|
132
|
-
it "can exec given command non-parallel" do
|
133
|
-
result = `#{executable} -e 'ruby -e "sleep(rand(10)/100.0); puts ENV[:TEST_ENV_NUMBER.to_s].inspect"' -n 4 --non-parallel`
|
134
|
-
result.split("\n").should == %w["" "2" "3" "4"]
|
135
|
-
end
|
136
|
-
|
137
|
-
it "can serialize stdout" do
|
138
|
-
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`
|
139
|
-
result.should_not =~ /0.*2.*0/m
|
140
|
-
result.should_not =~ /2.*0.*2/m
|
141
|
-
end
|
142
|
-
|
143
|
-
it "exists with success if all sub-processes returned success" do
|
144
|
-
system("#{executable} -e 'cat /dev/null' -n 4").should == true
|
145
|
-
end
|
146
|
-
|
147
|
-
it "exists with failure if any sub-processes returned failure" do
|
148
|
-
system("#{executable} -e 'test -e xxxx' -n 4").should == false
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
it "runs through parallel_rspec" do
|
153
|
-
version = `#{executable} -v`
|
154
|
-
`#{bin_folder}/parallel_rspec -v`.should == version
|
155
|
-
end
|
156
|
-
|
157
|
-
it "runs through parallel_cucumber" do
|
158
|
-
version = `#{executable} -v`
|
159
|
-
`#{bin_folder}/parallel_cucumber -v`.should == version
|
160
|
-
end
|
161
|
-
|
162
|
-
it "runs through parallel_spinach" do
|
163
|
-
version = `#{executable} -v`
|
164
|
-
`#{bin_folder}/parallel_spinach -v`.should == version
|
165
|
-
end
|
166
|
-
|
167
|
-
it "runs with --group-by found" do
|
168
|
-
# it only tests that it does not blow up, as it did before fixing...
|
169
|
-
write "spec/x1_spec.rb", "puts '111'"
|
170
|
-
run_tests "spec", :type => 'rspec', :add => '--group-by found'
|
171
|
-
end
|
172
|
-
|
173
|
-
it "runs faster with more processes" do
|
174
|
-
pending if RUBY_PLATFORM == "java" # just too slow ...
|
175
|
-
2.times{|i|
|
176
|
-
write "spec/xxx#{i}_spec.rb", 'describe("it"){it("should"){sleep 5}}; $stderr.puts ENV["TEST_ENV_NUMBER"]'
|
177
|
-
}
|
178
|
-
t = Time.now
|
179
|
-
run_tests("spec", :processes => 2, :type => 'rspec')
|
180
|
-
expected = 10
|
181
|
-
(Time.now - t).should <= expected
|
182
|
-
end
|
183
|
-
|
184
|
-
it "can run with given files" do
|
185
|
-
write "spec/x1_spec.rb", "puts '111'"
|
186
|
-
write "spec/x2_spec.rb", "puts '222'"
|
187
|
-
write "spec/x3_spec.rb", "puts '333'"
|
188
|
-
result = run_tests "spec/x1_spec.rb spec/x3_spec.rb", :type => 'rspec'
|
189
|
-
result.should include('111')
|
190
|
-
result.should include('333')
|
191
|
-
result.should_not include('222')
|
192
|
-
end
|
193
|
-
|
194
|
-
it "runs successfully without any files" do
|
195
|
-
results = run_tests "", :type => 'rspec'
|
196
|
-
results.should include("2 processes for 0 specs")
|
197
|
-
results.should include("Took")
|
198
|
-
end
|
199
|
-
|
200
|
-
it "can run with test-options" do
|
201
|
-
write "spec/x1_spec.rb", "111"
|
202
|
-
write "spec/x2_spec.rb", "111"
|
203
|
-
result = run_tests "spec",
|
204
|
-
:add => "--test-options ' --version'",
|
205
|
-
:processes => 2,
|
206
|
-
:type => 'rspec'
|
207
|
-
result.should =~ /\d+\.\d+\.\d+.*\d+\.\d+\.\d+/m # prints version twice
|
208
|
-
end
|
209
|
-
|
210
|
-
it "runs with PARALLEL_TEST_PROCESSORS processes" do
|
211
|
-
processes = 5
|
212
|
-
processes.times{|i|
|
213
|
-
write "spec/x#{i}_spec.rb", "puts %{ENV-\#{ENV['TEST_ENV_NUMBER']}-}"
|
214
|
-
}
|
215
|
-
result = run_tests "spec",
|
216
|
-
:export => "PARALLEL_TEST_PROCESSORS=#{processes}",
|
217
|
-
:processes => processes,
|
218
|
-
:type => 'rspec'
|
219
|
-
result.scan(/ENV-.?-/).should =~ ["ENV--", "ENV-2-", "ENV-3-", "ENV-4-", "ENV-5-"]
|
220
|
-
end
|
221
|
-
|
222
|
-
it "filters test by given pattern and relative paths" do
|
223
|
-
write "spec/x_spec.rb", "puts 'XXX'"
|
224
|
-
write "spec/y_spec.rb", "puts 'YYY'"
|
225
|
-
write "spec/z_spec.rb", "puts 'ZZZ'"
|
226
|
-
result = run_tests "spec", :add => "-p '^spec/(x|z)'", :type => "rspec"
|
227
|
-
result.should include('XXX')
|
228
|
-
result.should_not include('YYY')
|
229
|
-
result.should include('ZZZ')
|
230
|
-
end
|
231
|
-
|
232
|
-
it "can wait_for_other_processes_to_finish" do
|
233
|
-
pending if RUBY_PLATFORM == "java" # just too slow ...
|
234
|
-
write "test/a_test.rb", "require 'parallel_tests'; sleep 0.5 ; ParallelTests.wait_for_other_processes_to_finish; puts 'a'"
|
235
|
-
write "test/b_test.rb", "sleep 1; puts 'b'"
|
236
|
-
write "test/c_test.rb", "sleep 1.5; puts 'c'"
|
237
|
-
write "test/d_test.rb", "sleep 2; puts 'd'"
|
238
|
-
run_tests("test", :processes => 4).should include("b\nc\nd\na\n")
|
239
|
-
end
|
240
|
-
|
241
|
-
it "can run only a single group" do
|
242
|
-
pending if RUBY_PLATFORM == "java" # just too slow ...
|
243
|
-
write "test/long_test.rb", "puts 'this is a long test'"
|
244
|
-
write "test/short_test.rb", "puts 'short test'"
|
245
|
-
|
246
|
-
group_1_result = run_tests("test", :processes => 2, :add => '--only-group 1')
|
247
|
-
group_1_result.should include("this is a long test")
|
248
|
-
group_1_result.should_not include("short test")
|
249
|
-
|
250
|
-
group_2_result = run_tests("test", :processes => 2, :add => '--only-group 2')
|
251
|
-
group_2_result.should_not include("this is a long test")
|
252
|
-
group_2_result.should include("short test")
|
253
|
-
end
|
254
|
-
|
255
|
-
context "Test::Unit" do
|
256
|
-
it "runs" do
|
257
|
-
write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
|
258
|
-
result = run_tests("test")
|
259
|
-
result.should include('1 test')
|
260
|
-
end
|
261
|
-
|
262
|
-
it "passes test options" do
|
263
|
-
write "test/x1_test.rb", "require 'test/unit'; class XTest < Test::Unit::TestCase; def test_xxx; end; end"
|
264
|
-
result = run_tests("test", :add => '--test-options "-v"')
|
265
|
-
result.should include('test_xxx') # verbose output of every test
|
266
|
-
end
|
267
|
-
|
268
|
-
it "runs successfully without any files" do
|
269
|
-
results = run_tests("")
|
270
|
-
results.should include("2 processes for 0 tests")
|
271
|
-
results.should include("Took")
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
context "Cucumber" do
|
276
|
-
before do
|
277
|
-
write "features/steps/a.rb", "
|
278
|
-
Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }
|
279
|
-
And('I sleep a bit'){ sleep 0.2 }
|
280
|
-
And('I pass'){ true }
|
281
|
-
And('I fail'){ fail }
|
282
|
-
"
|
283
|
-
end
|
284
|
-
|
285
|
-
it "runs tests which outputs accented characters" do
|
286
|
-
write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print accented characters"
|
287
|
-
write "features/steps/a.rb", "#encoding: utf-8\nGiven('I print accented characters'){ puts \"I tu też\" }"
|
288
|
-
result = run_tests "features", :type => "cucumber", :add => '--pattern good'
|
289
|
-
result.should include('I tu też')
|
290
|
-
end
|
291
|
-
|
292
|
-
it "passes TEST_ENV_NUMBER when running with pattern (issue #86)" do
|
293
|
-
write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
294
|
-
write "features/good2.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
295
|
-
write "features/b.feature", "Feature: xxx\n Scenario: xxx\n Given I FAIL"
|
296
|
-
write "features/steps/a.rb", "Given('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }"
|
297
|
-
|
298
|
-
result = run_tests "features", :type => "cucumber", :add => '--pattern good'
|
299
|
-
|
300
|
-
result.should include('YOUR TEST ENV IS 2!')
|
301
|
-
result.should include('YOUR TEST ENV IS !')
|
302
|
-
result.should_not include('I FAIL')
|
303
|
-
end
|
304
|
-
|
305
|
-
it "writes a runtime log" do
|
306
|
-
pending "TODO find out why this fails" if RUBY_PLATFORM == "java"
|
307
|
-
|
308
|
-
log = "tmp/parallel_runtime_cucumber.log"
|
309
|
-
write(log, "x")
|
310
|
-
2.times{|i|
|
311
|
-
# needs sleep so that runtime loggers dont overwrite each other initially
|
312
|
-
write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n And I sleep a bit"
|
313
|
-
}
|
314
|
-
run_tests "features", :type => "cucumber"
|
315
|
-
read(log).gsub(/\.\d+/,'').split("\n").should =~ [
|
316
|
-
"features/good0.feature:0",
|
317
|
-
"features/good1.feature:0"
|
318
|
-
]
|
319
|
-
end
|
320
|
-
|
321
|
-
it "runs each feature once when there are more processes then features (issue #89)" do
|
322
|
-
2.times{|i|
|
323
|
-
write "features/good#{i}.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
324
|
-
}
|
325
|
-
result = run_tests "features", :type => "cucumber", :add => '-n 3'
|
326
|
-
result.scan(/YOUR TEST ENV IS \d?!/).sort.should == ["YOUR TEST ENV IS !", "YOUR TEST ENV IS 2!"]
|
327
|
-
end
|
328
|
-
|
329
|
-
it "runs successfully without any files" do
|
330
|
-
results = run_tests("", :type => "cucumber")
|
331
|
-
results.should include("2 processes for 0 features")
|
332
|
-
results.should include("Took")
|
333
|
-
end
|
334
|
-
|
335
|
-
it "collates failing scenarios" do
|
336
|
-
write "features/pass.feature", "Feature: xxx\n Scenario: xxx\n Given I pass"
|
337
|
-
write "features/fail1.feature", "Feature: xxx\n Scenario: xxx\n Given I fail"
|
338
|
-
write "features/fail2.feature", "Feature: xxx\n Scenario: xxx\n Given I fail"
|
339
|
-
results = run_tests "features", :processes => 3, :type => "cucumber", :fail => true
|
340
|
-
|
341
|
-
results.should include """
|
342
|
-
Failing Scenarios:
|
343
|
-
cucumber features/fail2.feature:2 # Scenario: xxx
|
344
|
-
cucumber features/fail1.feature:2 # Scenario: xxx
|
345
|
-
|
346
|
-
3 scenarios (2 failed, 1 passed)
|
347
|
-
3 steps (2 failed, 1 passed)
|
348
|
-
"""
|
349
|
-
end
|
350
|
-
|
351
|
-
it "groups by scenario" do
|
352
|
-
write "features/long.feature", <<-EOS
|
353
|
-
Feature: xxx
|
354
|
-
Scenario: xxx
|
355
|
-
Given I print TEST_ENV_NUMBER
|
356
|
-
|
357
|
-
Scenario: xxx
|
358
|
-
Given I print TEST_ENV_NUMBER
|
359
|
-
|
360
|
-
Scenario Outline: xxx
|
361
|
-
Given I print TEST_ENV_NUMBER
|
362
|
-
|
363
|
-
Examples:
|
364
|
-
| num |
|
365
|
-
| one |
|
366
|
-
| two |
|
367
|
-
EOS
|
368
|
-
result = run_tests "features", :type => "cucumber", :add => "--group-by scenarios"
|
369
|
-
result.should include("2 processes for 4 scenarios")
|
370
|
-
end
|
371
|
-
|
372
|
-
it "groups by step" do
|
373
|
-
write "features/good1.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
374
|
-
write "features/good2.feature", "Feature: xxx\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
375
|
-
|
376
|
-
result = run_tests "features", :type => "cucumber", :add => '--group-by steps'
|
377
|
-
|
378
|
-
result.should include("2 processes for 2 features")
|
379
|
-
end
|
380
|
-
end
|
381
|
-
|
382
|
-
context "Spinach", :fails_on_ruby_187 => true do
|
383
|
-
before do
|
384
|
-
write "features/steps/a.rb", "class A < Spinach::FeatureSteps\n Given 'I print TEST_ENV_NUMBER' do\n puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\"\n end\n And 'I sleep a bit' do\n sleep 0.2\n end\nend"
|
385
|
-
end
|
386
|
-
|
387
|
-
it "runs tests which outputs accented characters" do
|
388
|
-
write "features/good1.feature", "Feature: a\n Scenario: xxx\n Given I print accented characters"
|
389
|
-
write "features/steps/a.rb", "#encoding: utf-8\nclass A < Spinach::FeatureSteps\nGiven 'I print accented characters' do\n puts \"I tu też\" \n end\nend"
|
390
|
-
result = run_tests "features", :type => "spinach", :add => 'features/good1.feature'#, :add => '--pattern good'
|
391
|
-
result.should include('I tu też')
|
392
|
-
end
|
393
|
-
|
394
|
-
it "passes TEST_ENV_NUMBER when running with pattern (issue #86)" do
|
395
|
-
write "features/good1.feature", "Feature: a\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
396
|
-
write "features/good2.feature", "Feature: a\n Scenario: xxx\n Given I print TEST_ENV_NUMBER"
|
397
|
-
write "features/b.feature", "Feature: b\n Scenario: xxx\n Given I FAIL" #Expect this not to be run
|
398
|
-
write "features/steps/a.rb", "class A < Spinach::FeatureSteps\nGiven('I print TEST_ENV_NUMBER'){ puts \"YOUR TEST ENV IS \#{ENV['TEST_ENV_NUMBER']}!\" }\nend"
|
399
|
-
|
400
|
-
result = run_tests "features", :type => "spinach", :add => '--pattern good'
|
401
|
-
|
402
|
-
result.should include('YOUR TEST ENV IS 2!')
|
403
|
-
result.should include('YOUR TEST ENV IS !')
|
404
|
-
result.should_not include('I FAIL')
|
405
|
-
end
|
406
|
-
|
407
|
-
it "writes a runtime log" do
|
408
|
-
pending 'not yet implemented -- custom runtime logging'
|
409
|
-
log = "tmp/parallel_runtime_spinach.log"
|
410
|
-
write(log, "x")
|
411
|
-
|
412
|
-
2.times{|i|
|
413
|
-
# needs sleep so that runtime loggers dont overwrite each other initially
|
414
|
-
write "features/good#{i}.feature", "Feature: A\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n And I sleep a bit"
|
415
|
-
}
|
416
|
-
result = run_tests "features", :type => "spinach"
|
417
|
-
read(log).gsub(/\.\d+/,'').split("\n").should =~ [
|
418
|
-
"features/good0.feature:0",
|
419
|
-
"features/good1.feature:0"
|
420
|
-
]
|
421
|
-
end
|
422
|
-
|
423
|
-
it "runs each feature once when there are more processes then features (issue #89)" do
|
424
|
-
2.times{|i|
|
425
|
-
write "features/good#{i}.feature", "Feature: A\n Scenario: xxx\n Given I print TEST_ENV_NUMBER\n"
|
426
|
-
}
|
427
|
-
result = run_tests "features", :type => "spinach", :add => '-n 3'
|
428
|
-
result.scan(/YOUR TEST ENV IS \d?!/).sort.should == ["YOUR TEST ENV IS !", "YOUR TEST ENV IS 2!"]
|
429
|
-
end
|
430
|
-
|
431
|
-
it "runs successfully without any files" do
|
432
|
-
results = run_tests("", :type => "spinach")
|
433
|
-
results.should include("2 processes for 0 features")
|
434
|
-
results.should include("Took")
|
435
|
-
end
|
436
|
-
end
|
437
|
-
end
|