1require 'minitest/unit'
2require 'pp'
3
4module Test
5  module Unit
6    module Assertions
7      include MiniTest::Assertions
8
9      def mu_pp(obj) #:nodoc:
10        obj.pretty_inspect.chomp
11      end
12
13      MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc:
14
15      UNASSIGNED = Object.new # :nodoc:
16
17      # :call-seq:
18      #   assert( test, failure_message = UNASSIGNED )
19      #
20      #Tests if +test+ is true.
21      #
22      #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used
23      #as the failure message. Otherwise, the result of calling +msg+ will be
24      #used as the message if the assertion fails.
25      #
26      #If no +msg+ is given, a default message will be used.
27      #
28      #    assert(false, "This was expected to be true")
29      def assert(test, msg = UNASSIGNED)
30        case msg
31        when UNASSIGNED
32          msg = nil
33        when String, Proc
34        else
35          bt = caller.reject { |s| s.rindex(MINI_DIR, 0) }
36          raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt
37        end
38        super
39      end
40
41      # :call-seq:
42      #   assert_block( failure_message = nil )
43      #
44      #Tests the result of the given block. If the block does not return true,
45      #the assertion will fail. The optional +failure_message+ argument is the same as in
46      #Assertions#assert.
47      #
48      #    assert_block do
49      #      [1, 2, 3].any? { |num| num < 1 }
50      #    end
51      def assert_block(*msgs)
52        assert yield, *msgs
53      end
54
55      # :call-seq:
56      #   assert_raise( *args, &block )
57      #
58      #Tests if the given block raises an exception. Acceptable exception
59      #types maye be given as optional arguments. If the last argument is a
60      #String, it will be used as the error message.
61      #
62      #    assert_raise do #Fails, no Exceptions are raised
63      #    end
64      #
65      #    assert_raise NameError do
66      #      puts x  #Raises NameError, so assertion succeeds
67      #    end
68      def assert_raise(*args, &b)
69        assert_raises(*args, &b)
70      end
71
72      # :call-seq:
73      #   assert_nothing_raised( *args, &block )
74      #
75      #If any exceptions are given as arguments, the assertion will
76      #fail if one of those exceptions are raised. Otherwise, the test fails
77      #if any exceptions are raised.
78      #
79      #The final argument may be a failure message.
80      #
81      #    assert_nothing_raised RuntimeError do
82      #      raise Exception #Assertion passes, Exception is not a RuntimeError
83      #    end
84      #
85      #    assert_nothing_raised do
86      #      raise Exception #Assertion fails
87      #    end
88      def assert_nothing_raised(*args)
89        self._assertions += 1
90        if Module === args.last
91          msg = nil
92        else
93          msg = args.pop
94        end
95        begin
96          line = __LINE__; yield
97        rescue MiniTest::Skip
98          raise
99        rescue Exception => e
100          bt = e.backtrace
101          as = e.instance_of?(MiniTest::Assertion)
102          if as
103            ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o
104            bt.reject! {|ln| ans =~ ln}
105          end
106          if ((args.empty? && !as) ||
107              args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a })
108            msg = message(msg) { "Exception raised:\n<#{mu_pp(e)}>" }
109            raise MiniTest::Assertion, msg.call, bt
110          else
111            raise
112          end
113        end
114        nil
115      end
116
117      # :call-seq:
118      #   assert_nothing_thrown( failure_message = nil, &block )
119      #
120      #Fails if the given block uses a call to Kernel#throw.
121      #
122      #An optional failure message may be provided as the final argument.
123      #
124      #    assert_nothing_thrown "Something was thrown!" do
125      #      throw :problem?
126      #    end
127      def assert_nothing_thrown(msg=nil)
128        begin
129          yield
130        rescue ArgumentError => error
131          raise error if /\Auncaught throw (.+)\z/m !~ error.message
132          msg = message(msg) { "<#{$1}> was thrown when nothing was expected" }
133          flunk(msg)
134        end
135        assert(true, "Expected nothing to be thrown")
136      end
137
138      # :call-seq:
139      #   assert_equal( expected, actual, failure_message = nil )
140      #
141      #Tests if +expected+ is equal to +actual+.
142      #
143      #An optional failure message may be provided as the final argument.
144      def assert_equal(exp, act, msg = nil)
145        msg = message(msg) {
146          exp_str = mu_pp(exp)
147          act_str = mu_pp(act)
148          exp_comment = ''
149          act_comment = ''
150          if exp_str == act_str
151            if (exp.is_a?(String) && act.is_a?(String)) ||
152               (exp.is_a?(Regexp) && act.is_a?(Regexp))
153              exp_comment = " (#{exp.encoding})"
154              act_comment = " (#{act.encoding})"
155            elsif exp.is_a?(Float) && act.is_a?(Float)
156              exp_str = "%\#.#{Float::DIG+2}g" % exp
157              act_str = "%\#.#{Float::DIG+2}g" % act
158            elsif exp.is_a?(Time) && act.is_a?(Time)
159              if exp.subsec * 1000_000_000 == exp.nsec
160                exp_comment = " (#{exp.nsec}[ns])"
161              else
162                exp_comment = " (subsec=#{exp.subsec})"
163              end
164              if act.subsec * 1000_000_000 == act.nsec
165                act_comment = " (#{act.nsec}[ns])"
166              else
167                act_comment = " (subsec=#{act.subsec})"
168              end
169            elsif exp.class != act.class
170              # a subclass of Range, for example.
171              exp_comment = " (#{exp.class})"
172              act_comment = " (#{act.class})"
173            end
174          elsif !Encoding.compatible?(exp_str, act_str)
175            if exp.is_a?(String) && act.is_a?(String)
176              exp_str = exp.dump
177              act_str = act.dump
178              exp_comment = " (#{exp.encoding})"
179              act_comment = " (#{act.encoding})"
180            else
181              exp_str = exp_str.dump
182              act_str = act_str.dump
183            end
184          end
185          "<#{exp_str}>#{exp_comment} expected but was\n<#{act_str}>#{act_comment}"
186        }
187        assert(exp == act, msg)
188      end
189
190      # :call-seq:
191      #   assert_not_nil( expression, failure_message = nil )
192      #
193      #Tests if +expression+ is not nil.
194      #
195      #An optional failure message may be provided as the final argument.
196      def assert_not_nil(exp, msg=nil)
197        msg = message(msg) { "<#{mu_pp(exp)}> expected to not be nil" }
198        assert(!exp.nil?, msg)
199      end
200
201      # :call-seq:
202      #   assert_not_equal( expected, actual, failure_message = nil )
203      #
204      #Tests if +expected+ is not equal to +actual+.
205      #
206      #An optional failure message may be provided as the final argument.
207      def assert_not_equal(exp, act, msg=nil)
208        msg = message(msg) { "<#{mu_pp(exp)}> expected to be != to\n<#{mu_pp(act)}>" }
209        assert(exp != act, msg)
210      end
211
212      # :call-seq:
213      #   assert_no_match( regexp, string, failure_message = nil )
214      #
215      #Tests if the given Regexp does not match a given String.
216      #
217      #An optional failure message may be provided as the final argument.
218      def assert_no_match(regexp, string, msg=nil)
219        assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
220        self._assertions -= 1
221        msg = message(msg) { "<#{mu_pp(regexp)}> expected to not match\n<#{mu_pp(string)}>" }
222        assert(regexp !~ string, msg)
223      end
224
225      # :call-seq:
226      #   assert_not_same( expected, actual, failure_message = nil )
227      #
228      #Tests if +expected+ is not the same object as +actual+.
229      #This test uses Object#equal? to test equality.
230      #
231      #An optional failure message may be provided as the final argument.
232      #
233      #    assert_not_same("x", "x") #Succeeds
234      def assert_not_same(expected, actual, message="")
235        msg = message(msg) { build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__) }
236<?>
237with id <?> expected to not be equal\\? to
238<?>
239with id <?>.
240EOT
241        assert(!actual.equal?(expected), msg)
242      end
243
244      # :call-seq:
245      #   assert_respond_to( object, method, failure_message = nil )
246      #
247      #Tests if the given Object responds to +method+.
248      #
249      #An optional failure message may be provided as the final argument.
250      #
251      #    assert_respond_to("hello", :reverse)  #Succeeds
252      #    assert_respond_to("hello", :does_not_exist)  #Fails
253      def assert_respond_to obj, (meth, priv), msg = nil
254        if priv
255          msg = message(msg) {
256            "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv}"
257          }
258          return assert obj.respond_to?(meth, priv), msg
259        end
260        #get rid of overcounting
261        super if !caller[0].rindex(MINI_DIR, 0) || !obj.respond_to?(meth)
262      end
263
264      # :call-seq:
265      #   assert_send( +send_array+, failure_message = nil )
266      #
267      # Passes if the method send returns a true value.
268      #
269      # +send_array+ is composed of:
270      # * A receiver
271      # * A method
272      # * Arguments to the method
273      #
274      # Example:
275      #   assert_send([[1, 2], :member?, 1]) # -> pass
276      #   assert_send([[1, 2], :member?, 4]) # -> fail
277      def assert_send send_ary, m = nil
278        recv, msg, *args = send_ary
279        m = message(m) {
280          if args.empty?
281            argsstr = ""
282          else
283            (argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
284          end
285          "Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return true"
286        }
287        assert recv.__send__(msg, *args), m
288      end
289
290      # :call-seq:
291      #   assert_not_send( +send_array+, failure_message = nil )
292      #
293      # Passes if the method send doesn't return a true value.
294      #
295      # +send_array+ is composed of:
296      # * A receiver
297      # * A method
298      # * Arguments to the method
299      #
300      # Example:
301      #   assert_not_send([[1, 2], :member?, 1]) # -> fail
302      #   assert_not_send([[1, 2], :member?, 4]) # -> pass
303      def assert_not_send send_ary, m = nil
304        recv, msg, *args = send_ary
305        m = message(m) {
306          if args.empty?
307            argsstr = ""
308          else
309            (argsstr = mu_pp(args)).sub!(/\A\[(.*)\]\z/m, '(\1)')
310          end
311          "Expected #{mu_pp(recv)}.#{msg}#{argsstr} to return false"
312        }
313        assert !recv.__send__(msg, *args), m
314      end
315
316      ms = instance_methods(true).map {|sym| sym.to_s }
317      ms.grep(/\Arefute_/) do |m|
318        mname = ('assert_not_' << m.to_s[/.*?_(.*)/, 1])
319        alias_method(mname, m) unless ms.include? mname
320      end
321      alias assert_include assert_includes
322      alias assert_not_include assert_not_includes
323
324      def build_message(head, template=nil, *arguments) #:nodoc:
325        template &&= template.chomp
326        template.gsub(/\G((?:[^\\]|\\.)*?)(\\)?\?/) { $1 + ($2 ? "?" : mu_pp(arguments.shift)) }
327      end
328    end
329  end
330end
331