filename./lib/contract/exception.rb
total coverage50.0
code coverage50.0
  1 # This file contains things needed to map Test::Unit status objects to 
  2 # Exceptions, Exception related infrastructure and similar things.
  3 # 
  4 # See Contract::ContractException, Contract::ContractMismatch and
  5 # Contract::ContractError.
  6 
  7 
  8 class Contract < Test::Unit::TestCase
  9   # Exceptions raised by Contract contain some useful meta-information.
 10   # This module is mixed into Exceptions that provide such information.
 11   module ContractException
 12     def ce_initialize(original_message, backtrace, test_object,
 13       test_method, test_contract, *more) # :nodoc:
 14       @original_message = original_message
 15       message = original_message.dup
 16       detail = " for #{test_object.inspect} in " +
 17         "#{test_contract.inspect}"
 18       message[/(\s*)[.!?]?\s*\Z/, 1] = detail
 19       Exception.instance_method(:initialize).bind(self).call(message)
 20 
 21       set_backtrace(backtrace)
 22       @test_object = test_object
 23       @test_method = test_method
 24       @test_contract = test_contract
 25     end
 26     private :ce_initialize
 27 
 28     # What object was tested when this Exception was raised?
 29     attr_reader :test_object
 30     # What method implemented that test?
 31     attr_reader :test_method
 32     # What contract was that method part of?
 33     attr_reader :test_contract
 34     # The original, unfiltered exception message.
 35     attr_reader :original_message
 36   end
 37 
 38   # Represents a failed test in a contract. This means that an object
 39   # simply does not fulfill one of the parts of a contract. Subclass of
 40   # TypeError.
 41   class ContractMismatch < TypeError
 42     def initialize(*args) # :nodoc:
 43       ce_initialize(*args)
 44     end
 45 
 46     include ContractException
 47   end
 48 
 49   # Represents an unexpected failure while processing a contract test.
 50   # This is more critical than ContractMismatch and usually means that
 51   # something is wrong with the test itself.
 52   class ContractError < StandardError
 53     def initialize(*args) # :nodoc:
 54       @type = args.pop
 55       ce_initialize(*args)
 56     end
 57 
 58     # The type of the original Exception that triggered the unexpected
 59     # failure.
 60     attr_reader :type
 61 
 62     include ContractException
 63   end
 64 
 65 
 66   # Maps a Test::Unit::Failure instance to an actual Exception with the
 67   # specified meta data.
 68   def self.failure_to_exception(failure, object, contract) # :nodoc:
 69     ContractMismatch.new(failure.message, failure.location, object,
 70       extract_method_name(failure.test_name), contract)
 71   end
 72 
 73   # Maps a Test::Unit::Error instance to an actual Exception with the
 74   # specified meta data.
 75   def self.error_to_exception(error, object, contract) # :nodoc:
 76     original = error.exception
 77     ContractError.new(original.message, original.backtrace, object,
 78       extract_method_name(error.test_name), contract, original.class)
 79   end
 80 
 81   # Maps a Test::Unit fault (either a Failure or Error) to an actual
 82   # exception with the specified meta data.
 83   def self.fault_to_exception(fault, *args) # :nodoc:
 84     if fault.is_a?(Test::Unit::Failure) then
 85       failure_to_exception(fault, *args)
 86     else
 87       error_to_exception(fault, *args)
 88     end
 89   end
 90 
 91   # Extracts the method name from a Test::Unit test_name style String.
 92   def self.extract_method_name(test_name) # :nodoc:
 93     test_name[/\A(.+?)\(.+?\)\Z/, 1]
 94   end
 95 end
 96 # This file contains things needed to map Test::Unit status objects to 
 97 # Exceptions, Exception related infrastructure and similar things.
 98 # 
 99 # See Contract::ContractException, Contract::ContractMismatch and
100 # Contract::ContractError.
101 
102 
103 class Contract < Test::Unit::TestCase
104   # Exceptions raised by Contract contain some useful meta-information.
105   # This module is mixed into Exceptions that provide such information.
106   module ContractException
107     def ce_initialize(original_message, backtrace, test_object,
108       test_method, test_contract, *more) # :nodoc:
109       @original_message = original_message
110       message = original_message.dup
111       detail = " for #{test_object.inspect} in " +
112         "#{test_contract.inspect}"
113       message[/(\s*)[.!?]?\s*\Z/, 1] = detail
114       Exception.instance_method(:initialize).bind(self).call(message)
115 
116       set_backtrace(backtrace)
117       @test_object = test_object
118       @test_method = test_method
119       @test_contract = test_contract
120     end
121     private :ce_initialize
122 
123     # What object was tested when this Exception was raised?
124     attr_reader :test_object
125     # What method implemented that test?
126     attr_reader :test_method
127     # What contract was that method part of?
128     attr_reader :test_contract
129     # The original, unfiltered exception message.
130     attr_reader :original_message
131   end
132 
133   # Represents a failed test in a contract. This means that an object
134   # simply does not fulfill one of the parts of a contract. Subclass of
135   # TypeError.
136   class ContractMismatch < TypeError
137     def initialize(*args) # :nodoc:
138       ce_initialize(*args)
139     end
140 
141     include ContractException
142   end
143 
144   # Represents an unexpected failure while processing a contract test.
145   # This is more critical than ContractMismatch and usually means that
146   # something is wrong with the test itself.
147   class ContractError < StandardError
148     def initialize(*args) # :nodoc:
149       @type = args.pop
150       ce_initialize(*args)
151     end
152 
153     # The type of the original Exception that triggered the unexpected
154     # failure.
155     attr_reader :type
156 
157     include ContractException
158   end
159 
160 
161   # Maps a Test::Unit::Failure instance to an actual Exception with the
162   # specified meta data.
163   def self.failure_to_exception(failure, object, contract) # :nodoc:
164     ContractMismatch.new(failure.message, failure.location, object,
165       extract_method_name(failure.test_name), contract)
166   end
167 
168   # Maps a Test::Unit::Error instance to an actual Exception with the
169   # specified meta data.
170   def self.error_to_exception(error, object, contract) # :nodoc:
171     original = error.exception
172     ContractError.new(original.message, original.backtrace, object,
173       extract_method_name(error.test_name), contract, original.class)
174   end
175 
176   # Maps a Test::Unit fault (either a Failure or Error) to an actual
177   # exception with the specified meta data.
178   def self.fault_to_exception(fault, *args) # :nodoc:
179     if fault.is_a?(Test::Unit::Failure) then
180       failure_to_exception(fault, *args)
181     else
182       error_to_exception(fault, *args)
183     end
184   end
185 
186   # Extracts the method name from a Test::Unit test_name style String.
187   def self.extract_method_name(test_name) # :nodoc:
188     test_name[/\A(.+?)\(.+?\)\Z/, 1]
189   end
190 end

Valid XHTML 1.1! Valid CSS!