test-unit 2.0.3 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +22 -0
- data/Manifest.txt +5 -3
- data/README.txt +2 -1
- data/images/color-diff.png +0 -0
- data/lib/test/unit.rb +20 -40
- data/lib/test/unit/assertionfailederror.rb +11 -0
- data/lib/test/unit/assertions.rb +30 -9
- data/lib/test/unit/autorunner.rb +32 -6
- data/lib/test/unit/collector/load.rb +3 -1
- data/lib/test/unit/color-scheme.rb +17 -1
- data/lib/test/unit/diff.rb +221 -37
- data/lib/test/unit/error.rb +7 -5
- data/lib/test/unit/failure.rb +27 -5
- data/lib/test/unit/runner/tap.rb +8 -0
- data/lib/test/unit/ui/console/testrunner.rb +251 -10
- data/lib/test/unit/ui/emacs/testrunner.rb +14 -0
- data/lib/test/unit/ui/tap/testrunner.rb +92 -0
- data/lib/test/unit/ui/testrunner.rb +8 -0
- data/lib/test/unit/version.rb +1 -1
- data/sample/{tc_adder.rb → test_adder.rb} +3 -1
- data/sample/{tc_subtracter.rb → test_subtracter.rb} +3 -1
- data/sample/test_user.rb +1 -0
- data/test/collector/test-load.rb +1 -5
- data/test/run-test.rb +2 -0
- data/test/test-color-scheme.rb +11 -0
- data/test/test-diff.rb +33 -7
- data/test/test_assertions.rb +8 -10
- metadata +15 -13
- data/sample/ts_examples.rb +0 -7
data/lib/test/unit/error.rb
CHANGED
@@ -69,18 +69,20 @@ module Test
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
NOT_PASS_THROUGH_EXCEPTIONS = []
|
72
73
|
PASS_THROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt,
|
73
74
|
SystemExit]
|
74
75
|
private
|
75
76
|
def handle_all_exception(exception)
|
76
77
|
case exception
|
78
|
+
when *NOT_PASS_THROUGH_EXCEPTIONS
|
77
79
|
when *PASS_THROUGH_EXCEPTIONS
|
78
|
-
false
|
79
|
-
else
|
80
|
-
problem_occurred
|
81
|
-
add_error(exception)
|
82
|
-
true
|
80
|
+
return false
|
83
81
|
end
|
82
|
+
|
83
|
+
problem_occurred
|
84
|
+
add_error(exception)
|
85
|
+
true
|
84
86
|
end
|
85
87
|
|
86
88
|
def add_error(exception)
|
data/lib/test/unit/failure.rb
CHANGED
@@ -11,16 +11,23 @@ module Test
|
|
11
11
|
# when an assertion fails.
|
12
12
|
class Failure
|
13
13
|
attr_reader :test_name, :location, :message
|
14
|
-
|
14
|
+
attr_reader :expected, :actual, :user_message
|
15
|
+
attr_reader :inspected_expected, :inspected_actual
|
16
|
+
|
15
17
|
SINGLE_CHARACTER = 'F'
|
16
18
|
LABEL = "Failure"
|
17
19
|
|
18
20
|
# Creates a new Failure with the given location and
|
19
21
|
# message.
|
20
|
-
def initialize(test_name, location, message)
|
22
|
+
def initialize(test_name, location, message, options={})
|
21
23
|
@test_name = test_name
|
22
24
|
@location = location
|
23
25
|
@message = message
|
26
|
+
@expected = options[:expected]
|
27
|
+
@actual = options[:actual]
|
28
|
+
@inspected_expected = options[:inspected_expected]
|
29
|
+
@inspected_actual = options[:inspected_actual]
|
30
|
+
@user_message = options[:user_message]
|
24
31
|
end
|
25
32
|
|
26
33
|
# Returns a single character representation of a failure.
|
@@ -51,6 +58,15 @@ module Test
|
|
51
58
|
def to_s
|
52
59
|
long_display
|
53
60
|
end
|
61
|
+
|
62
|
+
def diff
|
63
|
+
@diff ||= compute_diff
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def compute_diff
|
68
|
+
Assertions::AssertionMessage.delayed_diff(@expected, @actual).inspect
|
69
|
+
end
|
54
70
|
end
|
55
71
|
|
56
72
|
module FailureHandler
|
@@ -64,12 +80,18 @@ module Test
|
|
64
80
|
def handle_assertion_failed_error(exception)
|
65
81
|
return false unless exception.is_a?(AssertionFailedError)
|
66
82
|
problem_occurred
|
67
|
-
add_failure(exception.message, exception.backtrace
|
83
|
+
add_failure(exception.message, exception.backtrace,
|
84
|
+
:expected => exception.expected,
|
85
|
+
:actual => exception.actual,
|
86
|
+
:inspected_expected => exception.inspected_expected,
|
87
|
+
:inspected_actual => exception.inspected_actual,
|
88
|
+
:user_message => exception.user_message)
|
68
89
|
true
|
69
90
|
end
|
70
91
|
|
71
|
-
def add_failure(message, backtrace)
|
72
|
-
failure = Failure.new(name, filter_backtrace(backtrace), message
|
92
|
+
def add_failure(message, backtrace, options={})
|
93
|
+
failure = Failure.new(name, filter_backtrace(backtrace), message,
|
94
|
+
options)
|
73
95
|
current_result.add_failure(failure)
|
74
96
|
end
|
75
97
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
#--
|
2
2
|
#
|
3
3
|
# Author:: Nathaniel Talbott.
|
4
|
-
# Copyright::
|
4
|
+
# Copyright::
|
5
|
+
# * Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
|
6
|
+
# * Copyright (c) 2008-2009 Kouhei Sutou <kou@clear-code.com>
|
5
7
|
# License:: Ruby license.
|
6
8
|
|
7
9
|
require 'test/unit/color-scheme'
|
@@ -36,6 +38,9 @@ module Test
|
|
36
38
|
@progress_row_max = @options[:progress_row_max]
|
37
39
|
@progress_row_max ||= guess_progress_row_max
|
38
40
|
@already_outputted = false
|
41
|
+
@n_successes = 0
|
42
|
+
@indent = 0
|
43
|
+
@top_level = true
|
39
44
|
@faults = []
|
40
45
|
end
|
41
46
|
|
@@ -68,6 +73,8 @@ module Test
|
|
68
73
|
@mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
69
74
|
@mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
70
75
|
@mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
|
76
|
+
@mediator.add_listener(TestSuite::STARTED, &method(:test_suite_started))
|
77
|
+
@mediator.add_listener(TestSuite::FINISHED, &method(:test_suite_finished))
|
71
78
|
end
|
72
79
|
|
73
80
|
def start_mediator
|
@@ -91,35 +98,140 @@ module Test
|
|
91
98
|
|
92
99
|
def finished(elapsed_time)
|
93
100
|
nl if output?(NORMAL) and !output?(VERBOSE)
|
94
|
-
nl
|
95
|
-
output("Finished in #{elapsed_time} seconds.")
|
96
101
|
@faults.each_with_index do |fault, index|
|
97
102
|
nl
|
98
103
|
output_single("%3d) " % (index + 1))
|
104
|
+
output_fault(fault)
|
105
|
+
end
|
106
|
+
nl
|
107
|
+
output("Finished in #{elapsed_time} seconds.")
|
108
|
+
nl
|
109
|
+
output(@result, result_color)
|
110
|
+
n_tests = @result.run_count
|
111
|
+
if n_tests.zero?
|
112
|
+
pass_percentage = 0
|
113
|
+
else
|
114
|
+
pass_percentage = 100.0 * (@n_successes / n_tests.to_f)
|
115
|
+
end
|
116
|
+
output("%g%% passed" % pass_percentage, result_color)
|
117
|
+
end
|
118
|
+
|
119
|
+
def output_fault(fault)
|
120
|
+
if @use_color and fault.is_a?(Failure) and
|
121
|
+
fault.inspected_expected and fault.inspected_actual
|
122
|
+
output_single(fault.label, fault_color(fault))
|
123
|
+
output(":")
|
124
|
+
output_fault_backtrace(fault)
|
125
|
+
output_fault_message(fault)
|
126
|
+
else
|
99
127
|
label, detail = format_fault(fault).split(/\r?\n/, 2)
|
100
128
|
output(label, fault_color(fault))
|
101
129
|
output(detail)
|
102
130
|
end
|
103
|
-
|
104
|
-
|
131
|
+
end
|
132
|
+
|
133
|
+
def output_fault_backtrace(fault)
|
134
|
+
backtrace = fault.location
|
135
|
+
if backtrace.size == 1
|
136
|
+
output(fault.test_name +
|
137
|
+
backtrace[0].sub(/\A(.+:\d+).*/, ' [\\1]') +
|
138
|
+
":")
|
139
|
+
else
|
140
|
+
output(fault.test_name)
|
141
|
+
backtrace.each_with_index do |entry, i|
|
142
|
+
if i.zero?
|
143
|
+
prefix = "["
|
144
|
+
postfix = ""
|
145
|
+
elsif i == backtrace.size - 1
|
146
|
+
prefix = " "
|
147
|
+
postfix = "]:"
|
148
|
+
else
|
149
|
+
prefix = " "
|
150
|
+
postfix = ""
|
151
|
+
end
|
152
|
+
output(" #{prefix}#{entry}#{postfix}")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def output_fault_message(fault)
|
158
|
+
output(fault.user_message) if fault.user_message
|
159
|
+
output_single("<")
|
160
|
+
output_single(fault.inspected_expected, color("success"))
|
161
|
+
output("> expected but was")
|
162
|
+
output_single("<")
|
163
|
+
output_single(fault.inspected_actual, color("failure"))
|
164
|
+
output(">")
|
165
|
+
from, to = prepare_for_diff(fault.expected, fault.actual)
|
166
|
+
if from and to
|
167
|
+
differ = ColorizedReadableDiffer.new(from.split(/\r?\n/),
|
168
|
+
to.split(/\r?\n/),
|
169
|
+
self)
|
170
|
+
if differ.need_diff?
|
171
|
+
output("")
|
172
|
+
output("diff:")
|
173
|
+
differ.diff
|
174
|
+
end
|
175
|
+
end
|
105
176
|
end
|
106
177
|
|
107
178
|
def format_fault(fault)
|
108
179
|
fault.long_display
|
109
180
|
end
|
110
|
-
|
181
|
+
|
111
182
|
def test_started(name)
|
112
|
-
|
183
|
+
return unless output?(VERBOSE)
|
184
|
+
|
185
|
+
name = name.sub(/\(.+?\)\z/, '')
|
186
|
+
right_space = 8 * 2
|
187
|
+
left_space = @progress_row_max - right_space
|
188
|
+
left_space = left_space - indent.size - name.size
|
189
|
+
tab_stop = "\t" * ((left_space - 1) / 8)
|
190
|
+
output_single("#{indent}#{name}:#{tab_stop}", nil, VERBOSE)
|
191
|
+
@test_start = Time.now
|
113
192
|
end
|
114
|
-
|
193
|
+
|
115
194
|
def test_finished(name)
|
116
195
|
unless @already_outputted
|
196
|
+
@n_successes += 1
|
117
197
|
output_progress(".", color("success"))
|
118
198
|
end
|
119
|
-
nl(VERBOSE)
|
120
199
|
@already_outputted = false
|
200
|
+
|
201
|
+
return unless output?(VERBOSE)
|
202
|
+
|
203
|
+
output(": (%f)" % (Time.now - @test_start), nil, VERBOSE)
|
121
204
|
end
|
122
|
-
|
205
|
+
|
206
|
+
def test_suite_started(name)
|
207
|
+
if @top_level
|
208
|
+
@top_level = false
|
209
|
+
return
|
210
|
+
end
|
211
|
+
|
212
|
+
output_single(indent, nil, VERBOSE)
|
213
|
+
if /\A[A-Z]/ =~ name
|
214
|
+
_color = color("case")
|
215
|
+
else
|
216
|
+
_color = color("suite")
|
217
|
+
end
|
218
|
+
output_single(name, _color, VERBOSE)
|
219
|
+
output(": ", nil, VERBOSE)
|
220
|
+
@indent += 2
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_suite_finished(name)
|
224
|
+
@indent -= 2
|
225
|
+
end
|
226
|
+
|
227
|
+
def indent
|
228
|
+
if output?(VERBOSE)
|
229
|
+
" " * @indent
|
230
|
+
else
|
231
|
+
""
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
123
235
|
def nl(level=NORMAL)
|
124
236
|
output("", nil, level)
|
125
237
|
end
|
@@ -213,6 +325,135 @@ module Test
|
|
213
325
|
0
|
214
326
|
end
|
215
327
|
end
|
328
|
+
|
329
|
+
class ColorizedReadableDiffer < Diff::ReadableDiffer
|
330
|
+
def initialize(from, to, runner)
|
331
|
+
@runner = runner
|
332
|
+
super(from, to)
|
333
|
+
end
|
334
|
+
|
335
|
+
def need_diff?(options={})
|
336
|
+
operations.each do |tag,|
|
337
|
+
return true if [:replace, :equal].include?(tag)
|
338
|
+
end
|
339
|
+
false
|
340
|
+
end
|
341
|
+
|
342
|
+
private
|
343
|
+
def output_single(something, color=nil)
|
344
|
+
@runner.send(:output_single, something, color)
|
345
|
+
end
|
346
|
+
|
347
|
+
def output(something, color=nil)
|
348
|
+
@runner.send(:output, something, color)
|
349
|
+
end
|
350
|
+
|
351
|
+
def color(name)
|
352
|
+
@runner.send(:color, name)
|
353
|
+
end
|
354
|
+
|
355
|
+
def cut_off_ratio
|
356
|
+
0
|
357
|
+
end
|
358
|
+
|
359
|
+
def default_ratio
|
360
|
+
0
|
361
|
+
end
|
362
|
+
|
363
|
+
def tag(mark, color_name, contents)
|
364
|
+
_color = color(color_name)
|
365
|
+
contents.each do |content|
|
366
|
+
output_single(mark, _color)
|
367
|
+
output_single(" ")
|
368
|
+
output(content)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def tag_deleted(contents)
|
373
|
+
tag("-", "diff-deleted-tag", contents)
|
374
|
+
end
|
375
|
+
|
376
|
+
def tag_inserted(contents)
|
377
|
+
tag("+", "diff-inserted-tag", contents)
|
378
|
+
end
|
379
|
+
|
380
|
+
def tag_equal(contents)
|
381
|
+
tag(" ", "normal", contents)
|
382
|
+
end
|
383
|
+
|
384
|
+
def tag_difference(contents)
|
385
|
+
tag("?", "diff-difference-tag", contents)
|
386
|
+
end
|
387
|
+
|
388
|
+
def diff_line(from_line, to_line)
|
389
|
+
to_operations = []
|
390
|
+
from_line, to_line, _operations = line_operations(from_line, to_line)
|
391
|
+
|
392
|
+
no_replace = true
|
393
|
+
_operations.each do |tag,|
|
394
|
+
if tag == :replace
|
395
|
+
no_replace = false
|
396
|
+
break
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
output_single("?", color("diff-difference-tag"))
|
401
|
+
output_single(" ")
|
402
|
+
_operations.each do |tag, from_start, from_end, to_start, to_end|
|
403
|
+
from_width = compute_width(from_line, from_start, from_end)
|
404
|
+
to_width = compute_width(to_line, to_start, to_end)
|
405
|
+
case tag
|
406
|
+
when :replace
|
407
|
+
output_single(from_line[from_start...from_end],
|
408
|
+
color("diff-deleted"))
|
409
|
+
if (from_width < to_width)
|
410
|
+
output_single(" " * (to_width - from_width))
|
411
|
+
end
|
412
|
+
to_operations << Proc.new do
|
413
|
+
output_single(to_line[to_start...to_end],
|
414
|
+
color("diff-inserted"))
|
415
|
+
if (to_width < from_width)
|
416
|
+
output_single(" " * (from_width - to_width))
|
417
|
+
end
|
418
|
+
end
|
419
|
+
when :delete
|
420
|
+
output_single(from_line[from_start...from_end],
|
421
|
+
color("diff-deleted"))
|
422
|
+
unless no_replace
|
423
|
+
to_operations << Proc.new {output_single(" " * from_width)}
|
424
|
+
end
|
425
|
+
when :insert
|
426
|
+
if no_replace
|
427
|
+
output_single(to_line[to_start...to_end],
|
428
|
+
color("diff-inserted"))
|
429
|
+
else
|
430
|
+
output_single(" " * to_width)
|
431
|
+
to_operations << Proc.new do
|
432
|
+
output_single(to_line[to_start...to_end],
|
433
|
+
color("diff-inserted"))
|
434
|
+
end
|
435
|
+
end
|
436
|
+
when :equal
|
437
|
+
output_single(from_line[from_start...from_end])
|
438
|
+
unless no_replace
|
439
|
+
to_operations << Proc.new {output_single(" " * to_width)}
|
440
|
+
end
|
441
|
+
else
|
442
|
+
raise "unknown tag: #{tag}"
|
443
|
+
end
|
444
|
+
end
|
445
|
+
output("")
|
446
|
+
|
447
|
+
unless to_operations.empty?
|
448
|
+
output_single("?", color("diff-difference-tag"))
|
449
|
+
output_single(" ")
|
450
|
+
to_operations.each do |operation|
|
451
|
+
operation.call
|
452
|
+
end
|
453
|
+
output("")
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
216
457
|
end
|
217
458
|
end
|
218
459
|
end
|
@@ -22,6 +22,20 @@ module Test
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
def output_fault_backtrace(fault)
|
26
|
+
backtrace = fault.location
|
27
|
+
if backtrace.size == 1
|
28
|
+
output(fault.test_name +
|
29
|
+
backtrace[0].sub(/\A(.+:\d+).*/, ' [\\1]') +
|
30
|
+
":")
|
31
|
+
else
|
32
|
+
output(fault.test_name)
|
33
|
+
backtrace.each do |entry|
|
34
|
+
output(entry)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
25
39
|
def format_fault_failure(failure)
|
26
40
|
if failure.location.size == 1
|
27
41
|
location = failure.location[0]
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Kouhei Sutou.
|
4
|
+
# Copyright:: Copyright (c) 2009 Kouhei Sutou <kou@clear-code.com>.
|
5
|
+
# License:: Ruby license.
|
6
|
+
|
7
|
+
require 'test/unit/ui/testrunner'
|
8
|
+
require 'test/unit/ui/testrunnermediator'
|
9
|
+
|
10
|
+
module Test
|
11
|
+
module Unit
|
12
|
+
module UI
|
13
|
+
module Tap
|
14
|
+
|
15
|
+
# Runs a Test::Unit::TestSuite and outputs result
|
16
|
+
# as TAP format.
|
17
|
+
class TestRunner < UI::TestRunner
|
18
|
+
def initialize(suite, options={})
|
19
|
+
super
|
20
|
+
@output = @options[:output] || STDOUT
|
21
|
+
@n_tests = 0
|
22
|
+
@already_outputted = false
|
23
|
+
end
|
24
|
+
|
25
|
+
# Begins the test run.
|
26
|
+
def start
|
27
|
+
setup_mediator
|
28
|
+
result = start_mediator
|
29
|
+
def result.passed?
|
30
|
+
true # for prove commend :<
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def setup_mediator
|
37
|
+
@mediator = TestRunnerMediator.new(@suite)
|
38
|
+
attach_to_mediator
|
39
|
+
end
|
40
|
+
|
41
|
+
def attach_to_mediator
|
42
|
+
@mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
43
|
+
@mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
44
|
+
@mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
45
|
+
@mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
46
|
+
@mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
|
47
|
+
end
|
48
|
+
|
49
|
+
def start_mediator
|
50
|
+
@mediator.run_suite
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_fault(fault)
|
54
|
+
puts("not ok #{@n_tests} - #{fault.short_display}")
|
55
|
+
fault.long_display.each_line do |line|
|
56
|
+
puts("# #{line}")
|
57
|
+
end
|
58
|
+
@already_outputted = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def started(result)
|
62
|
+
@result = result
|
63
|
+
puts("1..#{@suite.size}")
|
64
|
+
end
|
65
|
+
|
66
|
+
def finished(elapsed_time)
|
67
|
+
puts("# Finished in #{elapsed_time} seconds.")
|
68
|
+
@result.to_s.each_line do |line|
|
69
|
+
puts("# #{line}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_started(name)
|
74
|
+
@n_tests += 1
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_finished(name)
|
78
|
+
unless @already_outputted
|
79
|
+
puts("ok #{@n_tests} - #{name}")
|
80
|
+
end
|
81
|
+
@already_outputted = false
|
82
|
+
end
|
83
|
+
|
84
|
+
def puts(*args)
|
85
|
+
@output.puts(*args)
|
86
|
+
@output.flush
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|