mattly-minitest 1.4.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,530 @@
1
+ ##
2
+ #
3
+ # Totally minimal drop-in replacement for test-unit
4
+ #
5
+ # TODO: refute -> debunk, prove/rebut, show/deny... lots of possibilities
6
+
7
+ module MiniTest
8
+ class Assertion < Exception; end
9
+ class Skip < Assertion; end
10
+
11
+ file = if RUBY_VERSION =~ /^1\.9/ then # bt's expanded, but __FILE__ isn't :(
12
+ File.expand_path __FILE__
13
+ elsif __FILE__ =~ /^[^\.]/ then # assume both relative
14
+ require 'pathname'
15
+ pwd = Pathname.new Dir.pwd
16
+ pn = Pathname.new File.expand_path(__FILE__)
17
+ pn = File.join(".", pn.relative_path_from(pwd)) unless pn.relative?
18
+ pn.to_s
19
+ else # assume both are expanded
20
+ __FILE__
21
+ end
22
+
23
+ # './lib' in project dir, or '/usr/local/blahblah' if installed
24
+ MINI_DIR = File.dirname(File.dirname(file))
25
+
26
+ def self.filter_backtrace bt
27
+ return ["No backtrace"] unless bt
28
+
29
+ new_bt = []
30
+ bt.each do |line|
31
+ break if line.rindex(MINI_DIR, 0)
32
+ new_bt << line
33
+ end
34
+
35
+ new_bt = bt.reject { |line| line.rindex(MINI_DIR, 0) } if new_bt.empty?
36
+ new_bt = bt.dup if new_bt.empty?
37
+ new_bt
38
+ end
39
+
40
+ module Assertions
41
+ def mu_pp(obj)
42
+ s = obj.inspect
43
+ s = s.force_encoding(Encoding.default_external) if defined? Encoding
44
+ s
45
+ end
46
+
47
+ def _assertions= n
48
+ @_assertions = n
49
+ end
50
+
51
+ def _assertions
52
+ @_assertions ||= 0
53
+ end
54
+
55
+ def assert test, msg = nil
56
+ msg ||= "Failed assertion, no message given."
57
+ self._assertions += 1
58
+ unless test then
59
+ msg = msg.call if Proc === msg
60
+ raise MiniTest::Assertion, msg
61
+ end
62
+ true
63
+ end
64
+
65
+ def assert_block msg = nil
66
+ msg = message(msg) { "Expected block to return true value" }
67
+ assert yield, msg
68
+ end
69
+
70
+ def assert_empty obj, msg = nil
71
+ msg = message(msg) { "Expected #{obj.inspect} to be empty" }
72
+ assert_respond_to obj, :empty?
73
+ assert obj.empty?, msg
74
+ end
75
+
76
+ def assert_equal exp, act, msg = nil
77
+ msg = message(msg) { "Expected #{mu_pp(exp)}, not #{mu_pp(act)}" }
78
+ assert(exp == act, msg)
79
+ end
80
+
81
+ def assert_in_delta exp, act, delta = 0.001, msg = nil
82
+ n = (exp - act).abs
83
+ msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to be < #{delta}" }
84
+ assert delta >= n, msg
85
+ end
86
+
87
+ def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
88
+ assert_in_delta a, b, [a, b].min * epsilon, msg
89
+ end
90
+
91
+ def assert_includes collection, obj, msg = nil
92
+ msg = message(msg) { "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}" }
93
+ flip = (collection.is_a?(String)) || (obj.respond_to? :include?) && ! (collection.respond_to? :include?) # HACK for specs
94
+ obj, collection = collection, obj if flip
95
+ assert_respond_to collection, :include?
96
+ assert collection.include?(obj), msg
97
+ end
98
+
99
+ def assert_instance_of cls, obj, msg = nil
100
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}" }
101
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
102
+ obj, cls = cls, obj if flip
103
+ assert obj.instance_of?(cls), msg
104
+ end
105
+
106
+ def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
107
+ msg = message(msg) {
108
+ "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
109
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
110
+ obj, cls = cls, obj if flip
111
+ assert obj.kind_of?(cls), msg
112
+ end
113
+
114
+ def assert_match exp, act, msg = nil
115
+ msg = message(msg) { "Expected #{mu_pp(exp)} to match #{mu_pp(act)}" }
116
+ assert_respond_to act, :"=~"
117
+ exp = /#{Regexp.escape(exp)}/ if String === exp && String === act
118
+ assert exp =~ act, msg
119
+ end
120
+
121
+ def assert_nil obj, msg = nil
122
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
123
+ assert obj.nil?, msg
124
+ end
125
+
126
+ def assert_operator o1, op, o2, msg = nil
127
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
128
+ assert o1.__send__(op, o2), msg
129
+ end
130
+
131
+ def assert_raises *exp
132
+ msg = String === exp.last ? exp.pop : nil
133
+ should_raise = false
134
+ begin
135
+ yield
136
+ should_raise = true
137
+ rescue Exception => e
138
+ assert(exp.any? { |ex|
139
+ ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class
140
+ }, exception_details(e, "#{mu_pp(exp)} exception expected, not"))
141
+
142
+ return e
143
+ end
144
+
145
+ exp = exp.first if exp.size == 1
146
+ flunk "#{mu_pp(exp)} expected but nothing was raised." if should_raise
147
+ end
148
+
149
+ def assert_respond_to obj, meth, msg = nil
150
+ msg = message(msg) {
151
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
152
+ }
153
+ flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
154
+ obj, meth = meth, obj if flip
155
+ assert obj.respond_to?(meth), msg
156
+ end
157
+
158
+ def assert_same exp, act, msg = nil
159
+ msg = message(msg) {
160
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
161
+ "Expected %s (0x%x) to be the same as %s (0x%x)" % data
162
+ }
163
+ assert exp.equal?(act), msg
164
+ end
165
+
166
+ def assert_send send_ary, m = nil
167
+ recv, msg, *args = send_ary
168
+ m = message(m) {
169
+ "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
170
+ assert recv.__send__(msg, *args), m
171
+ end
172
+
173
+ def assert_throws sym, msg = nil
174
+ default = "Expected #{mu_pp(sym)} to have been thrown"
175
+ caught = true
176
+ catch(sym) do
177
+ begin
178
+ yield
179
+ rescue ArgumentError => e # 1.9 exception
180
+ default += ", not #{e.message.split(/ /).last}"
181
+ rescue NameError => e # 1.8 exception
182
+ default += ", not #{e.name.inspect}"
183
+ end
184
+ caught = false
185
+ end
186
+
187
+ assert caught, message(msg) { default }
188
+ end
189
+
190
+ def capture_io
191
+ require 'stringio'
192
+
193
+ orig_stdout, orig_stderr = $stdout, $stderr
194
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
195
+ $stdout, $stderr = captured_stdout, captured_stderr
196
+
197
+ yield
198
+
199
+ return captured_stdout.string, captured_stderr.string
200
+ ensure
201
+ $stdout = orig_stdout
202
+ $stderr = orig_stderr
203
+ end
204
+
205
+ def exception_details e, msg
206
+ "#{msg}\nClass: <#{e.class}>\nMessage: <#{e.message.inspect}>\n---Backtrace---\n#{MiniTest::filter_backtrace(e.backtrace).join("\n")}\n---------------"
207
+ end
208
+
209
+ def flunk msg = nil
210
+ msg ||= "Epic Fail!"
211
+ assert false, msg
212
+ end
213
+
214
+ def message msg = nil, &default
215
+ proc {
216
+ if msg then
217
+ msg = msg.to_s unless String === msg
218
+ msg += '.' unless msg.empty?
219
+ msg += "\n#{default.call}."
220
+ msg.strip
221
+ else
222
+ "#{default.call}."
223
+ end
224
+ }
225
+ end
226
+
227
+ # used for counting assertions
228
+ def pass msg = nil
229
+ assert true
230
+ end
231
+
232
+ def refute test, msg = nil
233
+ msg ||= "Failed refutation, no message given"
234
+ not assert(! test, msg)
235
+ end
236
+
237
+ def refute_empty obj, msg = nil
238
+ msg = message(msg) { "Expected #{obj.inspect} to not be empty" }
239
+ assert_respond_to obj, :empty?
240
+ refute obj.empty?, msg
241
+ end
242
+
243
+ def refute_equal exp, act, msg = nil
244
+ msg = message(msg) { "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}" }
245
+ refute exp == act, msg
246
+ end
247
+
248
+ def refute_in_delta exp, act, delta = 0.001, msg = nil
249
+ n = (exp - act).abs
250
+ msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to not be < #{delta}" }
251
+ refute delta > n, msg
252
+ end
253
+
254
+ def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
255
+ refute_in_delta a, b, a * epsilon, msg
256
+ end
257
+
258
+ def refute_includes collection, obj, msg = nil
259
+ msg = message(msg) { "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}" }
260
+ flip = collection.is_a?(String) || (obj.respond_to? :include?) && ! (collection.respond_to? :include?) # HACK for specs
261
+ obj, collection = collection, obj if flip
262
+ assert_respond_to collection, :include?
263
+ refute collection.include?(obj), msg
264
+ end
265
+
266
+ def refute_instance_of cls, obj, msg = nil
267
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be an instance of #{cls}" }
268
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
269
+ obj, cls = cls, obj if flip
270
+ refute obj.instance_of?(cls), msg
271
+ end
272
+
273
+ def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
274
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
275
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
276
+ obj, cls = cls, obj if flip
277
+ refute obj.kind_of?(cls), msg
278
+ end
279
+
280
+ def refute_match exp, act, msg = nil
281
+ msg = message(msg) { "Expected #{mu_pp(exp)} to not match #{mu_pp(act)}" }
282
+ assert_respond_to act, :"=~"
283
+ exp = /#{Regexp.escape(exp)}/ if String === exp && String === act
284
+ refute exp =~ act, msg
285
+ end
286
+
287
+ def refute_nil obj, msg = nil
288
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
289
+ refute obj.nil?, msg
290
+ end
291
+
292
+ def refute_operator o1, op, o2, msg = nil
293
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}" }
294
+ refute o1.__send__(op, o2), msg
295
+ end
296
+
297
+ def refute_respond_to obj, meth, msg = nil
298
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
299
+ flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
300
+ obj, meth = meth, obj if flip
301
+ refute obj.respond_to?(meth), msg
302
+ end
303
+
304
+ def refute_same exp, act, msg = nil
305
+ msg = message(msg) { "Expected #{mu_pp(act)} to not be the same as #{mu_pp(exp)}" }
306
+ refute exp.equal?(act), msg
307
+ end
308
+
309
+ def skip msg = nil, bt = caller
310
+ msg ||= "Skipped, no message given"
311
+ raise MiniTest::Skip, msg, bt
312
+ end
313
+ end
314
+
315
+ class Unit
316
+ VERSION = "1.4.2"
317
+
318
+ attr_accessor :report, :failures, :errors, :skips
319
+ attr_accessor :test_count, :assertion_count
320
+ attr_accessor :start_time
321
+
322
+ @@installed_at_exit ||= false
323
+ @@out = $stdout
324
+
325
+ def self.autorun
326
+ at_exit {
327
+ next if $! # don't run if there was an exception
328
+ exit_code = MiniTest::Unit.new.run(ARGV)
329
+ exit false if exit_code && exit_code != 0
330
+ } unless @@installed_at_exit
331
+ @@installed_at_exit = true
332
+ end
333
+
334
+ def self.output= stream
335
+ @@out = stream
336
+ end
337
+
338
+ def location e
339
+ last_before_assertion = ""
340
+ e.backtrace.reverse_each do |s|
341
+ break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
342
+ last_before_assertion = s
343
+ end
344
+ last_before_assertion.sub(/:in .*$/, '')
345
+ end
346
+
347
+ def puke klass, meth, e
348
+ e = case e
349
+ when MiniTest::Skip then
350
+ @skips += 1
351
+ "Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
352
+ when MiniTest::Assertion then
353
+ @failures += 1
354
+ "Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
355
+ else
356
+ @errors += 1
357
+ bt = MiniTest::filter_backtrace(e.backtrace).join("\n ")
358
+ "Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
359
+ end
360
+ @report << e
361
+ e[0, 1]
362
+ end
363
+
364
+ def initialize
365
+ @report = []
366
+ @errors = @failures = @skips = 0
367
+ @verbose = false
368
+ end
369
+
370
+ ##
371
+ # Top level driver, controls all output and filtering.
372
+
373
+ def run args = []
374
+ @verbose = args.delete('-v')
375
+
376
+ filter = if args.first =~ /^(-n|--name)$/ then
377
+ args.shift
378
+ arg = args.shift
379
+ arg =~ /\/(.*)\// ? Regexp.new($1) : arg
380
+ else
381
+ /./ # anything - ^test_ already filtered by #tests
382
+ end
383
+
384
+ @@out.puts "Loaded suite #{$0.sub(/\.rb$/, '')}\nStarted"
385
+
386
+ start = Time.now
387
+ run_test_suites filter
388
+
389
+ @@out.puts
390
+ @@out.puts "Finished in #{'%.6f' % (Time.now - start)} seconds."
391
+
392
+ @report.each_with_index do |msg, i|
393
+ @@out.puts "\n%3d) %s" % [i + 1, msg]
394
+ end
395
+
396
+ @@out.puts
397
+
398
+ status
399
+
400
+ return failures + errors if @test_count > 0 # or return nil...
401
+ rescue Interrupt
402
+ abort 'Interrupted'
403
+ end
404
+
405
+ def status io = @@out
406
+ format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
407
+ io.puts format % [test_count, assertion_count, failures, errors, skips]
408
+ end
409
+
410
+ def run_test_suites filter = /./
411
+ @test_count, @assertion_count = 0, 0
412
+ old_sync, @@out.sync = @@out.sync, true if @@out.respond_to? :sync=
413
+ TestCase.test_suites.each do |suite|
414
+ suite.test_methods.grep(filter).each do |test|
415
+ inst = suite.new test
416
+ inst._assertions = 0
417
+ @@out.print "#{suite}##{test}: " if @verbose
418
+
419
+ @start_time = Time.now
420
+ result = inst.run(self)
421
+
422
+ @@out.print "%.2f s: " % (Time.now - @start_time) if @verbose
423
+ @@out.print result
424
+ @@out.puts if @verbose
425
+ @test_count += 1
426
+ @assertion_count += inst._assertions
427
+ end
428
+ end
429
+ @@out.sync = old_sync if @@out.respond_to? :sync=
430
+ [@test_count, @assertion_count]
431
+ end
432
+
433
+ class TestCase
434
+ attr_reader :__name__
435
+
436
+ PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt,
437
+ SystemExit]
438
+
439
+ SUPPORTS_INFO_SIGNAL = Signal.list['INFO']
440
+
441
+ def run runner
442
+ trap 'INFO' do
443
+ warn '%s#%s %.2fs' % [self.class, self.__name__,
444
+ (Time.now - runner.start_time)]
445
+ runner.status $stderr
446
+ end if SUPPORTS_INFO_SIGNAL
447
+
448
+ result = '.'
449
+ begin
450
+ @passed = nil
451
+ self.setup
452
+ self.__send__ self.__name__
453
+ @passed = true
454
+ rescue *PASSTHROUGH_EXCEPTIONS
455
+ raise
456
+ rescue Exception => e
457
+ @passed = false
458
+ result = runner.puke(self.class, self.__name__, e)
459
+ ensure
460
+ begin
461
+ self.teardown
462
+ rescue *PASSTHROUGH_EXCEPTIONS
463
+ raise
464
+ rescue Exception => e
465
+ result = runner.puke(self.class, self.__name__, e)
466
+ end
467
+ trap 'INFO', 'DEFAULT' if SUPPORTS_INFO_SIGNAL
468
+ end
469
+ result
470
+ end
471
+
472
+ def initialize name
473
+ @__name__ = name
474
+ @passed = nil
475
+ end
476
+
477
+ def self.reset
478
+ @@test_suites = {}
479
+ end
480
+
481
+ reset
482
+
483
+ def self.inherited klass
484
+ @@test_suites[klass] = true
485
+ end
486
+
487
+ def self.test_order
488
+ :random
489
+ end
490
+
491
+ def self.test_suites
492
+ @@test_suites.keys.sort_by { |ts| ts.name }
493
+ end
494
+
495
+ def self.test_methods
496
+ methods = public_instance_methods(true).grep(/^test/).map { |m|
497
+ m.to_s
498
+ }.sort
499
+
500
+ if self.test_order == :random then
501
+ max = methods.size
502
+ methods = methods.sort_by { rand(max) }
503
+ end
504
+
505
+ methods
506
+ end
507
+
508
+ def setup; end
509
+ def teardown; end
510
+
511
+ def passed?
512
+ @passed
513
+ end
514
+
515
+ include MiniTest::Assertions
516
+ end # class TestCase
517
+ end # class Test
518
+ end # module Mini
519
+
520
+ if $DEBUG then
521
+ # this helps me ferret out porting issues
522
+ module Test; end
523
+ module Test::Unit; end
524
+ class Test::Unit::TestCase
525
+ def self.inherited x
526
+ raise "You're running minitest and test/unit in the same process: #{x}"
527
+ end
528
+ end
529
+ end
530
+