1require 'test/unit/util/backtracefilter'
2
3module Test
4  module Unit
5    class Omission
6      include Util::BacktraceFilter
7      attr_reader :test_name, :location, :message
8      attr_reader :method_name
9
10      SINGLE_CHARACTER = 'O'
11      LABEL = "Omission"
12
13      # Creates a new Omission with the given location and
14      # message.
15      def initialize(test_name, location, message, options={})
16        @test_name = test_name
17        @location = location
18        @message = message
19        @method_name = options[:method_name]
20      end
21
22      # Returns a single character representation of a omission.
23      def single_character_display
24        SINGLE_CHARACTER
25      end
26
27      def label
28        LABEL
29      end
30
31      # Returns a brief version of the error description.
32      def short_display
33        "#{@test_name}: #{@message.split("\n")[0]}"
34      end
35
36      # Returns a verbose version of the error description.
37      def long_display
38        backtrace = filter_backtrace(location).join("\n")
39        "#{label}: #{@message}\n#{@test_name}\n#{backtrace}"
40      end
41
42      # Overridden to return long_display.
43      def to_s
44        long_display
45      end
46
47      def critical?
48        true
49      end
50    end
51
52    class OmittedError < StandardError
53    end
54
55
56    module TestCaseOmissionSupport
57      class << self
58        def included(base)
59          base.class_eval do
60            include OmissionHandler
61          end
62        end
63      end
64
65      # Omit the test or part of the test.
66      #
67      # Example:
68      #   def test_omission
69      #     omit
70      #     # Not reached here
71      #   end
72      #
73      #   def test_omission_with_here
74      #     omit do
75      #       # Not ran here
76      #     end
77      #     # Reached here
78      #   end
79      def omit(message=nil, &block)
80        message ||= "omitted."
81        if block_given?
82          omission = Omission.new(name, filter_backtrace(caller), message,
83                                  :method_name => @method_name)
84          add_omission(omission)
85        else
86          raise OmittedError.new(message)
87        end
88      end
89
90      # Omit the test or part of the test if _condition_ is
91      # true.
92      #
93      # Example:
94      #   def test_omission
95      #     omit_if("".empty?)
96      #     # Not reached here
97      #   end
98      #
99      #   def test_omission_with_here
100      #     omit_if(true) do
101      #       # Not ran here
102      #     end
103      #     omit_if(false) do
104      #       # Reached here
105      #     end
106      #     # Reached here too
107      #   end
108      def omit_if(condition, *args, &block)
109        if condition
110          omit(*args, &block)
111        else
112          block.call if block
113        end
114      end
115
116      # Omit the test or part of the test if _condition_ is
117      # not true.
118      #
119      # Example:
120      #   def test_omission
121      #     omit_unless("string".empty?)
122      #     # Not reached here
123      #   end
124      #
125      #   def test_omission_with_here
126      #     omit_unless(true) do
127      #       # Reached here
128      #     end
129      #     omit_unless(false) do
130      #       # Not ran here
131      #     end
132      #     # Reached here too
133      #   end
134      def omit_unless(condition, *args, &block)
135        if condition
136          block.call if block
137        else
138          omit(*args, &block)
139        end
140      end
141
142      private
143      def add_omission(omission)
144        current_result.add_omission(omission)
145      end
146    end
147
148    module OmissionHandler
149      class << self
150        def included(base)
151          base.exception_handler(:handle_omitted_error)
152        end
153      end
154
155      private
156      def handle_omitted_error(exception)
157        return false unless exception.is_a?(OmittedError)
158        omission = Omission.new(name,
159                                filter_backtrace(exception.backtrace),
160                                exception.message,
161                                :method_name => @method_name)
162        add_omission(omission)
163        true
164      end
165    end
166
167    module TestResultOmissionSupport
168      attr_reader :omissions
169
170      # Records a Test::Unit::Omission.
171      def add_omission(omission)
172        @omissions << omission
173        notify_fault(omission)
174        notify_changed
175      end
176
177      # Returns the number of omissions this TestResult has
178      # recorded.
179      def omission_count
180        @omissions.size
181      end
182
183      private
184      def initialize_containers
185        super
186        @omissions = []
187        @summary_generators << :omission_summary
188      end
189
190      def omission_summary
191        "#{omission_count} omissions"
192      end
193    end
194  end
195end
196