1#--
2#
3# Author:: Nathaniel Talbott.
4# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
5# License:: Ruby license.
6
7module Test
8  module Unit
9
10    # Encapsulates a test failure. Created by Test::Unit::TestCase
11    # when an assertion fails.
12    class Failure
13      attr_reader :test_name, :location, :message
14      attr_reader :method_name, :source_location
15      attr_reader :expected, :actual, :user_message
16      attr_reader :inspected_expected, :inspected_actual
17
18      SINGLE_CHARACTER = 'F'
19      LABEL = "Failure"
20
21      # Creates a new Failure with the given location and
22      # message.
23      def initialize(test_name, location, message, options={})
24        @test_name = test_name
25        @location = location
26        @message = message
27        @method_name = options[:method_name]
28        @source_location = options[:source_location]
29        @expected = options[:expected]
30        @actual = options[:actual]
31        @inspected_expected = options[:inspected_expected]
32        @inspected_actual = options[:inspected_actual]
33        @user_message = options[:user_message]
34      end
35
36      # Returns a single character representation of a failure.
37      def single_character_display
38        SINGLE_CHARACTER
39      end
40
41      def label
42        LABEL
43      end
44
45      # Returns a brief version of the error description.
46      def short_display
47        "#@test_name: #{@message.split("\n")[0]}"
48      end
49
50      # Returns a verbose version of the error description.
51      def long_display
52        if location.size == 1
53          location_display = location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
54        else
55          location_display = "\n    [#{location.join("\n     ")}]"
56        end
57        "#{label}:\n#@test_name#{location_display}:\n#@message"
58      end
59
60      # Overridden to return long_display.
61      def to_s
62        long_display
63      end
64
65      def critical?
66        true
67      end
68
69      def diff
70        @diff ||= compute_diff
71      end
72
73      private
74      def compute_diff
75        Assertions::AssertionMessage.delayed_diff(@expected, @actual).inspect
76      end
77    end
78
79    module FailureHandler
80      class << self
81        def included(base)
82          base.exception_handler(:handle_assertion_failed_error)
83        end
84      end
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
122      private
123      def handle_assertion_failed_error(exception)
124        return false unless exception.is_a?(AssertionFailedError)
125        problem_occurred
126        add_failure(exception.message, exception.backtrace,
127                    :expected => exception.expected,
128                    :actual => exception.actual,
129                    :inspected_expected => exception.inspected_expected,
130                    :inspected_actual => exception.inspected_actual,
131                    :user_message => exception.user_message)
132        true
133      end
134    end
135
136    module TestResultFailureSupport
137      attr_reader :failures
138
139      # Records a Test::Unit::Failure.
140      def add_failure(failure)
141        @failures << failure
142        notify_fault(failure)
143        notify_changed
144      end
145
146      # Returns the number of failures this TestResult has
147      # recorded.
148      def failure_count
149        @failures.size
150      end
151
152      def failure_occurred?
153        not @failures.empty?
154      end
155
156      private
157      def initialize_containers
158        super
159        @failures = []
160        @summary_generators << :failure_summary
161        @problem_checkers << :failure_occurred?
162      end
163
164      def failure_summary
165        "#{failure_count} failures"
166      end
167    end
168  end
169end
170