filename | ./lib/contract/exception.rb |
total coverage | 50.0 |
code coverage | 50.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