test 0.2.0

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