testbot 0.6.5 → 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/README.markdown +1 -2
- data/lib/requester/requester.rb +10 -2
- data/lib/runner/job.rb +33 -10
- data/lib/runner/runner.rb +10 -4
- data/lib/shared/adapters/helpers/ruby_env.rb +1 -1
- data/lib/shared/version.rb +1 -1
- data/lib/tasks/testbot.rake +1 -1
- data/test/requester/requester_test.rb +3 -2
- data/test/runner/runner_test.rb +29 -1
- data/test/shared/adapters/helpers/ruby_env_test.rb +3 -4
- data/testbot.gemspec +2 -0
- metadata +35 -19
data/CHANGELOG
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.6.6
|
2
|
+
|
3
|
+
The runner will now run config/testbot/before_run.rb if it exists.
|
4
|
+
|
5
|
+
Replaced the old process code with the posix-spawn gem's methods. This fixed
|
6
|
+
process shutdown in my current setup. Hopefully more stable overall.
|
7
|
+
|
8
|
+
The requester will now request that the build is stopped when exiting early (like when killed),
|
9
|
+
and not only on ctrl+c (SIGINT).
|
10
|
+
|
11
|
+
Most of this was based on a fork by https://github.com/Skalar.
|
12
|
+
|
1
13
|
0.6.5
|
2
14
|
|
3
15
|
Fixed test unit require path so that it works with ruby 1.9.
|
data/README.markdown
CHANGED
@@ -67,7 +67,7 @@ Using testbot with Rails 2:
|
|
67
67
|
|
68
68
|
# Add testbot to your Gemfile if you use bundler. You also need the plugin because
|
69
69
|
# Rails 2 does not load raketasks from gems.
|
70
|
-
ruby script/plugin install git://github.com/joakimk/testbot.git -r 'refs/tags/v0.6.
|
70
|
+
ruby script/plugin install git://github.com/joakimk/testbot.git -r 'refs/tags/v0.6.6'
|
71
71
|
script/generate testbot --connect 192.168.0.100
|
72
72
|
|
73
73
|
rake testbot:spec (or :rspec, :test, :features)
|
@@ -140,4 +140,3 @@ More
|
|
140
140
|
----
|
141
141
|
|
142
142
|
* Check the [wiki](http://github.com/joakimk/testbot/wiki) for more info.
|
143
|
-
* Chat: [https://convore.com/github/testbot](https://convore.com/github/testbot)
|
data/lib/requester/requester.rb
CHANGED
@@ -43,7 +43,8 @@ module Testbot::Requester
|
|
43
43
|
|
44
44
|
log "Syncing files" do
|
45
45
|
rsync_ignores = config.rsync_ignores.to_s.split.map { |pattern| "--exclude='#{pattern}'" }.join(' ')
|
46
|
-
|
46
|
+
# todo: exit when this fails
|
47
|
+
system "rsync -az --delete --delete-excluded -e ssh #{rsync_ignores} . #{rsync_uri}"
|
47
48
|
end
|
48
49
|
|
49
50
|
files = adapter.test_files(dir)
|
@@ -70,7 +71,13 @@ module Testbot::Requester
|
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
|
-
|
74
|
+
at_exit do
|
75
|
+
unless ENV['IN_TEST'] || @done
|
76
|
+
log "Notifying server we want to stop the run" do
|
77
|
+
HTTParty.delete("#{server_uri}/builds/#{build_id}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
74
81
|
|
75
82
|
puts if config.logging
|
76
83
|
|
@@ -114,6 +121,7 @@ module Testbot::Requester
|
|
114
121
|
puts "\n" + adapter.sum_results(@build['results'])
|
115
122
|
end
|
116
123
|
|
124
|
+
@done = true
|
117
125
|
@build["success"]
|
118
126
|
end
|
119
127
|
|
data/lib/runner/job.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), 'runner.rb'))
|
2
2
|
require File.expand_path(File.join(File.dirname(__FILE__), 'safe_result_text.rb'))
|
3
|
+
require 'posix/spawn'
|
3
4
|
|
4
5
|
module Testbot::Runner
|
5
6
|
class Job
|
@@ -8,6 +9,7 @@ module Testbot::Runner
|
|
8
9
|
def initialize(runner, id, build_id, project, root, type, ruby_interpreter, files)
|
9
10
|
@runner, @id, @build_id, @project, @root, @type, @ruby_interpreter, @files =
|
10
11
|
runner, id, build_id, project, root, type, ruby_interpreter, files
|
12
|
+
@success = true
|
11
13
|
end
|
12
14
|
|
13
15
|
def jruby?
|
@@ -31,15 +33,19 @@ module Testbot::Runner
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def kill!(build_id)
|
34
|
-
if @build_id == build_id && @
|
35
|
-
|
36
|
-
system("pkill -KILL -P #{@test_process.pid}")
|
36
|
+
if @build_id == build_id && @pid
|
37
|
+
kill_processes
|
37
38
|
@killed = true
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
private
|
42
43
|
|
44
|
+
def kill_processes
|
45
|
+
# Kill process and its children (processes in the same group)
|
46
|
+
Process.kill('KILL', -@pid) rescue :failed_to_kill_process
|
47
|
+
end
|
48
|
+
|
43
49
|
def status
|
44
50
|
success? ? "successful" : "failed"
|
45
51
|
end
|
@@ -55,23 +61,40 @@ module Testbot::Runner
|
|
55
61
|
end
|
56
62
|
|
57
63
|
def run_and_return_result(command)
|
58
|
-
|
64
|
+
read_pipe = spawn_process(command)
|
65
|
+
|
59
66
|
output = ""
|
60
|
-
|
61
|
-
while char =
|
67
|
+
last_post_time = Time.now
|
68
|
+
while char = read_pipe.getc
|
62
69
|
char = (char.is_a?(Fixnum) ? char.chr : char) # 1.8 <-> 1.9
|
63
70
|
output << char
|
64
|
-
if Time.now -
|
71
|
+
if Time.now - last_post_time > 0.5
|
65
72
|
post_results(output)
|
66
|
-
|
73
|
+
last_post_time = Time.now
|
67
74
|
end
|
68
75
|
end
|
69
|
-
|
76
|
+
|
77
|
+
# Kill child processes, if any
|
78
|
+
kill_processes
|
79
|
+
|
70
80
|
output
|
71
81
|
end
|
72
82
|
|
83
|
+
def spawn_process(command)
|
84
|
+
read_pipe, write_pipe = IO.pipe
|
85
|
+
@pid = POSIX::Spawn::spawn(command, :err => write_pipe, :out => write_pipe, :pgroup => true)
|
86
|
+
|
87
|
+
Thread.new do
|
88
|
+
Process.waitpid(@pid)
|
89
|
+
@success = ($?.exitstatus == 0)
|
90
|
+
write_pipe.close
|
91
|
+
end
|
92
|
+
|
93
|
+
read_pipe
|
94
|
+
end
|
95
|
+
|
73
96
|
def success?
|
74
|
-
|
97
|
+
@success
|
75
98
|
end
|
76
99
|
|
77
100
|
def ruby_cmd
|
data/lib/runner/runner.rb
CHANGED
@@ -104,7 +104,7 @@ module Testbot::Runner
|
|
104
104
|
job = Job.new(*([ self, next_job.split(',') ].flatten))
|
105
105
|
if first_job_from_build?
|
106
106
|
fetch_code(job)
|
107
|
-
before_run(job)
|
107
|
+
before_run(job)
|
108
108
|
end
|
109
109
|
|
110
110
|
@last_build_id = job.build_id
|
@@ -123,12 +123,18 @@ module Testbot::Runner
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def fetch_code(job)
|
126
|
-
system "rsync -az --delete -e ssh #{job.root}/ #{job.project}"
|
126
|
+
system "rsync -az --delete --delete-excluded -e ssh #{job.root}/ #{job.project}"
|
127
127
|
end
|
128
128
|
|
129
129
|
def before_run(job)
|
130
|
-
|
131
|
-
|
130
|
+
using_bundler = RubyEnv.bundler?(job.project)
|
131
|
+
bundler_cmd = using_bundler ? "bundle exec" : ""
|
132
|
+
command_prefix = "cd #{job.project}; RAILS_ENV=test TEST_INSTANCES=#{@config.max_instances} #{bundler_cmd}"
|
133
|
+
|
134
|
+
# todo: exit if this fails, report back, etc.
|
135
|
+
system("cd #{job.project}; bundle") if using_bundler
|
136
|
+
system "#{command_prefix} rake testbot:before_run" if File.exists?("#{job.project}/lib/tasks/testbot.rake")
|
137
|
+
system "#{command_prefix} ruby config/testbot/before_run.rb" if File.exists?("#{job.project}/config/testbot/before_run.rb")
|
132
138
|
end
|
133
139
|
|
134
140
|
def first_job_from_build?
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class RubyEnv
|
2
2
|
|
3
3
|
def self.bundler?(project_path)
|
4
|
-
Gem.
|
4
|
+
Gem::Specification.find_by_name("bundler") && File.exists?("#{project_path}/Gemfile") rescue false
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.ruby_command(project_path, opts = {})
|
data/lib/shared/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Testbot
|
2
2
|
# Don't forget to update readme and changelog
|
3
3
|
def self.version
|
4
|
-
version = "0.6.
|
4
|
+
version = "0.6.6"
|
5
5
|
dev_version_file = File.join(File.dirname(__FILE__), '..', '..', 'DEV_VERSION')
|
6
6
|
if File.exists?(dev_version_file)
|
7
7
|
version += File.read(dev_version_file)
|
data/lib/tasks/testbot.rake
CHANGED
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../shared/adapters/adapter'
|
|
3
3
|
namespace :testbot do
|
4
4
|
|
5
5
|
def run_and_show_results(adapter, custom_path)
|
6
|
-
Rake::Task[
|
6
|
+
'testbot:before_request'.tap { |t| Rake::Task.task_defined?(t) && Rake::Task[t].invoke }
|
7
7
|
|
8
8
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'requester', 'requester.rb'))
|
9
9
|
requester = Testbot::Requester::Requester.create_by_config("#{Rails.root}/config/testbot.yml")
|
@@ -41,6 +41,7 @@ module Testbot::Requester
|
|
41
41
|
|
42
42
|
def setup
|
43
43
|
ENV['USE_JRUBY'] = nil
|
44
|
+
ENV['IN_TEST'] = 'true'
|
44
45
|
end
|
45
46
|
|
46
47
|
def mock_file_sizes
|
@@ -233,7 +234,7 @@ module Testbot::Requester
|
|
233
234
|
flexmock(HTTParty).should_receive(:get).once.with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
|
234
235
|
:format => :json).and_return({ "done" => true, "results" => "" })
|
235
236
|
|
236
|
-
flexmock(requester).should_receive('system').with("rsync -az --delete -e ssh --exclude='.git' --exclude='tmp' . testbot@192.168.1.100:/path")
|
237
|
+
flexmock(requester).should_receive('system').with("rsync -az --delete --delete-excluded -e ssh --exclude='.git' --exclude='tmp' . testbot@192.168.1.100:/path")
|
237
238
|
mock_file_sizes
|
238
239
|
|
239
240
|
requester.run_tests(RspecAdapter, 'spec')
|
@@ -336,7 +337,7 @@ module Testbot::Requester
|
|
336
337
|
flexmock(requester).should_receive(:print)
|
337
338
|
flexmock(requester).should_receive(:puts)
|
338
339
|
|
339
|
-
flexmock(requester).should_receive('system').with("rsync -az --delete -e ssh . cruise@somewhere:/tmp/testbot/foo")
|
340
|
+
flexmock(requester).should_receive('system').with("rsync -az --delete --delete-excluded -e ssh . cruise@somewhere:/tmp/testbot/foo")
|
340
341
|
mock_file_sizes
|
341
342
|
|
342
343
|
requester.run_tests(RspecAdapter, 'spec')
|
data/test/runner/runner_test.rb
CHANGED
@@ -12,10 +12,38 @@ module Testbot::Runner
|
|
12
12
|
flexmock(RubyEnv).should_receive(:bundler?).with("/path").returns(true)
|
13
13
|
|
14
14
|
runner = Runner.new({:max_instances => 1})
|
15
|
+
flexmock(File).should_receive(:exists?).with("/path/lib/tasks/testbot.rake").and_return(true)
|
16
|
+
flexmock(File).should_receive(:exists?).with("/path/config/testbot/before_run.rb").and_return(false)
|
15
17
|
flexmock(runner)
|
16
|
-
flexmock(runner).should_receive(:system).with("
|
18
|
+
flexmock(runner).should_receive(:system).with("cd /path; bundle").once
|
19
|
+
flexmock(runner).should_receive(:system).with("cd /path; RAILS_ENV=test TEST_INSTANCES=1 bundle exec rake testbot:before_run").once
|
17
20
|
runner.send(:before_run, job)
|
18
21
|
end
|
22
|
+
|
23
|
+
should "be able to use a plain ruby before_run file" do
|
24
|
+
job = flexmock(:job, :project => "/path")
|
25
|
+
flexmock(RubyEnv).should_receive(:bundler?).with("/path").returns(true)
|
26
|
+
|
27
|
+
runner = Runner.new({:max_instances => 1})
|
28
|
+
flexmock(File).should_receive(:exists?).with("/path/lib/tasks/testbot.rake").and_return(false)
|
29
|
+
flexmock(File).should_receive(:exists?).with("/path/config/testbot/before_run.rb").and_return(true)
|
30
|
+
flexmock(runner)
|
31
|
+
flexmock(runner).should_receive(:system).with("cd /path; bundle").once
|
32
|
+
flexmock(runner).should_receive(:system).with("cd /path; RAILS_ENV=test TEST_INSTANCES=1 bundle exec ruby config/testbot/before_run.rb").once
|
33
|
+
runner.send(:before_run, job)
|
34
|
+
end
|
35
|
+
|
36
|
+
should "be able to run without bundler" do
|
37
|
+
job = flexmock(:job, :project => "/path")
|
38
|
+
flexmock(RubyEnv).should_receive(:bundler?).with("/path").returns(false)
|
39
|
+
|
40
|
+
runner = Runner.new({:max_instances => 1})
|
41
|
+
flexmock(File).should_receive(:exists?).with("/path/lib/tasks/testbot.rake").and_return(false)
|
42
|
+
flexmock(File).should_receive(:exists?).with("/path/config/testbot/before_run.rb").and_return(true)
|
43
|
+
flexmock(runner)
|
44
|
+
flexmock(runner).should_receive(:system).with("cd /path; RAILS_ENV=test TEST_INSTANCES=1 ruby config/testbot/before_run.rb").once
|
45
|
+
runner.send(:before_run, job)
|
46
|
+
end
|
19
47
|
end
|
20
48
|
end
|
21
49
|
|
@@ -8,19 +8,19 @@ class RubyEnvTest < Test::Unit::TestCase
|
|
8
8
|
context "self.bundler?" do
|
9
9
|
|
10
10
|
should "return true if bundler is installed and there is a Gemfile" do
|
11
|
-
flexmock(Gem).should_receive(:
|
11
|
+
flexmock(Gem::Specification).should_receive(:find_by_name).with("bundler").once.and_return(true)
|
12
12
|
flexmock(File).should_receive(:exists?).with("path/to/project/Gemfile").once.and_return(true)
|
13
13
|
assert_equal true, RubyEnv.bundler?("path/to/project")
|
14
14
|
end
|
15
15
|
|
16
16
|
should "return false if bundler is installed but there is no Gemfile" do
|
17
|
-
flexmock(Gem).should_receive(:
|
17
|
+
flexmock(Gem::Specification).should_receive(:find_by_name).with("bundler").once.and_return(true)
|
18
18
|
flexmock(File).should_receive(:exists?).and_return(false)
|
19
19
|
assert_equal false, RubyEnv.bundler?("path/to/project")
|
20
20
|
end
|
21
21
|
|
22
22
|
should "return false if bundler is not installed" do
|
23
|
-
flexmock(Gem).should_receive(:
|
23
|
+
flexmock(Gem::Specification).should_receive(:find_by_name).with("bundler").once.and_return(false)
|
24
24
|
assert_equal false, RubyEnv.bundler?("path/to/project")
|
25
25
|
end
|
26
26
|
|
@@ -53,7 +53,6 @@ class RubyEnvTest < Test::Unit::TestCase
|
|
53
53
|
end
|
54
54
|
|
55
55
|
should "not look for a script when none is provided" do
|
56
|
-
flexmock(File).should_receive(:exists?).once # Once for bundler check
|
57
56
|
assert_equal 'ruby -S rspec', RubyEnv.ruby_command("path/to/project", :bin => "rspec")
|
58
57
|
end
|
59
58
|
|
data/testbot.gemspec
CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.executables = [ "testbot" ]
|
14
14
|
s.files = Dir.glob("lib/**/*") + Dir.glob("test/**/*") + %w(Gemfile .gemtest Rakefile testbot.gemspec CHANGELOG README.markdown bin/testbot) +
|
15
15
|
(File.exists?("DEV_VERSION") ? [ "DEV_VERSION" ] : [])
|
16
|
+
|
16
17
|
s.add_dependency('sinatra', '>=1.0.0')
|
17
18
|
s.add_dependency('httparty', '>= 0.6.1')
|
18
19
|
s.add_dependency('macaddr', '>= 1.0.0')
|
@@ -20,6 +21,7 @@ Gem::Specification.new do |s|
|
|
20
21
|
s.add_dependency('json_pure', '>= 1.4.6')
|
21
22
|
s.add_dependency('daemons', '>= 1.0.10')
|
22
23
|
s.add_dependency('acts_as_rails3_generator')
|
24
|
+
s.add_dependency('posix-spawn', '>= 0.3.6')
|
23
25
|
|
24
26
|
s.add_development_dependency("shoulda")
|
25
27
|
s.add_development_dependency("rack-test")
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
9
|
+
- 6
|
10
|
+
version: 0.6.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Joakim Kolsj\xC3\xB6"
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-04-
|
18
|
+
date: 2012-04-27 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -129,21 +129,23 @@ dependencies:
|
|
129
129
|
type: :runtime
|
130
130
|
version_requirements: *id007
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name:
|
132
|
+
name: posix-spawn
|
133
133
|
prerelease: false
|
134
134
|
requirement: &id008 !ruby/object:Gem::Requirement
|
135
135
|
none: false
|
136
136
|
requirements:
|
137
137
|
- - ">="
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
hash:
|
139
|
+
hash: 31
|
140
140
|
segments:
|
141
141
|
- 0
|
142
|
-
|
143
|
-
|
142
|
+
- 3
|
143
|
+
- 6
|
144
|
+
version: 0.3.6
|
145
|
+
type: :runtime
|
144
146
|
version_requirements: *id008
|
145
147
|
- !ruby/object:Gem::Dependency
|
146
|
-
name:
|
148
|
+
name: shoulda
|
147
149
|
prerelease: false
|
148
150
|
requirement: &id009 !ruby/object:Gem::Requirement
|
149
151
|
none: false
|
@@ -157,7 +159,7 @@ dependencies:
|
|
157
159
|
type: :development
|
158
160
|
version_requirements: *id009
|
159
161
|
- !ruby/object:Gem::Dependency
|
160
|
-
name:
|
162
|
+
name: rack-test
|
161
163
|
prerelease: false
|
162
164
|
requirement: &id010 !ruby/object:Gem::Requirement
|
163
165
|
none: false
|
@@ -171,7 +173,7 @@ dependencies:
|
|
171
173
|
type: :development
|
172
174
|
version_requirements: *id010
|
173
175
|
- !ruby/object:Gem::Dependency
|
174
|
-
name:
|
176
|
+
name: flexmock
|
175
177
|
prerelease: false
|
176
178
|
requirement: &id011 !ruby/object:Gem::Requirement
|
177
179
|
none: false
|
@@ -185,9 +187,23 @@ dependencies:
|
|
185
187
|
type: :development
|
186
188
|
version_requirements: *id011
|
187
189
|
- !ruby/object:Gem::Dependency
|
188
|
-
name:
|
190
|
+
name: rvm
|
189
191
|
prerelease: false
|
190
192
|
requirement: &id012 !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
hash: 3
|
198
|
+
segments:
|
199
|
+
- 0
|
200
|
+
version: "0"
|
201
|
+
type: :development
|
202
|
+
version_requirements: *id012
|
203
|
+
- !ruby/object:Gem::Dependency
|
204
|
+
name: rake
|
205
|
+
prerelease: false
|
206
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
191
207
|
none: false
|
192
208
|
requirements:
|
193
209
|
- - "="
|
@@ -199,11 +215,11 @@ dependencies:
|
|
199
215
|
- 7
|
200
216
|
version: 0.8.7
|
201
217
|
type: :development
|
202
|
-
version_requirements: *
|
218
|
+
version_requirements: *id013
|
203
219
|
- !ruby/object:Gem::Dependency
|
204
220
|
name: bundler
|
205
221
|
prerelease: false
|
206
|
-
requirement: &
|
222
|
+
requirement: &id014 !ruby/object:Gem::Requirement
|
207
223
|
none: false
|
208
224
|
requirements:
|
209
225
|
- - ">="
|
@@ -213,11 +229,11 @@ dependencies:
|
|
213
229
|
- 0
|
214
230
|
version: "0"
|
215
231
|
type: :development
|
216
|
-
version_requirements: *
|
232
|
+
version_requirements: *id014
|
217
233
|
- !ruby/object:Gem::Dependency
|
218
234
|
name: guard
|
219
235
|
prerelease: false
|
220
|
-
requirement: &
|
236
|
+
requirement: &id015 !ruby/object:Gem::Requirement
|
221
237
|
none: false
|
222
238
|
requirements:
|
223
239
|
- - ">="
|
@@ -227,11 +243,11 @@ dependencies:
|
|
227
243
|
- 0
|
228
244
|
version: "0"
|
229
245
|
type: :development
|
230
|
-
version_requirements: *
|
246
|
+
version_requirements: *id015
|
231
247
|
- !ruby/object:Gem::Dependency
|
232
248
|
name: guard-test
|
233
249
|
prerelease: false
|
234
|
-
requirement: &
|
250
|
+
requirement: &id016 !ruby/object:Gem::Requirement
|
235
251
|
none: false
|
236
252
|
requirements:
|
237
253
|
- - ">="
|
@@ -241,7 +257,7 @@ dependencies:
|
|
241
257
|
- 0
|
242
258
|
version: "0"
|
243
259
|
type: :development
|
244
|
-
version_requirements: *
|
260
|
+
version_requirements: *id016
|
245
261
|
description: Testbot is a test distribution tool that works with Rails, RSpec, RSpec2, Test::Unit and Cucumber.
|
246
262
|
email:
|
247
263
|
- joakim.kolsjo@gmail.com
|