miniunit 1.1.0 → 1.2.0

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/.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