miniunit 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest CHANGED
@@ -1,5 +1,13 @@
1
1
  # -*- ruby -*-
2
2
 
3
- Autotest.add_hook(:initialize) do |at|
4
- at.libs.sub!(/lib:/, '')
3
+ Autotest.add_hook :initialize do |at|
4
+ at.extra_class_map["MiniSpec"] = "test/test_mini_spec.rb"
5
+ at.extra_class_map["TestMiniTestTestCase"] = "test/test_mini_test.rb"
6
+
7
+ at.add_exception 'coverage.info'
8
+ at.add_exception 'coverage'
5
9
  end
10
+
11
+ require 'autotest/rcov'
12
+ Autotest::RCov.command = 'rcov_info'
13
+
@@ -1,16 +1,71 @@
1
- == 1.1.0 / 2007-11-08
1
+ === 1.2.0 / 2008-06-09
2
+
3
+ * 2 major enhancements:
4
+
5
+ * Added Mini::Spec.
6
+ * Added Mini::Mock. Thanks Steven Baker!!
7
+
8
+ * 23 minor enhancements:
9
+
10
+ * Added bin/use_miniunit to make it easy to test out miniunit.
11
+ * Added -n filtering, thanks to Phil Hagelberg!
12
+ * Added args argument to #run, takes ARGV from at_exit.
13
+ * Added test name output if $DEBUG.
14
+ * Added a refute (was deny) for every assert.
15
+ * Added capture_io and a bunch of nice assertions from zentest.
16
+ * Added deprecation mechanism for assert_no/not methods to test/unit/assertions.
17
+ * Added pp output when available.
18
+ * Added tests for all assertions. Pretty much maxed out coverage.
19
+ * Added tests to verify consistency and good naming.
20
+ * Aliased and deprecated all ugly assertions.
21
+ * Cleaned out test/unit. Moved autorun there.
22
+ * Code cleanup to make extensions easier. Thanks Chad!
23
+ * Got spec args reversed in all but a couple assertions. Much more readable.
24
+ * Improved error messages across the board. Adds your message to the default.
25
+ * Moved into Mini namespace, renamed to Mini::Test and Mini::Spec.
26
+ * Pulled the assertions into their own module...
27
+ * Removed as much code as I could while still maintaining full functionality.
28
+ * Moved filter_backtrace into MiniTest.
29
+ * Removed MiniTest::Unit::run. Unnecessary.
30
+ * Removed location_of_failure. Unnecessary.
31
+ * Rewrote test/unit's filter_backtrace. Flog from 37.0 to 18.1
32
+ * Removed assert_send. Google says it is never used.
33
+ * Renamed MiniTest::Unit.autotest to #run.
34
+ * Renamed deny to refute.
35
+ * Rewrote some ugly/confusing default assertion messages.
36
+ * assert_in_delta now defaults to 0.001 precision. Makes specs prettier.
37
+
38
+ * 9 bug fixes:
39
+
40
+ * Fixed assert_raises to raise outside of the inner-begin/rescue.
41
+ * Fixed for ruby 1.9 and rubinius.
42
+ * No longer exits 0 if exception in code PRE-test run causes early exit.
43
+ * Removed implementors method list from mini/test.rb - too stale.
44
+ * assert_nothing_raised takes a class as an arg. wtf? STUPID
45
+ * ".EF" output is now unbuffered.
46
+ * Bunch of changes to get working with rails... UGH.
47
+ * Added stupid hacks to deal with rails not requiring their dependencies.
48
+ * Now bitch loudly if someone defines one of my classes instead of requiring.
49
+ * Fixed infect method to work better on 1.9.
50
+ * Fixed all shadowed variable warnings in 1.9.
51
+
52
+ === 1.1.0 / 2007-11-08
2
53
 
3
54
  * 4 major enhancements:
55
+
4
56
  * Finished writing all missing assertions.
5
57
  * Output matches original test/unit.
6
58
  * Documented every method needed by language implementor.
7
59
  * Fully switched over to self-testing setup.
60
+
8
61
  * 2 minor enhancements:
62
+
9
63
  * Added deny (assert ! test), our favorite extension to test/unit.
10
64
  * Added .autotest and fairly complete unit tests. (thanks Chad for help here)
11
65
 
12
- == 1.0.0 / 2006-10-30
66
+ === 1.0.0 / 2006-10-30
13
67
 
