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.
@@ -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