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