test-garden 0.4
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.
- checksums.yaml +7 -0
- data/COPYING +22 -0
- data/README.md +64 -0
- data/Rakefile +65 -0
- data/lib/test-garden.rb +250 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5355162fcc4ae930d4cdc5ebf84ffa0bd241bae8
|
4
|
+
data.tar.gz: 1ec76a8eefcc3e54601c2bb02b0f55cdf7f08c36
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88e3890465b22a04480b2fdfcb862bede60305a038a0c90bd9dc7097d87eabdfe65ff89dcbc63bd59d432c7b57a7a0e842332397ea200953a7067852bcaabe2a
|
7
|
+
data.tar.gz: c5eca2fdf237d5abb003ee7f99af9a686728ee291bced3892b8653c188adec99068a0c9f7f2b91acb85aa5f3609a0d10251b0651402907b07c05eb71f4b3edcc
|
data/COPYING
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013-2014, Joel VanderWerf, vjoel@users.sourceforge.net
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
TestGarden
|
2
|
+
==========
|
3
|
+
|
4
|
+
A garden of forking tests.[1]
|
5
|
+
|
6
|
+
TestGarden is a testing framework for concisely sharing several stages of
|
7
|
+
setup code across tests. The shared code is executed once for each test
|
8
|
+
that needs it. Tests may be nested to any depth.
|
9
|
+
|
10
|
+
TestGarden generates summary output, reporting how many pass, fail, skip,
|
11
|
+
and error cases were detected for each group of tests. TestGarden assumes
|
12
|
+
assertion failure exceptions are generated by Wrong::Assert#assert.
|
13
|
+
|
14
|
+
Synopsis:
|
15
|
+
|
16
|
+
require 'test-garden'
|
17
|
+
|
18
|
+
test Thing do
|
19
|
+
thing = Thing.new; teardown {thing.cleanup()}
|
20
|
+
assert {thing.ok?}
|
21
|
+
|
22
|
+
test "assign foo" do
|
23
|
+
thing.foo = "baz" # does not affect foo in subsequent tests
|
24
|
+
assert {thing.foo == "baz"}
|
25
|
+
end
|
26
|
+
|
27
|
+
test "compare foo in two instances" do
|
28
|
+
thing2 = Thing.new; teardown {thing2.cleanup()}
|
29
|
+
assert {thing.foo == thing2.foo}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Run the test like so:
|
34
|
+
|
35
|
+
ruby test_thing.rb [-v | --verbose] [topic topic ...]
|
36
|
+
|
37
|
+
If no topics are given, the verbose output is:
|
38
|
+
|
39
|
+
T: Thing
|
40
|
+
T: Thing: assign foo
|
41
|
+
P: Thing: assign foo
|
42
|
+
T: Thing
|
43
|
+
T: Thing: compare foo in two instances
|
44
|
+
P: Thing: compare foo in two instances
|
45
|
+
2 passed, 0 failed, 0 skipped, 0 errors in Thing
|
46
|
+
|
47
|
+
If a topic list is given, it is treated as a sequence of regular expressions.
|
48
|
+
Only tests whose topic path matches those regular expressions, one for one,
|
49
|
+
are executed. (Matching is case insensitive.) For example:
|
50
|
+
|
51
|
+
ruby testfilename.rb thing compare
|
52
|
+
|
53
|
+
This executes only the the last test. The verbose output is:
|
54
|
+
|
55
|
+
T: Thing
|
56
|
+
S: Thing: assign foo
|
57
|
+
T: Thing
|
58
|
+
T: Thing: compare foo in two instances
|
59
|
+
P: Thing: compare foo in two instances
|
60
|
+
1 passed, 0 failed, 1 skipped, 0 errors in Thing
|
61
|
+
|
62
|
+
Note that the "assign foo" test was skipped, and counted as such.
|
63
|
+
|
64
|
+
[1] In reference to "The garden of forking paths", by J.L. Borges.
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
PRJ = "test-garden"
|
5
|
+
|
6
|
+
def version
|
7
|
+
@version ||= begin
|
8
|
+
require 'test-garden'
|
9
|
+
warn "TestGarden::VERSION not a string" unless
|
10
|
+
TestGarden::VERSION.kind_of? String
|
11
|
+
TestGarden::VERSION
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def tag
|
16
|
+
@tag ||= "#{PRJ}-#{version}"
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Run tests"
|
20
|
+
Rake::TestTask.new :test do |t|
|
21
|
+
t.libs << "lib"
|
22
|
+
t.libs << "ext"
|
23
|
+
t.test_files = FileList["test/**/*.rb"]
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Commit, tag, and push repo; build and push gem"
|
27
|
+
task :release => "release:is_new_version" do
|
28
|
+
require 'tempfile'
|
29
|
+
|
30
|
+
sh "gem build #{PRJ}.gemspec"
|
31
|
+
|
32
|
+
file = Tempfile.new "template"
|
33
|
+
begin
|
34
|
+
file.puts "release #{version}"
|
35
|
+
file.close
|
36
|
+
sh "git commit --allow-empty -a -v -t #{file.path}"
|
37
|
+
ensure
|
38
|
+
file.close unless file.closed?
|
39
|
+
file.unlink
|
40
|
+
end
|
41
|
+
|
42
|
+
sh "git tag #{tag}"
|
43
|
+
sh "git push"
|
44
|
+
sh "git push --tags"
|
45
|
+
|
46
|
+
sh "gem push #{tag}.gem"
|
47
|
+
end
|
48
|
+
|
49
|
+
namespace :release do
|
50
|
+
desc "Diff to latest release"
|
51
|
+
task :diff do
|
52
|
+
latest = `git describe --abbrev=0 --tags --match '#{PRJ}-*'`.chomp
|
53
|
+
sh "git diff #{latest}"
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "Log to latest release"
|
57
|
+
task :log do
|
58
|
+
latest = `git describe --abbrev=0 --tags --match '#{PRJ}-*'`.chomp
|
59
|
+
sh "git log #{latest}.."
|
60
|
+
end
|
61
|
+
|
62
|
+
task :is_new_version do
|
63
|
+
abort "#{tag} exists; update version!" unless `git tag -l #{tag}`.empty?
|
64
|
+
end
|
65
|
+
end
|
data/lib/test-garden.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'wrong'
|
2
|
+
include Wrong::Assert
|
3
|
+
|
4
|
+
if Wrong.config[:color]
|
5
|
+
require "wrong/rainbow"
|
6
|
+
else
|
7
|
+
class String
|
8
|
+
def color(*); self; end
|
9
|
+
def bright; self; end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Not directly instantiated by the user. See README and examples.
|
14
|
+
class TestGarden
|
15
|
+
# Array of nested topics in descending order from the main topic to the topic
|
16
|
+
# of the current test.
|
17
|
+
attr_reader :stack
|
18
|
+
|
19
|
+
# Hash of counters for pass, fail, skip, error cases.
|
20
|
+
attr_reader :status
|
21
|
+
|
22
|
+
# Array of regexes that restrict which topics are traversed.
|
23
|
+
attr_reader :pattern
|
24
|
+
|
25
|
+
# Stack of arrays of procs that will be called to tear down the current setup.
|
26
|
+
attr_reader :teardowns
|
27
|
+
|
28
|
+
VERSION = '0.4'
|
29
|
+
|
30
|
+
class IncompleteTest < StandardError; end
|
31
|
+
|
32
|
+
# Reads params from command line, or from given array of strings. If
|
33
|
+
# passing an array, you should call this method *before* all tests.
|
34
|
+
def self.params argv=ARGV
|
35
|
+
@params ||= {
|
36
|
+
:verbose => argv.delete("-v") || argv.delete("--verbose"),
|
37
|
+
:pattern => argv.map {|arg| /#{arg}/i}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# By default, share params for all TestGardens, but allow per-instance
|
42
|
+
# variation by modifying the params hash.
|
43
|
+
def params
|
44
|
+
@params ||= self.class.params.dup
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@pos = []
|
49
|
+
@next = nil
|
50
|
+
@did_one_test = false
|
51
|
+
@stack = []
|
52
|
+
@status = Hash.new(0)
|
53
|
+
@enabled = false
|
54
|
+
@pattern = params[:pattern]
|
55
|
+
@teardowns = []
|
56
|
+
@finishing = false
|
57
|
+
end
|
58
|
+
|
59
|
+
def enabled?
|
60
|
+
@enabled
|
61
|
+
end
|
62
|
+
|
63
|
+
def verbose?
|
64
|
+
params[:verbose]
|
65
|
+
end
|
66
|
+
|
67
|
+
def nest topic
|
68
|
+
topic = topic.to_s
|
69
|
+
@main_topic ||= topic
|
70
|
+
|
71
|
+
if @did_one_test
|
72
|
+
if not @next
|
73
|
+
@next = @pos.dup
|
74
|
+
end
|
75
|
+
@pos[-1] += 1 if @pos.length > 0
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
if @next
|
80
|
+
len = [@pos.length, @next.length].min
|
81
|
+
if @next[0...len] != @pos[0...len]
|
82
|
+
@pos[-1] += 1 if @pos.length > 0
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
if @next == @pos
|
87
|
+
@next = nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
stack.push topic
|
93
|
+
@pos << 0
|
94
|
+
teardowns << []
|
95
|
+
old_enabled = @enabled
|
96
|
+
@enabled = pattern.zip(stack).all? {|pat,subj| !subj or pat === subj}
|
97
|
+
if enabled?
|
98
|
+
puts "T: #{stack.join(": ")}" if verbose?
|
99
|
+
@finishing = false
|
100
|
+
catch :break_test do
|
101
|
+
yield
|
102
|
+
@finishing = stack.dup
|
103
|
+
end
|
104
|
+
else
|
105
|
+
puts "S: #{stack.join(": ")}" if verbose?
|
106
|
+
status[:skip] += 1
|
107
|
+
end
|
108
|
+
|
109
|
+
ensure
|
110
|
+
if not @did_one_test
|
111
|
+
@did_one_test = true
|
112
|
+
else
|
113
|
+
@finishing = false
|
114
|
+
end
|
115
|
+
|
116
|
+
@enabled = old_enabled
|
117
|
+
@pos.pop
|
118
|
+
stack.pop
|
119
|
+
@pos[-1] += 1 if @pos.length > 0
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def do_teardowns
|
124
|
+
teardowns.pop.reverse_each {|block| block.call}
|
125
|
+
end
|
126
|
+
|
127
|
+
def print_report
|
128
|
+
ps = "%3d passed" % status[:pass]
|
129
|
+
fs = "%3d failed" % status[:fail]
|
130
|
+
fs = fs.color(:yellow) if status[:fail] > 0
|
131
|
+
ss = "%3d skipped" % status[:skip]
|
132
|
+
es = "%3d errors" % status[:err]
|
133
|
+
es = es.color(:red) if status[:err] > 0
|
134
|
+
report = [ps,fs,ss,es].join(", ")
|
135
|
+
|
136
|
+
inc = status[:incomplete]
|
137
|
+
if inc > 0
|
138
|
+
is = "%3d incomplete" % inc
|
139
|
+
is = is.color(:white)
|
140
|
+
report << ", #{is}"
|
141
|
+
end
|
142
|
+
|
143
|
+
line = "#{report} in #{@main_topic}"
|
144
|
+
line = line.bright if verbose?
|
145
|
+
puts line
|
146
|
+
end
|
147
|
+
|
148
|
+
def handle_test_exceptions
|
149
|
+
yield
|
150
|
+
|
151
|
+
rescue Wrong::Assert::AssertionFailedError => ex
|
152
|
+
status[:fail] += 1
|
153
|
+
line = nil
|
154
|
+
ex.backtrace.reverse_each {|l| break if /wrong\/assert.rb/ =~ l; line = l}
|
155
|
+
msg = "F: #{stack.join(": ")}: failed assertion, at #{line}"
|
156
|
+
puts msg.color(:yellow), ex.message
|
157
|
+
throw :break_test
|
158
|
+
|
159
|
+
rescue IncompleteTest => ex
|
160
|
+
status[:incomplete] += 1
|
161
|
+
if verbose?
|
162
|
+
msg = "I: #{stack.join(": ")}"
|
163
|
+
msg = msg.color(:white)
|
164
|
+
puts msg
|
165
|
+
end
|
166
|
+
throw :break_test
|
167
|
+
|
168
|
+
rescue => ex
|
169
|
+
status[:err] += 1
|
170
|
+
bt = []
|
171
|
+
ex.backtrace.each {|l| break if /wrong\/assert.rb/ =~ l; bt << l}
|
172
|
+
bts = bt.join("\n from ")
|
173
|
+
msg = "E: #{stack.join(": ")}: #{ex} (#{ex.class}), at #{bts}"
|
174
|
+
puts msg.color(:red)
|
175
|
+
throw :break_test
|
176
|
+
|
177
|
+
else
|
178
|
+
if enabled?
|
179
|
+
if @finishing
|
180
|
+
status[:pass] += 1
|
181
|
+
puts "P: #{@finishing.join(": ")}" if verbose?
|
182
|
+
@finishing = false
|
183
|
+
end
|
184
|
+
else
|
185
|
+
raise
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def main topic
|
190
|
+
begin
|
191
|
+
nest topic do
|
192
|
+
handle_test_exceptions do
|
193
|
+
yield
|
194
|
+
do_teardowns
|
195
|
+
end
|
196
|
+
end
|
197
|
+
@did_one_test = false
|
198
|
+
end while @next
|
199
|
+
ensure
|
200
|
+
print_report
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Begin a test block. The topic can be any object; its to_s method is applied
|
205
|
+
# to generate the output string. A class or string is typical.
|
206
|
+
#
|
207
|
+
# The block can include essentially any code, including more #test calls,
|
208
|
+
# method calls that call #test, assert{} and teardown{} calls, etc.
|
209
|
+
#
|
210
|
+
# If the block is omitted, then the test is assumed to be incomplete, perhaps
|
211
|
+
# a stub indicating future work. Incomplete tests are counted and reported.
|
212
|
+
def test topic
|
213
|
+
if @test
|
214
|
+
@test.nest topic do
|
215
|
+
@test.handle_test_exceptions do
|
216
|
+
if block_given?
|
217
|
+
yield
|
218
|
+
@test.do_teardowns
|
219
|
+
else
|
220
|
+
raise TestGarden::IncompleteTest
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
else
|
226
|
+
begin
|
227
|
+
@test = TestGarden.new
|
228
|
+
@test.main topic do
|
229
|
+
if block_given?
|
230
|
+
yield
|
231
|
+
else
|
232
|
+
raise TestGarden::IncompleteTest
|
233
|
+
end
|
234
|
+
end
|
235
|
+
ensure
|
236
|
+
@test = nil
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Alternative to putting the teardown code after all relevant tests.
|
242
|
+
# This can be used to keep related setup and teardown code together.
|
243
|
+
# Teardows are executed in the reverse of their creation order.
|
244
|
+
def teardown(&block)
|
245
|
+
if @test
|
246
|
+
@test.teardowns.last << block
|
247
|
+
else
|
248
|
+
raise "Cannot teardown: not in a test"
|
249
|
+
end
|
250
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: test-garden
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.4'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joel VanderWerf
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: wrong
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.7'
|
27
|
+
description: |-
|
28
|
+
A testing framework for concisely sharing several stages of
|
29
|
+
setup code across tests.
|
30
|
+
email: vjoel@users.sourceforge.net
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files:
|
34
|
+
- README.md
|
35
|
+
- COPYING
|
36
|
+
files:
|
37
|
+
- COPYING
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- lib/test-garden.rb
|
41
|
+
homepage: https://github.com/vjoel/test-garden
|
42
|
+
licenses:
|
43
|
+
- BSD
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- "--quiet"
|
48
|
+
- "--line-numbers"
|
49
|
+
- "--inline-source"
|
50
|
+
- "--title"
|
51
|
+
- test-garden
|
52
|
+
- "--main"
|
53
|
+
- README.md
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.2.1
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: A garden of forking tests
|
72
|
+
test_files: []
|
73
|
+
has_rdoc:
|