test 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|