filename../lib/contract/exception.rb
total coverage100.0
code coverage100.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

Valid XHTML 1.1! Valid CSS!