test-unit 3.2.0 → 3.3.6
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.
- checksums.yaml +5 -5
- data/COPYING +4 -1
- data/README.md +11 -11
- data/Rakefile +10 -1
- data/doc/text/getting-started.md +246 -0
- data/doc/text/news.md +372 -1
- data/lib/test/unit.rb +171 -157
- data/lib/test/unit/assertions.rb +187 -149
- data/lib/test/unit/attribute.rb +71 -2
- data/lib/test/unit/autorunner.rb +65 -32
- data/lib/test/unit/code-snippet-fetcher.rb +7 -7
- data/lib/test/unit/collector/load.rb +8 -13
- data/lib/test/unit/data-sets.rb +116 -0
- data/lib/test/unit/data.rb +121 -12
- data/lib/test/unit/diff.rb +11 -11
- data/lib/test/unit/fixture.rb +3 -0
- data/lib/test/unit/notification.rb +9 -7
- data/lib/test/unit/omission.rb +34 -31
- data/lib/test/unit/pending.rb +12 -11
- data/lib/test/unit/priority.rb +7 -3
- data/lib/test/unit/runner/console.rb +25 -0
- data/lib/test/unit/test-suite-creator.rb +22 -8
- data/lib/test/unit/testcase.rb +270 -182
- data/lib/test/unit/ui/console/testrunner.rb +90 -35
- data/lib/test/unit/ui/emacs/testrunner.rb +5 -5
- data/lib/test/unit/util/observable.rb +2 -2
- data/lib/test/unit/util/output.rb +5 -4
- data/lib/test/unit/util/procwrapper.rb +4 -4
- data/lib/test/unit/version.rb +1 -1
- data/test/collector/test-descendant.rb +4 -0
- data/test/collector/test-load.rb +35 -2
- data/test/collector/test_dir.rb +5 -4
- data/test/collector/test_objectspace.rb +7 -5
- data/test/test-assertions.rb +128 -101
- data/test/test-code-snippet.rb +42 -0
- data/test/test-data.rb +195 -79
- data/test/test-priority.rb +19 -8
- data/test/test-test-case.rb +111 -3
- data/test/test-test-suite.rb +1 -0
- data/test/testunit-test-util.rb +2 -0
- metadata +38 -37
data/lib/test/unit/attribute.rb
CHANGED
@@ -43,12 +43,80 @@ module Test
|
|
43
43
|
kept_attributes = StringifyKeyHash.new
|
44
44
|
@current_attributes.each do |attribute_name, attribute|
|
45
45
|
attributes[attribute_name] = attribute[:value]
|
46
|
-
|
46
|
+
if attribute[:keep]
|
47
|
+
keep_hook = attribute[:keep_hook]
|
48
|
+
attribute = keep_hook.call(attribute) if keep_hook
|
49
|
+
kept_attributes[attribute_name] = attribute
|
50
|
+
end
|
47
51
|
end
|
48
52
|
set_attributes(name, attributes)
|
49
53
|
@current_attributes = kept_attributes
|
50
54
|
end
|
51
55
|
|
56
|
+
# Set an attribute to test methods.
|
57
|
+
#
|
58
|
+
# @overload attribute(name, value)
|
59
|
+
# @example
|
60
|
+
# attribute :speed, :slow
|
61
|
+
# def test_my_slow_method
|
62
|
+
# self[:speed] # => :slow
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# @param [Object] name the attribute name
|
66
|
+
# @param [Object] value the attribute value
|
67
|
+
# @return [void]
|
68
|
+
#
|
69
|
+
# @overload attribute(name, value, *method_names)
|
70
|
+
# @example
|
71
|
+
# def test_my_slow_method1
|
72
|
+
# self[:speed] # => :slow
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# attribute :speed, :slow, :test_my_slow_method1, :test_my_slow_method2
|
76
|
+
#
|
77
|
+
# def test_my_slow_method2
|
78
|
+
# self[:speed] # => :slow
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# @param [Object] name the attribute name
|
82
|
+
# @param [Object] value the attribute value
|
83
|
+
# @param [Array<Symbol, String>] method_names the test method names set the attribute
|
84
|
+
# @return [void]
|
85
|
+
#
|
86
|
+
# @overload attribute(name, value, options)
|
87
|
+
# @example
|
88
|
+
# attribute :speed, :slow, keep: true
|
89
|
+
# def test_my_slow_method1
|
90
|
+
# self[:speed] # => :slow
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# def test_my_slow_method2
|
94
|
+
# self[:speed] # => :slow
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# @param [Object] name the attribute name
|
98
|
+
# @param [Object] value the attribute value
|
99
|
+
# @option options [Boolean] :keep whether or not to set attribute to following test methods
|
100
|
+
# @return [void]
|
101
|
+
#
|
102
|
+
# @overload attribute(name, value, options, *method_names)
|
103
|
+
# @example
|
104
|
+
# def test_my_slow_method1
|
105
|
+
# self[:speed] # => :slow
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# # There are no valid options for now.
|
109
|
+
# attribute :speed, :slow, {}, :test_my_slow_method1
|
110
|
+
#
|
111
|
+
# def test_my_slow_method2
|
112
|
+
# self[:speed] # => nil
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# @param [Object] name the attribute name
|
116
|
+
# @param [Object] value the attribute value
|
117
|
+
# @param [Hash] options ignored
|
118
|
+
# @param [Array<Symbol, String>] method_names the test method names set the attribute
|
119
|
+
# @return [void]
|
52
120
|
def attribute(name, value, options={}, *method_names)
|
53
121
|
unless options.is_a?(Hash)
|
54
122
|
method_names << options
|
@@ -136,7 +204,8 @@ module Test
|
|
136
204
|
end
|
137
205
|
|
138
206
|
@@attribute_observers = StringifyKeyHash.new
|
139
|
-
def register_attribute_observer(attribute_name, observer=
|
207
|
+
def register_attribute_observer(attribute_name, observer=nil, &block)
|
208
|
+
observer ||= Proc.new(&block)
|
140
209
|
@@attribute_observers[attribute_name] ||= []
|
141
210
|
@@attribute_observers[attribute_name] << observer
|
142
211
|
end
|
data/lib/test/unit/autorunner.rb
CHANGED
@@ -15,7 +15,8 @@ module Test
|
|
15
15
|
PREPARE_HOOKS = []
|
16
16
|
|
17
17
|
class << self
|
18
|
-
def register_runner(id, runner_builder=
|
18
|
+
def register_runner(id, runner_builder=nil, &block)
|
19
|
+
runner_builder ||= Proc.new(&block)
|
19
20
|
RUNNERS[id] = runner_builder
|
20
21
|
RUNNERS[id.to_s] = runner_builder
|
21
22
|
end
|
@@ -33,7 +34,8 @@ module Test
|
|
33
34
|
@@default_runner = id
|
34
35
|
end
|
35
36
|
|
36
|
-
def register_collector(id, collector_builder=
|
37
|
+
def register_collector(id, collector_builder=nil, &block)
|
38
|
+
collector_builder ||= Proc.new(&block)
|
37
39
|
COLLECTORS[id] = collector_builder
|
38
40
|
COLLECTORS[id.to_s] = collector_builder
|
39
41
|
end
|
@@ -46,11 +48,13 @@ module Test
|
|
46
48
|
ColorScheme[id] = scheme
|
47
49
|
end
|
48
50
|
|
49
|
-
def setup_option(option_builder=
|
51
|
+
def setup_option(option_builder=nil, &block)
|
52
|
+
option_builder ||= Proc.new(&block)
|
50
53
|
ADDITIONAL_OPTIONS << option_builder
|
51
54
|
end
|
52
55
|
|
53
|
-
def prepare(hook=
|
56
|
+
def prepare(hook=nil, &block)
|
57
|
+
hook ||= Proc.new(&block)
|
54
58
|
PREPARE_HOOKS << hook
|
55
59
|
end
|
56
60
|
|
@@ -139,7 +143,7 @@ module Test
|
|
139
143
|
attr_accessor :default_test_paths
|
140
144
|
attr_accessor :pattern, :exclude, :base, :workdir
|
141
145
|
attr_accessor :color_scheme, :listeners
|
142
|
-
attr_writer :
|
146
|
+
attr_writer :stop_on_failure
|
143
147
|
attr_writer :runner, :collector
|
144
148
|
|
145
149
|
def initialize(standalone)
|
@@ -189,7 +193,7 @@ module Test
|
|
189
193
|
def options
|
190
194
|
@options ||= OptionParser.new do |o|
|
191
195
|
o.banner = "Test::Unit automatic runner."
|
192
|
-
o.banner
|
196
|
+
o.banner += "\nUsage: #{$0} [options] [-- untouched arguments]"
|
193
197
|
|
194
198
|
o.on("-r", "--runner=RUNNER", RUNNERS,
|
195
199
|
"Use the given RUNNER.",
|
@@ -242,57 +246,61 @@ module Test
|
|
242
246
|
|
243
247
|
o.on("-n", "--name=NAME", String,
|
244
248
|
"Runs tests matching NAME.",
|
245
|
-
"Use '/PATTERN/' for NAME to use regular expression."
|
246
|
-
|
249
|
+
"Use '/PATTERN/' for NAME to use regular expression.",
|
250
|
+
"Regular expression accepts options.",
|
251
|
+
"Example: '/taRget/i' matches 'target' and 'TARGET'") do |name|
|
252
|
+
name = prepare_name(name)
|
247
253
|
@filters << lambda do |test|
|
248
|
-
|
249
|
-
test_name_without_class_name = test.name.gsub(/\(.+?\)\z/, "")
|
250
|
-
if test_name_without_class_name != test.method_name
|
251
|
-
return true if name === test_name_without_class_name
|
252
|
-
end
|
253
|
-
false
|
254
|
+
match_test_name(test, name)
|
254
255
|
end
|
255
256
|
end
|
256
257
|
|
257
258
|
o.on("--ignore-name=NAME", String,
|
258
259
|
"Ignores tests matching NAME.",
|
259
|
-
"Use '/PATTERN/' for NAME to use regular expression."
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
@filters << proc {|t| n != t.method_name}
|
260
|
+
"Use '/PATTERN/' for NAME to use regular expression.",
|
261
|
+
"Regular expression accepts options.",
|
262
|
+
"Example: '/taRget/i' matches 'target' and 'TARGET'") do |name|
|
263
|
+
name = prepare_name(name)
|
264
|
+
@filters << lambda do |test|
|
265
|
+
not match_test_name(test, name)
|
266
266
|
end
|
267
267
|
end
|
268
268
|
|
269
269
|
o.on("-t", "--testcase=TESTCASE", String,
|
270
270
|
"Runs tests in TestCases matching TESTCASE.",
|
271
|
-
"Use '/PATTERN/' for TESTCASE to use regular expression."
|
272
|
-
|
271
|
+
"Use '/PATTERN/' for TESTCASE to use regular expression.",
|
272
|
+
"Regular expression accepts options.",
|
273
|
+
"Example: '/taRget/i' matches 'target' and 'TARGET'") do |name|
|
274
|
+
name = prepare_name(name)
|
273
275
|
@filters << lambda do |test|
|
274
|
-
match_test_case_name(test,
|
276
|
+
match_test_case_name(test, name)
|
275
277
|
end
|
276
278
|
end
|
277
279
|
|
278
280
|
o.on("--ignore-testcase=TESTCASE", String,
|
279
281
|
"Ignores tests in TestCases matching TESTCASE.",
|
280
|
-
"Use '/PATTERN/' for TESTCASE to use regular expression."
|
281
|
-
|
282
|
+
"Use '/PATTERN/' for TESTCASE to use regular expression.",
|
283
|
+
"Regular expression accepts options.",
|
284
|
+
"Example: '/taRget/i' matches 'target' and 'TARGET'") do |name|
|
285
|
+
name = prepare_name(name)
|
282
286
|
@filters << lambda do |test|
|
283
|
-
not match_test_case_name(test,
|
287
|
+
not match_test_case_name(test, name)
|
284
288
|
end
|
285
289
|
end
|
286
290
|
|
287
291
|
o.on("--location=LOCATION", String,
|
288
292
|
"Runs tests that defined in LOCATION.",
|
289
|
-
"LOCATION is one of PATH:LINE, PATH or LINE") do |location|
|
290
|
-
|
293
|
+
"LOCATION is one of PATH:LINE, PATH or LINE.") do |location|
|
294
|
+
case location
|
295
|
+
when /\A(\d+)\z/
|
291
296
|
path = nil
|
292
|
-
line =
|
297
|
+
line = $1.to_i
|
298
|
+
when /:(\d+)\z/
|
299
|
+
path = $PREMATCH
|
300
|
+
line = $1.to_i
|
293
301
|
else
|
294
|
-
path
|
295
|
-
line =
|
302
|
+
path = location
|
303
|
+
line = nil
|
296
304
|
end
|
297
305
|
add_location_filter(path, line)
|
298
306
|
end
|
@@ -495,6 +503,31 @@ module Test
|
|
495
503
|
end
|
496
504
|
end
|
497
505
|
|
506
|
+
def prepare_name(name)
|
507
|
+
case name
|
508
|
+
when /\A\/(.*)\/([imx]*)\z/
|
509
|
+
pattern = $1
|
510
|
+
options_raw = $2
|
511
|
+
options = 0
|
512
|
+
options |= Regexp::IGNORECASE if options_raw.include?("i")
|
513
|
+
options |= Regexp::MULTILINE if options_raw.include?("m")
|
514
|
+
options |= Regexp::EXTENDED if options_raw.include?("x")
|
515
|
+
Regexp.new(pattern, options)
|
516
|
+
else
|
517
|
+
name
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def match_test_name(test, pattern)
|
522
|
+
return true if pattern === test.method_name
|
523
|
+
return true if pattern === test.local_name
|
524
|
+
if pattern.is_a?(String)
|
525
|
+
return true if pattern === "#{test.class}##{test.method_name}"
|
526
|
+
return true if pattern === "#{test.class}##{test.local_name}"
|
527
|
+
end
|
528
|
+
false
|
529
|
+
end
|
530
|
+
|
498
531
|
def match_test_case_name(test, pattern)
|
499
532
|
test.class.ancestors.each do |test_class|
|
500
533
|
break if test_class == TestCase
|
@@ -26,16 +26,16 @@ module Test
|
|
26
26
|
def read_source(path)
|
27
27
|
return nil unless File.exist?(path)
|
28
28
|
lines = []
|
29
|
-
File.open(path) do |file|
|
29
|
+
File.open(path, "rb") do |file|
|
30
30
|
first_line = file.gets
|
31
31
|
break if first_line.nil?
|
32
|
-
encoding = detect_encoding(first_line)
|
33
|
-
|
34
|
-
first_line.force_encoding(encoding)
|
35
|
-
file.set_encoding(encoding, encoding)
|
36
|
-
end
|
32
|
+
encoding = detect_encoding(first_line) || Encoding::UTF_8
|
33
|
+
first_line.force_encoding(encoding)
|
37
34
|
lines << first_line
|
38
|
-
|
35
|
+
file.each_line do |line|
|
36
|
+
line.force_encoding(encoding)
|
37
|
+
lines << line
|
38
|
+
end
|
39
39
|
end
|
40
40
|
lines
|
41
41
|
end
|
@@ -111,7 +111,7 @@ module Test
|
|
111
111
|
return if @program_file == expanded_path.to_s
|
112
112
|
add_load_path(expanded_path.dirname) do
|
113
113
|
begin
|
114
|
-
require(
|
114
|
+
require(expanded_path.to_s)
|
115
115
|
rescue LoadError
|
116
116
|
@require_failed_infos << {:path => expanded_path, :exception => $!}
|
117
117
|
end
|
@@ -131,8 +131,6 @@ module Test
|
|
131
131
|
return yield if path.nil?
|
132
132
|
|
133
133
|
path = path.to_s
|
134
|
-
return yield if $LOAD_PATH.index(path)
|
135
|
-
|
136
134
|
begin
|
137
135
|
$LOAD_PATH.unshift(path)
|
138
136
|
yield
|
@@ -166,11 +164,11 @@ module Test
|
|
166
164
|
return if @require_failed_infos.empty?
|
167
165
|
|
168
166
|
require_failed_infos = @require_failed_infos
|
169
|
-
|
170
|
-
|
167
|
+
require_failed_errors = Class.new(Test::Unit::TestCase)
|
168
|
+
require_failed_errors.class_eval do
|
171
169
|
class << self
|
172
170
|
def name
|
173
|
-
"
|
171
|
+
"RequireFailedErrors"
|
174
172
|
end
|
175
173
|
end
|
176
174
|
|
@@ -180,21 +178,18 @@ module Test
|
|
180
178
|
normalized_path = normalized_path.gsub(/\A_+/, '')
|
181
179
|
exception = info[:exception]
|
182
180
|
define_method("test_require_#{normalized_path}") do
|
183
|
-
|
184
|
-
|
181
|
+
raise(exception.class,
|
182
|
+
"failed to load <#{path}>: #{exception.message}",
|
183
|
+
exception.backtrace)
|
185
184
|
end
|
186
185
|
end
|
187
186
|
|
188
187
|
def priority
|
189
188
|
100
|
190
189
|
end
|
191
|
-
|
192
|
-
def filter_backtrace(location)
|
193
|
-
super(@require_failed_exception.backtrace)
|
194
|
-
end
|
195
190
|
end
|
196
191
|
|
197
|
-
add_suite(test_suites,
|
192
|
+
add_suite(test_suites, require_failed_errors.suite)
|
198
193
|
end
|
199
194
|
end
|
200
195
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Test
|
2
|
+
module Unit
|
3
|
+
class DataSets
|
4
|
+
def initialize
|
5
|
+
@variables = []
|
6
|
+
@procs = []
|
7
|
+
@value_sets = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(data_set, options=nil)
|
11
|
+
options ||= {}
|
12
|
+
if data_set.respond_to?(:call)
|
13
|
+
@procs << [data_set, options]
|
14
|
+
elsif data_set.is_a?(Array)
|
15
|
+
@variables << [data_set, options]
|
16
|
+
else
|
17
|
+
@value_sets << [data_set, options]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def <<(data_set)
|
22
|
+
add(data_set)
|
23
|
+
end
|
24
|
+
|
25
|
+
def keep
|
26
|
+
new_data_sets = self.class.new
|
27
|
+
all_data_sets = Enumerator.new do |yielder|
|
28
|
+
block = lambda do |(data_set, options)|
|
29
|
+
yielder << [data_set, options]
|
30
|
+
end
|
31
|
+
@procs.each(&block)
|
32
|
+
@variables.each(&block)
|
33
|
+
@value_sets.each(&block)
|
34
|
+
end
|
35
|
+
all_data_sets.each do |data_set, options|
|
36
|
+
next if options.nil?
|
37
|
+
next unless options[:keep]
|
38
|
+
new_data_sets.add(data_set, options)
|
39
|
+
end
|
40
|
+
new_data_sets
|
41
|
+
end
|
42
|
+
|
43
|
+
def each
|
44
|
+
variables = @variables
|
45
|
+
value_sets = @value_sets
|
46
|
+
@procs.each do |proc, options|
|
47
|
+
data_set = proc.call
|
48
|
+
case data_set
|
49
|
+
when Array
|
50
|
+
variables += [[data_set, options]]
|
51
|
+
else
|
52
|
+
value_sets += [[data_set, options]]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
value_sets.each do |values, _options|
|
57
|
+
values.each do |label, data|
|
58
|
+
yield(label, data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
each_pattern(variables) do |label, data|
|
63
|
+
yield(label, data)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def ==(other)
|
68
|
+
@variables == other.instance_variable_get(:@variables) and
|
69
|
+
@procs == other.instance_variable_get(:@procs) and
|
70
|
+
@value_sets == other.instance_variable_get(:@value_sets)
|
71
|
+
end
|
72
|
+
|
73
|
+
def eql?(other)
|
74
|
+
self == other
|
75
|
+
end
|
76
|
+
|
77
|
+
def hash
|
78
|
+
[@variables, @procs, @value_sets].hash
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def each_pattern(variables)
|
83
|
+
grouped_variables = variables.group_by do |_, options|
|
84
|
+
options[:group]
|
85
|
+
end
|
86
|
+
grouped_variables.each do |group, group_variables|
|
87
|
+
each_raw_pattern(group_variables) do |cell|
|
88
|
+
label = String.new
|
89
|
+
label << "group: #{group.inspect}" unless group.nil?
|
90
|
+
data = {}
|
91
|
+
cell.each do |variable, pattern|
|
92
|
+
label << ", " unless label.empty?
|
93
|
+
label << "#{variable}: #{pattern.inspect}"
|
94
|
+
data[variable] = pattern
|
95
|
+
end
|
96
|
+
yield(label, data)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def each_raw_pattern(variables, &block)
|
102
|
+
return if variables.empty?
|
103
|
+
|
104
|
+
sorted_variables = variables.sort_by do |(variable, _), _|
|
105
|
+
variable
|
106
|
+
end
|
107
|
+
all_patterns = sorted_variables.collect do |(variable, patterns), _|
|
108
|
+
patterns.collect do |pattern|
|
109
|
+
[variable, pattern]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
all_patterns[0].product(*all_patterns[1..-1], &block)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|