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.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/COPYING +4 -1
  3. data/README.md +11 -11
  4. data/Rakefile +10 -1
  5. data/doc/text/getting-started.md +246 -0
  6. data/doc/text/news.md +372 -1
  7. data/lib/test/unit.rb +171 -157
  8. data/lib/test/unit/assertions.rb +187 -149
  9. data/lib/test/unit/attribute.rb +71 -2
  10. data/lib/test/unit/autorunner.rb +65 -32
  11. data/lib/test/unit/code-snippet-fetcher.rb +7 -7
  12. data/lib/test/unit/collector/load.rb +8 -13
  13. data/lib/test/unit/data-sets.rb +116 -0
  14. data/lib/test/unit/data.rb +121 -12
  15. data/lib/test/unit/diff.rb +11 -11
  16. data/lib/test/unit/fixture.rb +3 -0
  17. data/lib/test/unit/notification.rb +9 -7
  18. data/lib/test/unit/omission.rb +34 -31
  19. data/lib/test/unit/pending.rb +12 -11
  20. data/lib/test/unit/priority.rb +7 -3
  21. data/lib/test/unit/runner/console.rb +25 -0
  22. data/lib/test/unit/test-suite-creator.rb +22 -8
  23. data/lib/test/unit/testcase.rb +270 -182
  24. data/lib/test/unit/ui/console/testrunner.rb +90 -35
  25. data/lib/test/unit/ui/emacs/testrunner.rb +5 -5
  26. data/lib/test/unit/util/observable.rb +2 -2
  27. data/lib/test/unit/util/output.rb +5 -4
  28. data/lib/test/unit/util/procwrapper.rb +4 -4
  29. data/lib/test/unit/version.rb +1 -1
  30. data/test/collector/test-descendant.rb +4 -0
  31. data/test/collector/test-load.rb +35 -2
  32. data/test/collector/test_dir.rb +5 -4
  33. data/test/collector/test_objectspace.rb +7 -5
  34. data/test/test-assertions.rb +128 -101
  35. data/test/test-code-snippet.rb +42 -0
  36. data/test/test-data.rb +195 -79
  37. data/test/test-priority.rb +19 -8
  38. data/test/test-test-case.rb +111 -3
  39. data/test/test-test-suite.rb +1 -0
  40. data/test/testunit-test-util.rb +2 -0
  41. metadata +38 -37
@@ -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
- kept_attributes[attribute_name] = attribute if attribute[:keep]
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=Proc.new)
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
@@ -15,7 +15,8 @@ module Test
15
15
  PREPARE_HOOKS = []
16
16
 
17
17
  class << self
18
- def register_runner(id, runner_builder=Proc.new)
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=Proc.new)
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=Proc.new)
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=Proc.new)
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 :stop_on_failuere
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 << "\nUsage: #{$0} [options] [-- untouched arguments]"
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.") do |name|
246
- name = (%r{\A/(.*)/\Z} =~ name ? Regexp.new($1) : name)
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
- return true if name === test.method_name
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.") do |n|
260
- n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
261
- case n
262
- when Regexp
263
- @filters << proc {|t| n =~ t.method_name ? false : true}
264
- else
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.") do |n|
272
- n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
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, n)
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.") do |n|
281
- n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
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, n)
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
- if /\A\d+\z/ =~ location
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 = location.to_i
297
+ line = $1.to_i
298
+ when /:(\d+)\z/
299
+ path = $PREMATCH
300
+ line = $1.to_i
293
301
  else
294
- path, line, = location.split(/:(\d+)/, 2)
295
- line = line.to_i unless line.nil?
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
- if encoding
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
- lines.concat(file.readlines)
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(path.basename.to_s)
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
- require_failed_omissions = Class.new(Test::Unit::TestCase)
170
- require_failed_omissions.class_eval do
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
- "RequireFailedOmissions"
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
- @require_failed_exception = exception
184
- omit("failed to load: <#{path}>: <#{exception.message}>")
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, require_failed_omissions.suite)
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