test-unit 2.5.1 → 2.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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