14
68
  * 1 major enhancement
69
+
15
70
  * Birthday!
16
71
 
@@ -3,7 +3,14 @@ History.txt
3
3
  Manifest.txt
4
4
  README.txt
5
5
  Rakefile
6
+ bin/use_miniunit
7
+ lib/mini/mock.rb
8
+ lib/mini/spec.rb
9
+ lib/mini/test.rb
6
10
  lib/test/unit.rb
11
+ lib/test/unit/assertions.rb
12
+ lib/test/unit/error.rb
7
13
  lib/test/unit/testcase.rb
8
- test/test_test_case.rb
9
- test/test_test_unit.rb
14
+ test/test_mini_mock.rb
15
+ test/test_mini_spec.rb
16
+ test/test_mini_test.rb
data/README.txt CHANGED
@@ -1,16 +1,25 @@
1
- miniunit by Ryan Davis
2
- http://rubyforge.org/projects/bfts
1
+ = mini/{test,spec,mock}
2
+
3
+ * http://rubyforge.org/projects/bfts
3
4
 
4
5
  == DESCRIPTION:
5
6
 
6
- miniunit is a completely minimial drop-in replacement for ruby's
7
+ mini/test is a completely minimial drop-in replacement for ruby's
7
8
  test/unit. This is meant to be clean and easy to use both as a regular
8
9
  test writer and for language implementors that need a minimal set of
9
10
  methods to bootstrap a working unit test suite.
10
11
 
12
+ mini/spec is a functionally complete spec engine.
13
+
14
+ mini/mock, by Steven Baker, is a beautifully tiny mock object framework.
15
+
11
16
  == FEATURES/PROBLEMS:
12
17
 
18
+ * Contains mini/test - a simple and clean test system (301 lines!).
19
+ * Contains mini/spec - a simple and clean spec system (52 lines!).
20
+ * Contains mini/mock - a simple and clean mock system (35 lines!).
13
21
  * Fully test/unit compatible assertions.
22
+ * Allows test/unit to be required, firing up an autorunner.
14
23
  * Incredibly small and fast runner, but no bells and whistles.
15
24
  * Incompatible at the runner level.
16
25
 
@@ -20,13 +29,13 @@ methods to bootstrap a working unit test suite.
20
29
 
21
30
  == INSTALL:
22
31
 
23
- + sudo gem install miniunit or sudo rake install
32
+ + sudo gem install miniunit
24
33
 
25
34
  == LICENSE:
26
35
 
27
36
  (The MIT License)
28
37
 
29
- Copyright (c) 2006-2007 Ryan Davis, Seattle.rb
38
+ Copyright (c) Ryan Davis, Seattle.rb
30
39
 
31
40
  Permission is hereby granted, free of charge, to any person obtaining
32
41
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -4,32 +4,62 @@ $TESTING_MINIUNIT = true
4
4
 
5
5
  require 'rubygems'
6
6
  require 'hoe'
7
- require './lib/test/unit.rb'
8
-
9
- Hoe.new('miniunit', Test::Unit::VERSION) do |p|
10
- p.rubyforge_name = 'bfts'
11
- p.description = p.paragraphs_of('README.txt', 2).join("\n\n")
12
- p.summary = p.description.split(/\. /)[0] + "."
13
- p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
- p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
7
+ require './lib/mini/test.rb'
8
+
9
+ Hoe.new('miniunit', Mini::Test::VERSION) do |miniunit|
10
+ miniunit.rubyforge_name = "bfts"
11
+
12
+ miniunit.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
15
13
  end
16
14
 
17
- task :sort do
18
- begin
19
- sh 'for f in lib/**/*.rb; do echo $f; grep "^ *def " $f | grep -v sort=skip > x; sort x > y; echo $f; echo; diff x y; done'
20
- sh 'for f in test/test_*.rb; do echo $f; grep "^ *def.test_" $f > x; sort x > y; echo $f; echo; diff x y; done'
21
- ensure
22
- sh 'rm x y'
15
+ begin
16
+ require 'rcov/rcovtask'
17
+ Rcov::RcovTask.new do |t|
18
+ t.verbose = true
19
+ t.rcov_opts << "--include-file lib/test"
20
+ t.rcov_opts << "--no-color"
23
21
  end
