rubytest 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.ruby +45 -0
- data/.test +11 -0
- data/HISTORY.md +43 -0
- data/NOTICE.md +33 -0
- data/README.md +112 -0
- data/bin/rubytest +4 -0
- data/demo/01_test.md +29 -0
- data/demo/02_case.md +34 -0
- data/demo/applique/ruby-test.rb +2 -0
- data/lib/rubytest/autorun.rb +19 -0
- data/lib/rubytest/cli.rb +106 -0
- data/lib/rubytest/code_snippet.rb +93 -0
- data/lib/rubytest/config.rb +106 -0
- data/lib/rubytest/core_ext/assertion.rb +30 -0
- data/lib/rubytest/core_ext/exception.rb +8 -0
- data/lib/rubytest/core_ext/string.rb +30 -0
- data/lib/rubytest/core_ext.rb +9 -0
- data/lib/rubytest/rake.rb +124 -0
- data/lib/rubytest/recorder.rb +53 -0
- data/lib/rubytest/reporters/abstract.rb +261 -0
- data/lib/rubytest/reporters/abstract_hash.rb +224 -0
- data/lib/rubytest/reporters/dotprogress.rb +89 -0
- data/lib/rubytest/reporters/html.rb +155 -0
- data/lib/rubytest/reporters/outline.rb +211 -0
- data/lib/rubytest/reporters/progress.rb +195 -0
- data/lib/rubytest/reporters/summary.rb +145 -0
- data/lib/rubytest/reporters/tap.rb +61 -0
- data/lib/rubytest/reporters/tapj.rb +53 -0
- data/lib/rubytest/reporters/tapy.rb +58 -0
- data/lib/rubytest/reporters/test.rb +51 -0
- data/lib/rubytest/runner.rb +349 -0
- data/lib/rubytest.rb +28 -0
- data/lib/test.rb +8 -0
- data/test/basic_case.rb +11 -0
- metadata +119 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
# Preserves relative tabbing.
|
4
|
+
# The first non-empty line ends up with n spaces before nonspace.
|
5
|
+
#
|
6
|
+
# This is a Ruby Facet (http://rubyworks.github.com/facets).
|
7
|
+
|
8
|
+
def tabto(n)
|
9
|
+
if self =~ /^( *)\S/
|
10
|
+
indent(n - $1.length)
|
11
|
+
else
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end unless method_defined?(:tabto)
|
15
|
+
|
16
|
+
# Indent left or right by n spaces.
|
17
|
+
# (This used to be called #tab and aliased as #indent.)
|
18
|
+
#
|
19
|
+
# This is a Ruby Facet (http://rubyworks.github.com/facets).
|
20
|
+
|
21
|
+
def indent(n, c=' ')
|
22
|
+
if n >= 0
|
23
|
+
gsub(/^/, c * n)
|
24
|
+
else
|
25
|
+
gsub(/^#{Regexp.escape(c)}{0,#{-n}}/, "")
|
26
|
+
end
|
27
|
+
end unless method_defined?(:indent)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
|
3
|
+
module Ruth
|
4
|
+
|
5
|
+
#
|
6
|
+
module Rake
|
7
|
+
|
8
|
+
# TODO: The test task uses #fork. Maybe it should shell out instead?
|
9
|
+
# Or provide the option for either?
|
10
|
+
|
11
|
+
# Define a test rake task.
|
12
|
+
#
|
13
|
+
# The `TEST` environment variable can be used to select tests
|
14
|
+
# when using the task.
|
15
|
+
#
|
16
|
+
class TestTask < ::Rake::TaskLib
|
17
|
+
|
18
|
+
# Glob patterns are used by default to select test scripts.
|
19
|
+
DEFAULT_TESTS = [
|
20
|
+
'test/**/case_*.rb',
|
21
|
+
'test/**/*_case.rb',
|
22
|
+
'test/**/test_*.rb',
|
23
|
+
'test/**/*_test.rb'
|
24
|
+
]
|
25
|
+
|
26
|
+
# Test scripts to load. Can be a file glob.
|
27
|
+
attr_accessor :tests
|
28
|
+
|
29
|
+
# Paths to add to $LOAD_PATH.
|
30
|
+
attr_accessor :loadpath
|
31
|
+
|
32
|
+
# Scripts to load prior to loading tests.
|
33
|
+
attr_accessor :requires
|
34
|
+
|
35
|
+
# Report format to use.
|
36
|
+
attr_accessor :format
|
37
|
+
|
38
|
+
# Filter tests based by tags.
|
39
|
+
attr_accessor :tags
|
40
|
+
|
41
|
+
# Filter tests by matching description.
|
42
|
+
attr_accessor :match
|
43
|
+
|
44
|
+
# From Rake's own TestTask.
|
45
|
+
alias_method :libs, :loadpath
|
46
|
+
alias_method :test_files, :tests
|
47
|
+
|
48
|
+
#
|
49
|
+
def initialize(name='test', desc="run tests", &block)
|
50
|
+
@name = name
|
51
|
+
@desc = desc
|
52
|
+
|
53
|
+
@loadpath = ['lib']
|
54
|
+
@requires = []
|
55
|
+
@tests = [ENV['TEST'] || DEFAULT_TESTS].flatten
|
56
|
+
@format = nil
|
57
|
+
@match = nil
|
58
|
+
@tags = []
|
59
|
+
|
60
|
+
block.call(self)
|
61
|
+
|
62
|
+
define_task
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
def define_task
|
67
|
+
desc @desc
|
68
|
+
task @name do
|
69
|
+
@tests ||= default_tests
|
70
|
+
run
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
def run
|
76
|
+
fork {
|
77
|
+
#require 'test'
|
78
|
+
require 'test/runner'
|
79
|
+
|
80
|
+
loadpath.each { |d| $LOAD_PATH.unshift(d) }
|
81
|
+
requires.each { |f| require f }
|
82
|
+
test_files.each { |f| require f }
|
83
|
+
|
84
|
+
suite = $TEST_SUITE || []
|
85
|
+
runner = new(suite, :format=>format, :tags=>tags, :match=>match)
|
86
|
+
success = runner.run
|
87
|
+
|
88
|
+
exit -1 unless success
|
89
|
+
}
|
90
|
+
Process.wait
|
91
|
+
end
|
92
|
+
|
93
|
+
# Resolve test globs.
|
94
|
+
#--
|
95
|
+
# TODO: simplify?
|
96
|
+
#++
|
97
|
+
def test_files
|
98
|
+
files = tests
|
99
|
+
files = files.map{ |f| Dir[f] }.flatten
|
100
|
+
files = files.map{ |f| File.directory?(f) ? Dir[File.join(f, '**/*.rb')] : f }
|
101
|
+
files = files.flatten.uniq
|
102
|
+
files = files.map{ |f| File.expand_path(f) }
|
103
|
+
files
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
def default_tests
|
108
|
+
if ENV['tests']
|
109
|
+
ENV['tests'].split(/[:;]/)
|
110
|
+
else
|
111
|
+
DEFAULT_TESTS
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
#def ruby_command
|
117
|
+
# File.join(RbConfig::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
|
118
|
+
#end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Test
|
2
|
+
|
3
|
+
# Recorder class is an observer that tracks all tests
|
4
|
+
# that are run and categorizes them according to their
|
5
|
+
# test status.
|
6
|
+
class Recorder
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@table = Hash.new{ |h,k| h[k] = [] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
@table[key.to_sym]
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
def skip(test)
|
18
|
+
self[:skip] << test
|
19
|
+
end
|
20
|
+
|
21
|
+
# Add `test` to pass set.
|
22
|
+
def pass(test)
|
23
|
+
self[:pass] << test
|
24
|
+
end
|
25
|
+
|
26
|
+
def fail(test, exception)
|
27
|
+
self[:fail] << [test, exception]
|
28
|
+
end
|
29
|
+
|
30
|
+
def error(test, exception)
|
31
|
+
self[:error] << [test, exception]
|
32
|
+
end
|
33
|
+
|
34
|
+
def todo(test, exception)
|
35
|
+
self[:todo] << [test, exception]
|
36
|
+
end
|
37
|
+
|
38
|
+
def omit(test, exception)
|
39
|
+
self[:omit] << [test, exception]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if their are no test errors or failures.
|
43
|
+
def success?
|
44
|
+
self[:error].size + self[:fail].size > 0 ? false : true
|
45
|
+
end
|
46
|
+
|
47
|
+
# Ignore any other signals.
|
48
|
+
def method_missing(*a)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Test
|
4
|
+
|
5
|
+
#
|
6
|
+
module Reporters
|
7
|
+
|
8
|
+
# Test Reporter Base Class
|
9
|
+
class Abstract
|
10
|
+
|
11
|
+
#
|
12
|
+
def self.inherited(base)
|
13
|
+
registry << base
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
def self.registry
|
18
|
+
@registry ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
def initialize(runner)
|
23
|
+
@runner = runner
|
24
|
+
#@source = {}
|
25
|
+
|
26
|
+
# in case start_suite is overridden
|
27
|
+
@start_time = Time.now
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
attr :runner
|
32
|
+
|
33
|
+
#
|
34
|
+
def begin_suite(test_suite)
|
35
|
+
@start_time = Time.now
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
def begin_case(test_case)
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def begin_test(test)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
def skip_case(test_case)
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
def skip_test(test)
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
#def test(test)
|
56
|
+
#end
|
57
|
+
|
58
|
+
#
|
59
|
+
def pass(test)
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
def fail(test, exception)
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
def error(test, exception)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Report a pending test.
|
71
|
+
def todo(test, exception)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Report an omitted test.
|
75
|
+
def omit(test, exception)
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
def end_test(test)
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
def end_case(test_case)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
def end_suite(test_suite)
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
|
92
|
+
def record
|
93
|
+
runner.recorder
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is coverage information requested?
|
97
|
+
#def cover?
|
98
|
+
# runner.cover?
|
99
|
+
#end
|
100
|
+
|
101
|
+
# Count up the total number of tests.
|
102
|
+
def total_count(suite)
|
103
|
+
c = 0
|
104
|
+
suite.each do |tc|
|
105
|
+
if tc.respond_to?(:each)
|
106
|
+
c += total_count(tc)
|
107
|
+
else
|
108
|
+
c += 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
return c
|
112
|
+
end
|
113
|
+
|
114
|
+
# Common timestamp any reporter can use.
|
115
|
+
def timestamp
|
116
|
+
seconds = Time.now - @start_time
|
117
|
+
|
118
|
+
"Finished in %.5fs, %.2f tests/s." % [seconds, total/seconds]
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
def total
|
123
|
+
@total ||= subtotal
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
def subtotal
|
128
|
+
[:todo, :pass, :fail, :error, :omit, :skip].inject(0) do |s,r|
|
129
|
+
s += record[r.to_sym].size; s
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# TODO: lump skipped and omitted into one group ?
|
134
|
+
|
135
|
+
TITLES = {
|
136
|
+
:pass => 'passing',
|
137
|
+
:fail => 'failures',
|
138
|
+
:error => 'errors',
|
139
|
+
:todo => 'pending',
|
140
|
+
:omit => 'omissions',
|
141
|
+
:skip => 'skipped'
|
142
|
+
}
|
143
|
+
|
144
|
+
# TODO: Add assertion counts (if reasonably possible).
|
145
|
+
|
146
|
+
# Common tally stamp any reporter can use.
|
147
|
+
#
|
148
|
+
# @return [String] tally stamp
|
149
|
+
def tally
|
150
|
+
sizes = {}
|
151
|
+
names = %w{pass error fail todo omit skip}.map{ |n| n.to_sym }
|
152
|
+
names.each do |r|
|
153
|
+
sizes[r] = record[r].size
|
154
|
+
end
|
155
|
+
|
156
|
+
#names.unshift(:tests)
|
157
|
+
#sizes[:tests] = total
|
158
|
+
|
159
|
+
s = []
|
160
|
+
names.each do |n|
|
161
|
+
next unless sizes[n] > 0
|
162
|
+
s << tally_item(n, sizes)
|
163
|
+
end
|
164
|
+
|
165
|
+
'Executed ' + "#{total}".ansi(:bold) + ' tests with ' + s.join(', ') + '.'
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
def tally_item(name, sizes)
|
170
|
+
x = []
|
171
|
+
x << "%s" % sizes[name].to_s.ansi(:bold)
|
172
|
+
x << " %s" % TITLES[name].downcase
|
173
|
+
x << " (%.1f%%)" % ((sizes[name].to_f/total*100)) if runner.verbose?
|
174
|
+
x.join('')
|
175
|
+
end
|
176
|
+
|
177
|
+
#--
|
178
|
+
# TODO: Matching `bin/ruby-test` is not robust.
|
179
|
+
#++
|
180
|
+
|
181
|
+
# Remove reference to lemon library from backtrace.
|
182
|
+
#
|
183
|
+
# @param [Exception] exception
|
184
|
+
# The error that was rasied.
|
185
|
+
#
|
186
|
+
# @return [Array] filtered backtrace
|
187
|
+
def clean_backtrace(exception)
|
188
|
+
trace = (Exception === exception ? exception.backtrace : exception)
|
189
|
+
return trace if $DEBUG
|
190
|
+
trace = trace.reject{ |t| RUBY_IGNORE_CALLERS.any?{ |r| r =~ t }}
|
191
|
+
trace = trace.map do |t|
|
192
|
+
i = t.index(':in')
|
193
|
+
i ? t[0...i] : t
|
194
|
+
end
|
195
|
+
#if trace.empty?
|
196
|
+
# exception
|
197
|
+
#else
|
198
|
+
# exception.set_backtrace(trace) if Exception === exception
|
199
|
+
# exception
|
200
|
+
#end
|
201
|
+
trace.uniq
|
202
|
+
end
|
203
|
+
|
204
|
+
# That an exception, backtrace or source code text and line
|
205
|
+
# number and return a CodeSnippet object.
|
206
|
+
#
|
207
|
+
# @return [CodeSnippet] code snippet
|
208
|
+
def code(source, line=nil)
|
209
|
+
case source
|
210
|
+
when Exception
|
211
|
+
CodeSnippet.from_backtrace(clean_backtrace(source.backtrace))
|
212
|
+
when Array
|
213
|
+
CodeSnippet.from_backtrace(clean_backtrace(source))
|
214
|
+
else
|
215
|
+
CodeSnippet.new(source, line)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
#--
|
220
|
+
# TODO: Show more of the file name than just the basename.
|
221
|
+
#++
|
222
|
+
|
223
|
+
#
|
224
|
+
def file_and_line(exception)
|
225
|
+
line = clean_backtrace(exception)[0]
|
226
|
+
return "" unless line
|
227
|
+
i = line.rindex(':in')
|
228
|
+
line = i ? line[0...i] : line
|
229
|
+
File.basename(line)
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
def file_and_line_array(exception)
|
234
|
+
case exception
|
235
|
+
when Exception
|
236
|
+
line = exception.backtrace[0]
|
237
|
+
else
|
238
|
+
line = exception[0] # backtrace
|
239
|
+
end
|
240
|
+
return ["", 0] unless line
|
241
|
+
i = line.rindex(':in')
|
242
|
+
line = i ? line[0...i] : line
|
243
|
+
f, l = File.basename(line).split(':')
|
244
|
+
return [f, l.to_i]
|
245
|
+
end
|
246
|
+
|
247
|
+
#
|
248
|
+
def file(exception)
|
249
|
+
file_and_line_array(exception).first
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
def line(exception)
|
254
|
+
file_and_line_array(exception).last
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Test::Reporters
|
4
|
+
|
5
|
+
# Hash Abstract is a base class for the TAP-Y
|
6
|
+
# and TAP-J reporters.
|
7
|
+
#
|
8
|
+
class AbstractHash < Abstract
|
9
|
+
|
10
|
+
#
|
11
|
+
def begin_suite(suite)
|
12
|
+
require 'yaml'
|
13
|
+
|
14
|
+
@start_time = Time.now
|
15
|
+
@case_level = 0
|
16
|
+
@test_index = 0
|
17
|
+
|
18
|
+
now = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
19
|
+
|
20
|
+
h = {
|
21
|
+
'type' => 'suite',
|
22
|
+
'start' => now,
|
23
|
+
'count' => total_count(suite)
|
24
|
+
}
|
25
|
+
|
26
|
+
return h
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
def begin_case(test_case)
|
31
|
+
h = {}
|
32
|
+
h['type' ] = 'case'
|
33
|
+
h['level'] = @case_level
|
34
|
+
|
35
|
+
merge_subtype h, test_case
|
36
|
+
merge_setup h, test_case
|
37
|
+
merge_label h, test_case
|
38
|
+
|
39
|
+
@case_level += 1
|
40
|
+
|
41
|
+
return h
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
def begin_test(test)
|
46
|
+
@test_index += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
def pass(test) #, backtrace=nil)
|
51
|
+
h = {}
|
52
|
+
h['type' ] = 'test'
|
53
|
+
h['status'] = 'pass'
|
54
|
+
|
55
|
+
merge_subtype h, test
|
56
|
+
merge_setup h, test
|
57
|
+
merge_label h, test
|
58
|
+
#merge_comparison h, test, exception
|
59
|
+
#merge_coverage h, test
|
60
|
+
merge_source h, test
|
61
|
+
merge_time h
|
62
|
+
|
63
|
+
return h
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
def fail(test, exception)
|
68
|
+
h = {}
|
69
|
+
h['type' ] = 'test'
|
70
|
+
h['status'] = 'fail'
|
71
|
+
|
72
|
+
merge_subtype h, test
|
73
|
+
merge_setup h, test
|
74
|
+
merge_label h, test
|
75
|
+
#merge_comparison h, test, exception
|
76
|
+
#merge_coverage h, test
|
77
|
+
merge_source h, test
|
78
|
+
merge_exception h, test, exception
|
79
|
+
merge_time h
|
80
|
+
|
81
|
+
return h
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
def error(test, exception)
|
86
|
+
h = {}
|
87
|
+
h['type' ] = 'test'
|
88
|
+
h['status'] = 'error'
|
89
|
+
|
90
|
+
merge_subtype h, test
|
91
|
+
merge_setup h, test
|
92
|
+
merge_label h, test
|
93
|
+
#merge_comparison h, test, exception
|
94
|
+
#merge_coverage h, test
|
95
|
+
merge_source h, test
|
96
|
+
merge_exception h, test, exception, true
|
97
|
+
merge_time h
|
98
|
+
|
99
|
+
return h
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
def todo(test, exception)
|
104
|
+
h = {}
|
105
|
+
h['type' ] = 'test'
|
106
|
+
h['status'] = 'todo'
|
107
|
+
|
108
|
+
merge_subtype h, test
|
109
|
+
merge_setup h, test
|
110
|
+
merge_label h, test
|
111
|
+
#merge_comparison h, test, exception
|
112
|
+
#merge_coverage h, test
|
113
|
+
merge_source h, test
|
114
|
+
merge_exception h, test, exception
|
115
|
+
merge_time h
|
116
|
+
|
117
|
+
return h
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
def omit(test, exception)
|
122
|
+
h = {}
|
123
|
+
h['type' ] = 'test'
|
124
|
+
h['status'] = 'omit'
|
125
|
+
|
126
|
+
merge_subtype h, test
|
127
|
+
merge_setup h, test
|
128
|
+
merge_label h, test
|
129
|
+
#merge_comparison h, test, exception
|
130
|
+
#merge_coverage h, test
|
131
|
+
merge_source h, test
|
132
|
+
merge_exception h, test, exception
|
133
|
+
merge_time h
|
134
|
+
|
135
|
+
return h
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
def end_case(test_case)
|
140
|
+
@case_level -= 1
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
def end_suite(suite)
|
145
|
+
h = {
|
146
|
+
'type' => 'tally',
|
147
|
+
'time' => Time.now - @start_time,
|
148
|
+
'counts' => {
|
149
|
+
'total' => total,
|
150
|
+
'pass' => record[:pass].size,
|
151
|
+
'fail' => record[:fail].size,
|
152
|
+
'error' => record[:error].size,
|
153
|
+
'omit' => record[:omit].size,
|
154
|
+
'todo' => record[:todo].size
|
155
|
+
}
|
156
|
+
}
|
157
|
+
return h
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
#
|
163
|
+
def merge_subtype(hash, test)
|
164
|
+
hash['subtype'] = test.type.to_s if test.respond_to?(:type)
|
165
|
+
end
|
166
|
+
|
167
|
+
# TODO: topic or setup ?
|
168
|
+
def merge_setup(hash, test)
|
169
|
+
#hash['setup'] = test.setup.to_s if test.respond_to?(:setup)
|
170
|
+
hash['setup'] = test.topic.to_s if test.respond_to?(:topic)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Add test description to hash.
|
174
|
+
def merge_label(hash, test)
|
175
|
+
hash['label'] = test.to_s.strip
|
176
|
+
end
|
177
|
+
|
178
|
+
# NOTE: Not presently used.
|
179
|
+
def merge_comparison(hash, test, exception)
|
180
|
+
hash['returned'] = exception.returned
|
181
|
+
hash['expected'] = exception.expected
|
182
|
+
end
|
183
|
+
|
184
|
+
# Add source location information to hash.
|
185
|
+
def merge_source(hash, test)
|
186
|
+
if test.respond_to?('source_location')
|
187
|
+
file, line = source_location
|
188
|
+
hash['file' ] = file
|
189
|
+
hash['line' ] = line
|
190
|
+
hash['source' ] = code(file, line).to_str
|
191
|
+
hash['snippet'] = code(file, line).to_omap
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Add exception subsection of hash.
|
196
|
+
def merge_exception(hash, test, exception, bt=false)
|
197
|
+
hash['exception'] = {}
|
198
|
+
hash['exception']['file' ] = code(exception).file
|
199
|
+
hash['exception']['line' ] = code(exception).line
|
200
|
+
hash['exception']['source' ] = code(exception).to_str
|
201
|
+
hash['exception']['snippet' ] = code(exception).to_omap
|
202
|
+
hash['exception']['message' ] = exception.message
|
203
|
+
hash['exception']['backtrace'] = clean_backtrace(exception) if bt
|
204
|
+
end
|
205
|
+
|
206
|
+
# TODO: Really?
|
207
|
+
def merge_coverage(hash, test)
|
208
|
+
if test.respond_to?(:file_coverage) or test.respond_to?(:code_coverage)
|
209
|
+
fc = test.file_coverage
|
210
|
+
cc = test.code_coverage
|
211
|
+
hash['coverage'] = {}
|
212
|
+
hash['coverage']['file'] = fc if fc
|
213
|
+
hash['coverage']['code'] = cc if cc
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
#
|
218
|
+
def merge_time(hash)
|
219
|
+
hash['time'] = Time.now - @start_time
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|