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 +28 -0
- data/README +25 -0
- data/Rakefile +17 -0
- data/lib/tinytest.rb +706 -0
- data/lib/tinytest/compat.rb +27 -0
- data/lib/tinytest/unit.rb +53 -0
- data/test/test_tinytest.rb +1251 -0
- metadata +71 -0
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
|
+
|
data/Rakefile
ADDED
|
@@ -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
|
+
|
data/lib/tinytest.rb
ADDED
|
@@ -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
|
+
|