22
+
23
+ task :rcov_info do
24
+ pattern = ENV['PATTERN'] || "test/test_*.rb"
25
+ ruby "-Ilib -S rcov --text-report --include-file lib/test --save coverage.info #{pattern}"
26
+ end
27
+
28
+ task :rcov_overlay do
29
+ rcov, eol = Marshal.load(File.read("coverage.info")).last[ENV["FILE"]], 1
30
+ puts rcov[:lines].zip(rcov[:coverage]).map { |line, coverage|
31
+ bol, eol = eol, eol + line.length
32
+ [bol, eol, "#ffcccc"] unless coverage
33
+ }.compact.inspect
34
+ end
35
+ rescue LoadError
36
+ # skip
37
+ end
38
+
39
+ def loc dir
40
+ system "find #{dir} -name \\*.rb | xargs wc -l | tail -1"
24
41
  end
25
42
 
26
- task :woot do
27
- begin
28
- system "ruby blah.rb > x; ruby -Ilib blah.rb > y"
29
- system "diff -u x y > z"
30
- # ensure
31
- # rm_f "x", "y", "z"
43
+ desc "stupid line count"
44
+ task :dickwag do
45
+ puts
46
+ puts "miniunit"
47
+ puts
48
+ print " lib loc"; loc "lib"
49
+ print " test loc"; loc "test"
50
+ print " totl loc"; loc "lib test"
51
+ print " flog = "; system "flog -s lib"
52
+
53
+ puts
54
+ puts "test/unit"
55
+ puts
56
+ Dir.chdir File.expand_path("~/Work/svn/ruby/ruby_1_8") do
57
+ print " lib loc"; loc "lib/test"
58
+ print " test loc"; loc "test/testunit"
59
+ print " totl loc"; loc "lib/test test/testunit"
60
+ print " flog = "; system "flog -s lib/test"
32
61
  end
62
+ puts
33
63
  end
34
64
 
