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 +10 -2
- data/History.txt +57 -2
- data/Manifest.txt +9 -2
- data/README.txt +14 -5
- data/Rakefile +50 -20
- data/bin/use_miniunit +23 -0
- data/lib/mini/mock.rb +31 -0
- data/lib/mini/spec.rb +81 -0
- data/lib/mini/test.rb +423 -0
- data/lib/test/unit.rb +14 -297
- data/lib/test/unit/assertions.rb +75 -0
- data/lib/test/unit/error.rb +8 -0
- data/lib/test/unit/testcase.rb +1 -6
- data/test/test_mini_mock.rb +76 -0
- data/test/test_mini_spec.rb +147 -0
- data/test/test_mini_test.rb +682 -0
- metadata +63 -47
- data/test/test_test_case.rb +0 -321
- data/test/test_test_unit.rb +0 -75
data/.autotest
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
|
-
Autotest.add_hook
|
4
|
-
at.
|
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
|
+
|
data/History.txt
CHANGED
@@ -1,16 +1,71 @@
|
|
1
|
-
|
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
|
-
|
66
|
+
=== 1.0.0 / 2006-10-30
|
13
67
|
|
14
68
|
* 1 major enhancement
|
69
|
+
|
15
70
|
* Birthday!
|
16
71
|
|
data/Manifest.txt
CHANGED
@@ -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/
|
9
|
-
test/
|
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
|
-
|
2
|
-
|
1
|
+
= mini/{test,spec,mock}
|
2
|
+
|
3
|
+
* http://rubyforge.org/projects/bfts
|
3
4
|
|
4
5
|
== DESCRIPTION:
|
5
6
|
|
6
|
-
|
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
|
32
|
+
+ sudo gem install miniunit
|
24
33
|
|
25
34
|
== LICENSE:
|
26
35
|
|
27
36
|
(The MIT License)
|
28
37
|
|
29
|
-
Copyright (c)
|
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
|
8
|
-
|
9
|
-
Hoe.new('miniunit', Test::
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
data/bin/use_miniunit
ADDED
@@ -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
|
data/lib/mini/mock.rb
ADDED
@@ -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
|
data/lib/mini/spec.rb
ADDED
@@ -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
|
data/lib/mini/test.rb
ADDED
@@ -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
|