parallel_tests 0.6.20 → 0.7.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +2 -4
- data/Gemfile.lock +7 -7
- data/Rakefile +18 -16
- data/Readme.md +15 -12
- data/bin/parallel_test +2 -98
- data/lib/parallel_tests.rb +2 -125
- data/lib/parallel_tests/cli.rb +102 -0
- data/lib/parallel_tests/cucumber/runner.rb +40 -0
- data/lib/parallel_tests/cucumber/runtime_logger.rb +58 -0
- data/lib/parallel_tests/grouper.rb +2 -2
- data/lib/parallel_tests/railtie.rb +3 -3
- data/lib/{parallel_specs/spec_failures_logger.rb → parallel_tests/spec/failures_logger.rb} +4 -3
- data/lib/{parallel_specs/spec_logger_base.rb → parallel_tests/spec/logger_base.rb} +7 -3
- data/lib/parallel_tests/spec/runner.rb +56 -0
- data/lib/{parallel_specs/spec_runtime_logger.rb → parallel_tests/spec/runtime_logger.rb} +2 -2
- data/lib/{parallel_specs/spec_summary_logger.rb → parallel_tests/spec/summary_logger.rb} +2 -2
- data/lib/parallel_tests/tasks.rb +0 -25
- data/lib/parallel_tests/test/runner.rb +126 -0
- data/lib/parallel_tests/test/runtime_logger.rb +92 -0
- data/lib/parallel_tests/version.rb +3 -0
- data/parallel_tests.gemspec +10 -61
- data/spec/parallel_tests/cucumber/runner_spec.rb +76 -0
- data/spec/{parallel_specs/spec_failure_logger_spec.rb → parallel_tests/spec/failure_logger_spec.rb} +8 -8
- data/spec/parallel_tests/spec/runner_spec.rb +178 -0
- data/spec/{parallel_specs/spec_runtime_logger_spec.rb → parallel_tests/spec/runtime_logger_spec.rb} +4 -4
- data/spec/{parallel_specs/spec_summary_logger_spec.rb → parallel_tests/spec/summary_logger_spec.rb} +2 -2
- data/spec/parallel_tests/test/runner_spec.rb +179 -0
- data/spec/parallel_tests/{runtime_logger_spec.rb → test/runtime_logger_spec.rb} +19 -16
- data/spec/parallel_tests_spec.rb +2 -158
- data/spec/spec_helper.rb +9 -7
- metadata +30 -26
- data/VERSION +0 -1
- data/lib/parallel_cucumber.rb +0 -36
- data/lib/parallel_cucumber/runtime_logger.rb +0 -57
- data/lib/parallel_specs.rb +0 -52
- data/lib/parallel_tests/runtime_logger.rb +0 -78
- data/lib/tasks/parallel_tests.rake +0 -1
- data/spec/parallel_cucumber_spec.rb +0 -72
- data/spec/parallel_specs_spec.rb +0 -173
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'parallel_tests/test/runner'
|
2
|
+
|
3
|
+
module ParallelTests
|
4
|
+
module Cucumber
|
5
|
+
class Runner < ParallelTests::Test::Runner
|
6
|
+
def self.run_tests(test_files, process_number, options)
|
7
|
+
color = ($stdout.tty? ? 'AUTOTEST=1 ; export AUTOTEST ;' : '')#display color when we are in a terminal
|
8
|
+
runtime_logging = " --format ParallelCucumber::RuntimeLogger --out #{runtime_log}"
|
9
|
+
cmd = "#{color} #{executable}"
|
10
|
+
cmd << runtime_logging if File.directory?(File.dirname(runtime_log))
|
11
|
+
cmd << " #{options[:test_options]} #{test_files*' '}"
|
12
|
+
execute_command(cmd, process_number, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.executable
|
16
|
+
if ParallelTests.bundler_enabled?
|
17
|
+
"bundle exec cucumber"
|
18
|
+
elsif File.file?("script/cucumber")
|
19
|
+
"script/cucumber"
|
20
|
+
else
|
21
|
+
"cucumber"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.runtime_log
|
26
|
+
'tmp/parallel_runtime_cucumber.log'
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def self.test_suffix
|
32
|
+
".feature"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.line_is_result?(line)
|
36
|
+
line =~ /^\d+ (steps|scenarios)/
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ParallelTests
|
2
|
+
module Cucumber
|
3
|
+
class RuntimeLogger
|
4
|
+
def initialize(step_mother, path_or_io, options=nil)
|
5
|
+
@io = prepare_io(path_or_io)
|
6
|
+
@example_times = Hash.new(0)
|
7
|
+
end
|
8
|
+
|
9
|
+
def before_feature(_)
|
10
|
+
@start_at = Time.now.to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_feature(feature)
|
14
|
+
@example_times[feature.file] += Time.now.to_f - @start_at
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_features(*args)
|
18
|
+
lock_output do
|
19
|
+
@io.puts @example_times.map { |file, time| "#{file}:#{time}" }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def prepare_io(path_or_io)
|
26
|
+
if path_or_io.respond_to?(:write)
|
27
|
+
path_or_io
|
28
|
+
else # its a path
|
29
|
+
File.open(path_or_io, 'w').close # clean out the file
|
30
|
+
file = File.open(path_or_io, 'a')
|
31
|
+
|
32
|
+
at_exit do
|
33
|
+
unless file.closed?
|
34
|
+
file.flush
|
35
|
+
file.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
file
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# do not let multiple processes get in each others way
|
44
|
+
def lock_output
|
45
|
+
if File === @io
|
46
|
+
begin
|
47
|
+
@io.flock File::LOCK_EX
|
48
|
+
yield
|
49
|
+
ensure
|
50
|
+
@io.flock File::LOCK_UN
|
51
|
+
end
|
52
|
+
else
|
53
|
+
yield
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
module ParallelTests
|
2
2
|
class Grouper
|
3
3
|
def self.in_groups(items, num_groups)
|
4
4
|
groups = Array.new(num_groups){ [] }
|
@@ -35,7 +35,7 @@ class ParallelTests
|
|
35
35
|
files.sort_by{|item, size| size }.reverse
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
private
|
39
39
|
|
40
40
|
def self.smallest_group(groups)
|
41
41
|
groups.min_by{|g| g[:size] }
|
@@ -1,9 +1,9 @@
|
|
1
|
-
# add rake tasks if we are inside Rails
|
1
|
+
# add rake tasks if we are inside Rails 3
|
2
2
|
if defined?(Rails::Railtie)
|
3
|
-
|
3
|
+
module ParallelTests
|
4
4
|
class Railtie < ::Rails::Railtie
|
5
5
|
rake_tasks do
|
6
|
-
load File.expand_path("
|
6
|
+
load File.expand_path("../tasks.rake", __FILE__)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
require '
|
1
|
+
require 'parallel_tests/spec/logger_base'
|
2
|
+
require 'parallel_tests/spec/runner'
|
2
3
|
|
3
|
-
class
|
4
|
+
class ParallelTests::Spec::FailuresLogger < ParallelTests::Spec::LoggerBase
|
4
5
|
# RSpec 1: does not keep track of failures, so we do
|
5
6
|
def example_failed(example, *args)
|
6
7
|
if RSPEC_1
|
@@ -37,7 +38,7 @@ class ParallelSpecs::SpecFailuresLogger < ParallelSpecs::SpecLoggerBase
|
|
37
38
|
file, line = example.location.to_s.split(':')
|
38
39
|
next unless file and line
|
39
40
|
file.gsub!(%r(^.*?/spec/), './spec/')
|
40
|
-
@output.puts "#{
|
41
|
+
@output.puts "#{ParallelTests::Spec::Runner.executable} #{file}:#{line} # #{example.description}"
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
module ParallelTests
|
2
|
+
module Spec
|
3
|
+
end
|
4
|
+
end
|
2
5
|
|
3
6
|
begin
|
4
7
|
require 'rspec/core/formatters/base_text_formatter'
|
@@ -7,9 +10,10 @@ rescue LoadError
|
|
7
10
|
require 'spec/runner/formatter/base_text_formatter'
|
8
11
|
base = Spec::Runner::Formatter::BaseTextFormatter
|
9
12
|
end
|
10
|
-
ParallelSpecs::SpecLoggerBaseBase = base
|
11
13
|
|
12
|
-
|
14
|
+
ParallelTests::Spec::LoggerBaseBase = base
|
15
|
+
|
16
|
+
class ParallelTests::Spec::LoggerBase < ParallelTests::Spec::LoggerBaseBase
|
13
17
|
RSPEC_1 = !defined?(RSpec::Core::Formatters::BaseTextFormatter) # do not test for Spec, this will trigger deprecation warning in rspec 2
|
14
18
|
|
15
19
|
def initialize(*args)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'parallel_tests/test/runner'
|
2
|
+
|
3
|
+
module ParallelTests
|
4
|
+
module Spec
|
5
|
+
class Runner < ParallelTests::Test::Runner
|
6
|
+
def self.run_tests(test_files, process_number, options)
|
7
|
+
exe = executable # expensive, so we cache
|
8
|
+
version = (exe =~ /\brspec\b/ ? 2 : 1)
|
9
|
+
cmd = "#{rspec_1_color if version == 1}#{exe} #{options[:test_options]} #{rspec_2_color if version == 2}#{spec_opts(version)} #{test_files*' '}"
|
10
|
+
execute_command(cmd, process_number, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.executable
|
14
|
+
cmd = if File.file?("script/spec")
|
15
|
+
"script/spec"
|
16
|
+
elsif ParallelTests.bundler_enabled?
|
17
|
+
cmd = (run("bundle show rspec") =~ %r{/rspec-1[^/]+$} ? "spec" : "rspec")
|
18
|
+
"bundle exec #{cmd}"
|
19
|
+
else
|
20
|
+
%w[spec rspec].detect{|cmd| system "#{cmd} --version > /dev/null 2>&1" }
|
21
|
+
end
|
22
|
+
cmd or raise("Can't find executables rspec or spec")
|
23
|
+
end
|
24
|
+
|
25
|
+
# legacy <-> people log to this file using rspec options
|
26
|
+
def self.runtime_log
|
27
|
+
'tmp/parallel_profile.log'
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
# so it can be stubbed....
|
33
|
+
def self.run(cmd)
|
34
|
+
`#{cmd}`
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.rspec_1_color
|
38
|
+
'RSPEC_COLOR=1 ; export RSPEC_COLOR ;' if $stdout.tty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.rspec_2_color
|
42
|
+
'--color --tty ' if $stdout.tty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.spec_opts(rspec_version)
|
46
|
+
options_file = ['.rspec_parallel', 'spec/parallel_spec.opts', 'spec/spec.opts'].detect{|f| File.file?(f) }
|
47
|
+
return unless options_file
|
48
|
+
"-O #{options_file}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.test_suffix
|
52
|
+
"_spec.rb"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require '
|
1
|
+
require 'parallel_tests/spec/logger_base'
|
2
2
|
|
3
|
-
class
|
3
|
+
class ParallelTests::Spec::RuntimeLogger < ParallelTests::Spec::LoggerBase
|
4
4
|
def initialize(*args)
|
5
5
|
super
|
6
6
|
@example_times = Hash.new(0)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require '
|
1
|
+
require 'parallel_tests/spec/failures_logger'
|
2
2
|
|
3
|
-
class
|
3
|
+
class ParallelTests::Spec::SummaryLogger < ParallelTests::Spec::LoggerBase
|
4
4
|
# RSpec 1: dumps 1 failed spec
|
5
5
|
def dump_failure(*args)
|
6
6
|
lock_output do
|
data/lib/parallel_tests/tasks.rb
CHANGED
@@ -53,28 +53,3 @@ namespace :parallel do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
57
|
-
#backwards compatability
|
58
|
-
#spec:parallel:prepare
|
59
|
-
#spec:parallel
|
60
|
-
#test:parallel
|
61
|
-
namespace :spec do
|
62
|
-
namespace :parallel do
|
63
|
-
task :prepare, :count do |t,args|
|
64
|
-
$stderr.puts "WARNING -- Deprecated! use parallel:prepare"
|
65
|
-
Rake::Task['parallel:prepare'].invoke(args[:count])
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
task :parallel, :count, :pattern do |t,args|
|
70
|
-
$stderr.puts "WARNING -- Deprecated! use parallel:spec"
|
71
|
-
Rake::Task['parallel:spec'].invoke(args[:count], args[:pattern])
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
namespace :test do
|
76
|
-
task :parallel, :count, :pattern do |t,args|
|
77
|
-
$stderr.puts "WARNING -- Deprecated! use parallel:test"
|
78
|
-
Rake::Task['parallel:test'].invoke(args[:count], args[:pattern])
|
79
|
-
end
|
80
|
-
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module ParallelTests
|
2
|
+
module Test
|
3
|
+
class Runner
|
4
|
+
# finds all tests and partitions them into groups
|
5
|
+
def self.tests_in_groups(root, num_groups, options={})
|
6
|
+
tests = find_tests(root, options)
|
7
|
+
if options[:no_sort] == true
|
8
|
+
Grouper.in_groups(tests, num_groups)
|
9
|
+
else
|
10
|
+
tests = with_runtime_info(tests)
|
11
|
+
Grouper.in_even_groups_by_size(tests, num_groups, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.run_tests(test_files, process_number, options)
|
16
|
+
require_list = test_files.map { |filename| %{"#{File.expand_path filename}"} }.join(",")
|
17
|
+
cmd = "ruby -Itest -e '[#{require_list}].each {|f| require f }' -- #{options[:test_options]}"
|
18
|
+
execute_command(cmd, process_number, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.execute_command(cmd, process_number, options)
|
22
|
+
cmd = "TEST_ENV_NUMBER=#{test_env_number(process_number)} ; export TEST_ENV_NUMBER; #{cmd}"
|
23
|
+
f = open("|#{cmd}", 'r')
|
24
|
+
output = fetch_output(f, options)
|
25
|
+
f.close
|
26
|
+
{:stdout => output, :exit_status => $?.exitstatus}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.find_results(test_output)
|
30
|
+
test_output.split("\n").map {|line|
|
31
|
+
line = line.gsub(/\.|F|\*/,'')
|
32
|
+
next unless line_is_result?(line)
|
33
|
+
line
|
34
|
+
}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.test_env_number(process_number)
|
38
|
+
process_number == 0 ? '' : process_number + 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.runtime_log
|
42
|
+
'tmp/parallel_runtime_test.log'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.summarize_results(results)
|
46
|
+
results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
|
47
|
+
counts = results.scan(/(\d+) (\w+)/)
|
48
|
+
sums = counts.inject(Hash.new(0)) do |sum, (number, word)|
|
49
|
+
sum[word] += number.to_i
|
50
|
+
sum
|
51
|
+
end
|
52
|
+
sums.sort.map{|word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# read output of the process and print in in chucks
|
58
|
+
def self.fetch_output(process, options)
|
59
|
+
all = ''
|
60
|
+
buffer = ''
|
61
|
+
timeout = options[:chunk_timeout] || 0.2
|
62
|
+
flushed = Time.now.to_f
|
63
|
+
|
64
|
+
while char = process.getc
|
65
|
+
char = (char.is_a?(Fixnum) ? char.chr : char) # 1.8 <-> 1.9
|
66
|
+
all << char
|
67
|
+
|
68
|
+
# print in chunks so large blocks stay together
|
69
|
+
now = Time.now.to_f
|
70
|
+
buffer << char
|
71
|
+
if flushed + timeout < now
|
72
|
+
$stdout.print buffer
|
73
|
+
$stdout.flush
|
74
|
+
buffer = ''
|
75
|
+
flushed = now
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# print the remainder
|
80
|
+
$stdout.print buffer
|
81
|
+
$stdout.flush
|
82
|
+
|
83
|
+
all
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.line_is_result?(line)
|
87
|
+
line =~ /\d+ failure/
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.test_suffix
|
91
|
+
"_test.rb"
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.with_runtime_info(tests)
|
95
|
+
lines = File.read(runtime_log).split("\n") rescue []
|
96
|
+
|
97
|
+
# use recorded test runtime if we got enough data
|
98
|
+
if lines.size * 1.5 > tests.size
|
99
|
+
puts "Using recorded test runtime"
|
100
|
+
times = Hash.new(1)
|
101
|
+
lines.each do |line|
|
102
|
+
test, time = line.split(":")
|
103
|
+
next unless test and time
|
104
|
+
times[File.expand_path(test)] = time.to_f
|
105
|
+
end
|
106
|
+
tests.sort.map{|test| [test, times[test]] }
|
107
|
+
else # use file sizes
|
108
|
+
tests.sort.map{|test| [test, File.stat(test).size] }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.find_tests(root, options={})
|
113
|
+
if root.is_a?(Array)
|
114
|
+
root
|
115
|
+
else
|
116
|
+
# follow one symlink and direct children
|
117
|
+
# http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
|
118
|
+
files = Dir["#{root}/**{,/*/**}/*#{test_suffix}"].uniq
|
119
|
+
files = files.map{|f| f.sub(root+'/','') }
|
120
|
+
files = files.grep(/#{options[:pattern]}/)
|
121
|
+
files.map{|f| "#{root}/#{f}" }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'parallel_tests/test/runner'
|
2
|
+
|
3
|
+
module ParallelTests
|
4
|
+
module Test
|
5
|
+
class RuntimeLogger
|
6
|
+
@@has_started = false
|
7
|
+
|
8
|
+
def self.log(test, start_time, end_time)
|
9
|
+
return if test.is_a? ::Test::Unit::TestSuite # don't log for suites-of-suites
|
10
|
+
|
11
|
+
if !@@has_started # make empty log file
|
12
|
+
File.open(logfile, 'w'){}
|
13
|
+
@@has_started = true
|
14
|
+
end
|
15
|
+
|
16
|
+
locked_appending_to(logfile) do |file|
|
17
|
+
file.puts(message(test, start_time, end_time))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.message(test, start_time, end_time)
|
22
|
+
delta = "%.2f" % (end_time.to_f-start_time.to_f)
|
23
|
+
filename = class_directory(test.class) + class_to_filename(test.class) + ".rb"
|
24
|
+
"#{filename}:#{delta}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Note: this is a best guess at conventional test directory structure, and may need
|
28
|
+
# tweaking / post-processing to match correctly for any given project
|
29
|
+
def self.class_directory(suspect)
|
30
|
+
result = "test/"
|
31
|
+
|
32
|
+
if defined?(Rails)
|
33
|
+
result += case suspect.superclass.name
|
34
|
+
when "ActionDispatch::IntegrationTest"
|
35
|
+
"integration/"
|
36
|
+
when "ActionDispatch::PerformanceTest"
|
37
|
+
"performance/"
|
38
|
+
when "ActionController::TestCase"
|
39
|
+
"functional/"
|
40
|
+
when "ActionView::TestCase"
|
41
|
+
"unit/helpers/"
|
42
|
+
else
|
43
|
+
"unit/"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
# based on https://github.com/grosser/single_test/blob/master/lib/single_test.rb#L117
|
50
|
+
def self.class_to_filename(suspect)
|
51
|
+
word = suspect.to_s.dup
|
52
|
+
return word unless word.match /^[A-Z]/ and not word.match %r{/[a-z]}
|
53
|
+
|
54
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
55
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
56
|
+
word.gsub!(/\:\:/, '/')
|
57
|
+
word.tr!("-", "_")
|
58
|
+
word.downcase!
|
59
|
+
word
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.locked_appending_to(file)
|
63
|
+
File.open(file, 'a') do |f|
|
64
|
+
begin
|
65
|
+
f.flock File::LOCK_EX
|
66
|
+
yield f
|
67
|
+
ensure
|
68
|
+
f.flock File::LOCK_UN
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.logfile
|
74
|
+
ParallelTests::Test::Runner.runtime_log
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
require 'test/unit/testsuite'
|
81
|
+
class ::Test::Unit::TestSuite
|
82
|
+
alias :run_without_timing :run unless defined? @@timing_installed
|
83
|
+
|
84
|
+
def run(result, &progress_block)
|
85
|
+
start_time=Time.now
|
86
|
+
run_without_timing(result, &progress_block)
|
87
|
+
end_time=Time.now
|
88
|
+
ParallelTests::Test::RuntimeLogger.log(self.tests.first, start_time, end_time)
|
89
|
+
end
|
90
|
+
|
91
|
+
@@timing_installed = true
|
92
|
+
end
|