35
65
  # vim: syntax=Ruby
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+
5
+ use_miniunit = ARGV.shift
6
+ sitelib = Config::CONFIG["sitelibdir"]
7
+ projdir = File.dirname(File.dirname(File.expand_path(__FILE__)))
8
+ minidir = File.join(projdir, "lib")
9
+
10
+ case use_miniunit
11
+ when /^y/ then
12
+ File.symlink File.join(minidir, "mini"), File.join(sitelib, "mini")
13
+ File.symlink File.join(minidir, "test"), File.join(sitelib, "test")
14
+ puts "Added links to #{sitelib}"
15
+ when /^n/ then
16
+ File.unlink File.join(sitelib, "mini")
17
+ File.unlink File.join(sitelib, "test")
18
+ puts "Removed links from #{sitelib}"
19
+ else
20
+ pgm = File.basename __FILE__
21
+ puts "usage: #{pgm} (yes|no)"
22
+ exit 1
23
+ end
@@ -0,0 +1,31 @@
1
+ class MockExpectationError < StandardError; end
2
+
3
+ require 'mini/test'
4
+
5
+ class Mini::Mock
6
+ def initialize
7
+ @expected_calls = {}
8
+ @actual_calls = Hash.new {|h,k| h[k] = [] }
9
+ end
10
+
11
+ def expect(name, retval, args=[])
12
+ n, r, a = name, retval, args # for the closure below
13
+ @expected_calls[name] = { :retval => retval, :args => args }
14
+ self.class.__send__(:define_method, name) { |*a|
15
+ raise ArgumentError unless @expected_calls[n][:args].size == a.size
16
+ @actual_calls[n] << { :retval => r, :args => a }
17
+ retval
18
+ }
19
+ self
20
+ end
21
+
22
+ def verify
23
+ @expected_calls.each_key do |name|
24
+ expected = @expected_calls[name]
25
+ msg = "expected #{name}, #{expected.inspect}"
26
+ raise MockExpectationError, msg unless
27
+ @actual_calls.has_key? name and @actual_calls[name].include?(expected)
28
+ end
29
+ true
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ require 'mini/test'
4
+
5
+ class Module
6
+ def infect_with_assertions pos_prefix, neg_prefix, skip_re, map = {}
7
+ Mini::Assertions.public_instance_methods(false).each do |meth|
8
+ meth = meth.to_s
9
+
10
+ new_name = case meth
11
+ when /^assert/ then
12
+ meth.sub(/^assert/, pos_prefix.to_s)
13
+ when /^refute/ then
14
+ meth.sub(/^refute/, neg_prefix.to_s)
15
+ end
16
+ next unless new_name
17
+ next if new_name =~ skip_re
18
+
19
+ regexp, replacement = map.find { |re, _| new_name =~ re }
20
+ new_name.sub! regexp, replacement if replacement
21
+
22
+ # warn "%-22p -> %p %p" % [meth, new_name, regexp]
23
+ self.class_eval <<-EOM
24
+ def #{new_name} *args, &block
25
+ return Mini::Spec.current.#{meth}(*args, &self) if Proc === self
26
+ return Mini::Spec.current.#{meth}(args.first, self) if args.size == 1
27
+ return Mini::Spec.current.#{meth}(self, *args)
28
+ end
29
+ EOM
30
+ end
31
+ end
32
+ end
33
+
34
+ Object.infect_with_assertions(:must, :wont,
35
+ /^(must|wont)$|wont_(throw)|must_(block|not?_|nothing)/,
36
+ /(must_throw)s/ => '\1',
37
+ /(?!not)_same/ => '_be_same_as',
38
+ /_in_/ => '_be_within_',
39
+ /_operator/ => '_be',
40
+ /_includes/ => '_include',
41
+ /(must|wont)_(.*_of|nil|empty)/ => '\1_be_\2',
42
+ /must_raises/ => 'must_raise')
43
+
44
+ class Object
45
+ alias :must_be_close_to :must_be_within_delta
46
+ alias :wont_be_close_to :wont_be_within_delta
47
+ end
48
+
49
+ module Kernel
50
+ def describe desc, &block
51
+ cls = Class.new(Mini::Spec)
52
+ Object.const_set desc.to_s.split(/\W+/).map { |s| s.capitalize }.join, cls
53
+
54
+ cls.class_eval(&block)
55
+ end
56
+ end
57
+
58
+ class Mini::Spec < Mini::Test::TestCase
59
+ def self.current
60
+ @@current_spec
61
+ end
62
+
63
+ def initialize name
64
+ super
65
+ @@current_spec = self
66
+ end
67
+
68
+ def self.before(type = :each, &block)
69
+ raise "unsupported before type: #{type}" unless type == :each
70
+ define_method :setup, &block
71
+ end
72
+
73
+ def self.after(type = :each, &block)
74
+ raise "unsupported after type: #{type}" unless type == :each
75
+ define_method :teardown, &block
76
+ end
77
+
78
+ def self.it desc, &block
79
+ define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &block
80
+ end
81
+ end
@@ -0,0 +1,423 @@
1
+ ##
2
+ #
3
+ # Totally minimal drop-in replacement for test-unit
4
+ #
5
+ # TODO: refute -> debunk, prove/rebut, show/deny... lots of possibilities
6
+
7
+ module Mini
8
+ class Assertion < Exception; end
9
+
10
+ file = if __FILE__ =~ /^[^\.]/ then # OMG ruby 1.9 is so lame (rubinius too)
11
+ require 'pathname'
12
+ pwd = Pathname.new(Dir.pwd)
13
+ pn = Pathname.new(File.expand_path(__FILE__))
14
+ pn = File.join(".", pn.relative_path_from(pwd)) unless pn.relative?
15
+ pn.to_s
16
+ else
17
+ __FILE__
18
+ end
19
+
20
+ MINI_DIR = File.dirname(File.dirname(file))
21
+
22
+ def self.filter_backtrace bt
23
+ return ["No backtrace"] unless bt
24
+
25
+ new_bt = []
26
+ bt.each do |line|
27
+ break if line.index(MINI_DIR) == 0
28
+ new_bt << line
29
+ end
30
+
31
+ new_bt = bt.reject { |line| line.index(MINI_DIR) == 0 } if
32
+ new_bt.empty?
33
+ new_bt = bt.dup if new_bt.empty?
34
+
35
+ new_bt
36
+ end
37
+
38
+ module Assertions
39
+ begin
40
+ require 'pp'
41
+ def mu_pp(obj)
42
+ PP.pp(obj, '').chomp
43
+ end
44
+ rescue LoadError
45
+ def mu_pp(obj)
46
+ obj.inspect
47
+ end
48
+ end
49
+
50
+ def _assertions= n
51
+ @_assertions = n
52
+ end
53
+
54
+ def _assertions
55
+ @_assertions ||= 0
56
+ end
57
+
58
+ def assert test, msg = nil
59
+ msg ||= "Failed assertion, no message given."
60
+ self._assertions += 1
61
+ raise Mini::Assertion, msg unless test
62
+ true
63
+ end
64
+
65
+ def assert_block msg = nil
66
+ msg = message msg, "Expected block to return true value"
67
+ assert yield, msg
68
+ end
69
+
70
+ def assert_empty obj, msg = nil
71
+ msg = message msg, "Expected #{obj.inspect} to be empty"
72
+ assert_respond_to obj, :empty?
73
+ assert obj.empty?, msg
74
+ end
75
+
76
+ def assert_equal exp, act, msg = nil
77
+ msg = message msg, "Expected #{mu_pp(exp)}, not #{mu_pp(act)}"
78
+ assert(exp == act, msg)
79
+ end
80
+
81
+ def assert_in_delta exp, act, delta = 0.001, msg = nil
82
+ n = (exp - act).abs
83
+ msg = message msg, "Expected #{exp} - #{act} (#{n}) to be < #{delta}"
84
+ assert delta > n, msg
85
+ end
86
+
87
+ def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
88
+ assert_in_delta a, b, [a, b].min * epsilon, msg
89
+ end
90
+
91
+ def assert_includes collection, obj, msg = nil
92
+ msg = message msg, "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
93
+ assert_respond_to collection, :include?
94
+ assert collection.include?(obj), msg
95
+ end
96
+
97
+ def assert_instance_of cls, obj, msg = nil
98
+ msg = message msg, "Expected #{mu_pp(obj)} to be an instance of #{cls}"
99
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
100
+ obj, cls = cls, obj if flip
101
+ assert cls === obj, msg
102
+ end
103
+
104
+ def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
105
+ msg = message msg, "Expected #{mu_pp(obj)} to be a kind of #{cls}"
106
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
107
+ obj, cls = cls, obj if flip
108
+ assert obj.kind_of?(cls), msg
109
+ end
110
+
111
+ def assert_match exp, act, msg = nil
112
+ msg = message msg, "Expected #{mu_pp(act)} to match #{mu_pp(exp)}"
113
+ assert act =~ exp, msg
114
+ end
115
+
116
+ def assert_nil obj, msg = nil
117
+ msg = message msg, "Expected #{mu_pp(obj)} to be nil"
118
+ assert obj.nil?, msg
119
+ end
120
+
121
+ def assert_operator o1, op, o2, msg = nil
122
+ msg = message msg, "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}"
123
+ assert o1.__send__(op, o2), msg
124
+ end
125
+
126
+ def assert_raises *exp
127
+ msg = String === exp.last ? exp.pop : nil
128
+ should_raise = false
129
+ begin
130
+ yield
131
+ should_raise = true
132
+ rescue Exception => e
133
+ assert_includes exp, e.class
134
+ exception_details(e, "<#{mu_pp(exp)}> exception expected, not")
135
+ return e
136
+ end
137
+
138
+ exp = exp.first if exp.size == 1
139
+ flunk "#{mu_pp(exp)} expected but nothing was raised." if should_raise
140
+ end
141
+
142
+ def assert_respond_to obj, meth, msg = nil
143
+ msg = message msg, "Expected #{mu_pp(obj)} to respond to #{meth}"
144
+ flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
145
+ obj, meth = meth, obj if flip
146
+ assert obj.respond_to?(meth), msg
147
+ end
148
+
149
+ def assert_same exp, act, msg = nil
150
+ msg = message msg, "Expected #{mu_pp(act)} to be the same as #{mu_pp(exp)}"
151
+ assert exp.equal?(act), msg
152
+ end
153
+
154
+ def assert_throws sym, msg = nil
155
+ default = "Expected #{mu_pp(sym)} to have been thrown"
156
+ caught = true
157
+ catch(sym) do
158
+ begin
159
+ yield
160
+ rescue ArgumentError => e # 1.9 exception
161
+ default += ", not #{e.message.split(/ /).last}"
162
+ rescue NameError => e # 1.8 exception
163
+ default += ", not #{e.name.inspect}"
164
+ end
165
+ caught = false
166
+ end
167
+
168
+ assert caught, message(msg, default)
169
+ end
170
+
171
+ def capture_io
172
+ require 'stringio'
173
+
174
+ orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
175
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
176
+ $stdout, $stderr = captured_stdout, captured_stderr
177
+
178
+ yield
179
+
180
+ return captured_stdout.string, captured_stderr.string
181
+ ensure
182
+ $stdout = orig_stdout
183
+ $stderr = orig_stderr
184
+ end
185
+
186
+ def exception_details e, msg
187
+ "#{msg}\nClass: <#{e.class}>\nMessage: <#{e.message.inspect}>\n---Backtrace---\n#{Mini::filter_backtrace(e.backtrace).join("\n")}\n---------------"
188
+ end
189
+
190
+ def fail msg = nil
191
+ msg ||= "Epic Fail!"
192
+ assert false, msg
193
+ end
194
+
195
+ alias :flunk :fail
196
+
197
+ def message msg, default
198
+ if msg then
199
+ msg += '.' unless msg.empty?
200
+ msg += "\n#{default}."
201
+ msg.strip
202
+ else
203
+ "#{default}."
204
+ end
205
+ end
206
+
207
+ # used for counting assertions
208
+ def pass msg = nil
209
+ assert true
210
+ end
211
+
212
+ def refute test, msg = nil
213
+ msg ||= "Failed refutation, no message given"
214
+ not assert(! test, msg)
215
+ end
216
+
217
+ def refute_empty obj, msg = nil
218
+ msg = message msg, "Expected #{obj.inspect} to not be empty"
219
+ assert_respond_to obj, :empty?
220
+ refute obj.empty?, msg
221
+ end
222
+
223
+ def refute_equal exp, act, msg = nil
224
+ msg = message msg, "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
225
+ refute exp == act, msg
226
+ end
227
+
228
+ def refute_in_delta exp, act, delta = 0.001, msg = nil
229
+ n = (exp - act).abs
230
+ msg = message msg, "Expected #{exp} - #{act} (#{n}) to not be < #{delta}"
231
+ refute delta > n, msg
232
+ end
233
+
234
+ def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
235
+ refute_in_delta a, b, a * epsilon, msg
236
+ end
237
+
238
+ def refute_includes collection, obj, msg = nil
239
+ msg = message msg, "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
240
+ assert_respond_to collection, :include?
241
+ refute collection.include?(obj), msg
242
+ end
243
+
244
+ def refute_instance_of cls, obj, msg = nil
245
+ msg = message msg, "Expected #{mu_pp(obj)} to not be an instance of #{cls}"
246
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
247
+ obj, cls = cls, obj if flip
248
+ refute cls === obj, msg
249
+ end
250
+
251
+ def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
252
+ msg = message msg, "Expected #{mu_pp(obj)} to not be a kind of #{cls}"
253
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
254
+ obj, cls = cls, obj if flip
255
+ refute obj.kind_of?(cls), msg
256
+ end
257
+
258
+ def refute_match exp, act, msg = nil
259
+ msg = message msg, "Expected #{mu_pp(act)} to not match #{mu_pp(exp)}"
260
+ refute act =~ exp, msg
261
+ end
262
+
263
+ def refute_nil obj, msg = nil
264
+ msg = message msg, "Expected #{mu_pp(obj)} to not be nil"
265
+ refute obj.nil?, msg
266
+ end
267
+
268
+ def refute_operator o1, op, o2, msg = nil
269
+ msg = message msg, "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"
270
+ refute o1.__send__(op, o2), msg
271
+ end
272
+
273
+ def refute_respond_to obj, meth, msg = nil
274
+ msg = message msg, "Expected #{mu_pp(obj)} to not respond to #{meth}"
275
+ flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
276
+ obj, meth = meth, obj if flip
277
+ refute obj.respond_to?(meth), msg
278
+ end
279
+
280
+ def refute_same exp, act, msg = nil
281
+ msg = message msg, "Expected #{mu_pp(act)} to not be the same as #{mu_pp(exp)}"
282
+ refute exp.equal?(act), msg
283
+ end
284
+ end
285
+
286
+ class Test
287
+ VERSION = "1.2.0"
288
+
289
+ attr_reader :report, :failures, :errors
290
+
291
+ @@installed_at_exit ||= false
292
+ @@out = $stdout
293
+
294
+ def self.autorun
295
+ at_exit {
296
+ exit_code = Mini::Test.new.run(ARGV)
297
+ exit exit_code if exit_code
298
+ } unless @@installed_at_exit
299
+ @@installed_at_exit = true
300
+ end
301
+
302
+ def self.output= stream
303
+ @@out = stream
304
+ end
305
+
306
+ def puke klass, meth, e
307
+ if Mini::Assertion === e then
308
+ @failures += 1
309
+
310
+ loc = e.backtrace.find { |s| s !~ /in .(assert|flunk|pass|fail|raise)/ }
311
+ loc.sub!(/:in .*$/, '')
312
+
313
+ @report << "Failure:\n#{meth}(#{klass}) [#{loc}]:\n#{e.message}\n"
314
+ 'F'
315
+ else
316
+ @errors += 1
317
+ bt = Mini::filter_backtrace(e.backtrace).join("\n ")
318
+ e = "Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
319
+ @report << e
320
+ 'E'
321
+ end
322
+ end
323
+
324
+ def initialize
325
+ @report = []
326
+ @errors = @failures = 0
327
+ end
328
+
329
+ ##
330
+ # Top level driver, controls all output and filtering.
331
+
332
+ def run args
333
+ filter = if args.first =~ /^(-n|--name)$/ then
334
+ args.shift
335
+ arg = args.shift
336
+ arg =~ /\/(.*)\// ? Regexp.new($1) : arg
337
+ else
338
+ /./ # anything - ^test_ already filtered by #tests
339
+ end
340
+
341
+ @@out.puts "Loaded suite #{$0.sub(/\.rb$/, '')}\nStarted"
342
+
343
+ start = Time.now
344
+ run_test_suites filter
345
+
346
+ @@out.puts
347
+ @@out.puts "Finished in #{'%.6f' % (Time.now - start)} seconds."
348
+
349
+ @report.each_with_index do |msg, i|
350
+ @@out.puts "\n%3d) %s" % [i + 1, msg]
351
+ end
352
+
353
+ @@out.puts
354
+
355
+ format = "%d tests, %d assertions, %d failures, %d errors"
356
+ @@out.puts format % [@test_count, @assertion_count, failures, errors]
357
+
358
+ return failures + errors if @test_count > 0 # or return nil...
359
+ end
360
+
361
+ def run_test_suites filter = /^test/
362
+ @test_count, @assertion_count = 0, 0
363
+ old_sync, @@out.sync = @@out.sync, true if @@out.respond_to? :sync=
364
+ TestCase.test_suites.each do |suite|
365
+ suite.test_methods.grep(filter).each do |test|
366
+ inst = suite.new test
367
+ inst._assertions = 0
368
+ @@out.puts "\n#{test}: " if $DEBUG
369
+ result = '.'
370
+ begin
371
+ inst.setup
372
+ inst.__send__ test
373
+ rescue Exception => e
374
+ result = puke(suite, test, e)
375
+ ensure
376
+ begin
377
+ inst.teardown
378
+ rescue Exception => e
379
+ result = puke(suite, test, e)
380
+ end
381
+ end
382
+ @@out.print result
383
+ @@out.puts if $DEBUG
384
+ @test_count += 1
385
+ @assertion_count += inst._assertions
386
+ end
387
+ end
388
+ @@out.sync = old_sync if @@out.respond_to? :sync=
389
+ [@test_count, @assertion_count]
390
+ end
391
+
392
+ class TestCase
393
+ attr_reader :name
394
+
395
+ def initialize name
396
+ @name = name
397
+ end
398
+
399
+ def self.reset
400
+ @@test_suites = {}
401
+ end
402
+
403
+ reset
404
+
405
+ def self.inherited klass
406
+ @@test_suites[klass] = true
407
+ end
408
+
409
+ def self.test_suites
410
+ @@test_suites.keys.sort_by { |ts| ts.name }
411
+ end
412
+
413
+ def self.test_methods
414
+ public_instance_methods(true).grep(/^test/).sort.map { |m| m.to_s }
415
+ end
416
+
417
+ def setup; end
418
+ def teardown; end
419
+
420
+ include Mini::Assertions
421
+ end # class TestCase
422
+ end # class Test
423
+ end # module Mini