dtr 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +10 -0
- data/LICENSE.TXT +203 -0
- data/LICENSE.txt +203 -0
- data/README +302 -0
- data/Rakefile +425 -0
- data/TODO +8 -0
- data/bin/dtr +124 -0
- data/doc/jamis.rb +591 -0
- data/install.rb +88 -0
- data/lib/dtr.rb +111 -0
- data/lib/dtr/base.rb +160 -0
- data/lib/dtr/raketasks.rb +68 -0
- data/lib/dtr/runner.rb +265 -0
- data/lib/dtr/service_provider.rb +160 -0
- data/lib/dtr/test_unit.rb +278 -0
- data/lib/dtr/test_unit_injection.rb +19 -0
- data/test/base_test.rb +31 -0
- data/test/logger_test.rb +54 -0
- data/test/scenario_tests.rb +166 -0
- data/test/test_helper.rb +17 -0
- data/test/test_unit_test.rb +25 -0
- metadata +89 -0
data/install.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'find'
|
3
|
+
require 'ftools'
|
4
|
+
|
5
|
+
include Config
|
6
|
+
|
7
|
+
$ruby = CONFIG['ruby_install_name']
|
8
|
+
|
9
|
+
##
|
10
|
+
# Install a binary file. We patch in on the way through to
|
11
|
+
# insert a #! line. If this is a Unix install, we name
|
12
|
+
# the command (for example) 'dtr' and let the shebang line
|
13
|
+
# handle running it. Under windows, we add a '.rb' extension
|
14
|
+
# and let file associations to their stuff
|
15
|
+
#
|
16
|
+
|
17
|
+
def installBIN(from, opfile)
|
18
|
+
|
19
|
+
tmp_dir = nil
|
20
|
+
for t in [".", "/tmp", "c:/temp", $bindir]
|
21
|
+
stat = File.stat(t) rescue next
|
22
|
+
if stat.directory? and stat.writable?
|
23
|
+
tmp_dir = t
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
fail "Cannot find a temporary directory" unless tmp_dir
|
29
|
+
tmp_file = File.join(tmp_dir, "_tmp")
|
30
|
+
|
31
|
+
File.open(from) do |ip|
|
32
|
+
File.open(tmp_file, "w") do |op|
|
33
|
+
ruby = File.join($realbindir, $ruby)
|
34
|
+
op.puts "#!#{ruby}"
|
35
|
+
op.write ip.read
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
opfile += ".rb" if CONFIG["target_os"] =~ /mswin/i
|
40
|
+
File::install(tmp_file, File.join($bindir, opfile), 0755, true)
|
41
|
+
File::unlink(tmp_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
$sitedir = CONFIG["sitelibdir"]
|
45
|
+
unless $sitedir
|
46
|
+
version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
47
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", version)
|
48
|
+
$sitedir = $:.find {|x| x =~ /site_ruby/}
|
49
|
+
if !$sitedir
|
50
|
+
$sitedir = File.join($libdir, "site_ruby")
|
51
|
+
elsif $sitedir !~ Regexp.quote(version)
|
52
|
+
$sitedir = File.join($sitedir, version)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
$bindir = CONFIG["bindir"]
|
57
|
+
|
58
|
+
$realbindir = $bindir
|
59
|
+
|
60
|
+
bindir = CONFIG["bindir"]
|
61
|
+
if (destdir = ENV['DESTDIR'])
|
62
|
+
$bindir = destdir + $bindir
|
63
|
+
$sitedir = destdir + $sitedir
|
64
|
+
|
65
|
+
File::makedirs($bindir)
|
66
|
+
File::makedirs($sitedir)
|
67
|
+
end
|
68
|
+
|
69
|
+
dtr_dest = File.join($sitedir, "dtr")
|
70
|
+
File::makedirs(dtr_dest, true)
|
71
|
+
File::chmod(0755, dtr_dest)
|
72
|
+
|
73
|
+
# The library files
|
74
|
+
|
75
|
+
files = Dir.chdir('lib') { Dir['**/*.rb', '**/*.rake'] }
|
76
|
+
|
77
|
+
for fn in files
|
78
|
+
fn_dir = File.dirname(fn)
|
79
|
+
target_dir = File.join($sitedir, fn_dir)
|
80
|
+
if ! File.exist?(target_dir)
|
81
|
+
File.makedirs(target_dir)
|
82
|
+
end
|
83
|
+
File::install(File.join('lib', fn), File.join($sitedir, fn), 0644, true)
|
84
|
+
end
|
85
|
+
|
86
|
+
# and the executable
|
87
|
+
|
88
|
+
installBIN("bin/dtr", "dtr")
|
data/lib/dtr.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Copyright (c) 2007-2008 Li Xiao
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
DTRVERSION="0.0.3"
|
16
|
+
DTROPTIONS = {} unless defined?(DTROPTIONS)
|
17
|
+
|
18
|
+
require 'fileutils'
|
19
|
+
|
20
|
+
module DTR
|
21
|
+
|
22
|
+
def start_server
|
23
|
+
require 'dtr/service_provider'
|
24
|
+
ServiceProvider.new.start
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_runners
|
28
|
+
launch_runners(DTROPTIONS[:names], DTROPTIONS[:setup])
|
29
|
+
end
|
30
|
+
|
31
|
+
def launch_runners(names, setup=nil)
|
32
|
+
require 'dtr/runner'
|
33
|
+
names = names || "DTR(#{Time.now})"
|
34
|
+
DTR::RunnerAgent.start(names, setup)
|
35
|
+
end
|
36
|
+
|
37
|
+
def lib_path
|
38
|
+
File.expand_path(File.dirname(__FILE__))
|
39
|
+
end
|
40
|
+
|
41
|
+
def broadcast_list=(list)
|
42
|
+
require 'dtr/service_provider'
|
43
|
+
ServiceProvider.broadcast_list = list
|
44
|
+
end
|
45
|
+
|
46
|
+
def port=(port)
|
47
|
+
require 'dtr/service_provider'
|
48
|
+
ServiceProvider.port = port
|
49
|
+
end
|
50
|
+
|
51
|
+
def runners
|
52
|
+
require 'dtr/service_provider'
|
53
|
+
ServiceProvider.new.runners
|
54
|
+
end
|
55
|
+
|
56
|
+
def monitor
|
57
|
+
require 'dtr/service_provider'
|
58
|
+
DTROPTIONS[:log_file] = 'dtr_monitor.log'
|
59
|
+
ServiceProvider.new.monitor
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop_runners_daemon_mode
|
63
|
+
with_daemon_gem do
|
64
|
+
Daemons.run_proc "dtr_runners", :ARGV => ['stop']
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def start_runners_daemon_mode
|
69
|
+
with_daemon_gem do
|
70
|
+
FileUtils.rm_rf('dtr_runners.output')
|
71
|
+
pwd = Dir.pwd
|
72
|
+
Daemons.run_proc "dtr_runners", :ARGV => ['start'], :backtrace => true do
|
73
|
+
Dir.chdir(pwd) do
|
74
|
+
start_runners
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def start_server_daemon_mode
|
81
|
+
with_daemon_gem do
|
82
|
+
Daemons.run_proc "dtr_server", :ARGV => ['start'] do
|
83
|
+
start_server
|
84
|
+
end
|
85
|
+
end
|
86
|
+
sleep(2)
|
87
|
+
end
|
88
|
+
|
89
|
+
def stop_server_daemon_mode
|
90
|
+
with_daemon_gem do
|
91
|
+
Daemons.run_proc "dtr_server", :ARGV => ['stop']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def with_daemon_gem
|
96
|
+
require "rubygems"
|
97
|
+
begin
|
98
|
+
require "daemons"
|
99
|
+
rescue LoadError
|
100
|
+
raise "The daemons gem must be installed"
|
101
|
+
end
|
102
|
+
yield
|
103
|
+
end
|
104
|
+
|
105
|
+
def clean_server
|
106
|
+
require 'dtr/service_provider'
|
107
|
+
ServiceProvider.new.clear_workspace
|
108
|
+
end
|
109
|
+
|
110
|
+
module_function :start_server, :start_runners, :launch_runners, :lib_path, :broadcast_list=, :runners, :with_daemon_gem, :start_runners_daemon_mode, :stop_runners_daemon_mode, :start_server_daemon_mode, :stop_server_daemon_mode, :monitor, :port=, :clean_server
|
111
|
+
end
|
data/lib/dtr/base.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# Copyright (c) 2007-2008 Li Xiao
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'pstore'
|
16
|
+
|
17
|
+
require 'logger'
|
18
|
+
|
19
|
+
module DTR
|
20
|
+
|
21
|
+
MESSAGE_KEY = :message
|
22
|
+
|
23
|
+
def logger
|
24
|
+
return DTROPTIONS[:logger] if DTROPTIONS[:logger]
|
25
|
+
DTROPTIONS[:logger] = if DTROPTIONS[:log_level] == Logger::DEBUG
|
26
|
+
Logger.new(STDOUT)
|
27
|
+
else
|
28
|
+
log_file = if File.exist?('log')
|
29
|
+
File.join("log", DTROPTIONS[:log_file] || 'dtr.log')
|
30
|
+
else
|
31
|
+
DTROPTIONS[:log_file] || 'dtr.log'
|
32
|
+
end
|
33
|
+
Logger.new(log_file, 1, 5*1024*1024)
|
34
|
+
end
|
35
|
+
DTROPTIONS[:logger].datetime_format = "%m-%d %H:%M:%S"
|
36
|
+
DTROPTIONS[:logger].level = DTROPTIONS[:log_level] || Logger::INFO
|
37
|
+
DTROPTIONS[:logger]
|
38
|
+
end
|
39
|
+
|
40
|
+
def debug(message=nil, &block)
|
41
|
+
output(:debug, message, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def info(message=nil, &block)
|
45
|
+
output(:info, message, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def error(message=nil, &block)
|
49
|
+
output(:error, message, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def output(level, msg=nil, &block)
|
53
|
+
logger.send(level) do
|
54
|
+
message = block_given? ? block.call : msg.to_s
|
55
|
+
EnvStore.new << [MESSAGE_KEY, "[#{Process.pid}-#{level.to_s.upcase}] #{message}"] if DTROPTIONS[:run_with_monitor]
|
56
|
+
message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def silent?
|
61
|
+
logger.level == Logger::ERROR
|
62
|
+
end
|
63
|
+
|
64
|
+
def with_monitor
|
65
|
+
DTROPTIONS[:run_with_monitor] = true
|
66
|
+
EnvStore.new[MESSAGE_KEY] = []
|
67
|
+
yield
|
68
|
+
rescue Exception => e
|
69
|
+
info {"stopping by Exception => #{e.class.name}, message => #{e.message}"}
|
70
|
+
wait_times = 0
|
71
|
+
until EnvStore.new[MESSAGE_KEY].empty? || wait_times > 14
|
72
|
+
wait_times += 1
|
73
|
+
sleep(1)
|
74
|
+
end
|
75
|
+
raise e
|
76
|
+
end
|
77
|
+
|
78
|
+
module_function :debug, :info, :error, :output, :with_monitor, :logger, :silent?
|
79
|
+
|
80
|
+
class CmdInterrupt < StandardError; end
|
81
|
+
|
82
|
+
class Cmd
|
83
|
+
def self.execute(cmd)
|
84
|
+
return true if cmd.nil? || cmd.empty?
|
85
|
+
DTR.info {"Executing: #{cmd.inspect}"}
|
86
|
+
output = %x[#{cmd} 2>&1]
|
87
|
+
DTR.info {"Execution is done, status: #{$?.exitstatus}"}
|
88
|
+
DTR.error {"#{cmd.inspect} output:\n#{output}"} if $?.exitstatus != 0
|
89
|
+
$?.exitstatus == 0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class EnvStore
|
94
|
+
|
95
|
+
FILE_NAME = '.dtr_env_pstore'
|
96
|
+
|
97
|
+
def self.destroy
|
98
|
+
File.delete(FILE_NAME) if File.exist?(FILE_NAME)
|
99
|
+
end
|
100
|
+
|
101
|
+
def [](key)
|
102
|
+
return nil unless File.exist?(FILE_NAME)
|
103
|
+
|
104
|
+
repository = PStore.new(FILE_NAME)
|
105
|
+
repository.transaction(true) do
|
106
|
+
repository[key]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def []=(key, value)
|
111
|
+
repository = PStore.new(FILE_NAME)
|
112
|
+
repository.transaction do
|
113
|
+
repository[key] = value
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def <<(key_value)
|
118
|
+
key, value = key_value
|
119
|
+
repository = PStore.new(FILE_NAME)
|
120
|
+
repository.transaction do
|
121
|
+
repository[key] = (repository[key] || []) << value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def shift(key)
|
126
|
+
repository = PStore.new(FILE_NAME)
|
127
|
+
repository.transaction do
|
128
|
+
if array = repository[key]
|
129
|
+
array.shift
|
130
|
+
repository[key] = array
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class WorkingEnv
|
137
|
+
|
138
|
+
@@current = nil
|
139
|
+
def self.refresh
|
140
|
+
@@current = self.new
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.current
|
144
|
+
@@current
|
145
|
+
end
|
146
|
+
|
147
|
+
def initialize
|
148
|
+
files = (defined?($argv_dup) ? $argv_dup : []).dup
|
149
|
+
@env = {:libs => $LOAD_PATH.dup, :files => files, :created_at => Time.now.to_s, :dtr_master_env => ENV['DTR_MASTER_ENV'], :identifier => "#{Time.now.to_s}:#{rand}:#{object_id}"}
|
150
|
+
end
|
151
|
+
|
152
|
+
def [](key)
|
153
|
+
@env[key]
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
@env.inspect
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Copyright (c) 2007-2008 Li Xiao
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "rubygems"
|
16
|
+
require 'dtr'
|
17
|
+
require 'rake/testtask'
|
18
|
+
|
19
|
+
module DTR
|
20
|
+
class MPTask < Rake::TestTask
|
21
|
+
attr_accessor :processes, :runner_options, :start_server
|
22
|
+
|
23
|
+
def define
|
24
|
+
@libs.unshift DTR.lib_path
|
25
|
+
lib_path = @libs.join(File::PATH_SEPARATOR)
|
26
|
+
|
27
|
+
desc "Run tests" + (@name==:test ? "" : " for #{@name}")
|
28
|
+
task @name do
|
29
|
+
DTR.start_server_daemon_mode if start_server?
|
30
|
+
start_runners
|
31
|
+
run_code = ''
|
32
|
+
begin
|
33
|
+
RakeFileUtils.verbose(@verbose) do
|
34
|
+
run_code = rake_loader
|
35
|
+
@ruby_opts.unshift( "-I#{lib_path}" )
|
36
|
+
@ruby_opts.unshift( "-w" ) if @warning
|
37
|
+
|
38
|
+
ruby @ruby_opts.join(" ") +
|
39
|
+
" \"#{run_code}\" " +
|
40
|
+
file_list.unshift('dtr/test_unit_injection.rb').collect { |fn| "\"#{fn}\"" }.join(' ') +
|
41
|
+
" #{option_list}"
|
42
|
+
end
|
43
|
+
ensure
|
44
|
+
DTR.stop_runners_daemon_mode rescue nil
|
45
|
+
if start_server?
|
46
|
+
DTR.stop_server_daemon_mode rescue nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def processes
|
54
|
+
@processes ? @processes.to_i : 2
|
55
|
+
end
|
56
|
+
|
57
|
+
def start_server?
|
58
|
+
defined?(@start_server) ? @start_server : true
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def start_runners
|
63
|
+
runner_names = []
|
64
|
+
self.processes.to_i.times {|i| runner_names << "runner#{i}"}
|
65
|
+
%x[dtr -r #{runner_names.join(',')} -D #{runner_options}]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/dtr/runner.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
# Copyright (c) 2007-2008 Li Xiao
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'dtr/base'
|
16
|
+
require 'dtr/service_provider'
|
17
|
+
require 'test/unit'
|
18
|
+
require 'drb'
|
19
|
+
|
20
|
+
class Test::Unit::TestCase
|
21
|
+
alias_method :add_error_without_hack, :add_error
|
22
|
+
def add_error(exception)
|
23
|
+
add_error_without_hack(DTR::RunnerRuntimeException.new(exception))
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :add_failure_without_hack, :add_failure
|
27
|
+
def add_failure(message, all_locations=caller())
|
28
|
+
add_failure_without_hack(DTR.decorate_error_message(message, 'Assertion failure'), all_locations)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module DTR
|
33
|
+
|
34
|
+
def service_provider
|
35
|
+
ServiceProvider.new
|
36
|
+
end
|
37
|
+
|
38
|
+
module_function :service_provider
|
39
|
+
|
40
|
+
class RunnerAgent
|
41
|
+
|
42
|
+
def self.start(runner_names=["Distributed Test Runner"], setup_cmd=nil)
|
43
|
+
DTR.with_monitor do
|
44
|
+
new(runner_names, setup_cmd).launch
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(runner_names, setup_cmd)
|
49
|
+
@runner_names = runner_names.is_a?(Array) ? runner_names : [runner_names.to_s]
|
50
|
+
@setup_cmd = setup_cmd || ""
|
51
|
+
@runner_pids = []
|
52
|
+
@herald = nil
|
53
|
+
@working_env_key = :working_env
|
54
|
+
@env_store = EnvStore.new
|
55
|
+
@agent_pid = Process.pid
|
56
|
+
at_exit {
|
57
|
+
if Process.pid == @agent_pid
|
58
|
+
DTR.info "*** Runner agent is stopping ***"
|
59
|
+
kill_all_runners
|
60
|
+
if @herald
|
61
|
+
Process.kill 'KILL', @herald rescue nil
|
62
|
+
DTR.info "=> Herald is killed."
|
63
|
+
end
|
64
|
+
if @heart
|
65
|
+
Process.kill 'KILL', @heart rescue nil
|
66
|
+
DTR.info "=> Heartbeat is stopped."
|
67
|
+
end
|
68
|
+
DTR.info "*** Runner agent stopped ***"
|
69
|
+
end
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def launch
|
74
|
+
DTR.info "=> Runner agent started at: #{Dir.pwd}, pid: #{Process.pid}"
|
75
|
+
@heart = drb_fork { Heart.new }
|
76
|
+
@herald = drb_fork { Herald.new @working_env_key }
|
77
|
+
working_env = {}
|
78
|
+
@env_store[@working_env_key] = nil
|
79
|
+
loop do
|
80
|
+
if @env_store[@working_env_key] && working_env[:identifier] != @env_store[@working_env_key][:identifier]
|
81
|
+
working_env = @env_store[@working_env_key]
|
82
|
+
|
83
|
+
DTR.info "=> Got new working environment created at #{working_env[:created_at]}"
|
84
|
+
|
85
|
+
kill_all_runners
|
86
|
+
ENV['DTR_MASTER_ENV'] = working_env[:dtr_master_env]
|
87
|
+
|
88
|
+
if Cmd.execute(@setup_cmd)
|
89
|
+
@runner_names.each do |name|
|
90
|
+
@runner_pids << drb_fork { Runner.start name, working_env }
|
91
|
+
end
|
92
|
+
else
|
93
|
+
DTR.info {'No runners started.'}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
sleep(2)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def kill_all_runners
|
103
|
+
unless @runner_pids.empty?
|
104
|
+
@runner_pids.each{ |pid| Process.kill 'KILL', pid rescue nil }
|
105
|
+
DTR.info "=> All runners(#{@runner_pids.join(", ")}) were killed."
|
106
|
+
@runner_pids = []
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def drb_fork
|
111
|
+
Process.fork do
|
112
|
+
at_exit {
|
113
|
+
DRb.stop_service
|
114
|
+
exit!
|
115
|
+
}
|
116
|
+
begin
|
117
|
+
yield
|
118
|
+
rescue Interrupt => e
|
119
|
+
raise e
|
120
|
+
rescue SystemExit => e
|
121
|
+
raise e
|
122
|
+
rescue Exception => e
|
123
|
+
DTR.error "Got an Exception #{e.message}:"
|
124
|
+
DTR.error e.backtrace.join("\n")
|
125
|
+
raise e
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Heart
|
132
|
+
def initialize(key=MESSAGE_KEY)
|
133
|
+
@key = key
|
134
|
+
@env_store = EnvStore.new
|
135
|
+
@provider = DTR.service_provider
|
136
|
+
beat
|
137
|
+
end
|
138
|
+
|
139
|
+
def beat
|
140
|
+
loop do
|
141
|
+
begin
|
142
|
+
if @env_store[@key].empty?
|
143
|
+
@provider.send_message('---/V---')
|
144
|
+
else
|
145
|
+
while message = @env_store[@key].first
|
146
|
+
@provider.send_message(message)
|
147
|
+
@env_store.shift(@key)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
sleep_any_way
|
151
|
+
rescue => e
|
152
|
+
DTR.info "Heart lost DTR Server(#{e.message}), going to sleep 10 sec..."
|
153
|
+
@env_store[@key] = []
|
154
|
+
sleep_any_way
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
def sleep_any_way
|
161
|
+
sleep(10)
|
162
|
+
rescue Exception
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class Herald
|
167
|
+
|
168
|
+
def initialize(key)
|
169
|
+
@key = key
|
170
|
+
@env_store = EnvStore.new
|
171
|
+
@env_store[@key] = nil
|
172
|
+
@provider = DTR.service_provider
|
173
|
+
start_off
|
174
|
+
end
|
175
|
+
|
176
|
+
def start_off
|
177
|
+
loop do
|
178
|
+
DTR.info "=> Herald starts off..."
|
179
|
+
begin
|
180
|
+
working_env = @provider.working_env
|
181
|
+
DTR.debug { "working env: #{working_env.inspect}" }
|
182
|
+
if working_env[:files].empty?
|
183
|
+
DTR.error "No test files need to load?(working env: #{working_env.inspect})"
|
184
|
+
else
|
185
|
+
@env_store[@key] = working_env if @env_store[@key].nil? || @env_store[@key][:identifier] != working_env[:identifier]
|
186
|
+
@provider.wait_until_teardown
|
187
|
+
end
|
188
|
+
|
189
|
+
sleep(2)
|
190
|
+
rescue => e
|
191
|
+
DTR.info "Herald lost DTR Server(#{e.message}), going to sleep 5 sec..."
|
192
|
+
sleep(5)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class Runner
|
199
|
+
include DRbUndumped
|
200
|
+
|
201
|
+
def self.start(name, env)
|
202
|
+
DTR.info "#{name}: Initialize working environment..."
|
203
|
+
env[:libs].select{ |lib| !$LOAD_PATH.include?(lib) && File.exists?(lib) }.each do |lib|
|
204
|
+
$LOAD_PATH << lib
|
205
|
+
DTR.debug {"#{name}: appended lib: #{lib}"}
|
206
|
+
end
|
207
|
+
DTR.info "#{name}: libs loaded"
|
208
|
+
DTR.debug {"#{name}: $LOAD_PATH: #{$LOAD_PATH.inspect}"}
|
209
|
+
|
210
|
+
env[:files].each do |f|
|
211
|
+
begin
|
212
|
+
load f unless f =~ /^-/
|
213
|
+
DTR.debug {"#{name}: loaded #{f}"}
|
214
|
+
rescue LoadError => e
|
215
|
+
DTR.error "#{name}: No such file to load -- #{f} (Environment: #{env.inspect})"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
DTR.info "#{name}: test files loaded"
|
219
|
+
|
220
|
+
@provider = DTR.service_provider
|
221
|
+
|
222
|
+
@provider.provide(self.new(@provider, name, env[:identifier]))
|
223
|
+
DTR.info "=> Runner #{name} provided"
|
224
|
+
DRb.thread.join if DRb.thread
|
225
|
+
end
|
226
|
+
|
227
|
+
attr_reader :name, :identifier
|
228
|
+
|
229
|
+
def initialize(provider, name, identifier)
|
230
|
+
Test::Unit.run = true
|
231
|
+
@name = name
|
232
|
+
@provider = provider
|
233
|
+
@identifier = identifier
|
234
|
+
@started = []
|
235
|
+
@run_finished = []
|
236
|
+
end
|
237
|
+
|
238
|
+
def run(test, result, &progress_block)
|
239
|
+
DTR.debug {"#{name}: running #{test}..."}
|
240
|
+
@started << test.name
|
241
|
+
test.run(result, &progress_block)
|
242
|
+
rescue DRb::DRbConnError => e
|
243
|
+
DTR.info{ "Rescued DRb::DRbConnError(#{e.message}), while running test: #{test.name}. The master process may be stopped." }
|
244
|
+
rescue Exception => e
|
245
|
+
DTR.error {"Unexpected exception: #{e.message}"}
|
246
|
+
DTR.error {e.backtrace.join("\n")}
|
247
|
+
result.add_error(Test::Unit::Error.new(test.name, e))
|
248
|
+
result.add_run
|
249
|
+
progress_block.call(Test::Unit::TestCase::FINISHED, test.name)
|
250
|
+
ensure
|
251
|
+
DTR.debug {"#{name}: done #{test}"}
|
252
|
+
@run_finished << test.name
|
253
|
+
@provider.provide(self)
|
254
|
+
end
|
255
|
+
|
256
|
+
def shutdown
|
257
|
+
DTR.info "#{self} is shutting down. Ran #{@started.size} tests, finished #{@run_finished.size}."
|
258
|
+
@provider.stop_service rescue exit!
|
259
|
+
end
|
260
|
+
|
261
|
+
def to_s
|
262
|
+
"Runner #{@name}"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|