baretest 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MANIFEST.txt +38 -0
- data/README.markdown +229 -0
- data/bin/baretest +102 -0
- data/examples/test.rake +37 -0
- data/examples/test.rb +93 -0
- data/lib/test/assertion/failure.rb +14 -0
- data/lib/test/assertion/support.rb +268 -0
- data/lib/test/assertion.rb +117 -0
- data/lib/test/debug.rb +34 -0
- data/lib/test/irb_mode.rb +104 -0
- data/lib/test/run/cli.rb +79 -0
- data/lib/test/run/errors.rb +42 -0
- data/lib/test/run/interactive.rb +60 -0
- data/lib/test/run/minimal.rb +31 -0
- data/lib/test/run/spec.rb +32 -0
- data/lib/test/run/tap.rb +32 -0
- data/lib/test/run/xml.rb +56 -0
- data/lib/test/run.rb +137 -0
- data/lib/test/suite.rb +95 -0
- data/lib/test/version.rb +19 -0
- data/lib/test.rb +118 -0
- data/test/external/bootstraptest.rb +5 -0
- data/test/external/bootstrapwrap.rb +2 -0
- data/test/helper/mocks.rb +0 -0
- data/test/lib/test/assertion/support.rb +240 -0
- data/test/lib/test/assertion.rb +142 -0
- data/test/lib/test/debug.rb +63 -0
- data/test/lib/test/irb_mode.rb +10 -0
- data/test/lib/test/run/cli.rb +9 -0
- data/test/lib/test/run/errors.rb +9 -0
- data/test/lib/test/run/interactive.rb +9 -0
- data/test/lib/test/run/spec.rb +9 -0
- data/test/lib/test/run/tap.rb +9 -0
- data/test/lib/test/run/xml.rb +9 -0
- data/test/lib/test/run.rb +235 -0
- data/test/lib/test/suite.rb +275 -0
- data/test/lib/test.rb +227 -0
- data/test/setup.rb +2 -0
- metadata +99 -0
@@ -0,0 +1,268 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'test/assertion/failure'
|
10
|
+
begin
|
11
|
+
require 'thread'
|
12
|
+
rescue LoadError; end # no thread support in this ruby
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
module Test
|
17
|
+
@touch = {}
|
18
|
+
# We don't want to litter in Assertion
|
19
|
+
# Touches are associated with
|
20
|
+
def self.touch(assertion, thing=nil)
|
21
|
+
@touch[assertion] ||= Hash.new(0)
|
22
|
+
@touch[assertion][thing] += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.touched(assertion, thing=nil)
|
26
|
+
@touch[assertion] ||= Hash.new(0)
|
27
|
+
@touch[assertion][thing]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.clean_touches(assertion)
|
31
|
+
@touch.delete(assertion)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Test
|
36
|
+
class Assertion
|
37
|
+
module Support
|
38
|
+
module SetupAndTeardown
|
39
|
+
def self.extended(run_obj)
|
40
|
+
run_obj.init do
|
41
|
+
suite.teardown do
|
42
|
+
Test.clean_touches(self) # instance evaled, self is the assertion
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Test.extender << self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Will raise a Failure if the given block doesn't raise or raises a different
|
51
|
+
# exception than the one provided
|
52
|
+
# You can optionally give an options :with_message, which is tested with === against
|
53
|
+
# the exception message.
|
54
|
+
# Examples:
|
55
|
+
# raises do raise "will work" end # => true
|
56
|
+
# raises SomeException do raise SomeException end # => true
|
57
|
+
# raises :with_message => "bar" do raise "bar" end # => true
|
58
|
+
# raises SomeException, :with_message => "bar"; raise SomeException, "bar" end # => true
|
59
|
+
# raises :with_message => /\Aknown \w+\z/; raise "known unknown" end # => true
|
60
|
+
def raises(exception_class=StandardError, opts={})
|
61
|
+
begin
|
62
|
+
yield
|
63
|
+
rescue exception_class => exception
|
64
|
+
if opts[:with_message] && !(opts[:with_message] === exception.message) then
|
65
|
+
failure "Expected block to raise with the message %p, but the message was %p",
|
66
|
+
exception.message, opts[:with_message]
|
67
|
+
else
|
68
|
+
true
|
69
|
+
end
|
70
|
+
rescue => exception
|
71
|
+
failure "Expected block to raise #{exception_class}, but it raised #{exception.class}."
|
72
|
+
else
|
73
|
+
failure "Expected block to raise #{exception_class}, but nothing was raised."
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Will raise a Failure if the given block raises.
|
78
|
+
def raises_nothing
|
79
|
+
yield
|
80
|
+
rescue => exception
|
81
|
+
failure "Expected block to raise nothing, but it raised #{exception.class}."
|
82
|
+
else
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
# For comparisons of Floats you shouldn't use == but
|
87
|
+
# for example a delta comparison instead, to take care
|
88
|
+
# of the possible rounding differences.
|
89
|
+
def within_delta(a, b, delta)
|
90
|
+
(a-b).abs < delta
|
91
|
+
end
|
92
|
+
|
93
|
+
# Use this method to test whether certain code (e.g. a callback) was reached.
|
94
|
+
# touch marks that it was reached, #touched tests for whether it was reached.
|
95
|
+
# Example:
|
96
|
+
# assert "Code in a Proc object is executed when invoking #call on it." do
|
97
|
+
# a_proc = proc { touch :executed }
|
98
|
+
# a_proc.call
|
99
|
+
# touched(:executed)
|
100
|
+
# end
|
101
|
+
def touch(thing=nil)
|
102
|
+
::Test.touch(self, thing)
|
103
|
+
end
|
104
|
+
|
105
|
+
# See #touch
|
106
|
+
def touched(thing=nil, times=nil)
|
107
|
+
touched_times = ::Test.touched(self, thing)
|
108
|
+
if times then
|
109
|
+
unless touched_times == times then
|
110
|
+
if thing then
|
111
|
+
failure "Expected the code to touch %p %s times, but did %s times.", thing, times, touched_times
|
112
|
+
else
|
113
|
+
failure "Expected the code to touch %s times, but did %s times.", times, touched_times
|
114
|
+
end
|
115
|
+
end
|
116
|
+
elsif touched_times < 1 then
|
117
|
+
if thing then
|
118
|
+
failure "Expected the code to touch %p, but it was not touched.", thing
|
119
|
+
else
|
120
|
+
failure "Expected the code to touch, but no touch happened."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
# See #touch
|
127
|
+
def not_touched(thing=nil)
|
128
|
+
touched(thing, 0)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Uses equal? to test whether the objects are the same
|
132
|
+
# same expected, actual
|
133
|
+
# same :expected => expected, :actual => actual
|
134
|
+
def same(*args)
|
135
|
+
expected, actual, message = extract_args(args, :expected, :actual, :message)
|
136
|
+
|
137
|
+
unless expected.equal?(actual) then
|
138
|
+
if message then
|
139
|
+
failure "Expected %s to be the same (equal?) as %p but was %p.", message, expected, actual
|
140
|
+
else
|
141
|
+
failure "Expected %p but got %p.", expected, actual
|
142
|
+
end
|
143
|
+
end
|
144
|
+
true
|
145
|
+
end
|
146
|
+
|
147
|
+
# Uses eql? to test whether the objects are equal
|
148
|
+
# equal expected, actual
|
149
|
+
# equal :expected => expected, :actual => actual
|
150
|
+
def hash_key_equal(*args)
|
151
|
+
expected, actual, message = extract_args(args, :expected, :actual, :message)
|
152
|
+
|
153
|
+
unless expected.eql?(actual) then
|
154
|
+
if message then
|
155
|
+
failure "Expected %s to be hash-key equal (eql?) to %p but was %p.", message, expected, actual
|
156
|
+
else
|
157
|
+
failure "Expected %p but got %p.", expected, actual
|
158
|
+
end
|
159
|
+
end
|
160
|
+
true
|
161
|
+
end
|
162
|
+
|
163
|
+
# Uses == to test whether the objects are equal
|
164
|
+
# equal expected, actual
|
165
|
+
# equal :expected => expected, :actual => actual
|
166
|
+
def order_equal(*args)
|
167
|
+
expected, actual, message = extract_args(args, :expected, :actual, :message)
|
168
|
+
|
169
|
+
unless expected == actual then
|
170
|
+
if message then
|
171
|
+
failure "Expected %s to be order equal (==) to %p but was %p.", message, expected, actual
|
172
|
+
else
|
173
|
+
failure "Expected %p but got %p.", expected, actual
|
174
|
+
end
|
175
|
+
end
|
176
|
+
true
|
177
|
+
end
|
178
|
+
alias equal order_equal
|
179
|
+
|
180
|
+
# Uses === to test whether the objects are equal
|
181
|
+
# equal expected, actual
|
182
|
+
# equal :expected => expected, :actual => actual
|
183
|
+
def case_equal(*args)
|
184
|
+
expected, actual, message = extract_args(args, :expected, :actual, :message)
|
185
|
+
|
186
|
+
unless expected === actual then
|
187
|
+
failure_with_optional_message \
|
188
|
+
"Expected %s to be case equal (===) to %p but was %p.",
|
189
|
+
"Expected %p but got %p.",
|
190
|
+
message, expected, actual
|
191
|
+
end
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
# To compare two collections (which must implement #each)
|
196
|
+
# without considering order. E.g. two sets, or the keys of
|
197
|
+
# two hashes.
|
198
|
+
def equal_unordered(*args)
|
199
|
+
expected, actual, message = extract_args(args, :expected, :actual, :message)
|
200
|
+
|
201
|
+
count = Hash.new(0)
|
202
|
+
expected.each { |element| count[element] += 1 }
|
203
|
+
actual.each { |element| count[element] -= 1 }
|
204
|
+
unless count.all? { |key, value| value.zero? } then
|
205
|
+
only_in_expected = count.select { |ele, n| n > 0 }.map { |ele, n| ele }
|
206
|
+
only_in_actual = count.select { |ele, n| n < 0 }.map { |ele, n| ele }
|
207
|
+
if message then
|
208
|
+
failure "Expected %s to have the same items the same number of times, " \
|
209
|
+
"but %p are only in expected, and %p only in actual.",
|
210
|
+
message, only_in_expected, only_in_actual
|
211
|
+
else
|
212
|
+
failure "Expected %p and %p to have the same items the same number of times, " \
|
213
|
+
"but %p are only in expected, and %p only in actual.",
|
214
|
+
expected, actual, only_in_expected, only_in_actual
|
215
|
+
end
|
216
|
+
end
|
217
|
+
true
|
218
|
+
end
|
219
|
+
|
220
|
+
# Raises a Failure if the given object is not an instance of the given class
|
221
|
+
# or a descendant thereof
|
222
|
+
def kind_of(*args)
|
223
|
+
expected, actual, message = extract_args(args, :expected, :actual, :message)
|
224
|
+
unless actual.kind_of?(expected) then
|
225
|
+
failure_with_optional_message \
|
226
|
+
"Expected %1$s to be a kind of %3$p, but was a %4$p",
|
227
|
+
"Expected %2$p to be a kind of %1$p, but was a %3$p",
|
228
|
+
message, expected, actual, actual.class
|
229
|
+
end
|
230
|
+
true
|
231
|
+
end
|
232
|
+
|
233
|
+
def failure_with_optional_message(with_message, without_message, message, *args)
|
234
|
+
if message then
|
235
|
+
failure(with_message, message, *args)
|
236
|
+
else
|
237
|
+
failure(without_message, *args)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Raises Test::Assertion::Failure and runs sprintf over message with *args
|
242
|
+
# Particularly useful with %p and %s.
|
243
|
+
def failure(message, *args)
|
244
|
+
raise Test::Assertion::Failure, sprintf(message, *args)
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
def extract_args(args, *named)
|
249
|
+
if args.size == 1 && Hash === args.first then
|
250
|
+
args.first.values_at(*named)
|
251
|
+
else
|
252
|
+
args.first(named.size)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end # Support
|
256
|
+
|
257
|
+
include Support
|
258
|
+
end # Assertion
|
259
|
+
end # Test
|
260
|
+
|
261
|
+
module Enumerable
|
262
|
+
def equal_unordered?(other)
|
263
|
+
count = Hash.new(0)
|
264
|
+
other.each { |element| count[element] += 1 }
|
265
|
+
each { |element| count[element] -= 1 }
|
266
|
+
count.all? { |key, value| value.zero? }
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'test/assertion/failure'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
module Test
|
14
|
+
|
15
|
+
# Defines an assertion
|
16
|
+
# An assertion belongs to a suite and consists of a description and a block.
|
17
|
+
# The verify the assertion, the suite's (and its ancestors) setup blocks are
|
18
|
+
# executed, then the assertions block is executed and after that, the suite's
|
19
|
+
# (and ancestors) teardown blocks are invoked.
|
20
|
+
#
|
21
|
+
# An assertion has 5 possible states, see Assertion#status for a list of them.
|
22
|
+
#
|
23
|
+
# There are various helper methods in lib/test/support.rb which help you
|
24
|
+
# defining nicer diagnostics or just easier ways to test common scenarios.
|
25
|
+
# The following are test helpers:
|
26
|
+
# * Kernel#raises(exception_class=StandardError)
|
27
|
+
# * Kernel#within_delta(a, b, delta)
|
28
|
+
# * Kernel#equal_unordered(a,b)
|
29
|
+
# * Enumerable#equal_unordered(other)
|
30
|
+
class Assertion
|
31
|
+
|
32
|
+
# An assertion has 5 possible states:
|
33
|
+
# :success
|
34
|
+
# : The assertion passed. This means the block returned a trueish value.
|
35
|
+
# :failure
|
36
|
+
# : The assertion failed. This means the block returned a falsish value.
|
37
|
+
# Alternatively it raised a Test::Failure (NOT YET IMPLEMENTED).
|
38
|
+
# The latter has the advantage that it can provide nicer diagnostics.
|
39
|
+
# :pending
|
40
|
+
# : No block given to the assertion to be run
|
41
|
+
# :skipped
|
42
|
+
# : If one of the parent suites is missing a dependency, its assertions
|
43
|
+
# will be skipped
|
44
|
+
# :error
|
45
|
+
# : The assertion errored out. This means the block raised an exception
|
46
|
+
attr_reader :status
|
47
|
+
|
48
|
+
# If an exception occured in Assertion#execute, this will contain the
|
49
|
+
# Exception object raised.
|
50
|
+
attr_reader :exception
|
51
|
+
|
52
|
+
# The description of this assertion.
|
53
|
+
attr_reader :description
|
54
|
+
|
55
|
+
# The failure reason.
|
56
|
+
attr_reader :failure_reason
|
57
|
+
|
58
|
+
# The suite this assertion belongs to
|
59
|
+
attr_reader :suite
|
60
|
+
|
61
|
+
# The block specifying the assertion
|
62
|
+
attr_reader :block
|
63
|
+
|
64
|
+
# suite
|
65
|
+
# : The suite the Assertion belongs to
|
66
|
+
# description
|
67
|
+
# : A descriptive string about what this Assertion tests.
|
68
|
+
# &block
|
69
|
+
# : The block definition. Without one, the Assertion will have a :pending
|
70
|
+
# status.
|
71
|
+
def initialize(suite, description, &block)
|
72
|
+
@suite = suite
|
73
|
+
@status = nil
|
74
|
+
@failure_reason = nil
|
75
|
+
@exception = nil
|
76
|
+
@description = description || "No description given"
|
77
|
+
@block = block
|
78
|
+
end
|
79
|
+
|
80
|
+
# Run all setups in the order of their nesting (outermost first, innermost last)
|
81
|
+
def setup
|
82
|
+
@suite.ancestry_setup.each { |setup| instance_eval(&setup) } if @suite
|
83
|
+
end
|
84
|
+
|
85
|
+
# Run all teardowns in the order of their nesting (innermost first, outermost last)
|
86
|
+
def teardown
|
87
|
+
@suite.ancestry_teardown.each { |setup| instance_eval(&setup) } if @suite
|
88
|
+
end
|
89
|
+
|
90
|
+
# Runs the assertion and sets the status and exception
|
91
|
+
def execute
|
92
|
+
@exception = nil
|
93
|
+
if @block then
|
94
|
+
setup
|
95
|
+
# run the assertion
|
96
|
+
begin
|
97
|
+
@status = instance_eval(&@block) ? :success : :failure
|
98
|
+
rescue ::Test::Assertion::Failure => failure
|
99
|
+
@status = :failure
|
100
|
+
@failure_reason = failure
|
101
|
+
rescue => exception
|
102
|
+
@failure_reason = "An error occurred"
|
103
|
+
@exception = exception
|
104
|
+
@status = :error
|
105
|
+
end
|
106
|
+
teardown
|
107
|
+
else
|
108
|
+
@status = :pending
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def clean_copy(use_class=nil)
|
114
|
+
(use_class || self.class).new(@suite, @description, &@block)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/lib/test/debug.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'pp'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
module Test
|
15
|
+
class Suite
|
16
|
+
def to_s
|
17
|
+
sprintf "%s %s", self.class, @description
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
sprintf "#<%s:%08x %p>", self.class, object_id>>1, @description
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Assertion
|
26
|
+
def to_s
|
27
|
+
sprintf "%s %s", self.class, @description
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
sprintf "#<%s:%08x @suite=%p %p>", self.class, object_id>>1, @suite, @description
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'test/debug'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
module Test
|
14
|
+
module IRBMode
|
15
|
+
module AssertionExtensions
|
16
|
+
end
|
17
|
+
|
18
|
+
class AssertionContext < ::Test::Assertion
|
19
|
+
attr_accessor :original_assertion
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"Assertion"
|
23
|
+
end
|
24
|
+
|
25
|
+
def e!
|
26
|
+
em!
|
27
|
+
bt!
|
28
|
+
end
|
29
|
+
|
30
|
+
def em!
|
31
|
+
puts @original_assertion.exception
|
32
|
+
end
|
33
|
+
|
34
|
+
def bt!
|
35
|
+
size = caller.size+3
|
36
|
+
puts @original_assertion.exception.backtrace[0..-size]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.extended(by)
|
41
|
+
by.init do
|
42
|
+
require 'irb'
|
43
|
+
require 'test/debug'
|
44
|
+
IRB.setup(nil) # must only be called once
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Formatter callback.
|
49
|
+
# Invoked once for every assertion.
|
50
|
+
# Gets the assertion to run as single argument.
|
51
|
+
def run_test(assertion)
|
52
|
+
rv = super
|
53
|
+
# drop into irb if assertion failed
|
54
|
+
if [:failure, :error].include?(rv.status) then
|
55
|
+
start_irb_mode(assertion)
|
56
|
+
irb_mode_for_assertion(assertion)
|
57
|
+
stop_irb_mode(assertion)
|
58
|
+
end
|
59
|
+
|
60
|
+
@count[:test] += 1
|
61
|
+
@count[assertion.status] += 1
|
62
|
+
rv
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_irb_mode(assertion)
|
66
|
+
ancestry = assertion.suite.ancestors.reverse.map { |suite| suite.name }
|
67
|
+
|
68
|
+
puts
|
69
|
+
puts "#{assertion.status.to_s.capitalize} in #{ancestry.join(' > ')}"
|
70
|
+
puts " #{assertion.description}"
|
71
|
+
puts "#{assertion.exception} - #{assertion.exception.backtrace.first}"
|
72
|
+
super
|
73
|
+
rescue NoMethodError # HAX, not happy about that. necessary due to order of extend
|
74
|
+
end
|
75
|
+
|
76
|
+
# This method is highlevel hax, try to add necessary API to
|
77
|
+
# Test::Assertion
|
78
|
+
def irb_mode_for_assertion(assertion)
|
79
|
+
irb_context = assertion.clean_copy(AssertionContext)
|
80
|
+
irb_context.original_assertion = assertion
|
81
|
+
irb_context.setup
|
82
|
+
@irb = IRB::Irb.new(IRB::WorkSpace.new(irb_context.send(:binding)))
|
83
|
+
irb = @irb # for closure
|
84
|
+
|
85
|
+
# HAX - cargo cult, taken from irb.rb, not yet really understood.
|
86
|
+
IRB.conf[:IRB_RC].call(irb.context) if IRB.conf[:IRB_RC] # loads the irbrc?
|
87
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context # why would the main context be set here?
|
88
|
+
# /HAX
|
89
|
+
|
90
|
+
trap("SIGINT") do
|
91
|
+
irb.signal_handle
|
92
|
+
end
|
93
|
+
catch(:IRB_EXIT) do irb.eval_input end
|
94
|
+
|
95
|
+
irb_context.teardown
|
96
|
+
end
|
97
|
+
|
98
|
+
def stop_irb_mode(assertion)
|
99
|
+
puts
|
100
|
+
super
|
101
|
+
rescue NoMethodError # HAX, not happy about that. necessary due to order of extend
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/test/run/cli.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module Test
|
10
|
+
class Run
|
11
|
+
module CLI
|
12
|
+
Formats = {
|
13
|
+
:pending => "\e[43m%9s\e[0m %s%s\n",
|
14
|
+
:skipped => "\e[43m%9s\e[0m %s%s\n",
|
15
|
+
:success => "\e[42m%9s\e[0m %s%s\n",
|
16
|
+
:failure => "\e[41m%9s\e[0m %s%s\n",
|
17
|
+
:error => "\e[37;40;1m%9s\e[0m %s%s\n" # ]]]]]]]] - bbedit hates open brackets...
|
18
|
+
}
|
19
|
+
|
20
|
+
FooterFormats = {
|
21
|
+
:incomplete => "\e[43m%9s\e[0m\n",
|
22
|
+
:success => "\e[42m%9s\e[0m\n",
|
23
|
+
:failure => "\e[41m%9s\e[0m\n",
|
24
|
+
:error => "\e[37;40;1m%9s\e[0m\n" # ]]]]]]]] - bbedit hates open brackets...
|
25
|
+
}
|
26
|
+
|
27
|
+
def run_all(*args)
|
28
|
+
@depth = 0
|
29
|
+
puts "Running all tests\n"
|
30
|
+
start = Time.now
|
31
|
+
super # run all suites
|
32
|
+
status = global_status
|
33
|
+
printf "\n%2$d tests run in %1$.1fs\n%3$d successful, %4$d pending, %5$d failures, %6$d errors\n",
|
34
|
+
Time.now-start, *@count.values_at(:test, :success, :pending, :failure, :error)
|
35
|
+
print "Final status: "
|
36
|
+
printf FooterFormats[status], status_label(status)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_suite(suite)
|
40
|
+
return super unless suite.description
|
41
|
+
#label, size = ' '*@depth+suite.description, suite.tests.size.to_s
|
42
|
+
#printf "\n\e[1m%-*s\e[0m (%d tests)\n", 71-size.length, label, size
|
43
|
+
case size = suite.tests.size
|
44
|
+
when 0
|
45
|
+
puts "\n \e[1m#{' '*@depth+suite.description}\e[0m"
|
46
|
+
when 1
|
47
|
+
puts "\n \e[1m#{' '*@depth+suite.description}\e[0m (1 test)"
|
48
|
+
else
|
49
|
+
puts "\n \e[1m#{' '*@depth+suite.description}\e[0m (#{size} tests)"
|
50
|
+
end
|
51
|
+
@depth += 1
|
52
|
+
super # run the suite
|
53
|
+
@depth -= 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def run_test(assertion)
|
57
|
+
rv = super # run the assertion
|
58
|
+
printf(Formats[rv.status], status_label(rv.status), ' '*@depth, rv.description)
|
59
|
+
if rv.status == :error then
|
60
|
+
indent = ' '+' '*@depth
|
61
|
+
print(indent, rv.exception.message, "\n", indent, rv.exception.backtrace.first, "\n")
|
62
|
+
elsif rv.status == :failure && rv.failure_reason then
|
63
|
+
print(' ', ' '*@depth, rv.failure_reason, "\n")
|
64
|
+
end
|
65
|
+
rv
|
66
|
+
end
|
67
|
+
|
68
|
+
def word_wrap(string, cols)
|
69
|
+
str.scan(/[^ ]+ /)
|
70
|
+
end
|
71
|
+
|
72
|
+
def status_label(status)
|
73
|
+
status.to_s.capitalize.center(9)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
@format["test/run/cli"] = Run::CLI # register the extender
|
79
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module Test
|
10
|
+
class Run
|
11
|
+
module Errors
|
12
|
+
def run_all
|
13
|
+
@depth = 0
|
14
|
+
puts "Running all tests, reporting errors"
|
15
|
+
super
|
16
|
+
start = Time.now
|
17
|
+
puts "\nDone, #{@count[:error]} errors encountered."
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_suite(suite)
|
21
|
+
return super unless suite.description
|
22
|
+
puts "#{' '*@depth+suite.description} (#{suite.tests.size} tests)"
|
23
|
+
@depth += 1
|
24
|
+
super # run the suite
|
25
|
+
@depth -= 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_test(assertion)
|
29
|
+
rv = super # run the assertion
|
30
|
+
puts(' '*@depth+rv.description)
|
31
|
+
if rv.exception then
|
32
|
+
size = caller.size+5
|
33
|
+
puts((['-'*80, rv.exception]+rv.exception.backtrace[0..-size]+['-'*80, '']).map { |l|
|
34
|
+
(' '*(@depth+1))+l
|
35
|
+
})
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@format["test/run/errors"] = Run::Errors # register the extender
|
42
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module Test
|
10
|
+
class Run
|
11
|
+
module Interactive
|
12
|
+
def self.extended(obj)
|
13
|
+
obj.init do
|
14
|
+
require "test/irb_mode"
|
15
|
+
extend(Test::IRBMode)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_all
|
20
|
+
count = proc { |acc,csuite| acc+csuite.tests.size+csuite.suites.inject(0, &count) }
|
21
|
+
@counted = count[0, suite] # count the number of tests to run
|
22
|
+
@depth = 0
|
23
|
+
@width = (Math.log(@counted)/Math.log(10)).floor+1
|
24
|
+
|
25
|
+
puts "Running all tests in interactive mode\n"
|
26
|
+
$stdout.sync = true
|
27
|
+
print_count
|
28
|
+
|
29
|
+
super
|
30
|
+
|
31
|
+
puts "\n\nDone"
|
32
|
+
$stdout.sync = false
|
33
|
+
$stdout.flush
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_suite(suite)
|
37
|
+
return super unless suite.description
|
38
|
+
@depth += 1
|
39
|
+
super
|
40
|
+
@depth -= 1
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_test(assertion)
|
44
|
+
rv = super
|
45
|
+
print_count
|
46
|
+
rv
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_count
|
50
|
+
printf "\rRan %*d of %*d assertions", @width, @count[:test], @width, @counted
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop_irb_mode(assertion)
|
54
|
+
puts "\n\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@format["test/run/interactive"] = Run::Interactive # register the extender
|
60
|
+
end
|