test-unit 2.5.1 → 2.5.2

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.
@@ -27,8 +27,15 @@ module Test
27
27
  attr_reader :name
28
28
  def initialize(name, options={})
29
29
  @name = name
30
- @foreground = options[:foreground]
31
- @foreground = true if @foreground.nil?
30
+ if options.has_key?(:foreground)
31
+ if options[:foreground].nil?
32
+ @background = false
33
+ else
34
+ @background = !options[:foreground]
35
+ end
36
+ else
37
+ @background = options[:background]
38
+ end
32
39
  @intensity = options[:intensity]
33
40
  @bold = options[:bold]
34
41
  @italic = options[:italic]
@@ -36,7 +43,11 @@ module Test
36
43
  end
37
44
 
38
45
  def foreground?
39
- @foreground
46
+ not background?
47
+ end
48
+
49
+ def background?
50
+ @background
40
51
  end
41
52
 
42
53
  def intensity?
@@ -57,9 +68,9 @@ module Test
57
68
 
58
69
  def ==(other)
59
70
  self.class === other and
60
- [name, foreground?, intensity?,
71
+ [name, background?, intensity?,
61
72
  bold?, italic?, underline?] ==
62
- [other.name, other.foreground?, other.intensity?,
73
+ [other.name, other.background?, other.intensity?,
63
74
  other.bold?, other.italic?, other.underline?]
64
75
  end
65
76
 
@@ -70,10 +81,10 @@ module Test
70
81
  sequence << "0"
71
82
  else
72
83
  if NAMES.include?(@name)
73
- foreground_parameter = foreground? ? 3 : 4
74
- foreground_parameter += 6 if intensity?
84
+ color_parameter = foreground? ? 3 : 4
85
+ color_parameter += 6 if intensity?
75
86
  color = NAMES.index(@name)
76
- sequence << "#{foreground_parameter}#{color}"
87
+ sequence << "#{color_parameter}#{color}"
77
88
  else
78
89
  sequence << (foreground? ? "38" : "48")
79
90
  sequence << "5"
@@ -15,16 +15,18 @@ module Test
15
15
  class Error
16
16
  include Util::BacktraceFilter
17
17
 
18
- attr_reader(:test_name, :exception)
18
+ attr_reader :test_name, :exception
19
+ attr_reader :method_name
19
20
 
20
21
  SINGLE_CHARACTER = 'E'
21
22
  LABEL = "Error"
22
23
 
23
24
  # Creates a new Error with the given test_name and
24
25
  # exception.
25
- def initialize(test_name, exception)
26
+ def initialize(test_name, exception, options={})
26
27
  @test_name = test_name
27
28
  @exception = exception
29
+ @method_name = options[:method_name]
28
30
  end
29
31
 
30
32
  # Returns a single character representation of an error.
@@ -111,7 +113,8 @@ module Test
111
113
  end
112
114
 
113
115
  def add_error(exception)
114
- current_result.add_error(Error.new(name, exception))
116
+ error = Error.new(name, exception, :method_name => @method_name)
117
+ current_result.add_error(error)
115
118
  end
116
119
  end
117
120
 
@@ -14,7 +14,7 @@ module Test
14
14
  if value
15
15
  @@exception_handlers.unshift(method_name)
16
16
  else
17
- @@exception_handlers -= [method_name]
17
+ @@exception_handlers.delete(method_name)
18
18
  end
19
19
  end
20
20
  base.register_attribute_observer(:exception_handler, &observer)
@@ -26,12 +26,55 @@ module Test
26
26
  ExceptionHandler.exception_handlers
27
27
  end
28
28
 
29
- def exception_handler(*method_names)
30
- attribute(:exception_handler, true, *method_names)
29
+ # @overload exception_handler(method_name)
30
+ # Add an exception handler method.
31
+ #
32
+ # @param method_name [Symbol]
33
+ # The method name that handles exception raised in tests.
34
+ # @return [void]
35
+ #
36
+ # @overload exception_handler(&callback)
37
+ # Add an exception handler.
38
+ #
39
+ # @yield [test, exception]
40
+ # Gives the test and the exception.
41
+ # @yieldparam test [Test::Unit::TestCase]
42
+ # The test where the exception is raised.
43
+ # @yieldparam exception [Exception]
44
+ # The exception that is raised in running the test.
45
+ # @yieldreturn [Boolean]
46
+ # Whether the handler handles the exception or not.
47
+ # The handler must return _true_ if the handler handles
48
+ # test exception, _false_ otherwise.
49
+ # @return [void]
50
+ #
51
+ # This is a public API for developers who extend test-unit.
52
+ def exception_handler(*method_name_or_handlers, &block)
53
+ if block_given?
54
+ exception_handlers.unshift(block)
55
+ else
56
+ method_name_or_handlers.each do |method_name_or_handler|
57
+ if method_name_or_handler.respond_to?(:call)
58
+ handler = method_name_or_handler
59
+ exception_handlers.unshift(handler)
60
+ else
61
+ method_name = method_name_or_handler
62
+ attribute(:exception_handler, true, {}, method_name)
63
+ end
64
+ end
65
+ end
31
66
  end
32
67
 
33
- def unregister_exception_handler(*method_names)
34
- attribute(:exception_handler, false, *method_names)
68
+ def unregister_exception_handler(*method_name_or_handlers)
69
+ method_name_or_handlers.each do |method_name_or_handler|
70
+ if method_name_or_handler.respond_to?(:call)
71
+ handler = method_name_or_handler
72
+ exception_handlers.delete(handler)
73
+ else
74
+ method_name = method_name_or_handler
75
+ attribute(:exception_handler, false, {}, method_name)
76
+ end
77
+ end
35
78
  end
36
79
  end
37
80
  end
@@ -11,6 +11,7 @@ module Test
11
11
  # when an assertion fails.
12
12
  class Failure
13
13
  attr_reader :test_name, :location, :message
14
+ attr_reader :method_name, :source_location
14
15
  attr_reader :expected, :actual, :user_message
15
16
  attr_reader :inspected_expected, :inspected_actual
16
17
 
@@ -23,6 +24,8 @@ module Test
23
24
  @test_name = test_name
24
25
  @location = location
25
26
  @message = message
27
+ @method_name = options[:method_name]
28
+ @source_location = options[:source_location]
26
29
  @expected = options[:expected]
27
30
  @actual = options[:actual]
28
31
  @inspected_expected = options[:inspected_expected]
@@ -80,6 +83,42 @@ module Test
80
83
  end
81
84
  end
82
85
 
86
+ # Report a failure.
87
+ #
88
+ # This is a public API for developers who extend test-unit.
89
+ #
90
+ # @param message [String] The description about the failure.
91
+ # @param backtrace [Array<String>] The backtrace for the failure.
92
+ # @option options [Object] :expected
93
+ # The expected value of the assertion.
94
+ # @option options [Object] :actual
95
+ # The actual value of the assertion.
96
+ # @option options [String] :inspected_expected
97
+ # The inspected expected value of the assertion.
98
+ # It is used for diff between expected and actual of the failure.
99
+ # @option options [String] :inspected_actual
100
+ # The inspected actual value of the assertion.
101
+ # It is used for diff between expected and actual of the failure.
102
+ # @option options [String] :user_message
103
+ # The message of the assertion from user.
104
+ # @option options [String] :method_name (@method_name)
105
+ # The method name of the test.
106
+ # @option options [Array<String, Integer>] :source_location
107
+ # The location where the test is defined. It is the same
108
+ # format as Proc#source_location. That is, it's an array of
109
+ # path and and line number where the test definition is
110
+ # started.
111
+ # @return [void]
112
+ def add_failure(message, backtrace, options={})
113
+ default_options = {
114
+ :method_name => @method_name,
115
+ :source_location => self[:source_location],
116
+ }
117
+ failure = Failure.new(name, filter_backtrace(backtrace), message,
118
+ default_options.merge(options))
119
+ current_result.add_failure(failure)
120
+ end
121
+
83
122
  private
84
123
  def handle_assertion_failed_error(exception)
85
124
  return false unless exception.is_a?(AssertionFailedError)
@@ -92,12 +131,6 @@ module Test
92
131
  :user_message => exception.user_message)
93
132
  true
94
133
  end
95
-
96
- def add_failure(message, backtrace, options={})
97
- failure = Failure.new(name, filter_backtrace(backtrace), message,
98
- options)
99
- current_result.add_failure(failure)
100
- end
101
134
  end
102
135
 
103
136
  module TestResultFailureSupport
@@ -0,0 +1,95 @@
1
+ # Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # License: Ruby's
4
+
5
+ module Test
6
+ module Unit
7
+ class FaultLocationDetector
8
+ def initialize(fault, code_snippet_fetcher)
9
+ @fault = fault
10
+ @code_snippet_fetcher = code_snippet_fetcher
11
+ extract_fault_information
12
+ end
13
+
14
+ def split_backtrace_entry(entry)
15
+ match_data = /\A(.+):(\d+)(?::(.*))?\z/.match(entry)
16
+ return nil if match_data.nil?
17
+ file, line_number, context = match_data.to_a[1..-1]
18
+ line_number = line_number.to_i
19
+ if /\Ain `(.+?)'/ =~ context
20
+ method_name = $1
21
+ else
22
+ method_name = nil
23
+ end
24
+ [file, line_number, context, method_name]
25
+ end
26
+
27
+ def target?(backtrace_entry)
28
+ file, line_number, context, method_name =
29
+ split_backtrace_entry(backtrace_entry)
30
+ _ = context
31
+ return false if file.nil?
32
+
33
+ if @fault_source_location
34
+ target_source_location?(file, line_number, method_name)
35
+ elsif @fault_method_name
36
+ target_method?(method_name)
37
+ else
38
+ true
39
+ end
40
+ end
41
+
42
+ private
43
+ def target_source_location?(file, line_number, method_name)
44
+ fault_file, fault_line_number = @fault_source_location
45
+ return false unless file.end_with?(fault_file)
46
+
47
+ return false if line_number < fault_line_number
48
+
49
+ lines = @code_snippet_fetcher.source(file)
50
+ return false if lines.nil?
51
+
52
+ base_indent_level = nil
53
+ fault_line_number.step(lines.size) do |current_line_number|
54
+ line = lines[current_line_number - 1]
55
+ current_indent_level = guess_indent_level(line)
56
+ base_indent_level ||= current_indent_level
57
+ return true if current_line_number == line_number
58
+
59
+ if current_line_number == fault_line_number
60
+ break if /(?:\send|\})\s*$/ =~ line
61
+ else
62
+ break if current_indent_level == base_indent_level
63
+ end
64
+ end
65
+ false
66
+ end
67
+
68
+ def target_method?(method_name)
69
+ @fault_method_name == method_name
70
+ end
71
+
72
+ def guess_indent_level(line)
73
+ if /\A(\s*)/ =~ line
74
+ $1.sub(/\t/, " " * 8).count(" ")
75
+ else
76
+ 0
77
+ end
78
+ end
79
+
80
+ def extract_fault_information
81
+ if @fault.respond_to?(:source_location)
82
+ @fault_source_location = @fault.source_location
83
+ else
84
+ @fault_source_location = nil
85
+ end
86
+
87
+ if @fault.respond_to?(:method_name)
88
+ @fault_method_name = @fault.method_name
89
+ else
90
+ @fault_method_name = nil
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -5,16 +5,18 @@ module Test
5
5
  class Notification
6
6
  include Util::BacktraceFilter
7
7
  attr_reader :test_name, :location, :message
8
+ attr_reader :method_name
8
9
 
9
10
  SINGLE_CHARACTER = 'N'
10
11
  LABEL = "Notification"
11
12
 
12
13
  # Creates a new Notification with the given location and
13
14
  # message.
14
- def initialize(test_name, location, message)
15
+ def initialize(test_name, location, message, options={})
15
16
  @test_name = test_name
16
17
  @location = location
17
18
  @message = message
19
+ @method_name = options[:method_name]
18
20
  end
19
21
 
20
22
  # Returns a single character representation of a notification.
@@ -74,7 +76,8 @@ module Test
74
76
  # :backtrace override backtrace.
75
77
  def notify(message, options={}, &block)
76
78
  backtrace = filter_backtrace(options[:backtrace] || caller)
77
- notification = Notification.new(name, backtrace, message)
79
+ notification = Notification.new(name, backtrace, message,
80
+ :method_name => @method_name)
78
81
  add_notification(notification)
79
82
  end
80
83
 
@@ -5,16 +5,18 @@ module Test
5
5
  class Omission
6
6
  include Util::BacktraceFilter
7
7
  attr_reader :test_name, :location, :message
8
+ attr_reader :method_name
8
9
 
9
10
  SINGLE_CHARACTER = 'O'
10
11
  LABEL = "Omission"
11
12
 
12
13
  # Creates a new Omission with the given location and
13
14
  # message.
14
- def initialize(test_name, location, message)
15
+ def initialize(test_name, location, message, options={})
15
16
  @test_name = test_name
16
17
  @location = location
17
18
  @message = message
19
+ @method_name = options[:method_name]
18
20
  end
19
21
 
20
22
  # Returns a single character representation of a omission.
@@ -77,7 +79,8 @@ module Test
77
79
  def omit(message=nil, &block)
78
80
  message ||= "omitted."
79
81
  if block_given?
80
- omission = Omission.new(name, filter_backtrace(caller), message)
82
+ omission = Omission.new(name, filter_backtrace(caller), message,
83
+ :method_name => @method_name)
81
84
  add_omission(omission)
82
85
  else
83
86
  raise OmittedError.new(message)
@@ -154,7 +157,8 @@ module Test
154
157
  return false unless exception.is_a?(OmittedError)
155
158
  omission = Omission.new(name,
156
159
  filter_backtrace(exception.backtrace),
157
- exception.message)
160
+ exception.message,
161
+ :method_name => @method_name)
158
162
  add_omission(omission)
159
163
  true
160
164
  end
@@ -5,16 +5,18 @@ module Test
5
5
  class Pending
6
6
  include Util::BacktraceFilter
7
7
  attr_reader :test_name, :location, :message
8
+ attr_reader :method_name
8
9
 
9
10
  SINGLE_CHARACTER = 'P'
10
11
  LABEL = "Pending"
11
12
 
12
13
  # Creates a new Pending with the given location and
13
14
  # message.
14
- def initialize(test_name, location, message)
15
+ def initialize(test_name, location, message, options={})
15
16
  @test_name = test_name
16
17
  @location = location
17
18
  @message = message
19
+ @method_name = options[:method_name]
18
20
  end
19
21
 
20
22
  # Returns a single character representation of a pending.
@@ -83,7 +85,8 @@ module Test
83
85
  begin
84
86
  yield
85
87
  rescue Exception
86
- pending = Pending.new(name, filter_backtrace(caller), message)
88
+ pending = Pending.new(name, filter_backtrace(caller), message,
89
+ :method_name => @method_name)
87
90
  add_pending(pending)
88
91
  end
89
92
  unless pending
@@ -113,7 +116,8 @@ module Test
113
116
  return false unless exception.is_a?(PendedError)
114
117
  pending = Pending.new(name,
115
118
  filter_backtrace(exception.backtrace),
116
- exception.message)
119
+ exception.message,
120
+ :method_name => @method_name)
117
121
  add_pending(pending)
118
122
  true
119
123
  end
@@ -116,6 +116,13 @@ module Test
116
116
  if _added_methods.include?(stringified_name)
117
117
  attribute(:redefined, {:backtrace => caller}, {}, stringified_name)
118
118
  end
119
+ path, line, = caller[0].split(/:(\d+)/,2)
120
+ line = line.to_i if line
121
+ method_locations << {
122
+ :method_name => stringified_name,
123
+ :path => path,
124
+ :line => line,
125
+ }
119
126
  _added_methods << stringified_name
120
127
  end
121
128
 
@@ -262,6 +269,9 @@ module Test
262
269
  define_method(method_name, &block)
263
270
  description(test_description, method_name)
264
271
  attribute(:test, true, {}, method_name)
272
+ if block.respond_to?(:source_location)
273
+ attribute(:source_location, block.source_location, {}, method_name)
274
+ end
265
275
  else
266
276
  targets = test_description_or_targets
267
277
  attribute(:test, true, {}, *targets)
@@ -281,6 +291,98 @@ module Test
281
291
  def description(value, target=nil)
282
292
  attribute(:description, value, {}, target || [])
283
293
  end
294
+
295
+ # Defines a sub test case.
296
+ #
297
+ # This is a syntax sugar. The both of the following codes are
298
+ # the same in meaning:
299
+ #
300
+ # Standard:
301
+ # class TestParent < Test::UnitTestCase
302
+ # class TestChild < self
303
+ # def test_in_child
304
+ # end
305
+ # end
306
+ # end
307
+ #
308
+ # Syntax sugar:
309
+ # class TestParent < Test::UnitTestCase
310
+ # sub_test_case("TestChild") do
311
+ # def test_in_child
312
+ # end
313
+ # end
314
+ # end
315
+ #
316
+ # The diffrence of them are the following:
317
+ #
318
+ # * Test case created by {sub_test_case} is an anonymous class.
319
+ # So you can't refer the test case by name.
320
+ # * The class name of class style must follow
321
+ # constant naming rule in Ruby. But the name of test case
322
+ # created by {sub_test_case} doesn't need to follow the rule.
323
+ # For example, you can use a space in name such as "child test".
324
+ #
325
+ # @param name [String] The name of newly created sub test case.
326
+ # @yield
327
+ # The block is evaludated under the newly created sub test
328
+ # case class context.
329
+ # @return [Test::Unit::TestCase] Created sub test case class.
330
+ def sub_test_case(name, &block)
331
+ sub_test_case = Class.new(self) do
332
+ singleton_class = class << self; self; end
333
+ singleton_class.send(:define_method, :name) do
334
+ name
335
+ end
336
+ end
337
+ sub_test_case.class_eval(&block)
338
+ sub_test_case
339
+ end
340
+
341
+ # Checkes whether a test that is mathched the query is
342
+ # defined.
343
+ #
344
+ # @option query [String] :path (nil)
345
+ # the path where a test is defined in.
346
+ # @option query [Numeric] :line (nil)
347
+ # the line number where a test is defined at.
348
+ # @option query [String] :method_name (nil)
349
+ # the method name for a test.
350
+ def test_defined?(query)
351
+ query_path = query[:path]
352
+ query_line = query[:line]
353
+ query_method_name = query[:method_name]
354
+
355
+ available_locations = method_locations
356
+ if query_path
357
+ available_locations = available_locations.find_all do |location|
358
+ location[:path].end_with?(query_path)
359
+ end
360
+ end
361
+ if query_line
362
+ available_location = available_locations.reverse.find do |location|
363
+ query_line >= location[:line]
364
+ end
365
+ return false if available_location.nil?
366
+ available_locations = [available_location]
367
+ end
368
+ if query_method_name
369
+ available_location = available_locations.find do |location|
370
+ query_method_name == location[:method_name]
371
+ end
372
+ return false if available_location.nil?
373
+ available_locations = [available_location]
374
+ end
375
+
376
+ not available_locations.empty?
377
+ end
378
+
379
+ private
380
+ # @private
381
+ @@method_locations = {}
382
+ # @private
383
+ def method_locations
384
+ @@method_locations[self] ||= []
385
+ end
284
386
  end
285
387
 
286
388
  attr_reader :method_name
@@ -546,6 +648,30 @@ module Test
546
648
  @internal_data.passed?
547
649
  end
548
650
 
651
+ # Notify that a problem is occurred in the test. It means that
652
+ # the test is a failed test. If any failed tests exist in test
653
+ # suites, the test process exits with failure exit status.
654
+ #
655
+ # This is a public API for developers who extend test-unit.
656
+ #
657
+ # @return [void]
658
+ def problem_occurred
659
+ @internal_data.problem_occurred
660
+ end
661
+
662
+ # Notify that the test is passed. Normally, it is not needed
663
+ # because #run calls it automatically. If you want to override
664
+ # #run, it is not a good idea. Please contact test-unit
665
+ # developers. We will help you without your custom #run. For
666
+ # example, we may add a new hook in #run.
667
+ #
668
+ # This is a public API for developers who extend test-unit.
669
+ #
670
+ # @return [void]
671
+ def add_pass
672
+ current_result.add_pass
673
+ end
674
+
549
675
  private
550
676
  def current_result
551
677
  @_result
@@ -566,23 +692,20 @@ module Test
566
692
 
567
693
  def handle_exception(exception)
568
694
  self.class.exception_handlers.each do |handler|
569
- return true if send(handler, exception)
695
+ if handler.respond_to?(:call)
696
+ handled = handler.call(self, exception)
697
+ else
698
+ handled = send(handler, exception)
699
+ end
700
+ return true if handled
570
701
  end
571
702
  false
572
703
  end
573
704
 
574
- def problem_occurred
575
- @internal_data.problem_occurred
576
- end
577
-
578
705
  def add_assertion
579
706
  current_result.add_assertion
580
707
  end
581
708
 
582
- def add_pass
583
- current_result.add_pass
584
- end
585
-
586
709
  class InternalData
587
710
  attr_reader :start_time, :elapsed_time
588
711
  attr_reader :test_data_label, :test_data