test-unit-runner-tap 1.0.0 → 1.1.1
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/Gemfile.lock +16 -0
- data/README.rdoc +17 -0
- data/lib/test/unit/runner/tap-version.rb +1 -1
- data/lib/test/unit/runner/tap.rb +32 -3
- data/lib/test/unit/ui/tap/base_testrunner.rb +550 -0
- data/lib/test/unit/ui/tap/json_testrunner.rb +55 -0
- data/lib/test/unit/ui/tap/perl_testrunner.rb +181 -0
- data/lib/test/unit/ui/tap/yaml_testrunner.rb +56 -0
- data/test/fixtures/test_example.rb +21 -0
- data/test/run-test.rb +27 -0
- data/test/test_tap.rb +15 -10
- data/test/test_tap0.rb +38 -0
- data/test/test_tapy.rb +116 -0
- metadata +17 -11
- data/.ruby +0 -40
- data/COPYING +0 -814
- data/lib/test/unit/ui/tap/testrunner.rb +0 -84
data/Gemfile.lock
ADDED
data/README.rdoc
CHANGED
@@ -18,8 +18,25 @@ TAP-Y/J format.
|
|
18
18
|
|
19
19
|
== USAGE:
|
20
20
|
|
21
|
+
It you test helper script use:
|
22
|
+
|
21
23
|
require 'test/unit/runner/tap'
|
22
24
|
|
25
|
+
Then you can select the runner via the `--runner` command line option.
|
26
|
+
|
27
|
+
$ ruby test/run_tests.rb --runner yaml
|
28
|
+
|
29
|
+
Available runners are `tap`, `tapy`/`yaml` or `tapj`/`json`.
|
30
|
+
|
31
|
+
The runner can also be specified in code, if need be. See API documentation
|
32
|
+
for more information on how to do this.
|
33
|
+
|
34
|
+
To use TAP-Y/J formats with TAPOUT, just pipe results to tapout utility.
|
35
|
+
|
36
|
+
$ ruby test/run_tests.rb --runner yaml | tapout
|
37
|
+
|
38
|
+
See TAPOUT poject for more information on that.
|
39
|
+
|
23
40
|
== LICENSE:
|
24
41
|
|
25
42
|
(LGPL v3.0 License)
|
data/lib/test/unit/runner/tap.rb
CHANGED
@@ -1,11 +1,40 @@
|
|
1
|
-
require 'test
|
1
|
+
require 'test-unit'
|
2
2
|
require 'test/unit/runner/tap-version'
|
3
3
|
|
4
4
|
module Test
|
5
5
|
module Unit
|
6
6
|
AutoRunner.register_runner(:tap) do |auto_runner|
|
7
|
-
require 'test/unit/ui/tap/
|
8
|
-
Test::Unit::UI::Tap::
|
7
|
+
require 'test/unit/ui/tap/perl_testrunner'
|
8
|
+
Test::Unit::UI::Tap::PerlTestRunner
|
9
|
+
end
|
10
|
+
|
11
|
+
AutoRunner.register_runner(:tapj) do |auto_runner|
|
12
|
+
require 'test/unit/ui/tap/json_testrunner'
|
13
|
+
Test::Unit::UI::Tap::JSONTestRunner
|
14
|
+
end
|
15
|
+
|
16
|
+
# alias for tap-j
|
17
|
+
AutoRunner.register_runner(:json) do |auto_runner|
|
18
|
+
require 'test/unit/ui/tap/json_testrunner'
|
19
|
+
Test::Unit::UI::Tap::JSONTestRunner
|
20
|
+
end
|
21
|
+
|
22
|
+
AutoRunner.register_runner(:tapy) do |auto_runner|
|
23
|
+
require 'test/unit/ui/tap/yaml_testrunner'
|
24
|
+
Test::Unit::UI::Tap::YAMLTestRunner
|
25
|
+
end
|
26
|
+
|
27
|
+
# alias for tap-y
|
28
|
+
AutoRunner.register_runner(:yaml) do |auto_runner|
|
29
|
+
require 'test/unit/ui/tap/yaml_testrunner'
|
30
|
+
Test::Unit::UI::Tap::YAMLTestRunner
|
31
|
+
end
|
32
|
+
|
33
|
+
# temporaryily available as fallback to orignal tap runner
|
34
|
+
# just in case the new runner exhibits any issues
|
35
|
+
AutoRunner.register_runner(:tap0) do |auto_runner|
|
36
|
+
require 'test/unit/ui/tap/perl_testrunner'
|
37
|
+
Test::Unit::UI::Tap::OldTestRunner
|
9
38
|
end
|
10
39
|
end
|
11
40
|
end
|
@@ -0,0 +1,550 @@
|
|
1
|
+
require 'test/unit/ui/testrunner'
|
2
|
+
require 'test/unit/ui/testrunnermediator'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module Test
|
6
|
+
module Unit
|
7
|
+
module UI
|
8
|
+
module Tap
|
9
|
+
|
10
|
+
# Base class for all TAP runners.
|
11
|
+
#
|
12
|
+
class BaseTestRunner < Test::Unit::UI::TestRunner
|
13
|
+
|
14
|
+
# TAP-Y/J Revision
|
15
|
+
REVISION = 4
|
16
|
+
|
17
|
+
#
|
18
|
+
def initialize(suite, options={})
|
19
|
+
super
|
20
|
+
|
21
|
+
@output = @options[:output] || STDOUT
|
22
|
+
|
23
|
+
@level = 0
|
24
|
+
|
25
|
+
@_source_cache = {}
|
26
|
+
@already_outputted = false
|
27
|
+
@top_level = true
|
28
|
+
|
29
|
+
@counts = Hash.new{ |h,k| h[k] = 0 }
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
#
|
35
|
+
def setup_mediator
|
36
|
+
super
|
37
|
+
|
38
|
+
#suite_name = @suite.to_s # file name
|
39
|
+
#suite_name = @suite.name if @suite.kind_of?(Module)
|
40
|
+
|
41
|
+
#reset_output # TODO: Should we do this up front?
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
def attach_to_mediator
|
46
|
+
@mediator.add_listener(Test::Unit::TestResult::FAULT, &method(:tapout_fault))
|
47
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::STARTED, &method(:tapout_before_suite))
|
48
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:tapout_after_suite))
|
49
|
+
@mediator.add_listener(Test::Unit::TestCase::STARTED_OBJECT, &method(:tapout_before_test))
|
50
|
+
@mediator.add_listener(Test::Unit::TestCase::FINISHED_OBJECT, &method(:tapout_pass))
|
51
|
+
@mediator.add_listener(Test::Unit::TestSuite::STARTED_OBJECT, &method(:tapout_before_case))
|
52
|
+
@mediator.add_listener(Test::Unit::TestSuite::FINISHED_OBJECT, &method(:tapout_after_case))
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Before everything else.
|
57
|
+
#
|
58
|
+
def tapout_before_suite(result)
|
59
|
+
@result = result
|
60
|
+
@suite_start = Time.now
|
61
|
+
|
62
|
+
doc = {
|
63
|
+
'type' => 'suite',
|
64
|
+
'start' => @suite_start.strftime('%Y-%m-%d %H:%M:%S'),
|
65
|
+
'count' => @suite.size,
|
66
|
+
#'seed' => #@suite.seed, # no seed?
|
67
|
+
'rev' => REVISION
|
68
|
+
}
|
69
|
+
return doc
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# After everything else.
|
74
|
+
#
|
75
|
+
def tapout_after_suite(elapsed_time)
|
76
|
+
doc = {
|
77
|
+
'type' => 'final',
|
78
|
+
'time' => elapsed_time, #Time.now - @suite_start,
|
79
|
+
'counts' => {
|
80
|
+
'total' => @counts[:total],
|
81
|
+
'pass' => @counts[:pass], #self.test_count - self.failures - self.errors - self.skips,
|
82
|
+
'fail' => @counts[:fail],
|
83
|
+
'error' => @counts[:error],
|
84
|
+
'omit' => @counts[:omit],
|
85
|
+
'todo' => @counts[:todo],
|
86
|
+
} #,
|
87
|
+
#'assertions' => {
|
88
|
+
# 'total' => @result.assertion_count + @counts[:fail],
|
89
|
+
# 'pass' => @result.assertion_count,
|
90
|
+
# 'fail' => @counts[:fail]
|
91
|
+
#}
|
92
|
+
}
|
93
|
+
return doc
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
#
|
98
|
+
#
|
99
|
+
def tapout_before_case(testcase)
|
100
|
+
return nil if testcase.test_case.nil?
|
101
|
+
|
102
|
+
@test_case = testcase
|
103
|
+
|
104
|
+
doc = {
|
105
|
+
'type' => 'case',
|
106
|
+
#'subtype' => '',
|
107
|
+
'label' => testcase.name,
|
108
|
+
'level' => @level
|
109
|
+
}
|
110
|
+
|
111
|
+
@level += 1
|
112
|
+
|
113
|
+
return doc
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# After each case, decrement the case level.
|
118
|
+
#
|
119
|
+
def tapout_after_case(testcase)
|
120
|
+
@level -= 1
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
def tapout_before_test(test)
|
125
|
+
@test_start = Time.now
|
126
|
+
# set up stdout and stderr to be captured
|
127
|
+
reset_output
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
def tapout_fault(fault)
|
132
|
+
case fault
|
133
|
+
when Test::Unit::Pending
|
134
|
+
tapout_todo(fault)
|
135
|
+
when Test::Unit::Omission
|
136
|
+
tapout_omit(fault)
|
137
|
+
when Test::Unit::Notification
|
138
|
+
tapout_note(fault)
|
139
|
+
when Test::Unit::Failure
|
140
|
+
tapout_fail(fault)
|
141
|
+
else
|
142
|
+
tapout_error(fault)
|
143
|
+
end
|
144
|
+
|
145
|
+
@already_outputted = true #if fault.critical?
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
def tapout_note(note)
|
150
|
+
doc = {
|
151
|
+
'type' => 'note',
|
152
|
+
'text' => note.message
|
153
|
+
}
|
154
|
+
return doc
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
def tapout_pass(test)
|
159
|
+
if @already_outputted
|
160
|
+
@already_outputted = false
|
161
|
+
return nil
|
162
|
+
end
|
163
|
+
|
164
|
+
@counts[:total] += 1
|
165
|
+
@counts[:pass] += 1
|
166
|
+
|
167
|
+
doc = {
|
168
|
+
'type' => 'test',
|
169
|
+
#'subtype' => '',
|
170
|
+
'status' => 'pass',
|
171
|
+
#'setup': foo instance
|
172
|
+
'label' => clean_label(test.name),
|
173
|
+
#'expected' => 2
|
174
|
+
#'returned' => 2
|
175
|
+
#'file' => test_file
|
176
|
+
#'line' => test_line
|
177
|
+
#'source' => source(test_file)[test_line-1].strip,
|
178
|
+
#'snippet' => code_snippet(test_file, test_line),
|
179
|
+
'time' => Time.now - @suite_start
|
180
|
+
}
|
181
|
+
|
182
|
+
doc.update(captured_output)
|
183
|
+
|
184
|
+
return doc
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
def tapout_todo(fault)
|
189
|
+
@counts[:total] += 1
|
190
|
+
@counts[:todo] += 1
|
191
|
+
|
192
|
+
file, line = location(fault.location)
|
193
|
+
rel_file = file.sub(Dir.pwd+'/', '')
|
194
|
+
|
195
|
+
doc = {
|
196
|
+
'type' => 'test',
|
197
|
+
#'subtype' => '',
|
198
|
+
'status' => 'todo',
|
199
|
+
'label' => clean_label(fault.test_name),
|
200
|
+
#'setup' => "foo instance",
|
201
|
+
#'expected' => 2,
|
202
|
+
#'returned' => 1,
|
203
|
+
#'file' => test_file
|
204
|
+
#'line' => test_line
|
205
|
+
#'source' => source(test_file)[test_line-1].strip,
|
206
|
+
#'snippet' => code_snippet(test_file, test_line),
|
207
|
+
'exception' => {
|
208
|
+
'message' => clean_message(fault.message),
|
209
|
+
'class' => fault.class.name,
|
210
|
+
'file' => rel_file,
|
211
|
+
'line' => line,
|
212
|
+
'source' => source(file)[line-1].strip,
|
213
|
+
'snippet' => code_snippet(file, line),
|
214
|
+
'backtrace' => filter_backtrace(fault.location)
|
215
|
+
},
|
216
|
+
'time' => Time.now - @suite_start
|
217
|
+
}
|
218
|
+
|
219
|
+
doc.update(captured_output)
|
220
|
+
|
221
|
+
return doc
|
222
|
+
end
|
223
|
+
|
224
|
+
#
|
225
|
+
def tapout_omit(fault)
|
226
|
+
@counts[:total] += 1
|
227
|
+
@counts[:omit] += 1
|
228
|
+
|
229
|
+
file, line = location(fault.location)
|
230
|
+
rel_file = file.sub(Dir.pwd+'/', '')
|
231
|
+
|
232
|
+
doc = {
|
233
|
+
'type' => 'test',
|
234
|
+
#'subtype' => '',
|
235
|
+
'status' => 'skip',
|
236
|
+
'label' => clean_label(fault.test_name),
|
237
|
+
#'setup' => "foo instance",
|
238
|
+
#'expected' => 2,
|
239
|
+
#'returned' => 1,
|
240
|
+
#'file' => test_file
|
241
|
+
#'line' => test_line
|
242
|
+
#'source' => source(test_file)[test_line-1].strip,
|
243
|
+
#'snippet' => code_snippet(test_file, test_line),
|
244
|
+
'exception' => {
|
245
|
+
'message' => clean_message(fault.message),
|
246
|
+
'class' => fault.class.name,
|
247
|
+
'file' => rel_file,
|
248
|
+
'line' => line,
|
249
|
+
'source' => source(file)[line-1].strip,
|
250
|
+
'snippet' => code_snippet(file, line),
|
251
|
+
'backtrace' => filter_backtrace(fault.location)
|
252
|
+
},
|
253
|
+
'time' => Time.now - @suite_start
|
254
|
+
}
|
255
|
+
|
256
|
+
doc.update(captured_output)
|
257
|
+
|
258
|
+
return doc
|
259
|
+
end
|
260
|
+
|
261
|
+
#
|
262
|
+
def tapout_fail(fault)
|
263
|
+
@counts[:total] += 1
|
264
|
+
@counts[:fail] += 1
|
265
|
+
|
266
|
+
file, line = location(fault.location)
|
267
|
+
rel_file = file.sub(Dir.pwd+'/', '')
|
268
|
+
|
269
|
+
doc = {
|
270
|
+
'type' => 'test',
|
271
|
+
#'subtype' => '',
|
272
|
+
'status' => 'fail',
|
273
|
+
'label' => clean_label(fault.test_name),
|
274
|
+
#'setup' => "foo instance",
|
275
|
+
'expected' => fault.inspected_expected,
|
276
|
+
'returned' => fault.inspected_actual,
|
277
|
+
#'file' => test_file
|
278
|
+
#'line' => test_line
|
279
|
+
#'source' => ok 1, 2
|
280
|
+
#'snippet' =>
|
281
|
+
# - 44: ok 0,0
|
282
|
+
# - 45: ok 1,2
|
283
|
+
# - 46: ok 2,4
|
284
|
+
'exception' => {
|
285
|
+
'message' => clean_message(fault.user_message || fault.message),
|
286
|
+
'class' => fault.class.name,
|
287
|
+
'file' => rel_file,
|
288
|
+
'line' => line,
|
289
|
+
'source' => source(file)[line-1].strip,
|
290
|
+
'snippet' => code_snippet(file, line),
|
291
|
+
'backtrace' => filter_backtrace(fault.location)
|
292
|
+
},
|
293
|
+
'time' => Time.now - @suite_start
|
294
|
+
}
|
295
|
+
|
296
|
+
doc.update(captured_output)
|
297
|
+
|
298
|
+
return doc
|
299
|
+
end
|
300
|
+
|
301
|
+
#
|
302
|
+
def tapout_error(fault)
|
303
|
+
@counts[:total] += 1
|
304
|
+
@counts[:error] += 1
|
305
|
+
|
306
|
+
file, line = location(fault.location)
|
307
|
+
rel_file = file.sub(Dir.pwd+'/', '')
|
308
|
+
|
309
|
+
doc = {
|
310
|
+
'type' => 'test',
|
311
|
+
#'subtype' => '',
|
312
|
+
'status' => 'error',
|
313
|
+
'label' => clean_label(fault.test_name),
|
314
|
+
#'setup' => "foo instance",
|
315
|
+
#'expected' => fault.inspected_expected,
|
316
|
+
#'returned' => fault.inspected_actual,
|
317
|
+
#'file' => test_file
|
318
|
+
#'line' => test_line
|
319
|
+
#'source' => ok 1, 2
|
320
|
+
#'snippet' =>
|
321
|
+
# - 44: ok 0,0
|
322
|
+
# - 45: ok 1,2
|
323
|
+
# - 46: ok 2,4
|
324
|
+
'exception' => {
|
325
|
+
'message' => clean_message(fault.message),
|
326
|
+
'class' => fault.class.name,
|
327
|
+
'file' => rel_file,
|
328
|
+
'line' => line,
|
329
|
+
'source' => source(file)[line-1].strip,
|
330
|
+
'snippet' => code_snippet(file, line),
|
331
|
+
'backtrace' => filter_backtrace(fault.location)
|
332
|
+
},
|
333
|
+
'time' => Time.now - @suite_start
|
334
|
+
}
|
335
|
+
|
336
|
+
doc.update(captured_output)
|
337
|
+
|
338
|
+
return doc
|
339
|
+
end
|
340
|
+
|
341
|
+
#
|
342
|
+
def clean_label(name)
|
343
|
+
name.sub(/\(.+?\)\z/, '').chomp('()')
|
344
|
+
end
|
345
|
+
|
346
|
+
# Clean the backtrace of any reference to test framework itself.
|
347
|
+
def filter_backtrace(backtrace)
|
348
|
+
trace = backtrace
|
349
|
+
|
350
|
+
## remove backtraces that match any pattern in $RUBY_IGNORE_CALLERS
|
351
|
+
#trace = race.reject{|b| $RUBY_IGNORE_CALLERS.any?{|i| i=~b}}
|
352
|
+
|
353
|
+
## remove `:in ...` portion of backtraces
|
354
|
+
trace = trace.map do |bt|
|
355
|
+
i = bt.index(':in')
|
356
|
+
i ? bt[0...i] : bt
|
357
|
+
end
|
358
|
+
|
359
|
+
# TODO: does TestUnit have a filter ?
|
360
|
+
## now apply MiniTest's own filter (note: doesn't work if done first, why?)
|
361
|
+
#trace = MiniTest::filter_backtrace(trace)
|
362
|
+
|
363
|
+
## if the backtrace is empty now then revert to the original
|
364
|
+
trace = backtrace if trace.empty?
|
365
|
+
|
366
|
+
## simplify paths to be relative to current workding diectory
|
367
|
+
trace = trace.map{ |bt| bt.sub(Dir.pwd+File::SEPARATOR,'') }
|
368
|
+
|
369
|
+
return trace
|
370
|
+
end
|
371
|
+
|
372
|
+
# Returns a Hash of source code.
|
373
|
+
def code_snippet(file, line)
|
374
|
+
s = []
|
375
|
+
if File.file?(file)
|
376
|
+
source = source(file)
|
377
|
+
radius = 2 # TODO: make customizable (number of surrounding lines to show)
|
378
|
+
region = [line - radius, 1].max ..
|
379
|
+
[line + radius, source.length].min
|
380
|
+
|
381
|
+
s = region.map do |n|
|
382
|
+
{n => source[n-1].chomp}
|
383
|
+
end
|
384
|
+
end
|
385
|
+
return s
|
386
|
+
end
|
387
|
+
|
388
|
+
# Return nicely formated String of code lines.
|
389
|
+
def code_snippet_string(file, line)
|
390
|
+
str = []
|
391
|
+
snp = code_snippet_array(file, line)
|
392
|
+
max = snp.map{ |n, c| n.to_s.size }.max
|
393
|
+
snp.each do |n, c|
|
394
|
+
if n == line
|
395
|
+
str << "=> %#{max}d %s" % [n, c]
|
396
|
+
else
|
397
|
+
str << " %#{max}d %s" % [n, c]
|
398
|
+
end
|
399
|
+
end
|
400
|
+
str.join("\n")
|
401
|
+
end
|
402
|
+
|
403
|
+
# Return Array of source code line numbers and text.
|
404
|
+
def code_snippet_array(file, line)
|
405
|
+
snp = []
|
406
|
+
if File.file?(file)
|
407
|
+
source = source(file)
|
408
|
+
radius = 2 # TODO: make customizable (number of surrounding lines to show)
|
409
|
+
region = [line - radius, 1].max ..
|
410
|
+
[line + radius, source.length].min
|
411
|
+
snp = region.map do |n|
|
412
|
+
[n, source[n-1].chomp]
|
413
|
+
end
|
414
|
+
end
|
415
|
+
return snp
|
416
|
+
end
|
417
|
+
|
418
|
+
# Cache source file text. This is only used if the TAP-Y stream
|
419
|
+
# doesn not provide a snippet and the test file is locatable.
|
420
|
+
def source(file)
|
421
|
+
@_source_cache[file] ||= (
|
422
|
+
File.readlines(file)
|
423
|
+
)
|
424
|
+
end
|
425
|
+
|
426
|
+
# Parse source location from caller, caller[0] or an Exception object.
|
427
|
+
def parse_source_location(caller)
|
428
|
+
case caller
|
429
|
+
when Exception
|
430
|
+
trace = caller.backtrace.reject{ |bt| bt =~ INTERNALS }
|
431
|
+
caller = trace.first
|
432
|
+
when Array
|
433
|
+
caller = caller.first
|
434
|
+
end
|
435
|
+
caller =~ /(.+?):(\d+(?=:|\z))/ or return ""
|
436
|
+
source_file, source_line = $1, $2.to_i
|
437
|
+
return source_file, source_line
|
438
|
+
end
|
439
|
+
|
440
|
+
# Get location of exception.
|
441
|
+
def location(backtrace)
|
442
|
+
last_before_assertion = ""
|
443
|
+
backtrace.reverse_each do |s|
|
444
|
+
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
|
445
|
+
last_before_assertion = s
|
446
|
+
end
|
447
|
+
file, line = last_before_assertion.sub(/:in .*$/, '').split(':')
|
448
|
+
line = line.to_i if line
|
449
|
+
return file, line
|
450
|
+
end
|
451
|
+
|
452
|
+
#
|
453
|
+
def clean_message(message)
|
454
|
+
message.strip.gsub(/\n+/, "\n")
|
455
|
+
end
|
456
|
+
|
457
|
+
#
|
458
|
+
def puts(string='')
|
459
|
+
@output.write(string.chomp+"\n")
|
460
|
+
@output.flush
|
461
|
+
end
|
462
|
+
|
463
|
+
#
|
464
|
+
def reset_output
|
465
|
+
@_oldout = $stdout
|
466
|
+
@_olderr = $stderr
|
467
|
+
|
468
|
+
@_newout = StringIO.new
|
469
|
+
@_newerr = StringIO.new
|
470
|
+
|
471
|
+
$stdout = @_newout
|
472
|
+
$stderr = @_newerr
|
473
|
+
end
|
474
|
+
|
475
|
+
#
|
476
|
+
def captured_output
|
477
|
+
stdout = @_newout.string.chomp("\n")
|
478
|
+
stderr = @_newerr.string.chomp("\n")
|
479
|
+
|
480
|
+
doc = {}
|
481
|
+
doc['stdout'] = stdout unless stdout.empty?
|
482
|
+
doc['stderr'] = stderr unless stderr.empty?
|
483
|
+
|
484
|
+
$stdout = @_oldout
|
485
|
+
$stderr = @_olderr
|
486
|
+
|
487
|
+
return doc
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|
491
|
+
|
492
|
+
end #module Tap
|
493
|
+
end #module UI
|
494
|
+
end #module Unit
|
495
|
+
end #module Test
|
496
|
+
|
497
|
+
|
498
|
+
|
499
|
+
|
500
|
+
=begin
|
501
|
+
# TEMPORARILY LEAVING THIS FOR REFERENCE. What's this about encoding?
|
502
|
+
|
503
|
+
def output_fault_message(fault)
|
504
|
+
if fault.expected.respond_to?(:encoding) and
|
505
|
+
fault.actual.respond_to?(:encoding) and
|
506
|
+
fault.expected.encoding != fault.actual.encoding
|
507
|
+
need_encoding = true
|
508
|
+
else
|
509
|
+
need_encoding = false
|
510
|
+
end
|
511
|
+
output(fault.user_message) if fault.user_message
|
512
|
+
output_single("<")
|
513
|
+
output_single(fault.inspected_expected, color("pass"))
|
514
|
+
output_single(">")
|
515
|
+
if need_encoding
|
516
|
+
output_single("(")
|
517
|
+
output_single(fault.expected.encoding.name, color("pass"))
|
518
|
+
output_single(")")
|
519
|
+
end
|
520
|
+
output(" expected but was")
|
521
|
+
output_single("<")
|
522
|
+
output_single(fault.inspected_actual, color("failure"))
|
523
|
+
output_single(">")
|
524
|
+
if need_encoding
|
525
|
+
output_single("(")
|
526
|
+
output_single(fault.actual.encoding.name, color("failure"))
|
527
|
+
output_single(")")
|
528
|
+
end
|
529
|
+
output("")
|
530
|
+
|
531
|
+
from, to = prepare_for_diff(fault.expected, fault.actual)
|
532
|
+
if from and to
|
533
|
+
from_lines = from.split(/\r?\n/)
|
534
|
+
to_lines = to.split(/\r?\n/)
|
535
|
+
if need_encoding
|
536
|
+
from_lines << ""
|
537
|
+
to_lines << ""
|
538
|
+
from_lines << "Encoding: #{fault.expected.encoding.name}"
|
539
|
+
to_lines << "Encoding: #{fault.actual.encoding.name}"
|
540
|
+
end
|
541
|
+
differ = ColorizedReadableDiffer.new(from_lines, to_lines, self)
|
542
|
+
if differ.need_diff?
|
543
|
+
output("")
|
544
|
+
output("diff:")
|
545
|
+
differ.diff
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
=end
|
550
|
+
|