micron 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +88 -0
  4. data/Rakefile +40 -0
  5. data/VERSION +1 -0
  6. data/bin/micron +4 -0
  7. data/lib/micron.rb +29 -0
  8. data/lib/micron/app.rb +127 -0
  9. data/lib/micron/app/options.rb +73 -0
  10. data/lib/micron/assertion.rb +10 -0
  11. data/lib/micron/fork_runner.rb +55 -0
  12. data/lib/micron/minitest.rb +45 -0
  13. data/lib/micron/proc_runner.rb +114 -0
  14. data/lib/micron/rake.rb +29 -0
  15. data/lib/micron/reporter.rb +30 -0
  16. data/lib/micron/reporter/console.rb +146 -0
  17. data/lib/micron/reporter/coverage.rb +37 -0
  18. data/lib/micron/runner.rb +95 -0
  19. data/lib/micron/runner/backtrace_filter.rb +39 -0
  20. data/lib/micron/runner/clazz.rb +45 -0
  21. data/lib/micron/runner/clazz19.rb +24 -0
  22. data/lib/micron/runner/debug.rb +22 -0
  23. data/lib/micron/runner/exception_info.rb +16 -0
  24. data/lib/micron/runner/fork_worker.rb +185 -0
  25. data/lib/micron/runner/forking_clazz.rb +40 -0
  26. data/lib/micron/runner/liveness_checker.rb +40 -0
  27. data/lib/micron/runner/liveness_checker/ping.rb +65 -0
  28. data/lib/micron/runner/liveness_checker/pong.rb +36 -0
  29. data/lib/micron/runner/method.rb +124 -0
  30. data/lib/micron/runner/parallel_clazz.rb +135 -0
  31. data/lib/micron/runner/proc_clazz.rb +48 -0
  32. data/lib/micron/runner/process_reaper.rb +98 -0
  33. data/lib/micron/runner/shim.rb +68 -0
  34. data/lib/micron/runner/test_file.rb +79 -0
  35. data/lib/micron/test_case.rb +36 -0
  36. data/lib/micron/test_case/assertions.rb +701 -0
  37. data/lib/micron/test_case/lifecycle_hooks.rb +74 -0
  38. data/lib/micron/test_case/redir_logging.rb +85 -0
  39. data/lib/micron/test_case/teardown_coverage.rb +13 -0
  40. data/lib/micron/util/ex.rb +23 -0
  41. data/lib/micron/util/io.rb +54 -0
  42. data/lib/micron/util/thread_dump.rb +29 -0
  43. data/micron.gemspec +97 -0
  44. metadata +184 -0
