mtbb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ /.mtbb-artifacts/
@@ -0,0 +1,10 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ script: bundle exec bin/mtbb
7
+ notifications:
8
+ email:
9
+ recipients:
10
+ - github+mtbb@modcloth.com
@@ -0,0 +1,4 @@
1
+ `mtbb` authors
2
+ ==============
3
+
4
+ - Dan Buch
@@ -0,0 +1,5 @@
1
+ `mtdd` Contribution Guidelines
2
+ ==============================
3
+
4
+ - Pull requests accepted!
5
+ - Please add yourself to the `AUTHORS.md` file alphabetically by first name.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mtbb.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 ModCloth, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ `mtbb`
2
+ ======
3
+
4
+ [![Build Status](https://travis-ci.org/modcloth-labs/mtbb.png)](https://travis-ci.org/modcloth-labs/mtbb)
5
+
6
+ Minitest-based black box testing thing.
7
+
8
+ ## Installation
9
+
10
+ Download the whole thing as a standalone executable:
11
+
12
+ ``` bash
13
+ curl -s -o mtbb https://raw.github.com/modcloth-labs/mtbb/master/lib/mtbb.rb
14
+ chmod +x mtbb
15
+ ```
16
+
17
+ Or install via `gem`:
18
+
19
+ ``` bash
20
+ gem install mtbb
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Given a `test` directory containing minitest files of the format
26
+ `*_test.rb`, run the `mtbb` executable over them:
27
+
28
+ ``` bash
29
+ mtbb
30
+ ```
31
+
32
+ An alternate test discovery glob may be given a la:
33
+
34
+ ``` bash
35
+ MTBB_TEST_GLOB='./black-box-tests/**/*_test.rb' mtbb
36
+ ```
37
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'mtbb'
3
+ Mtbb::TestRunner.main(ARGV)
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env ruby
2
+ # vim:fileencoding=utf-8
3
+ #
4
+ # Copyright (c) 2013 ModCloth, Inc.
5
+ #
6
+ # MIT License
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining
9
+ # a copy of this software and associated documentation files (the
10
+ # "Software"), to deal in the Software without restriction, including
11
+ # without limitation the rights to use, copy, modify, merge, publish,
12
+ # distribute, sublicense, and/or sell copies of the Software, and to
13
+ # permit persons to whom the Software is furnished to do so, subject to
14
+ # the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be
17
+ # included in all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ #
27
+
28
+ require 'fileutils'
29
+ require 'json'
30
+ require 'minitest/spec'
31
+ require 'net/http'
32
+ require 'time'
33
+ require 'uri'
34
+
35
+ begin
36
+ require_relative 'mtbb/version'
37
+ rescue LoadError
38
+ end
39
+
40
+ module Mtbb
41
+ VERSION = '0.1.0' unless defined?(VERSION)
42
+
43
+ YELLOW = "\033\[33m"
44
+ RESET = "\033\[0m"
45
+ GREEN = "\033\[32m"
46
+ RED = "\033\[31m"
47
+ MAGENTA = "\033\[35m"
48
+ BRIGHT_GREEN = "\033\[32;1m"
49
+ BRIGHT_RED = "\033\[31;1m"
50
+ BRIGHT_MAGENTA = "\033\[35;1m"
51
+ BRIGHT_YELLOW = "\033\[33;1m"
52
+
53
+ SERVERS = {}
54
+
55
+ def register(server_name, server_config)
56
+ SERVERS[server_name.to_sym] = Mtbb::ServerRunner.new(server_config)
57
+ end
58
+
59
+ def server(server_name)
60
+ SERVERS[server_name.to_sym]
61
+ end
62
+
63
+ module_function :register, :server
64
+
65
+ class TestRunner
66
+ def self.main(argv = [].freeze)
67
+ mtbb_test_glob = ENV['MTBB_TEST_GLOB'] || "#{Dir.pwd}/test/**/*_test.rb"
68
+ Dir.glob(mtbb_test_glob).each do |f|
69
+ require f
70
+ end
71
+
72
+ unless argv.include?('-h') || argv.include?('--help')
73
+ ServerControl.start_servers
74
+ Mtbb.announce! 'Started servers'
75
+ at_exit do
76
+ ServerControl.stop_servers
77
+ Mtbb.announce! 'Stopped servers'
78
+ end
79
+
80
+ MiniTest::Unit.output = MiniTestReporter.new
81
+ end
82
+
83
+ exit_code = MiniTest::Unit.new.run(argv) || 1
84
+
85
+ if exit_code == 0
86
+ $stderr.puts BRIGHT_GREEN
87
+ $stderr.puts <<-EOF.gsub(/^ {8}/, '')
88
+ ✓✓ ✓✓ ✓✓✓✓ ✓✓ ✓✓
89
+ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓✓ ✓✓
90
+ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓✓✓ ✓✓
91
+ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓
92
+ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓✓✓
93
+ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓ ✓✓✓
94
+ ✓✓✓ ✓✓✓ ✓✓✓✓ ✓✓ ✓✓
95
+ EOF
96
+ $stderr.puts RESET
97
+ else
98
+ $stderr.puts BRIGHT_RED
99
+ $stderr.puts <<-EOF.gsub(/^ {8}/, '')
100
+ ✘✘✘✘✘✘✘✘ ✘✘✘ ✘✘✘✘ ✘✘
101
+ ✘✘ ✘✘ ✘✘ ✘✘ ✘✘
102
+ ✘✘ ✘✘ ✘✘ ✘✘ ✘✘
103
+ ✘✘✘✘✘✘ ✘✘ ✘✘ ✘✘ ✘✘
104
+ ✘✘ ✘✘✘✘✘✘✘✘✘ ✘✘ ✘✘
105
+ ✘✘ ✘✘ ✘✘ ✘✘ ✘✘
106
+ ✘✘ ✘✘ ✘✘ ✘✘✘✘ ✘✘✘✘✘✘✘✘
107
+ EOF
108
+ $stderr.puts RESET
109
+ Mtbb::SERVERS.each do |_, server|
110
+ Mtbb.announce! "Dumping stdout for #{server.description}:"
111
+ server.dump(:stdout)
112
+ Mtbb.announce! "Dumping stderr for #{server.description}:"
113
+ server.dump(:stderr)
114
+ end
115
+ end
116
+
117
+ if ENV['HOLD_ON_A_SEC']
118
+ print 'Holding on a sec... [Enter] '
119
+ gets
120
+ end
121
+ exit exit_code
122
+ end
123
+ end
124
+
125
+ def announce!(something)
126
+ $stderr.puts "#{BRIGHT_GREEN}mtbb#{RESET}: #{BRIGHT_YELLOW}#{something}#{RESET}"
127
+ end
128
+
129
+ module_function :announce!
130
+
131
+ class ServerRunner
132
+ attr_reader :executable, :server_name, :argv, :port, :working_dir, :start_time
133
+ attr_reader :server_pid, :stdout_file, :stderr_file, :server_exit_code
134
+ attr_reader :options
135
+
136
+ def initialize(options = {})
137
+ @options = options
138
+ @executable = options.fetch(:executable)
139
+ options.delete(:executable)
140
+ @argv = options.delete(:argv) || []
141
+
142
+ @server_name = options.delete(:server_name) || "mtbb-server-#{Time.now.utc.to_i}"
143
+ @start_time = options.delete(:start) || Time.now.utc
144
+ @port = options.delete(:port)
145
+ @working_dir = options.delete(:working_dir) || default_working_dir
146
+ @stdout_file = options.delete(:stdout_file) || default_stdout_file
147
+ @stderr_file = options.delete(:stderr_file) || default_stderr_file
148
+
149
+ if !File.exist?(executable)
150
+ raise "Can't locate `#{File.basename(executable).inspect}` executable! " <<
151
+ "(it's not here: #{executable.inspect})"
152
+ end
153
+ end
154
+
155
+ def start
156
+ FileUtils.mkdir_p(working_dir)
157
+ process_command = command
158
+ spawn_args = process_command + [
159
+ out: [stdout_file, 'w'], err: [stderr_file, 'w']
160
+ ]
161
+ Mtbb.announce! "Starting #{description}"
162
+ Mtbb.announce! " ---> #{process_command.join(' ')}"
163
+ Mtbb.announce! " (#{config_details.inspect})"
164
+ @server_pid = Process.spawn(*spawn_args)
165
+ server_pid
166
+ end
167
+
168
+ def stop
169
+ return if !server_pid
170
+ Mtbb.announce! "Stopping #{description} (PID=#{server_pid})"
171
+ Process.kill(:TERM, server_pid)
172
+ begin
173
+ wait_for_server_process(Integer(ENV['MTBB_PROCESS_EXIT_TIMEOUT'] || 3))
174
+ rescue TimeoutError
175
+ end
176
+ Process.kill(:KILL, server_pid)
177
+ _, status = Process.waitpid2(server_pid)
178
+ @server_exit_code = status.exitstatus || status.termsig
179
+ rescue Errno::ECHILD, Errno::ESRCH
180
+ true
181
+ end
182
+
183
+ def description
184
+ "#{server_name} on port #{port}"
185
+ end
186
+
187
+ def config_details
188
+ {
189
+ argv: argv,
190
+ working_dir: working_dir,
191
+ start_time: start_time,
192
+ stdout_file: stdout_file,
193
+ stderr_file: stderr_file,
194
+ }
195
+ end
196
+
197
+ def command
198
+ cmd = [executable] + argv
199
+ options.each do |k,v|
200
+ if v.nil?
201
+ cmd << k
202
+ next
203
+ end
204
+ cmd << k << v.inspect
205
+ end
206
+ cmd
207
+ end
208
+
209
+ def dump(which = :stdout)
210
+ $stderr.puts File.read(send(:"#{which.to_s}_file"))
211
+ end
212
+
213
+ private
214
+
215
+ def wait_for_server_process(timeout)
216
+ toolate = Time.now + timeout
217
+ unless (done = server_exited?) || Time.now > toolate
218
+ sleep Float(ENV['MTBB_PROCESS_EXIT_POLL_INTERVAL'] || 0.1)
219
+ end
220
+
221
+ unless done
222
+ raise TimeoutError
223
+ end
224
+ end
225
+
226
+ # Largely inspired by ChildProcess::Unix::Process#exited?
227
+ def server_exited?
228
+ return true if server_exit_code
229
+
230
+ pid, status = Process.waitpid2(server_pid, Process::WNOHANG)
231
+ pid = pid == 0 ? nil : pid
232
+
233
+ if pid
234
+ @server_exit_code = status.exitstatus || status.termsig
235
+ end
236
+
237
+ !!pid
238
+ end
239
+
240
+ def default_working_dir
241
+ File.join(Dir.pwd, '.mtbb-artifacts')
242
+ end
243
+
244
+ def default_stdout_file
245
+ File.join(working_dir, "#{server_name}.out")
246
+ end
247
+
248
+ def default_stderr_file
249
+ File.join(working_dir, "#{server_name}.err")
250
+ end
251
+ end
252
+
253
+ class MiniTestReporter
254
+ def puts(*args)
255
+ args.each { |arg| Mtbb.announce! arg }
256
+ end
257
+
258
+ alias print puts
259
+ end
260
+
261
+ module NetThings
262
+ def http(method_name, path, options = {})
263
+ options[:path] ||= path
264
+ send("#{method_name.downcase}_request", options)
265
+ end
266
+
267
+ def post_request(options = {})
268
+ request = Net::HTTP::Post.new(options[:path] || '/')
269
+ request.content_type = 'application/x-www-form-urlencoded'
270
+ request.body = options.fetch(:body)
271
+ perform_request(request, options.fetch(:port))
272
+ end
273
+
274
+ def get_request(options = {})
275
+ perform_request(
276
+ Net::HTTP::Get.new(options[:path] || '/'),
277
+ options.fetch(:port)
278
+ )
279
+ end
280
+
281
+ def delete_request(options = {})
282
+ perform_request(
283
+ Net::HTTP::Delete.new(options[:path] || '/'),
284
+ options.fetch(:port)
285
+ )
286
+ end
287
+
288
+ private
289
+
290
+ def perform_request(request, port)
291
+ Net::HTTP.start(ENV['MTBB_SERVER_HOST'] || 'localhost', port) do |http|
292
+ http.open_timeout = Integer(ENV['MTBB_HTTP_OPEN_TIMEOUT'] || 1)
293
+ http.read_timeout = Integer(ENV['MTBB_HTTP_READ_TIMEOUT'] || 3)
294
+ response = http.request(request)
295
+ response
296
+ end
297
+ end
298
+ end
299
+
300
+ class ServerControl
301
+ def self.stop_servers
302
+ Mtbb::SERVERS.each do |_,server|
303
+ server.stop
304
+ end
305
+ end
306
+
307
+ def self.start_servers
308
+ Dir.chdir(File.expand_path('../', __FILE__)) do
309
+ FileUtils.mkdir_p('./.mtbb-artifacts')
310
+ Mtbb::SERVERS.each do |_,runner|
311
+ runner.start
312
+ wait_for_server(runner.port)
313
+ end
314
+ end
315
+ end
316
+
317
+ def self.wait_for_server(http_port)
318
+ maxloops = 10
319
+ curloop = 0
320
+ begin
321
+ TCPSocket.new(ENV['MTBB_SERVER_HOST'] || 'localhost', http_port).close
322
+ rescue
323
+ curloop += 1
324
+ if curloop < maxloops
325
+ sleep 0.5
326
+ retry
327
+ end
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ if __FILE__ == $0
334
+ Mtbb::TestRunner.main(ARGV)
335
+ end
@@ -0,0 +1,3 @@
1
+ module Mtbb
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,25 @@
1
+ # vim:fileencoding=utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mtbb/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'mtbb'
8
+ spec.version = Mtbb::VERSION
9
+ spec.authors = ['Dan Buch']
10
+ spec.email = ['d.buch@modcloth.com']
11
+ spec.description = %q{Minitest-based black box testing thing}
12
+ spec.summary = %q{Minitest-based black box testing thing}
13
+ spec.homepage = 'https://github.com/modcloth-labs/mtbb'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler'
22
+ spec.add_development_dependency 'rack'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'thin'
25
+ end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rack'
4
+ require 'time'
5
+
6
+ class NavelApp
7
+ def call(env)
8
+ send(:"handle_#{env['REQUEST_METHOD'].downcase}", Rack::Request.new(env))
9
+ end
10
+
11
+ private
12
+
13
+ def handle_get(request)
14
+ case request.path_info
15
+ when '/affirmation'
16
+ return [200, {'Content-Type' => 'text/plain'}, ["yup\n"]]
17
+ else
18
+ handle_404(request)
19
+ end
20
+ end
21
+
22
+ def handle_post(request)
23
+ case request.path_info
24
+ when '/dinner/new'
25
+ body = request.body.read
26
+ if body.split("\n").include?('soup=splitpea')
27
+ return [201, {'Content-Type' => 'text/plain'}, ["soup is on!\n"]]
28
+ else
29
+ return [
30
+ 400,
31
+ {'Content-Type' => 'text/plain'},
32
+ ["invalid dinner:\n" << body << "\n"]
33
+ ]
34
+ end
35
+ else
36
+ handle_404(request)
37
+ end
38
+ end
39
+
40
+ def handle_delete(request)
41
+ case request.path_info
42
+ when '/pestilence'
43
+ return [204, {'Content-Type' => 'text/plain'}, ["orphanage saved!\n"]]
44
+ else
45
+ handle_404(client)
46
+ end
47
+ end
48
+
49
+ def handle_404(client)
50
+ [404, {'Content-Type' => 'text/plain'} ["nope\n"]]
51
+ end
52
+ end
53
+
54
+ def main(argv = [])
55
+ port = Integer(argv.first || 19080)
56
+ $stdout.sync = true
57
+ $stderr.sync = true
58
+ Rack::Handler::Thin.run(NavelApp.new, Port: port)
59
+ end
60
+
61
+ if __FILE__ == $0
62
+ main(ARGV)
63
+ end
@@ -0,0 +1,39 @@
1
+ require 'tmpdir'
2
+ require 'minitest/unit'
3
+
4
+ Mtbb.register(:navel,
5
+ executable: File.expand_path('../navel', __FILE__),
6
+ argv: %w(9899),
7
+ server_name: 'mtbb-navel',
8
+ port: 9899,
9
+ working_dir: Dir.mktmpdir('mtbb-navel'),
10
+ )
11
+
12
+ describe 'navel test' do
13
+ include Mtbb::NetThings
14
+
15
+ it 'supports HTTP GET' do
16
+ response = http('GET', '/affirmation', port: Mtbb.server(:navel).port)
17
+ response.code.must_equal '200'
18
+ end
19
+
20
+ it 'supports HTTP POST' do
21
+ response = http('POST', '/dinner/new',
22
+ port: Mtbb.server(:navel).port, body: "soup=splitpea\n")
23
+ response.code.must_equal '201'
24
+ end
25
+
26
+ it 'supports HTTP DELETE' do
27
+ response = http('DELETE', '/pestilence', port: Mtbb.server(:navel).port)
28
+ response.code.must_equal '204'
29
+ end
30
+ end
31
+
32
+ class NavelTest < MiniTest::Unit
33
+ include Mtbb::NetThings
34
+
35
+ def test_works_with_unit_tests_too
36
+ response = http('GET', '/nerp', port: Mtbb.server(:navel).port)
37
+ response.code.must_equal '404'
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/unit'
3
+
4
+ describe 'vanilla' do
5
+ it 'supports regular old minitest specs' do
6
+ (1 + 1).must_equal 2
7
+ end
8
+ end
9
+
10
+ class TestVanilla < MiniTest::Unit
11
+ def test_supports_regular_old_minitest_tests
12
+ (1 - 1).must_equal 0
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mtbb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dan Buch
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: thin
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Minitest-based black box testing thing
79
+ email:
80
+ - d.buch@modcloth.com
81
+ executables:
82
+ - mtbb
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - .travis.yml
88
+ - AUTHORS.md
89
+ - CONTRIBUTING.md
90
+ - Gemfile
91
+ - LICENSE.txt
92
+ - README.md
93
+ - Rakefile
94
+ - bin/mtbb
95
+ - lib/mtbb.rb
96
+ - lib/mtbb/version.rb
97
+ - mtbb.gemspec
98
+ - test/navel
99
+ - test/navel_test.rb
100
+ - test/vanilla_test.rb
101
+ homepage: https://github.com/modcloth-labs/mtbb
102
+ licenses:
103
+ - MIT
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ segments:
115
+ - 0
116
+ hash: 4294838388783584232
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ segments:
124
+ - 0
125
+ hash: 4294838388783584232
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.23
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Minitest-based black box testing thing
132
+ test_files:
133
+ - test/navel
134
+ - test/navel_test.rb
135
+ - test/vanilla_test.rb