test 0.2.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/.gemspec +152 -0
- data/.gitignore +7 -0
- data/.ruby +43 -0
- data/.test +11 -0
- data/.yardopts +7 -0
- data/Assembly +46 -0
- data/COPYING.rdoc +31 -0
- data/HISTORY.md +29 -0
- data/LICENSE.txt +25 -0
- data/MANIFEST +38 -0
- data/PROFILE +31 -0
- data/README.md +110 -0
- data/VERSION +1 -0
- data/bin/ruby-test +4 -0
- data/lib/test.rb +3 -0
- data/lib/test/autorun.rb +18 -0
- data/lib/test/cli.rb +110 -0
- data/lib/test/code_snippet.rb +93 -0
- data/lib/test/config.rb +72 -0
- data/lib/test/core_ext.rb +9 -0
- data/lib/test/core_ext/assertion.rb +30 -0
- data/lib/test/core_ext/exception.rb +8 -0
- data/lib/test/core_ext/string.rb +30 -0
- data/lib/test/rake.rb +120 -0
- data/lib/test/recorder.rb +53 -0
- data/lib/test/reporters/abstract.rb +238 -0
- data/lib/test/reporters/abstract_hash.rb +224 -0
- data/lib/test/reporters/dotprogress.rb +89 -0
- data/lib/test/reporters/html.rb +155 -0
- data/lib/test/reporters/outline.rb +211 -0
- data/lib/test/reporters/progress.rb +197 -0
- data/lib/test/reporters/summary.rb +145 -0
- data/lib/test/reporters/tap.rb +61 -0
- data/lib/test/reporters/tapj.rb +53 -0
- data/lib/test/reporters/tapy.rb +53 -0
- data/lib/test/reporters/test.rb +51 -0
- data/lib/test/runner.rb +342 -0
- data/site/assets/images/test_pattern.jpg +0 -0
- data/site/index.html +31 -0
- data/spec/01_test.md +29 -0
- data/spec/02_case.md +34 -0
- data/spec/applique/ruby-test.rb +2 -0
- data/test/basic_case.rb +11 -0
- data/try/raw_example.rb +41 -0
- data/work/NOTES.md +17 -0
- metadata +129 -0
@@ -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,238 @@
|
|
1
|
+
require 'ansi/core'
|
2
|
+
require 'test/core_ext'
|
3
|
+
require 'test/code_snippet'
|
4
|
+
|
5
|
+
ignore_path = File.expand_path(File.join(__FILE__, '../../..'))
|
6
|
+
ignore_regexp = Regexp.new(Regexp.escape(ignore_path))
|
7
|
+
|
8
|
+
RUBY_IGNORE_CALLERS = [] unless defined? RUBY_IGNORE_CALLERS
|
9
|
+
RUBY_IGNORE_CALLERS << ignore_regexp
|
10
|
+
RUBY_IGNORE_CALLERS << /bin\/ruby-test/
|
11
|
+
|
12
|
+
module Test
|
13
|
+
|
14
|
+
#
|
15
|
+
module Reporters
|
16
|
+
|
17
|
+
# Test Reporter Base Class
|
18
|
+
class Abstract
|
19
|
+
|
20
|
+
#
|
21
|
+
def self.inherited(base)
|
22
|
+
registry << base
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
def self.registry
|
27
|
+
@registry ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
def initialize(runner)
|
32
|
+
@runner = runner
|
33
|
+
#@source = {}
|
34
|
+
|
35
|
+
# in case start_suite is overridden
|
36
|
+
@start_time = Time.now
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
attr :runner
|
41
|
+
|
42
|
+
#
|
43
|
+
def begin_suite(test_suite)
|
44
|
+
@start_time = Time.now
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def begin_case(test_case)
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
def begin_test(test)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
def skip_case(test_case)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
def skip_test(test)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
#def test(test)
|
65
|
+
#end
|
66
|
+
|
67
|
+
#
|
68
|
+
def pass(test)
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
def fail(test, exception)
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
def error(test, exception)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Report a pending test.
|
80
|
+
def todo(test, exception)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Report an omitted test.
|
84
|
+
def omit(test, exception)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
def end_test(test)
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
def end_case(test_case)
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
def end_suite(test_suite)
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
def record
|
102
|
+
runner.recorder
|
103
|
+
end
|
104
|
+
|
105
|
+
# Is coverage information requested?
|
106
|
+
#def cover?
|
107
|
+
# runner.cover?
|
108
|
+
#end
|
109
|
+
|
110
|
+
# Count up the total number of tests.
|
111
|
+
def total_count(suite)
|
112
|
+
c = 0
|
113
|
+
suite.each do |tc|
|
114
|
+
if tc.respond_to?(:each)
|
115
|
+
c += total_count(tc)
|
116
|
+
else
|
117
|
+
c += 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
return c
|
121
|
+
end
|
122
|
+
|
123
|
+
# Common timestamp any reporter can use.
|
124
|
+
def timestamp
|
125
|
+
seconds = Time.now - @start_time
|
126
|
+
|
127
|
+
"Finished in %.5fs, %.2f tests/s." % [seconds, total/seconds]
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
def total
|
132
|
+
@total ||= subtotal
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
def subtotal
|
137
|
+
[:todo, :pass, :fail, :error, :omit].inject(0) do |s,r|
|
138
|
+
s += record[r.to_sym].size; s
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# TODO: Add assertion counts (if reasonably possible).
|
143
|
+
|
144
|
+
# Common tally stamp any reporter can use.
|
145
|
+
#
|
146
|
+
# @return [String] tally stamp
|
147
|
+
def tally
|
148
|
+
sizes = %w{pass fail error todo omit}.map{ |r| record[r.to_sym].size }
|
149
|
+
data = [total] + sizes
|
150
|
+
|
151
|
+
s = "%s tests: %s passing, %s failures, %s errors, %s pending, %s omissions" % data
|
152
|
+
#s += "(#{uncovered_units.size} uncovered, #{undefined_units.size} undefined)" if cover?
|
153
|
+
s
|
154
|
+
end
|
155
|
+
|
156
|
+
#--
|
157
|
+
# TODO: Matching `bin/ruby-test` is not robust.
|
158
|
+
#++
|
159
|
+
|
160
|
+
# Remove reference to lemon library from backtrace.
|
161
|
+
#
|
162
|
+
# @param [Exception] exception
|
163
|
+
# The error that was rasied.
|
164
|
+
#
|
165
|
+
# @return [Array] filtered backtrace
|
166
|
+
def clean_backtrace(exception)
|
167
|
+
trace = (Exception === exception ? exception.backtrace : exception)
|
168
|
+
return trace if $DEBUG
|
169
|
+
trace = trace.reject{ |t| RUBY_IGNORE_CALLERS.any?{ |r| r =~ t }}
|
170
|
+
trace = trace.map do |t|
|
171
|
+
i = t.index(':in')
|
172
|
+
i ? t[0...i] : t
|
173
|
+
end
|
174
|
+
#if trace.empty?
|
175
|
+
# exception
|
176
|
+
#else
|
177
|
+
# exception.set_backtrace(trace) if Exception === exception
|
178
|
+
# exception
|
179
|
+
#end
|
180
|
+
trace.uniq
|
181
|
+
end
|
182
|
+
|
183
|
+
# That an exception, backtrace or source code text and line
|
184
|
+
# number and return a CodeSnippet object.
|
185
|
+
#
|
186
|
+
# @return [CodeSnippet] code snippet
|
187
|
+
def code(source, line=nil)
|
188
|
+
case source
|
189
|
+
when Exception, Array
|
190
|
+
CodeSnippet.from_backtrace(clean_backtrace(source))
|
191
|
+
else
|
192
|
+
CodeSnippet.new(source, line)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
#--
|
197
|
+
# TODO: Show more of the file name than just the basename.
|
198
|
+
#++
|
199
|
+
|
200
|
+
#
|
201
|
+
def file_and_line(exception)
|
202
|
+
line = clean_backtrace(exception)[0]
|
203
|
+
return "" unless line
|
204
|
+
i = line.rindex(':in')
|
205
|
+
line = i ? line[0...i] : line
|
206
|
+
File.basename(line)
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
def file_and_line_array(exception)
|
211
|
+
case exception
|
212
|
+
when Exception
|
213
|
+
line = exception.backtrace[0]
|
214
|
+
else
|
215
|
+
line = exception[0] # backtrace
|
216
|
+
end
|
217
|
+
return ["", 0] unless line
|
218
|
+
i = line.rindex(':in')
|
219
|
+
line = i ? line[0...i] : line
|
220
|
+
f, l = File.basename(line).split(':')
|
221
|
+
return [f, l.to_i]
|
222
|
+
end
|
223
|
+
|
224
|
+
#
|
225
|
+
def file(exception)
|
226
|
+
file_and_line_array(exception).first
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
def line(exception)
|
231
|
+
file_and_line_array(exception).last
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'test/reporters/abstract'
|
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_description 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_description 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_description 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_description 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_description 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_description 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_description(hash, test)
|
175
|
+
hash['description'] = 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
|