@@ -0,0 +1,79 @@
1
+
2
+ module Micron
3
+ class Runner
4
+ class TestFile
5
+
6
+ def initialize(filename)
7
+ @filename = filename
8
+ end
9
+
10
+ # Load the test file
11
+ #
12
+ # @throws [Exception] exception, if one was raised during loading
13
+ def load(coverage=false)
14
+ if coverage then
15
+ file = @filename
16
+ EasyCov.filters << lambda { |f| f == file }
17
+ EasyCov.start
18
+ end
19
+ require @filename
20
+ return nil
21
+ end
22
+
23
+ # Simply load the file and collect coverage
24
+ def collect_coverage
25
+ worker = ForkWorker.new do
26
+ load(true)
27
+ EasyCov.dump
28
+ end
29
+ worker.run
30
+ worker.wait
31
+ end
32
+
33
+ # Execute the tests in the file, using the given Clazz
34
+ #
35
+ # @param [Clazz] run_clazz
36
+ #
37
+ # @return [Array<Object>] array of Clazz and Exception objects
38
+ def run(run_clazz)
39
+
40
+ results = []
41
+ test_clazz = TestCase.subclasses.last
42
+
43
+ begin
44
+ clazz = run_clazz.new(test_clazz, @filename)
45
+
46
+ Micron.runner.report(:start_class, clazz)
47
+ if !clazz.methods.empty? then
48
+ clazz.run
49
+ results << clazz
50
+ end
51
+ Micron.runner.report(:end_class, clazz)
52
+
53
+ rescue Exception => ex
54
+ # Error with loading the test class itself
55
+ results << ex
56
+ return results
57
+ end
58
+
59
+ return results
60
+ end
61
+
62
+ # Run the given test method
63
+ #
64
+ # @param [String] test_clazz Name of the TestCase Class
65
+ # @param [String] test_method Method name
66
+ # @param [Clazz] run_clazz Clazz to run with
67
+ #
68
+ # @return [Method]
69
+ def run_method(test_clazz, test_method, run_clazz)
70
+ clazz = run_clazz.new(test_clazz, @filename)
71
+ method = clazz.methods.find{ |m| m.name.to_s == test_method }
72
+ method.run
73
+
74
+ return method
75
+ end
76
+
77
+ end # TestFile
78
+ end # Runner
79
+ end # Micron
@@ -0,0 +1,36 @@
1
+
2
+ require "micron/test_case/assertions"
3
+ require "micron/test_case/lifecycle_hooks"
4
+
5
+ module Micron
6
+
7
+ class TestCase
8
+
9
+ include LifecycleHooks
10
+ include Assertions
11
+
12
+ def setup
13
+ end
14
+
15
+ def teardown
16
+ end
17
+
18
+
19
+ # retrieve all loaded subclasses of this class
20
+ #
21
+ # @return [Array<Class>] List of subclasses
22
+ def self.subclasses
23
+ @subclasses
24
+ end
25
+
26
+ def self.inherited(subclass)
27
+ if superclass.respond_to? :inherited
28
+ superclass.inherited(subclass)
29
+ end
30
+ @subclasses ||= []
31
+ @subclasses << subclass
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,701 @@
1
+
2
+ module Micron
3
+
4
+ class TestCase
5
+
6
+ # Micron Assertions. All assertion methods accept a +msg+ which is
7
+ # printed if the assertion fails.
8
+ #
9
+ # Copied from MiniTest
10
+ module Assertions
11
+
12
+ UNDEFINED = Object.new # :nodoc:
13
+
14
+ def UNDEFINED.inspect # :nodoc:
15
+ "UNDEFINED" # again with the rdoc bugs... :(
16
+ end
17
+
18
+ ##
19
+ # Returns the diff command to use in #diff. Tries to intelligently
20
+ # figure out what diff to use.
21
+
22
+ def self.diff
23
+ @diff = if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ &&
24
+ system("diff.exe", __FILE__, __FILE__)) then
25
+ "diff.exe -u"
26
+ # elsif Minitest::Unit::Guard.maglev? then # HACK
27
+ # "diff -u"
28
+ elsif system("gdiff", __FILE__, __FILE__)
29
+ "gdiff -u" # solaris and kin suck
30
+ elsif system("diff", __FILE__, __FILE__)
31
+ "diff -u"
32
+ else
33
+ nil
34
+ end unless defined? @diff
35
+
36
+ @diff
37
+ end
38
+
39
+ ##
40
+ # Set the diff command to use in #diff.
41
+
42
+ def self.diff= o
43
+ @diff = o
44
+ end
45
+
46
+ ##
47
+ # Returns a diff between +exp+ and +act+. If there is no known
48
+ # diff command or if it doesn't make sense to diff the output
49
+ # (single line, short output), then it simply returns a basic
50
+ # comparison between the two.
51
+
52
+ def diff exp, act
53
+ require "tempfile"
54
+
55
+ expect = mu_pp_for_diff exp
56
+ butwas = mu_pp_for_diff act
57
+ result = nil
58
+
59
+ need_to_diff =
60
+ Micron::TestCase::Assertions.diff &&
61
+ (expect.include?("\n") ||
62
+ butwas.include?("\n") ||
63
+ expect.size > 30 ||
64
+ butwas.size > 30 ||
65
+ expect == butwas)
66
+
67
+ return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
68
+ need_to_diff
69
+
70
+ Tempfile.open("expect") do |a|
71
+ a.puts expect
72
+ a.flush
73
+
74
+ Tempfile.open("butwas") do |b|
75
+ b.puts butwas
76
+ b.flush
77
+
78
+ result = `#{Micron::TestCase::Assertions.diff} #{a.path} #{b.path}`
79
+ result.sub!(/^\-\-\- .+/, "--- expected")
80
+ result.sub!(/^\+\+\+ .+/, "+++ actual")
81
+
82
+ if result.empty? then
83
+ klass = exp.class
84
+ result = [
85
+ "No visible difference in the #{klass}#inspect output.\n",
86
+ "You should look at the implementation of #== on ",
87
+ "#{klass} or its members.\n",
88
+ expect,
89
+ ].join
90
+ end
91
+ end
92
+ end
93
+
94
+ result
95
+ end
96
+
97
+ ##
98
+ # This returns a human-readable version of +obj+. By default
99
+ # #inspect is called. You can override this to use #pretty_print
100
+ # if you want.
101
+
102
+ def mu_pp obj
103
+ s = obj.inspect
104
+ s = s.encode Encoding.default_external if defined? Encoding
105
+ s
106
+ end
107
+
108
+ ##
109
+ # This returns a diff-able human-readable version of +obj+. This
110
+ # differs from the regular mu_pp because it expands escaped
111
+ # newlines and makes hex-values generic (like object_ids). This
112
+ # uses mu_pp to do the first pass and then cleans it up.
113
+
114
+ def mu_pp_for_diff obj
115
+ mu_pp(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX')
116
+ end
117
+
118
+ def _assertions= n # :nodoc:
119
+ @_assertions = n
120
+ end
121
+
122
+ def _assertions # :nodoc:
123
+ @_assertions ||= 0
124
+ end
125
+
126
+ ##
127
+ # Fails unless +test+ is a true value.
128
+
129
+ def assert test, msg = nil
130
+ msg ||= "Failed assertion, no message given."
131
+ self._assertions += 1
132
+ unless test then
133
+ msg = msg.call if Proc === msg
134
+ raise Micron::Assertion, msg
135
+ end
136
+ true
137
+ end
138
+
139
+ ##
140
+ # Fails unless +obj+ is empty.
141
+
142
+ def assert_empty obj, msg = nil
143
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
144
+ assert_respond_to obj, :empty?
145
+ assert obj.empty?, msg
146
+ end
147
+
148
+ ##
149
+ # Fails unless <tt>exp == act</tt> printing the difference between
150
+ # the two, if possible.
151
+ #
152
+ # If there is no visible difference but the assertion fails, you
153
+ # should suspect that your #== is buggy, or your inspect output is
154
+ # missing crucial details.
155
+ #
156
+ # For floats use assert_in_delta.
157
+ #
158
+ # See also: Micron::TestCase::Assertions.diff
159
+
160
+ def assert_equal exp, act, msg = nil
161
+ msg = message(msg, "") { diff exp, act }
162
+ assert exp == act, msg
163
+ end
164
+
165
+ ##
166
+ # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
167
+ # of each other.
168
+ #
169
+ # assert_in_delta Math::PI, (22.0 / 7.0), 0.01
170
+
171
+ def assert_in_delta exp, act, delta = 0.001, msg = nil
172
+ n = (exp - act).abs
173
+ msg = message(msg) {
174
+ "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}"
175
+ }
176
+ assert delta >= n, msg
177
+ end
178
+
179
+ ##
180
+ # For comparing Floats. Fails unless +exp+ and +act+ have a relative
181
+ # error less than +epsilon+.
182
+
183
+ def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
184
+ assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
185
+ end
186
+
187
+ ##
188
+ # Fails unless +collection+ includes +obj+.
189
+
190
+ def assert_includes collection, obj, msg = nil
191
+ msg = message(msg) {
192
+ "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
193
+ }
194
+ assert_respond_to collection, :include?
195
+ assert collection.include?(obj), msg
196
+ end
197
+
198
+ ##
199
+ # Fails unless +obj+ is an instance of +cls+.
200
+
201
+ def assert_instance_of cls, obj, msg = nil
202
+ msg = message(msg) {
203
+ "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
204
+ }
205
+
206
+ assert obj.instance_of?(cls), msg
207
+ end
208
+
209
+ ##
210
+ # Fails unless +obj+ is a kind of +cls+.
211
+
212
+ def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
213
+ msg = message(msg) {
214
+ "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
215
+
216
+ assert obj.kind_of?(cls), msg
217
+ end
218
+
219
+ ##
220
+ # Fails unless +matcher+ <tt>=~</tt> +obj+.
221
+
222
+ def assert_match matcher, obj, msg = nil
223
+ msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
224
+ assert_respond_to matcher, :"=~"
225
+ matcher = Regexp.new Regexp.escape matcher if String === matcher
226
+ assert matcher =~ obj, msg
227
+ end
228
+
229
+ ##
230
+ # Fails unless +obj+ is nil
231
+
232
+ def assert_nil obj, msg = nil
233
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
234
+ assert obj.nil?, msg
235
+ end
236
+
237
+ ##
238
+ # For testing with binary operators.
239
+ #
240
+ # assert_operator 5, :<=, 4
241
+
242
+ def assert_operator o1, op, o2 = UNDEFINED, msg = nil
243
+ return assert_predicate o1, op, msg if UNDEFINED == o2
244
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
245
+ assert o1.__send__(op, o2), msg
246
+ end
247
+
248
+ ##
249
+ # Fails if stdout or stderr do not output the expected results.
250
+ # Pass in nil if you don't care about that streams output. Pass in
251
+ # "" if you require it to be silent. Pass in a regexp if you want
252
+ # to pattern match.
253
+ #
254
+ # NOTE: this uses #capture_io, not #capture_subprocess_io.
255
+ #
256
+ # See also: #assert_silent
257
+
258
+ def assert_output stdout = nil, stderr = nil
259
+ out, err = capture_io do
260
+ yield
261
+ end
262
+
263
+ err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
264
+ out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
265
+
266
+ y = send err_msg, stderr, err, "In stderr" if err_msg
267
+ x = send out_msg, stdout, out, "In stdout" if out_msg
268
+
269
+ (!stdout || x) && (!stderr || y)
270
+ end
271
+
272
+ ##
273
+ # For testing with predicates.
274
+ #
275
+ # assert_predicate str, :empty?
276
+ #
277
+ # This is really meant for specs and is front-ended by assert_operator:
278
+ #
279
+ # str.must_be :empty?
280
+
281
+ def assert_predicate o1, op, msg = nil
282
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
283
+ assert o1.__send__(op), msg
284
+ end
285
+
286
+ ##
287
+ # Fails unless the block raises one of +exp+. Returns the
288
+ # exception matched so you can check the message, attributes, etc.
289
+
290
+ def assert_raises *exp
291
+ msg = "#{exp.pop}.\n" if String === exp.last
292
+
293
+ begin
294
+ yield
295
+ rescue Micron::Skip => e
296
+ return e if exp.include? Micron::Skip
297
+ raise e
298
+ rescue Exception => e
299
+ expected = exp.any? { |ex|
300
+ if ex.instance_of? Module then
301
+ e.kind_of? ex
302
+ else
303
+ e.instance_of? ex
304
+ end
305
+ }
306
+
307
+ assert expected, proc {
308
+ exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
309
+ }
310
+
311
+ return e
312
+ end
313
+
314
+ exp = exp.first if exp.size == 1
315
+
316
+ flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
317
+ end
318
+ alias_method :assert_throws, :assert_raises
319
+
320
+ ##
321
+ # Fails unless +obj+ responds to +meth+.
322
+
323
+ def assert_respond_to obj, meth, msg = nil
324
+ msg = message(msg) {
325
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
326
+ }
327
+ assert obj.respond_to?(meth), msg
328
+ end
329
+
330
+ ##
331
+ # Fails unless +exp+ and +act+ are #equal?
332
+
333
+ def assert_same exp, act, msg = nil
334
+ msg = message(msg) {
335
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
336
+ "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
337
+ }
338
+ assert exp.equal?(act), msg
339
+ end
340
+
341
+ ##
342
+ # +send_ary+ is a receiver, message and arguments.
343
+ #
344
+ # Fails unless the call returns a true value
345
+ # TODO: I should prolly remove this from specs
346
+
347
+ def assert_send send_ary, m = nil
348
+ recv, msg, *args = send_ary
349
+ m = message(m) {
350
+ "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
351
+ assert recv.__send__(msg, *args), m
352
+ end
353
+
354
+ ##
355
+ # Fails if the block outputs anything to stderr or stdout.
356
+ #
357
+ # See also: #assert_output
358
+
359
+ def assert_silent
360
+ assert_output "", "" do
361
+ yield
362
+ end
363
+ end
364
+
365
+ ##
366
+ # Fails unless the block throws +sym+
367
+
368
+ # def assert_throws sym, msg = nil
369
+ # default = "Expected #{mu_pp(sym)} to have been thrown"
370
+ # caught = true
371
+ # catch(sym) do
372
+ # begin
373
+ # yield
374
+ # rescue ThreadError => e # wtf?!? 1.8 + threads == suck
375
+ # default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
376
+ # rescue ArgumentError => e # 1.9 exception
377
+ # default += ", not #{e.message.split(/ /).last}"
378
+ # rescue NameError => e # 1.8 exception
379
+ # default += ", not #{e.name.inspect}"
380
+ # end
381
+ # caught = false
382
+ # end
383
+
384
+ # assert caught, message(msg) { default }
385
+ # end
386
+
387
+ # Fails unless the block throws +clazz+ and +msg+ matches the exception message
388
+ #
389
+ # minitest assert_throws doesn't seem to work properly
390
+ # This version will catch *ALL* exceptions, including SystemExit
391
+ # def assert_throws(clazz, msg = nil, &block)
392
+ # begin
393
+ # yield
394
+
395
+ # rescue Exception => ex
396
+ # if ex.kind_of? clazz then
397
+ # if msg.nil?
398
+ # return
399
+ # elsif msg.kind_of?(Regexp) && msg.match(ex.message) then
400
+ # return
401
+ # elsif msg.kind_of?(String) && msg == ex.message then
402
+ # return
403
+ # end
404
+ # end
405
+
406
+ # puts "Caught an unexpected exception:"
407
+ # puts "Expected: <#{clazz}> #{msg.inspect}"
408
+ # puts " Caught: <#{ex.class}> #{ex.message.inspect}"
409
+ # puts " " + ex.backtrace.join("\n ")
410
+ # puts
411
+ # end
412
+
413
+ # flunk("Expected #{mu_pp(clazz)} to have been thrown")
414
+ # end
415
+
416
+ ##
417
+ # Captures $stdout and $stderr into strings:
418
+ #
419
+ # out, err = capture_io do
420
+ # puts "Some info"
421
+ # warn "You did a bad thing"
422
+ # end
423
+ #
424
+ # assert_match %r%info%, out
425
+ # assert_match %r%bad%, err
426
+ #
427
+ # NOTE: For efficiency, this method uses StringIO and does not
428
+ # capture IO for subprocesses. Use #capture_subprocess_io for
429
+ # that.
430
+
431
+ def capture_io
432
+ require 'stringio'
433
+
434
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
435
+
436
+ synchronize do
437
+ orig_stdout, orig_stderr = $stdout, $stderr
438
+ $stdout, $stderr = captured_stdout, captured_stderr
439
+
440
+ begin
441
+ yield
442
+ ensure
443
+ $stdout = orig_stdout
444
+ $stderr = orig_stderr
445
+ end
446
+ end
447
+
448
+ return captured_stdout.string, captured_stderr.string
449
+ end
450
+
451
+ ##
452
+ # Captures $stdout and $stderr into strings, using Tempfile to
453
+ # ensure that subprocess IO is captured as well.
454
+ #
455
+ # out, err = capture_subprocess_io do
456
+ # system "echo Some info"
457
+ # system "echo You did a bad thing 1>&2"
458
+ # end
459
+ #
460
+ # assert_match %r%info%, out
461
+ # assert_match %r%bad%, err
462
+ #
463
+ # NOTE: This method is approximately 10x slower than #capture_io so
464
+ # only use it when you need to test the output of a subprocess.
465
+
466
+ def capture_subprocess_io
467
+ require 'tempfile'
468
+
469
+ captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
470
+
471
+ synchronize do
472
+ orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
473
+ $stdout.reopen captured_stdout
474
+ $stderr.reopen captured_stderr
475
+
476
+ begin
477
+ yield
478
+
479
+ $stdout.rewind
480
+ $stderr.rewind
481
+
482
+ [captured_stdout.read, captured_stderr.read]
483
+ ensure
484
+ captured_stdout.unlink
485
+ captured_stderr.unlink
486
+ $stdout.reopen orig_stdout
487
+ $stderr.reopen orig_stderr
488
+ end
489
+ end
490
+ end
491
+
492
+ ##
493
+ # Returns details for exception +e+
494
+
495
+ def exception_details e, msg
496
+ [
497
+ "#{msg}",
498
+ "<#{e.class}> #{e.message.inspect}",
499
+ "#{Micron.filter_backtrace(e.backtrace).join("\n")}",
500
+ ].join "\n "
501
+ end
502
+
503
+ ##
504
+ # Fails with +msg+
505
+
506
+ def flunk msg = nil
507
+ msg ||= "Epic Fail!"
508
+ assert false, msg
509
+ end
510
+
511
+ ##
512
+ # Returns a proc that will output +msg+ along with the default message.
513
+
514
+ def message msg = nil, ending = ".", &default
515
+ proc {
516
+ msg = msg.call.chomp(".") if Proc === msg
517
+ custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
518
+ "#{custom_message}#{default.call}#{ending}"
519
+ }
520
+ end
521
+
522
+ ##
523
+ # used for counting assertions
524
+
525
+ def pass msg = nil
526
+ assert true
527
+ end
528
+
529
+ ##
530
+ # Fails if +test+ is a true value
531
+
532
+ def refute test, msg = nil
533
+ msg ||= "Failed refutation, no message given"
534
+ not assert(! test, msg)
535
+ end
536
+
537
+ ##
538
+ # Fails if +obj+ is empty.
539
+
540
+ def refute_empty obj, msg = nil
541
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
542
+ assert_respond_to obj, :empty?
543
+ refute obj.empty?, msg
544
+ end
545
+
546
+ ##
547
+ # Fails if <tt>exp == act</tt>.
548
+ #
549
+ # For floats use refute_in_delta.
550
+
551
+ def refute_equal exp, act, msg = nil
552
+ msg = message(msg) {
553
+ "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
554
+ }
555
+ refute exp == act, msg
556
+ end
557
+
558
+ ##
559
+ # For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
560
+ #
561
+ # refute_in_delta Math::PI, (22.0 / 7.0)
562
+
563
+ def refute_in_delta exp, act, delta = 0.001, msg = nil
564
+ n = (exp - act).abs
565
+ msg = message(msg) {
566
+ "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}"
567
+ }
568
+ refute delta >= n, msg
569
+ end
570
+
571
+ ##
572
+ # For comparing Floats. Fails if +exp+ and +act+ have a relative error
573
+ # less than +epsilon+.
574
+
575
+ def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
576
+ refute_in_delta a, b, a * epsilon, msg
577
+ end
578
+
579
+ ##
580
+ # Fails if +collection+ includes +obj+.
581
+
582
+ def refute_includes collection, obj, msg = nil
583
+ msg = message(msg) {
584
+ "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
585
+ }
586
+ assert_respond_to collection, :include?
587
+ refute collection.include?(obj), msg
588
+ end
589
+
590
+ ##
591
+ # Fails if +obj+ is an instance of +cls+.
592
+
593
+ def refute_instance_of cls, obj, msg = nil
594
+ msg = message(msg) {
595
+ "Expected #{mu_pp(obj)} to not be an instance of #{cls}"
596
+ }
597
+ refute obj.instance_of?(cls), msg
598
+ end
599
+
600
+ ##
601
+ # Fails if +obj+ is a kind of +cls+.
602
+
603
+ def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
604
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
605
+ refute obj.kind_of?(cls), msg
606
+ end
607
+
608
+ ##
609
+ # Fails if +matcher+ <tt>=~</tt> +obj+.
610
+
611
+ def refute_match matcher, obj, msg = nil
612
+ msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"}
613
+ assert_respond_to matcher, :"=~"
614
+ matcher = Regexp.new Regexp.escape matcher if String === matcher
615
+ refute matcher =~ obj, msg
616
+ end
617
+
618
+ ##
619
+ # Fails if +obj+ is nil.
620
+
621
+ def refute_nil obj, msg = nil
622
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
623
+ refute obj.nil?, msg
624
+ end
625
+
626
+ ##
627
+ # Fails if +o1+ is not +op+ +o2+. Eg:
628
+ #
629
+ # refute_operator 1, :>, 2 #=> pass
630
+ # refute_operator 1, :<, 2 #=> fail
631
+
632
+ def refute_operator o1, op, o2 = UNDEFINED, msg = nil
633
+ return refute_predicate o1, op, msg if UNDEFINED == o2
634
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"}
635
+ refute o1.__send__(op, o2), msg
636
+ end
637
+
638
+ ##
639
+ # For testing with predicates.
640
+ #
641
+ # refute_predicate str, :empty?
642
+ #
643
+ # This is really meant for specs and is front-ended by refute_operator:
644
+ #
645
+ # str.wont_be :empty?
646
+
647
+ def refute_predicate o1, op, msg = nil
648
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
649
+ refute o1.__send__(op), msg
650
+ end
651
+
652
+ ##
653
+ # Fails if +obj+ responds to the message +meth+.
654
+
655
+ def refute_respond_to obj, meth, msg = nil
656
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
657
+
658
+ refute obj.respond_to?(meth), msg
659
+ end
660
+
661
+ ##
662
+ # Fails if +exp+ is the same (by object identity) as +act+.
663
+
664
+ def refute_same exp, act, msg = nil
665
+ msg = message(msg) {
666
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
667
+ "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
668
+ }
669
+ refute exp.equal?(act), msg
670
+ end
671
+
672
+ ##
673
+ # Skips the current test. Gets listed at the end of the run but
674
+ # doesn't cause a failure exit code.
675
+
676
+ def skip msg = nil, bt = caller
677
+ msg ||= "Skipped, no message given"
678
+ @skip = true
679
+ raise Micron::Skip, msg, bt
680
+ end
681
+
682
+ ##
683
+ # Was this testcase skipped? Meant for #teardown.
684
+
685
+ def skipped?
686
+ defined?(@skip) and @skip
687
+ end
688
+
689
+ ##
690
+ # Takes a block and wraps it with the runner's shared mutex.
691
+
692
+ def synchronize
693
+ Micron.runner.synchronize do
694
+ yield
695
+ end
696
+ end
697
+
698
+ end # Assertions
699
+
700
+ end # TestCase
701
+ end # Micron