arikui1911-tinytest 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2009 arikui <arikui.ruby@gmail.com>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above
11
+ copyright notice, this list of conditions and the following
12
+ disclaimer in the documentation and/or other materials provided
13
+ with the distribution.
14
+ * Neither the name of the TinyTest nor the names of its
15
+ contributors may be used to endorse or promote products derived
16
+ from this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,25 @@
1
+ = TinyTest
2
+
3
+ TinyTest rips off minitest-1.3.1.
4
+
5
+ It is a simple testing library.
6
+
7
+ Developed on:
8
+ - ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-mswin32]
9
+ - ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-mswin32]
10
+ - ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32]
11
+
12
+ This list means these environments are enable to make all of /test pass.
13
+
14
+
15
+ == TODO
16
+
17
+ - Write RDoc
18
+ ... by broken English :-(
19
+ - Compatible to test/unit
20
+
21
+
22
+ == Signature
23
+
24
+ arikui (arikui.ruby@gmail.com)
25
+
@@ -0,0 +1,17 @@
1
+ require 'rake/testtask'
2
+ task 'default' => 'test'
3
+ Rake::TestTask.new
4
+
5
+ require 'rake/rdoctask'
6
+ Rake::RDocTask.new do |t|
7
+ t.rdoc_dir = 'doc'
8
+ t.rdoc_files = FileList["lib/**/*.rb"].include("README")
9
+ t.options.push '-S', '-N'
10
+ end
11
+
12
+ begin
13
+ require 'rcov/rcovtask'
14
+ Rcov::RcovTask.new
15
+ rescue LoadError
16
+ end
17
+
@@ -0,0 +1,706 @@
1
+ #
2
+ # Distributes under The modified BSD license.
3
+ #
4
+ # Copyright (c) 2009 arikui <http://d.hatena.ne.jp/arikui/>
5
+ # All rights reserved.
6
+ #
7
+
8
+ require 'benchmark'
9
+
10
+ module TinyTest
11
+ # exception which express assertion.
12
+ class AssertError < RuntimeError
13
+ ;
14
+ end
15
+
16
+ # Exception which express skipping.
17
+ class SkipError < AssertError
18
+ ;
19
+ end
20
+
21
+ class Runner
22
+ # Receives _args_ as a keyword-argument-hash.
23
+ #
24
+ # [reporter] reporter, an object to respond to messages like TinyTest::Reporter.
25
+ # [testname] matcher for testing method names.
26
+ # (Default; /\Atest/)
27
+ # [testcase] matcher for TestCase's subclass names.
28
+ # (Default; matchs to any name)
29
+ # [verbose] run on verbose mode or not;
30
+ # set value to attribute #verbose.
31
+ #
32
+ # matcher means a object which is enable to respond to #===;
33
+ # it receives a test method name or testcase class and
34
+ # returns a the argument matches or not.
35
+ #
36
+ def initialize(args = {})
37
+ @reporter = args.fetch(:reporter, Reporter.new)
38
+ tn_matcher = args.fetch(:testname, /\Atest/)
39
+ @testname_matcher = normalize_callable_matcher(tn_matcher)
40
+ tc_matcher = args.fetch(:testcase, /./)
41
+ @testcase_matcher = normalize_callable_matcher(tc_matcher){|klass| klass.name }
42
+ self.verbose = args.fetch(:verbose, false)
43
+ end
44
+
45
+ attr_accessor :verbose
46
+
47
+ # Executes each test.
48
+ def run
49
+ @reporter.load_suite($0)
50
+ n_assertions, results, bench = run_suites(collect_suites())
51
+ if self.verbose
52
+ @reporter.mark_results_with_times(results)
53
+ else
54
+ @reporter.mark_results(results)
55
+ end
56
+ @reporter.running_times(bench)
57
+ not_succeededs = results.reject{|r| r.success? }
58
+ if not_succeededs.empty?
59
+ @reporter.blank
60
+ else
61
+ @reporter.error_reports(not_succeededs)
62
+ end
63
+ f, e, s = count_suiteresults_types(results)
64
+ @reporter.counts_report(results.size, n_assertions, f, e, s)
65
+ f + e unless results.empty?
66
+ end
67
+
68
+ private
69
+
70
+ def normalize_callable_matcher(matcher, &block)
71
+ return matcher if matcher.respond_to?(:call)
72
+ pattern = matcher
73
+ block_given? ? lambda{|arg| pattern =~ block.call(arg) } : lambda{|arg| pattern =~ arg }
74
+ end
75
+
76
+ def each_class(root = TestCase)
77
+ ObjectSpace.each_object(Class) do |c|
78
+ next unless c < root
79
+ yield(c)
80
+ end
81
+ end
82
+
83
+ def collect_suites
84
+ suites = []
85
+ each_class do |c|
86
+ next unless @testcase_matcher.call(c)
87
+ c.public_instance_methods(true).each do |name|
88
+ next unless @testname_matcher.call(name)
89
+ name = name.intern if name.respond_to?(:intern)
90
+ suites << Suite.new(c.new, name)
91
+ end
92
+ end
93
+ suites
94
+ end
95
+
96
+ def run_suites(suites)
97
+ n_assertions = 0
98
+ results = []
99
+ benchmark = Benchmark.measure{
100
+ suites.each do |suite|
101
+ n_assertions += suite.count_assertions{ results << suite.execute }
102
+ end
103
+ }
104
+ return n_assertions, results, benchmark
105
+ end
106
+
107
+ def count_suiteresults_types(results)
108
+ results_chars = results.map{|r| r.char }
109
+ counts = Hash.new(0)
110
+ results_chars.each{|c| counts[c] += 1 }
111
+ ['F', 'E', 'S'].map{|k| counts[k] }
112
+ end
113
+ end
114
+
115
+ # Make testing reports be abstructive.
116
+ class Reporter
117
+ # _out_ is able to be duck-typed as IO.
118
+ def initialize(out = $stdout)
119
+ @f = out
120
+ end
121
+
122
+ # Put separater blank.
123
+ def blank
124
+ @f.puts
125
+ end
126
+
127
+ # Announce to start executing some tests of _script_ at first.
128
+ def load_suite(script)
129
+ @f.puts "Loaded suite #{script.sub(/\.rb\Z/, '')}"
130
+ @f.puts "Started"
131
+ end
132
+
133
+ # Marks about each SuiteResult.
134
+ def mark_results(results)
135
+ @f.puts results.map{|r| r.char }.join('')
136
+ end
137
+
138
+ # Marks about each SuiteResult and refers to more details
139
+ # (test-executing-used time).
140
+ def mark_results_with_times(results)
141
+ unless results.empty?
142
+ vals = results.map{|r|
143
+ ["#{r.suite.inspect}:", r.benchmark.real, r.char]
144
+ }
145
+ w = vals.map{|a| a.first.size }.max
146
+ fmt = "%-#{w}s %.2f sec: %s"
147
+ result_descriptions = vals.map{|a| fmt % a }
148
+ else
149
+ result_descriptions = []
150
+ end
151
+ blank
152
+ @f.puts result_descriptions
153
+ blank
154
+ end
155
+
156
+ # After tests are executed, reports about used time.
157
+ def running_times(benchmark)
158
+ @f.puts "Finished in %.6f seconds." % benchmark.real
159
+ end
160
+
161
+ # Refers to each error report of SuiteResult and puts them with index.
162
+ def error_reports(not_succeeded_results)
163
+ not_succeeded_results.each_with_index do |r, i|
164
+ blank
165
+ @f.puts "%3d) #{r.report}" % i.succ
166
+ blank
167
+ end
168
+ end
169
+
170
+ # Reports about some test-concerned numbers.
171
+ def counts_report(n_tests, n_assertions, n_failures, n_errors, n_skips)
172
+ @f.puts [
173
+ "#{n_tests} tests",
174
+ "#{n_assertions} assertions",
175
+ "#{n_failures} failures",
176
+ "#{n_errors} errors",
177
+ "#{n_skips} skips"
178
+ ].join(', ')
179
+ end
180
+ end
181
+
182
+ # A unit of test executing.
183
+ class Suite
184
+ def initialize(testcase, testname)
185
+ @testcase = testcase
186
+ @testname = testname
187
+ @testcase.bind_testsuite(self)
188
+ @state = :base
189
+ @count_table = { :base => 0, :counting => 0, :grouping => 0 }
190
+ end
191
+
192
+ attr_reader :testcase
193
+ attr_reader :testname
194
+
195
+ def inspect
196
+ "#{testcase.class}##{testname}"
197
+ end
198
+
199
+ # Returns SuiteResult.
200
+ def execute
201
+ report = nil
202
+ benchmark = Benchmark.measure{ report = execute_report() }
203
+ SuiteResult.new(self, benchmark, report)
204
+ end
205
+
206
+ private
207
+
208
+ def execute_report
209
+ begin
210
+ @testcase.setup
211
+ @testcase.send @testname
212
+ nil
213
+ ensure
214
+ @testcase.teardown
215
+ end
216
+ rescue Exception => ex
217
+ filename, = method_location(@testcase, @testname)
218
+ bt = prune_backtrace(ex.backtrace, filename, nil, @testname)
219
+ location = parse_backtrace(bt.last)[0, 2].join(':')
220
+ tag, repo = case ex
221
+ when SkipError then ["Skipped:", [ex.message]]
222
+ when AssertError then ["Failure:", [ex.message]]
223
+ else ["Error:", ["#{ex.class}: #{ex.message}",
224
+ *bt.map{|b| " #{b}" }]]
225
+ end
226
+ repo.unshift "#{@testname}(#{@testcase.class}) [#{location}]:"
227
+ repo.unshift tag
228
+ repo.join("\n") << "\n"
229
+ end
230
+
231
+ # Sometimes, 3rd paren part is omitted by older Ruby1.9.1
232
+ CALLER_RE = /\A(.+):(\d+)(?::in `(.+)'|\Z)/ #'
233
+
234
+ def method_location(obj, name)
235
+ obj.method(name).source_location
236
+ end
237
+
238
+ def parse_backtrace(line)
239
+ fname, lineno, meth = line.match(CALLER_RE).captures
240
+ meth ||= '__unknown__' #for older Ruby1.9.1
241
+ return fname, Integer(lineno), meth.intern
242
+ end
243
+
244
+ def prune_backtrace(org, filename, lineno, method)
245
+ term = [filename, lineno, method]
246
+ org.each_with_index do |b, i|
247
+ return org[0, i.succ] if parse_backtrace(b).zip(term).all?{|e, t| t ? e == t : true }
248
+ end
249
+ org
250
+ end
251
+
252
+ public
253
+
254
+ # Measure assertion count in block.
255
+ def count_assertions(&block)
256
+ state_shunting(:counting, &block)
257
+ @count_table[:counting]
258
+ end
259
+
260
+ # To call by assertion methods calling.
261
+ def succ_assertion_count
262
+ @count_table[@state] += 1
263
+ end
264
+
265
+ # Bundle more than 1 assertion count to only 1.
266
+ def assertion_count_grouping(&block)
267
+ state_shunting(:grouping, &block)
268
+ ensure
269
+ @count_table[:counting] += 1 unless @count_table[:grouping].zero?
270
+ end
271
+
272
+ private
273
+
274
+ def state_shunting(state, init_val = 0, &block)
275
+ shunted, @state = @state, state
276
+ @count_table[state] = init_val
277
+ yield()
278
+ ensure
279
+ @state = shunted
280
+ end
281
+ end
282
+
283
+ class SuiteResult
284
+ SUCCESS_CHAR = '.'
285
+
286
+ # If _report_ was ommited, it means it's succeeded result.
287
+ def initialize(suite, benchmark, report = nil)
288
+ @suite = suite
289
+ @benchmark = benchmark
290
+ @report = report
291
+ @char = @report ? @report[0, 1] : SUCCESS_CHAR
292
+ @success_p = !@report
293
+ end
294
+
295
+ attr_reader :suite
296
+ attr_reader :benchmark
297
+ attr_reader :report
298
+ attr_reader :char
299
+
300
+ def inspect
301
+ super.split(/\s+/).first << "[#{char}]>"
302
+ end
303
+
304
+ # Self is succeeded result or not.
305
+ def success?
306
+ @success_p
307
+ end
308
+ end
309
+
310
+ # TinyTest's testcase definition is witten as class definition
311
+ # which inherit this class.
312
+ class TestCase
313
+ # Write procedures before executing tests.
314
+ def setup
315
+ ;
316
+ end
317
+
318
+ # Write procedures after executing tests.
319
+ def teardown
320
+ ;
321
+ end
322
+
323
+ def bind_testsuite(suite)
324
+ define_singleton_method(:suite){ suite }
325
+ end
326
+
327
+ # Pass examination if _cond_ was evaluated as true.
328
+ def assert(cond, message = nil)
329
+ message ||= "Failed assertion, no message given."
330
+ suite.succ_assertion_count
331
+ unless cond
332
+ message = message.call if message.respond_to?(:call)
333
+ raise AssertError, message
334
+ end
335
+ true
336
+ end
337
+
338
+ # Pass examination if _cond_ was evaluated as false.
339
+ def refute(cond, message = nil)
340
+ not assert(!cond, message || "Failed refutation, no message given")
341
+ end
342
+
343
+ # Certainty fail to examination.
344
+ def flunk(message = nil)
345
+ assert(false, message || "Epic Fail!")
346
+ end
347
+
348
+ # Certainty pass examination.
349
+ def pass(message = nil)
350
+ assert(true)
351
+ end
352
+
353
+ # Raise skipping.
354
+ def skip(message = nil)
355
+ raise(SkipError, message || "Skipped, no message given")
356
+ end
357
+
358
+ # Pass examination if block returns true.
359
+ def assert_block(message = nil, &block)
360
+ assertion_frame(yield(), message) do
361
+ "Expected block to return a value evaluated as true"
362
+ end
363
+ end
364
+
365
+ # Pass examination if block returns false.
366
+ def refute_block(message = nil, &block)
367
+ refutation_frame(yield(), message) do
368
+ "Expected block to return a value evaluated as false"
369
+ end
370
+ end
371
+
372
+ # Pass examination if _expected_ equals to _actual_. (by #==)
373
+ def assert_equal(expected, actual, message = nil)
374
+ assertion_frame(expected == actual, message) do
375
+ "Expected #{expected.inspect}, not #{actual.inspect}"
376
+ end
377
+ end
378
+
379
+ # Pass examination unless _expected_ equals to _actual_. (by #==)
380
+ def refute_equal(expected, actual, message = nil)
381
+ refutation_frame(expected == actual, message) do
382
+ "Expected #{actual.inspect} to not be equal to #{expected.inspect}"
383
+ end
384
+ end
385
+
386
+ # Pass examination if _object_ is an instance of _klass_. (by #instance_of?)
387
+ def assert_instance_of(klass, object, message = nil)
388
+ assertion_frame(object.instance_of?(klass), message) do
389
+ "Expected #{object.inspect} to be an instance of #{klass}"
390
+ end
391
+ end
392
+
393
+ # Pass examination unless _object_ is an instance of _klass_. (by #instance_of?)
394
+ def refute_instance_of(klass, object, message = nil)
395
+ refutation_frame(object.instance_of?(klass), message) do
396
+ "Expected #{object.inspect} to not be an instance of #{klass}"
397
+ end
398
+ end
399
+
400
+ # Pass examination if _object_ is an instance of a class
401
+ # which is _klass_ or a subclass of _klass_. (by #kind_of?)
402
+ def assert_kind_of(klass, object, message = nil)
403
+ assertion_frame(object.kind_of?(klass), message) do
404
+ "Expected #{object.inspect} to be a kind of #{klass}"
405
+ end
406
+ end
407
+
408
+ # Pass examination unless _object_ is an instance of a class
409
+ # which is _klass_ or a subclass of _klass_. (by #kind_of?)
410
+ def refute_kind_of(klass, object, message = nil)
411
+ refutation_frame(object.kind_of?(klass), message) do
412
+ "Expected #{object.inspect} to not be a kind of #{klass}"
413
+ end
414
+ end
415
+
416
+ # Pass examination if _expected_ matches _actual_. (by =~)
417
+ def assert_match(expected, actual, message = nil)
418
+ cond = Util.assertion_match_test(expected, actual)
419
+ assertion_frame(cond, message) do
420
+ "Expected #{expected.inspect} to match #{actual.inspect}"
421
+ end
422
+ end
423
+
424
+ # Pass examination unless _expected_ matches _actual_. (by =~)
425
+ def refute_match(expected, actual, message = nil)
426
+ cond = Util.assertion_match_test(expected, actual)
427
+ refutation_frame(cond, message) do
428
+ "Expected #{expected.inspect} to not match #{actual.inspect}"
429
+ end
430
+ end
431
+
432
+ # Pass examination if _object_ is nil. (by #nil?)
433
+ def assert_nil(object, message = nil)
434
+ assertion_frame(object.nil?, message) do
435
+ "Expected #{object.inspect} to be nil"
436
+ end
437
+ end
438
+
439
+ # Pass examination unless _object_ is nil. (by #nil?)
440
+ def refute_nil(object, message = nil)
441
+ refutation_frame(object.nil?, message) do
442
+ "Expected #{object.inspect} to not be nil"
443
+ end
444
+ end
445
+
446
+ # Pass examination if _expected_ is _actual_ based on object identity. (by #equal?)
447
+ def assert_same(expected, actual, message = nil)
448
+ assertion_frame(expected.equal?(actual), message) do
449
+ sprintf(
450
+ "Expected %s (0x%x) to be the same as %s (0x%x)",
451
+ expected.inspect, expected.object_id,
452
+ actual.inspect, actual.object_id
453
+ )
454
+ end
455
+ end
456
+
457
+ # Pass examination unless _expected_ is _actual_ based on object identity. (by #equal?)
458
+ def refute_same(expected, actual, message = nil)
459
+ refutation_frame(expected.equal?(actual), message) do
460
+ "Expected #{expected.inspect} to not be the same as #{actual.inspect}"
461
+ end
462
+ end
463
+
464
+ # Pass examination if _object_ is respondable to _method_. (by #respond_to?)
465
+ def assert_respond_to(object, method, message = nil)
466
+ assertion_frame(object.respond_to?(method), message) do
467
+ "Expected #{object.inspect} (#{object.class}) to respond to #{method}"
468
+ end
469
+ end
470
+
471
+ # Pass examination unless _object_ is respondable to _method_. (by #respond_to?)
472
+ def refute_respond_to(object, method, message = nil)
473
+ refutation_frame(object.respond_to?(method), message) do
474
+ "Expected #{object.inspect} to not respond to #{method}"
475
+ end
476
+ end
477
+
478
+ # Pass examination if _object_ is empty. (by #empty?)
479
+ def assert_empty(object, message = nil)
480
+ suite.assertion_count_grouping do
481
+ assert_respond_to(object, :empty?, message)
482
+ assertion_frame(object.empty?, message) do
483
+ "Expected #{object.inspect} to be empty"
484
+ end
485
+ end
486
+ end
487
+
488
+ # Pass examination unless _object_ is empty. (by #empty?)
489
+ def refute_empty(object, message = nil)
490
+ suite.assertion_count_grouping do
491
+ assert_respond_to(object, :empty?, message)
492
+ refutation_frame(object.empty?, message) do
493
+ "Expected #{object.inspect} to not be empty"
494
+ end
495
+ end
496
+ end
497
+
498
+ # Pass examination if _collection_ includes _object_. (by #include?)
499
+ def assert_includes(collection, object, message = nil)
500
+ suite.assertion_count_grouping do
501
+ assert_respond_to(collection, :include?, message)
502
+ assertion_frame(collection.include?(object), message) do
503
+ "Expected #{collection.inspect} to include #{object.inspect}"
504
+ end
505
+ end
506
+ end
507
+
508
+ # Pass examination unless _collection_ includes _object_. (by #include?)
509
+ def refute_includes(collection, object, message = nil)
510
+ suite.assertion_count_grouping do
511
+ assert_respond_to(collection, :include?, message)
512
+ refutation_frame(collection.include?(object), message) do
513
+ "Expected #{collection.inspect} to not include #{object.inspect}"
514
+ end
515
+ end
516
+ end
517
+
518
+ def assert_in_delta(expected, actual, delta = 0.001, message = nil)
519
+ expected, actual, delta, message = Util.normalize_in_delta_epsilon_arguments(
520
+ expected, actual, delta, message
521
+ )
522
+ gap = (expected - actual).abs
523
+ assertion_frame(delta >= gap, message) do
524
+ "Expected #{expected} - #{actual} (#{gap}) to be < #{delta}"
525
+ end
526
+ end
527
+
528
+ def refute_in_delta(expected, actual, delta = 0.001, message = nil)
529
+ expected, actual, delta, message = Util.normalize_in_delta_epsilon_arguments(
530
+ expected, actual, delta, message
531
+ )
532
+ gap = (expected - actual).abs
533
+ refutation_frame(delta >= gap, message) do
534
+ "Expected #{expected} - #{actual} (#{gap}) to not be < #{delta}"
535
+ end
536
+ end
537
+
538
+ def assert_in_epsilon(a, b, epsilon = 0.001, message = nil)
539
+ a, b, epsilon, message = Util.normalize_in_delta_epsilon_arguments(
540
+ a, b, epsilon, message
541
+ )
542
+ assert_in_delta(a, b, [a, b].min * epsilon, message)
543
+ end
544
+
545
+ def refute_in_epsilon(a, b, epsilon = 0.001, message = nil)
546
+ a, b, epsilon, message = Util.normalize_in_delta_epsilon_arguments(
547
+ a, b, epsilon, message
548
+ )
549
+ refute_in_delta(a, b, [a, b].min * epsilon, message)
550
+ end
551
+
552
+ def assert_operator(operand1, operator, operand2, message = nil)
553
+ assertion_frame(operand1.__send__(operator, operand2), message) do
554
+ "Expected #{operand1.inspect} to be #{operator} #{operand2.inspect}"
555
+ end
556
+ end
557
+
558
+ def refute_operator(operand1, operator, operand2, message = nil)
559
+ refutation_frame(operand1.__send__(operator, operand2), message) do
560
+ "Expected #{operand1.inspect} to not be #{operator} #{operand2.inspect}"
561
+ end
562
+ end
563
+
564
+ def assert_send(send_concerneds, message = nil)
565
+ assertion_frame(Util.assertion_send_dispatch(*send_concerneds), message) do
566
+ inspection = Util.assertion_send_inspection(*send_concerneds)
567
+ "Expected #{inspection} to be evaluated as true"
568
+ end
569
+ end
570
+
571
+ def refute_send(send_concerneds, message = nil)
572
+ refutation_frame(Util.assertion_send_dispatch(*send_concerneds), message) do
573
+ inspection = Util.assertion_send_inspection(*send_concerneds)
574
+ "Expected #{inspection} to be evaluated as false"
575
+ end
576
+ end
577
+
578
+ RAISES_MSG_HOOK = lambda{|msg| msg.sub(/\.\Z/, '') }
579
+
580
+ def assert_raises(exceptions, message = nil, &block)
581
+ exceptions = Util.normalize_raises_arguments(exceptions, &block)
582
+ begin
583
+ yield()
584
+ rescue Exception => actual
585
+ cond = exceptions.any?{|e| actual.kind_of?(e) }
586
+ assertion_frame(cond, message, RAISES_MSG_HOOK) do
587
+ "#{exceptions.inspect} expected, but\n#{Util.describe_exception(actual)}"
588
+ end
589
+ else
590
+ assertion_frame(false, message, RAISES_MSG_HOOK) do
591
+ "#{exceptions.inspect} expected but nothing was raised"
592
+ end
593
+ end
594
+ end
595
+
596
+ def refute_raises(exceptions, message = nil, &block)
597
+ exceptions = Util.normalize_raises_arguments(exceptions, &block)
598
+ begin
599
+ yield()
600
+ rescue Exception => actual
601
+ cond = exceptions.any?{|e| actual.kind_of?(e) }
602
+ refutation_frame(cond, message, RAISES_MSG_HOOK) do
603
+ "#{exceptions.inspect} not expected, but\n#{Util.describe_exception(actual)}"
604
+ end
605
+ end
606
+ end
607
+
608
+ def assert_throws(tag, message = nil, &block)
609
+ msg = "Expected #{tag.inspect} to have been thrown"
610
+ thrown = true
611
+ catch(tag) do
612
+ yield(tag)
613
+ thrown = false
614
+ end
615
+ assertion_frame(thrown, message){ msg }
616
+ end
617
+
618
+ def refute_throws(tag, message = nil, &block)
619
+ thrown = true
620
+ catch(tag) do
621
+ yield(tag)
622
+ thrown = false
623
+ end
624
+ refutation_frame(thrown, message) do
625
+ "Expected #{tag.inspect} to not have been thrown"
626
+ end
627
+ end
628
+
629
+ private
630
+
631
+ def assertion_frame(cond, message, hook = nil, &default_message)
632
+ assert(cond, Util.build_message(message, hook, &default_message))
633
+ end
634
+
635
+ def refutation_frame(cond, message, hook = nil, &default_message)
636
+ refute(cond, Util.build_message(message, hook, &default_message))
637
+ end
638
+
639
+ module Util
640
+ def self.assertion_match_test(expected, actual)
641
+ expected =~ actual
642
+ rescue TypeError
643
+ /#{Regexp.escape(expected)}/ =~ actual
644
+ end
645
+
646
+ def self.normalize_in_delta_epsilon_arguments(expected, actual, delta, message)
647
+ unless message
648
+ begin
649
+ delta = Float(delta)
650
+ rescue TypeError, ArgumentError
651
+ delta, message = 0.001, delta
652
+ end
653
+ end
654
+ return expected, actual, Float(delta), message
655
+ end
656
+
657
+ def self.assertion_send_dispatch(receiver, message, *args)
658
+ receiver.__send__(message, *args)
659
+ end
660
+
661
+ def self.assertion_send_inspection(receiver, message, *args)
662
+ "#{receiver.inspect}.#{message}(#{args.map{|e| e.inspect }.join(', ')})"
663
+ end
664
+
665
+ def self.normalize_raises_arguments(exceptions, &block)
666
+ exceptions = [*exceptions]
667
+ raise ArgumentError, "wrong number of arguments(0 for 1)" if exceptions.empty?
668
+ raise LocalJumpError, "no block given (yield)" unless block_given?
669
+ exceptions
670
+ end
671
+
672
+ def self.describe_exception(ex)
673
+ bt = ex.backtrace.reject{|s| i = s.index(__FILE__) and i == 0 }
674
+ [
675
+ "Class: <#{ex.class}>",
676
+ "Message: <#{ex.message}>",
677
+ "---Backtrace---",
678
+ bt.join("\n"),
679
+ "---------------",
680
+ ].join("\n")
681
+ end
682
+
683
+ def self.build_message(message = nil, hook_end = nil, &default)
684
+ if message
685
+ lambda{
686
+ message = message.to_s
687
+ message << '.' unless message.empty?
688
+ message << "\n#{default.call}."
689
+ message.strip!
690
+ message = hook_end.call(message) if hook_end
691
+ message
692
+ }
693
+ else
694
+ lambda{
695
+ message = "#{default.call}."
696
+ message = hook_end.call(message) if hook_end
697
+ message
698
+ }
699
+ end
700
+ end
701
+ end
702
+ end
703
+ end
704
+
705
+ require 'tinytest/compat'
706
+