arikui1911-tinytest 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+