testbot_instructure 0.7.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/CHANGELOG +264 -0
- data/Gemfile +3 -0
- data/README.markdown +141 -0
- data/Rakefile +35 -0
- data/bin/testbot +59 -0
- data/lib/generators/testbot/templates/testbot.rake.erb +35 -0
- data/lib/generators/testbot/templates/testbot.yml.erb +45 -0
- data/lib/generators/testbot/testbot_generator.rb +19 -0
- data/lib/railtie.rb +16 -0
- data/lib/requester/requester.rb +171 -0
- data/lib/runner/job.rb +112 -0
- data/lib/runner/runner.rb +222 -0
- data/lib/runner/safe_result_text.rb +29 -0
- data/lib/server/build.rb +36 -0
- data/lib/server/group.rb +48 -0
- data/lib/server/job.rb +64 -0
- data/lib/server/memory_model.rb +91 -0
- data/lib/server/runner.rb +47 -0
- data/lib/server/server.rb +103 -0
- data/lib/server/status/javascripts/jquery-1.4.4.min.js +167 -0
- data/lib/server/status/status.html +48 -0
- data/lib/server/status/stylesheets/status.css +14 -0
- data/lib/shared/adapters/adapter.rb +27 -0
- data/lib/shared/adapters/cucumber_adapter.rb +91 -0
- data/lib/shared/adapters/helpers/ruby_env.rb +47 -0
- data/lib/shared/adapters/rspec2_adapter.rb +61 -0
- data/lib/shared/adapters/rspec_adapter.rb +79 -0
- data/lib/shared/adapters/test_unit_adapter.rb +44 -0
- data/lib/shared/color.rb +16 -0
- data/lib/shared/simple_daemonize.rb +25 -0
- data/lib/shared/ssh_tunnel.rb +36 -0
- data/lib/shared/testbot.rb +132 -0
- data/lib/shared/version.rb +12 -0
- data/lib/tasks/testbot.rake +30 -0
- data/lib/testbot.rb +2 -0
- data/test/fixtures/local/Rakefile +7 -0
- data/test/fixtures/local/config/testbot.yml +5 -0
- data/test/fixtures/local/log/test.log +0 -0
- data/test/fixtures/local/script/spec +2 -0
- data/test/fixtures/local/spec/models/car_spec.rb +0 -0
- data/test/fixtures/local/spec/models/house_spec.rb +0 -0
- data/test/fixtures/local/spec/spec.opts +0 -0
- data/test/fixtures/local/tmp/restart.txt +0 -0
- data/test/integration_test.rb +55 -0
- data/test/requester/requester_test.rb +407 -0
- data/test/requester/testbot.yml +7 -0
- data/test/requester/testbot_with_erb.yml +2 -0
- data/test/runner/job_test.rb +94 -0
- data/test/runner/safe_result_text_test.rb +20 -0
- data/test/server/group_test.rb +43 -0
- data/test/server/server_test.rb +511 -0
- data/test/shared/adapters/adapter_test.rb +22 -0
- data/test/shared/adapters/cucumber_adapter_test.rb +72 -0
- data/test/shared/adapters/helpers/ruby_env_test.rb +108 -0
- data/test/shared/adapters/rspec_adapter_test.rb +109 -0
- data/test/shared/testbot_test.rb +185 -0
- data/testbot.gemspec +34 -0
- metadata +313 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
class Adapter
|
2
|
+
|
3
|
+
FILES = Dir[File.dirname(__FILE__) + "/*_adapter.rb"]
|
4
|
+
FILES.each { |file| require(file) }
|
5
|
+
|
6
|
+
def self.all
|
7
|
+
FILES.map { |file| load_adapter(file) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.find(type)
|
11
|
+
if adapter = all.find { |adapter| adapter.type == type.to_s }
|
12
|
+
adapter
|
13
|
+
else
|
14
|
+
raise "Unknown adapter: #{type}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.load_adapter(file)
|
21
|
+
eval("::" + File.basename(file).
|
22
|
+
gsub(/\.rb/, '').
|
23
|
+
gsub(/(?:^|_)(.)/) { $1.upcase })
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "/helpers/ruby_env"))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "../color"))
|
3
|
+
|
4
|
+
class CucumberAdapter
|
5
|
+
|
6
|
+
def self.command(project_path, ruby_interpreter, files)
|
7
|
+
cucumber_command = RubyEnv.ruby_command(project_path, :script => "script/cucumber", :bin => "cucumber",
|
8
|
+
:ruby_interpreter => ruby_interpreter)
|
9
|
+
"export AUTOTEST=1; #{cucumber_command} -f progress --backtrace -r features/support -r features/step_definitions #{files} -t ~@disabled"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.test_files(dir)
|
13
|
+
Dir["#{dir}/#{file_pattern}"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.get_sizes(files)
|
17
|
+
files.map { |file| File.stat(file).size }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.requester_port
|
21
|
+
2230
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.pluralized
|
25
|
+
'features'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.base_path
|
29
|
+
pluralized
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.name
|
33
|
+
'Cucumber'
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.type
|
37
|
+
pluralized
|
38
|
+
end
|
39
|
+
|
40
|
+
# This is an optional method. It gets passed the entire test result and summarizes it. See the tests.
|
41
|
+
def self.sum_results(text)
|
42
|
+
scenarios, steps = parse_scenarios_and_steps(text)
|
43
|
+
|
44
|
+
scenarios_line = "#{scenarios[:total]} scenarios (" + [
|
45
|
+
(Color.colorize("#{scenarios[:failed]} failed", :red) if scenarios[:failed] > 0),
|
46
|
+
(Color.colorize("#{scenarios[:undefined]} undefined", :orange) if scenarios[:undefined] > 0),
|
47
|
+
(Color.colorize("#{scenarios[:passed]} passed", :green) if scenarios[:passed] > 0)
|
48
|
+
].compact.join(', ') + ")"
|
49
|
+
|
50
|
+
steps_line = "#{steps[:total]} steps (" + [
|
51
|
+
(Color.colorize("#{steps[:failed]} failed", :red) if steps[:failed] > 0),
|
52
|
+
(Color.colorize("#{steps[:skipped]} skipped", :cyan) if steps[:skipped] > 0),
|
53
|
+
(Color.colorize("#{steps[:undefined]} undefined", :orange) if steps[:undefined] > 0),
|
54
|
+
(Color.colorize("#{steps[:passed]} passed", :green) if steps[:passed] > 0)
|
55
|
+
].compact.join(', ') + ")"
|
56
|
+
|
57
|
+
scenarios_line + "\n" + steps_line
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def self.parse_scenarios_and_steps(text)
|
63
|
+
results = {
|
64
|
+
:scenarios => { :total => 0, :passed => 0, :failed => 0, :undefined => 0 },
|
65
|
+
:steps => { :total => 0, :passed => 0, :failed => 0, :skipped => 0, :undefined => 0 }
|
66
|
+
}
|
67
|
+
|
68
|
+
Color.strip(text).split("\n").each do |line|
|
69
|
+
type = line.include?("scenarios") ? :scenarios : :steps
|
70
|
+
|
71
|
+
if match = line.match(/\((.+)\)/)
|
72
|
+
results[type][:total] += line.split.first.to_i
|
73
|
+
parse_status_counts(results[type], match[1])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
[ results[:scenarios], results[:steps] ]
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.parse_status_counts(results, status_counts)
|
81
|
+
status_counts.split(', ').each do |part|
|
82
|
+
results.keys.each do |key|
|
83
|
+
results[key] += part.split.first.to_i if part.include?(key.to_s)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.file_pattern
|
89
|
+
'**/**/*.feature'
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class RubyEnv
|
2
|
+
def self.bundler?(project_path)
|
3
|
+
gem_exists?("bundler") && File.exists?("#{project_path}/Gemfile")
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.gem_exists?(gem)
|
7
|
+
if Gem::Specification.respond_to?(:find_by_name)
|
8
|
+
Gem::Specification.find_by_name(gem)
|
9
|
+
else
|
10
|
+
# older depricated method
|
11
|
+
Gem.available?(gem)
|
12
|
+
end
|
13
|
+
rescue Gem::LoadError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.ruby_command(project_path, opts = {})
|
18
|
+
ruby_interpreter = opts[:ruby_interpreter] || "ruby"
|
19
|
+
|
20
|
+
if opts[:script] && File.exists?("#{project_path}/#{opts[:script]}")
|
21
|
+
command = opts[:script]
|
22
|
+
elsif opts[:bin]
|
23
|
+
command = opts[:bin]
|
24
|
+
else
|
25
|
+
command = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
if bundler?(project_path)
|
29
|
+
"#{rvm_prefix(project_path)} #{ruby_interpreter} -S bundle exec #{command}".strip
|
30
|
+
else
|
31
|
+
"#{rvm_prefix(project_path)} #{ruby_interpreter} -S #{command}".strip
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.rvm_prefix(project_path)
|
36
|
+
if rvm?
|
37
|
+
rvmrc_path = File.join project_path, ".rvmrc"
|
38
|
+
if File.exists?(rvmrc_path)
|
39
|
+
File.read(rvmrc_path).to_s.strip + " exec"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.rvm?
|
45
|
+
system("rvm info") != nil
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "/helpers/ruby_env"))
|
2
|
+
|
3
|
+
class Rspec2Adapter
|
4
|
+
|
5
|
+
def self.command(project_path, ruby_interpreter, files)
|
6
|
+
spec_command = RubyEnv.ruby_command(project_path,
|
7
|
+
:bin => "rspec",
|
8
|
+
:ruby_interpreter => ruby_interpreter)
|
9
|
+
|
10
|
+
if File.exists?("#{project_path}/spec/spec.opts")
|
11
|
+
spec_command += " -O spec/spec.opts"
|
12
|
+
end
|
13
|
+
|
14
|
+
"export RSPEC_COLOR=true; #{spec_command} #{files}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.test_files(dir)
|
18
|
+
if ENV['SELENIUM_SPECS']
|
19
|
+
puts 'running selenium specs'
|
20
|
+
test_files = FileList['spec/selenium/**/*_spec.rb'] + FileList['vendor/plugins/*/spec_canvas/selenium/*_spec.rb']
|
21
|
+
elsif ENV['PLUGIN_SPECS']
|
22
|
+
puts 'running plugin specs'
|
23
|
+
test_files = FileList['vendor/plugins/*/spec_canvas/**/*_spec.rb'].exclude('vendor/plugins/*/spec_canvas/selenium/*_spec.rb') + FileList['spec/**/*_spec.rb'].exclude('spec/selenium/**/*_spec.rb')
|
24
|
+
:ew3else
|
25
|
+
puts 'normal pattern'
|
26
|
+
test_files = Dir["#{dir}/#{file_pattern}"]
|
27
|
+
end
|
28
|
+
test_files
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_sizes(files)
|
32
|
+
files.map { |file| File.stat(file).size }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.requester_port
|
36
|
+
2299
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.pluralized
|
40
|
+
'specs'
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.base_path
|
44
|
+
"spec"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.name
|
48
|
+
'RSpec2'
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.type
|
52
|
+
'rspec'
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def self.file_pattern
|
58
|
+
'**/**/*_spec.rb'
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "/helpers/ruby_env"))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "../color"))
|
3
|
+
|
4
|
+
class RspecAdapter
|
5
|
+
|
6
|
+
def self.command(project_path, ruby_interpreter, files)
|
7
|
+
spec_command = RubyEnv.ruby_command(project_path, :script => "script/spec", :bin => "rspec",
|
8
|
+
:ruby_interpreter => ruby_interpreter)
|
9
|
+
if File.exists?("#{project_path}/spec/spec.opts")
|
10
|
+
spec_command += " -O spec/spec.opts"
|
11
|
+
end
|
12
|
+
|
13
|
+
"export RSPEC_COLOR=true; #{spec_command} #{files}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.test_files(dir)
|
17
|
+
Dir["#{dir}/#{file_pattern}"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.get_sizes(files)
|
21
|
+
files.map { |file| File.stat(file).size }
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.requester_port
|
25
|
+
2299
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.pluralized
|
29
|
+
'specs'
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.base_path
|
33
|
+
type
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.name
|
37
|
+
'RSpec'
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.type
|
41
|
+
'spec'
|
42
|
+
end
|
43
|
+
|
44
|
+
# This is an optional method. It gets passed the entire test result and summarizes it. See the tests.
|
45
|
+
def self.sum_results(results)
|
46
|
+
examples, failures, pending = 0, 0, 0
|
47
|
+
results.split("\n").each do |line|
|
48
|
+
line =~ /(\d+) examples?, (\d+) failures?(, (\d+) pending)?/
|
49
|
+
next unless $1
|
50
|
+
examples += $1.to_i
|
51
|
+
failures += $2.to_i
|
52
|
+
pending += $4.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
result = [ pluralize(examples, 'example'), pluralize(failures, 'failure'), (pending > 0 ? "#{pending} pending" : nil) ].compact.join(', ')
|
56
|
+
if failures == 0 && pending == 0
|
57
|
+
Color.colorize(result, :green)
|
58
|
+
elsif failures == 0 && pending > 0
|
59
|
+
Color.colorize(result, :orange)
|
60
|
+
else
|
61
|
+
Color.colorize(result, :red)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def self.pluralize(count, singular)
|
68
|
+
if count == 1
|
69
|
+
"#{count} #{singular}"
|
70
|
+
else
|
71
|
+
"#{count} #{singular}s"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.file_pattern
|
76
|
+
'**/**/*_spec.rb'
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "/helpers/ruby_env"))
|
2
|
+
|
3
|
+
class TestUnitAdapter
|
4
|
+
|
5
|
+
def self.command(project_path, ruby_interpreter, files)
|
6
|
+
ruby_command = RubyEnv.ruby_command(project_path, :ruby_interpreter => ruby_interpreter)
|
7
|
+
%{#{ruby_command} -Itest -e '%w(#{files}).each { |file| require(Dir.pwd + "/" + file) }'}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.test_files(dir)
|
11
|
+
Dir["#{dir}/#{file_pattern}"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_sizes(files)
|
15
|
+
files.map { |file| File.stat(file).size }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.requester_port
|
19
|
+
2231
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.pluralized
|
23
|
+
'tests'
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.base_path
|
27
|
+
type
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.name
|
31
|
+
'Test::Unit'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.type
|
35
|
+
'test'
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def self.file_pattern
|
41
|
+
'**/**/*_test.rb'
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/lib/shared/color.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Color
|
2
|
+
def self.colorize(text, color)
|
3
|
+
colors = { :green => 32, :orange => 33, :red => 31, :cyan => 36 }
|
4
|
+
|
5
|
+
if colors[color]
|
6
|
+
"\033[#{colors[color]}m#{text}\033[0m"
|
7
|
+
else
|
8
|
+
raise "Color not implemented: #{color}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.strip(text)
|
13
|
+
text.gsub(/\e.+?m/, '')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'daemons'
|
3
|
+
|
4
|
+
class SimpleDaemonize
|
5
|
+
|
6
|
+
def self.start(proc, pid_path, app_name)
|
7
|
+
working_dir = Dir.pwd
|
8
|
+
|
9
|
+
group = Daemons::ApplicationGroup.new(app_name)
|
10
|
+
group.new_application(:mode => :none).start
|
11
|
+
|
12
|
+
File.open(pid_path, 'w') { |file| file.write(Process.pid) }
|
13
|
+
Dir.chdir(working_dir)
|
14
|
+
proc.call
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.stop(pid_path)
|
18
|
+
return unless File.exists?(pid_path)
|
19
|
+
pid = File.read(pid_path)
|
20
|
+
|
21
|
+
system "kill -9 #{pid} &> /dev/null"
|
22
|
+
system "rm #{pid_path} &> /dev/null"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'net/ssh'
|
3
|
+
|
4
|
+
class SSHTunnel
|
5
|
+
|
6
|
+
def initialize(host, user, local_port = 2288)
|
7
|
+
@host, @user, @local_port = host, user, local_port
|
8
|
+
end
|
9
|
+
|
10
|
+
def open
|
11
|
+
connect
|
12
|
+
|
13
|
+
start_time = Time.now
|
14
|
+
while true
|
15
|
+
break if @up
|
16
|
+
sleep 0.5
|
17
|
+
|
18
|
+
if Time.now - start_time > 5
|
19
|
+
puts "SSH connection failed, trying again..."
|
20
|
+
start_time = Time.now
|
21
|
+
connect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect
|
27
|
+
@thread.kill! if @thread
|
28
|
+
@thread = Thread.new do
|
29
|
+
Net::SSH.start(@host, @user, { :timeout => 1 }) do |ssh|
|
30
|
+
ssh.forward.local(@local_port, 'localhost', Testbot::SERVER_PORT)
|
31
|
+
ssh.loop { @up = true }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/version'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/simple_daemonize'))
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/adapters/adapter'))
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Testbot
|
7
|
+
require 'railtie' if defined?(Rails)
|
8
|
+
|
9
|
+
if ENV['INTEGRATION_TEST']
|
10
|
+
SERVER_PID = "/tmp/integration_test_testbot_server.pid"
|
11
|
+
RUNNER_PID = "/tmp/integration_test_testbot_runner.pid"
|
12
|
+
else
|
13
|
+
SERVER_PID = "/tmp/testbot_server.pid"
|
14
|
+
RUNNER_PID = "/tmp/testbot_runner.pid"
|
15
|
+
end
|
16
|
+
|
17
|
+
DEFAULT_WORKING_DIR = "/tmp/testbot"
|
18
|
+
DEFAULT_SERVER_PATH = "/tmp/testbot/#{ENV['USER']}"
|
19
|
+
DEFAULT_USER = "testbot"
|
20
|
+
DEFAULT_PROJECT = "project"
|
21
|
+
DEFAULT_RUNNER_USAGE = "100%"
|
22
|
+
SERVER_PORT = ENV['INTEGRATION_TEST'] ? 22880 : 2288
|
23
|
+
|
24
|
+
class CLI
|
25
|
+
|
26
|
+
def self.run(argv)
|
27
|
+
return false if argv == []
|
28
|
+
opts = parse_args(argv)
|
29
|
+
|
30
|
+
if opts[:help]
|
31
|
+
return false
|
32
|
+
elsif opts[:version]
|
33
|
+
puts "Testbot #{Testbot.version}"
|
34
|
+
elsif [ true, 'run', 'start' ].include?(opts[:server])
|
35
|
+
start_server(opts[:server])
|
36
|
+
elsif opts[:server] == 'stop'
|
37
|
+
stop('server', Testbot::SERVER_PID)
|
38
|
+
elsif [ true, 'run', 'start' ].include?(opts[:runner])
|
39
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/../runner/runner'))
|
40
|
+
return false unless valid_runner_opts?(opts)
|
41
|
+
start_runner(opts)
|
42
|
+
elsif opts[:runner] == 'stop'
|
43
|
+
stop('runner', Testbot::RUNNER_PID)
|
44
|
+
elsif adapter = Adapter.all.find { |adapter| opts[adapter.type.to_sym] }
|
45
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/../requester/requester'))
|
46
|
+
start_requester(opts, adapter)
|
47
|
+
end
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.parse_args(argv)
|
53
|
+
last_setter = nil
|
54
|
+
hash = {}
|
55
|
+
str = ''
|
56
|
+
argv.each_with_index do |arg, i|
|
57
|
+
if arg.include?('--')
|
58
|
+
str = ''
|
59
|
+
last_setter = arg.split('--').last.to_sym
|
60
|
+
hash[last_setter] = true if (i == argv.size - 1) || argv[i+1].include?('--')
|
61
|
+
else
|
62
|
+
str += ' ' + arg
|
63
|
+
hash[last_setter] = str.strip
|
64
|
+
end
|
65
|
+
end
|
66
|
+
hash
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.start_runner(opts)
|
70
|
+
stop('runner', Testbot::RUNNER_PID)
|
71
|
+
|
72
|
+
proc = lambda {
|
73
|
+
working_dir = opts[:working_dir] || Testbot::DEFAULT_WORKING_DIR
|
74
|
+
FileUtils.mkdir_p(working_dir)
|
75
|
+
Dir.chdir(working_dir)
|
76
|
+
runner = Runner::Runner.new(:server_host => opts[:connect],
|
77
|
+
:auto_update => opts[:auto_update], :max_instances => opts[:cpus],
|
78
|
+
:ssh_tunnel => opts[:ssh_tunnel], :server_user => opts[:user],
|
79
|
+
:max_jruby_instances => opts[:max_jruby_instances],
|
80
|
+
:dev_gem_root => opts[:dev_gem_root],
|
81
|
+
:wait_for_updated_gem => opts[:wait_for_updated_gem],
|
82
|
+
:jruby_opts => opts[:jruby_opts])
|
83
|
+
runner.run!
|
84
|
+
}
|
85
|
+
|
86
|
+
if opts[:runner] == 'run'
|
87
|
+
proc.call
|
88
|
+
else
|
89
|
+
puts "Testbot runner started (pid: #{Process.pid})"
|
90
|
+
SimpleDaemonize.start(proc, Testbot::RUNNER_PID, "testbot (runner)")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.start_server(type)
|
95
|
+
stop('server', Testbot::SERVER_PID)
|
96
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/../server/server'))
|
97
|
+
|
98
|
+
if type == 'run'
|
99
|
+
Sinatra::Application.run! :environment => "production"
|
100
|
+
else
|
101
|
+
puts "Testbot server started (pid: #{Process.pid})"
|
102
|
+
SimpleDaemonize.start(lambda {
|
103
|
+
Sinatra::Application.run! :environment => "production"
|
104
|
+
}, Testbot::SERVER_PID, "testbot (server)")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.stop(name, pid)
|
109
|
+
puts "Testbot #{name} stopped" if SimpleDaemonize.stop(pid)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.start_requester(opts, adapter)
|
113
|
+
requester = Requester::Requester.new(:server_host => opts[:connect],
|
114
|
+
:rsync_path => opts[:rsync_path],
|
115
|
+
:rsync_ignores => opts[:rsync_ignores].to_s,
|
116
|
+
:available_runner_usage => nil,
|
117
|
+
:project => opts[:project],
|
118
|
+
:ssh_tunnel => opts[:ssh_tunnel], :server_user => opts[:user])
|
119
|
+
requester.run_tests(adapter, adapter.base_path)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.valid_runner_opts?(opts)
|
123
|
+
opts[:connect].is_a?(String)
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.lib_path
|
127
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Testbot
|
2
|
+
# Don't forget to update readme and changelog
|
3
|
+
def self.version
|
4
|
+
version = "0.7.8"
|
5
|
+
dev_version_file = File.join(File.dirname(__FILE__), '..', '..', 'DEV_VERSION')
|
6
|
+
if File.exists?(dev_version_file)
|
7
|
+
version += File.read(dev_version_file)
|
8
|
+
end
|
9
|
+
version
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../shared/adapters/adapter'
|
2
|
+
|
3
|
+
namespace :testbot do
|
4
|
+
|
5
|
+
def run_and_show_results(adapter, custom_path)
|
6
|
+
'testbot:before_request'.tap { |t| Rake::Task.task_defined?(t) && Rake::Task[t].invoke }
|
7
|
+
|
8
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'requester', 'requester.rb'))
|
9
|
+
requester = Testbot::Requester::Requester.create_by_config("#{Rails.root}/config/testbot.yml")
|
10
|
+
|
11
|
+
puts "Running #{adapter.pluralized}..."
|
12
|
+
start_time = Time.now
|
13
|
+
|
14
|
+
path = custom_path ? "#{adapter.base_path}/#{custom_path}" : adapter.base_path
|
15
|
+
success = requester.run_tests(adapter, path)
|
16
|
+
|
17
|
+
puts
|
18
|
+
puts "Finished in #{Time.now - start_time} seconds."
|
19
|
+
success
|
20
|
+
end
|
21
|
+
|
22
|
+
Adapter.all.each do |adapter|
|
23
|
+
|
24
|
+
desc "Run the #{adapter.name} tests using testbot"
|
25
|
+
task adapter.type, :custom_path do |_, args|
|
26
|
+
exit 1 unless run_and_show_results(adapter, args[:custom_path])
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/testbot.rb
ADDED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|