xli-dtr 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -0
- data/LICENSE.TXT +203 -0
- data/README +291 -0
- data/Rakefile +373 -0
- data/TODO +8 -0
- data/bin/dtr +124 -0
- data/dtr.gemspec +33 -0
- data/install.rb +88 -0
- data/lib/dtr/base.rb +172 -0
- data/lib/dtr/raketasks.rb +69 -0
- data/lib/dtr/runner.rb +270 -0
- data/lib/dtr/service_provider.rb +160 -0
- data/lib/dtr/test_unit.rb +289 -0
- data/lib/dtr/test_unit_injection.rb +19 -0
- data/lib/dtr.rb +111 -0
- metadata +78 -0
@@ -0,0 +1,289 @@
|
|
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/testcase'
|
18
|
+
require 'test/unit/util/observable'
|
19
|
+
require 'monitor'
|
20
|
+
require 'drb'
|
21
|
+
require 'timeout'
|
22
|
+
|
23
|
+
DTROPTIONS = {} unless defined?(DTROPTIONS)
|
24
|
+
DTROPTIONS[:log_file] = 'dtr_master_process.log' unless DTROPTIONS[:log_file]
|
25
|
+
|
26
|
+
module DTR
|
27
|
+
def reject
|
28
|
+
return unless Test::Unit::TestSuite.method_defined?(:dtr_injected?)
|
29
|
+
Test::Unit::TestCase.send(:include, Rejection)
|
30
|
+
Test::Unit::TestSuite.send(:include, Rejection)
|
31
|
+
end
|
32
|
+
|
33
|
+
def inject
|
34
|
+
return if Test::Unit::TestSuite.method_defined?(:dtr_injected?)
|
35
|
+
Test::Unit::TestCase.send(:include, TestCaseInjection)
|
36
|
+
Test::Unit::TestSuite.send(:include, TestSuiteInjection)
|
37
|
+
end
|
38
|
+
|
39
|
+
def service_provider
|
40
|
+
$dtr_service_provider ||= DTR::ServiceProvider.new
|
41
|
+
end
|
42
|
+
|
43
|
+
module_function :reject, :inject, :service_provider
|
44
|
+
|
45
|
+
class Counter
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
extend MonitorMixin
|
49
|
+
@start_count, @finish_count = 0, 0
|
50
|
+
@complete_cond = new_cond
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_start_count
|
54
|
+
synchronize do
|
55
|
+
@start_count += 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_finish_count
|
60
|
+
synchronize do
|
61
|
+
@finish_count += 1
|
62
|
+
@complete_cond.signal
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
synchronize do
|
68
|
+
status
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def wait_until_complete
|
73
|
+
synchronize do
|
74
|
+
@complete_cond.wait_until {complete?}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def complete?
|
80
|
+
DTR.info{ "Counter status: #{status}" }
|
81
|
+
@finish_count >= @start_count
|
82
|
+
end
|
83
|
+
|
84
|
+
def status
|
85
|
+
"finish_count => #{@finish_count}, start_count => #{@start_count}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class ThreadSafeTestResult
|
90
|
+
include Test::Unit::Util::Observable
|
91
|
+
include DRbUndumped
|
92
|
+
|
93
|
+
def initialize(rs)
|
94
|
+
extend MonitorMixin
|
95
|
+
@rs = rs
|
96
|
+
@channels = @rs.send(:channels).dup
|
97
|
+
@rs.send(:channels).clear
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_run
|
101
|
+
synchronize do
|
102
|
+
@rs.add_run
|
103
|
+
end
|
104
|
+
notify_listeners(Test::Unit::TestResult::CHANGED, self)
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_failure(failure)
|
108
|
+
synchronize do
|
109
|
+
@rs.add_failure(failure)
|
110
|
+
end
|
111
|
+
notify_listeners(Test::Unit::TestResult::FAULT, failure)
|
112
|
+
notify_listeners(Test::Unit::TestResult::CHANGED, self)
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_error(error)
|
116
|
+
synchronize do
|
117
|
+
@rs.add_error(error)
|
118
|
+
end
|
119
|
+
notify_listeners(Test::Unit::TestResult::FAULT, error)
|
120
|
+
notify_listeners(Test::Unit::TestResult::CHANGED, self)
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_assertion
|
124
|
+
synchronize do
|
125
|
+
@rs.add_assertion
|
126
|
+
end
|
127
|
+
notify_listeners(Test::Unit::TestResult::CHANGED, self)
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_s
|
131
|
+
synchronize do
|
132
|
+
@rs.to_s
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def passed?
|
137
|
+
synchronize do
|
138
|
+
@rs.passed?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def failure_count
|
143
|
+
synchronize do
|
144
|
+
@rs.failure_count
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def error_count
|
149
|
+
synchronize do
|
150
|
+
@rs.error_count
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class DRbTestRunner
|
156
|
+
|
157
|
+
# because some test case will rewrite TestCase#run to ignore some tests, which
|
158
|
+
# makes TestResult#run_count different with TestSuite#size, so we need to count
|
159
|
+
# by ourselves.(for example: ActionController::IntegrationTest)
|
160
|
+
class << self
|
161
|
+
def counter
|
162
|
+
@counter ||= Counter.new
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
RUN_TEST_FINISHED = "::DRbTestRunner::RUN_TEST_FINISHED"
|
167
|
+
DEFAULT_RUN_TEST_TIMEOUT = 60 #seconds
|
168
|
+
|
169
|
+
def initialize(test, result, &progress_block)
|
170
|
+
@test = test
|
171
|
+
@result = result
|
172
|
+
@progress_block = progress_block
|
173
|
+
|
174
|
+
DRbTestRunner.counter.add_start_count
|
175
|
+
end
|
176
|
+
|
177
|
+
def run
|
178
|
+
if runner = lookup_runner
|
179
|
+
run_test_on(runner)
|
180
|
+
else
|
181
|
+
self.run
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def run_test_on(runner)
|
186
|
+
Thread.start do
|
187
|
+
begin
|
188
|
+
Timeout.timeout(ENV['RUN_TEST_TIMEOUT'] || DEFAULT_RUN_TEST_TIMEOUT) do
|
189
|
+
runner.run(@test, @result, &@progress_block)
|
190
|
+
end
|
191
|
+
@progress_block.call(RUN_TEST_FINISHED, @test.name)
|
192
|
+
rescue Timeout::Error => e
|
193
|
+
DTR.info {"Run test timeout(#{ENV['RUN_TEST_TIMEOUT'] || DEFAULT_RUN_TEST_TIMEOUT}), reboot runner"}
|
194
|
+
runner.reboot rescue nil
|
195
|
+
DTR.info {"rerun test: #{@test.name}"}
|
196
|
+
self.run
|
197
|
+
rescue DRb::DRbConnError => e
|
198
|
+
DTR.info {"DRb::DRbConnError(#{e.message}), rerun test: #{@test.name}"}
|
199
|
+
self.run
|
200
|
+
rescue Exception => e
|
201
|
+
DTR.info{ "#{test.name}, rescue an exception: #{e.message}, add error into result." }
|
202
|
+
@result.add_error(Test::Unit::Error.new(@test.name, e))
|
203
|
+
@result.add_run
|
204
|
+
@progress_block.call(Test::Unit::TestCase::FINISHED, @test.name)
|
205
|
+
@progress_block.call(RUN_TEST_FINISHED, @test.name)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def lookup_runner
|
211
|
+
runner = DTR.service_provider.lookup_runner
|
212
|
+
begin
|
213
|
+
DTR.debug {"#{runner.name}.env ==?: #{WorkingEnv.current[:identifier] == runner.identifier}"}
|
214
|
+
return runner if runner.identifier == WorkingEnv.current[:identifier]
|
215
|
+
runner.shutdown
|
216
|
+
rescue DRb::DRbConnError => e
|
217
|
+
DTR.debug {"DRb::DRbConnError(#{e.message})"}
|
218
|
+
end
|
219
|
+
nil
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
module TestCaseInjection
|
224
|
+
|
225
|
+
def self.included(base)
|
226
|
+
base.class_eval do
|
227
|
+
alias_method :__run__, :run
|
228
|
+
|
229
|
+
def run(result, &progress_block)
|
230
|
+
DTR.debug {"start of run TestCase(#{name})"}
|
231
|
+
DRbTestRunner.new(self, result, &progress_block).run
|
232
|
+
DTR.debug {"end of run TestCase(#{name})"}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
module TestSuiteInjection
|
239
|
+
def self.included(base)
|
240
|
+
base.class_eval do
|
241
|
+
def dtr_injected?
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
245
|
+
alias_method :__run__, :run
|
246
|
+
|
247
|
+
def run(result, &progress_block)
|
248
|
+
DTR.info { "start of run suite(#{name}), size: #{size};"}
|
249
|
+
|
250
|
+
if result.kind_of?(ThreadSafeTestResult)
|
251
|
+
__run__(result, &progress_block)
|
252
|
+
else
|
253
|
+
if defined?(ActiveRecord::Base)
|
254
|
+
ActiveRecord::Base.clear_active_connections! rescue nil
|
255
|
+
end
|
256
|
+
|
257
|
+
DTR.service_provider.setup_working_env WorkingEnv.refresh
|
258
|
+
|
259
|
+
puts 'Refreshed dtr working environment, looking for runner service...' unless ENV['DTR_ENV'] == 'test'
|
260
|
+
DTR.info {"Master process started at #{Time.now}"}
|
261
|
+
result = ThreadSafeTestResult.new(result)
|
262
|
+
__run__(result) do |channel, value|
|
263
|
+
DTR.debug { "=> channel: #{channel}, value: #{value}" }
|
264
|
+
progress_block.call(channel, value)
|
265
|
+
if channel == DTR::DRbTestRunner::RUN_TEST_FINISHED
|
266
|
+
DRbTestRunner.counter.add_finish_count
|
267
|
+
end
|
268
|
+
end
|
269
|
+
DRbTestRunner.counter.wait_until_complete
|
270
|
+
DTR.debug { "==> teardown" }
|
271
|
+
DTR.service_provider.teardown_working_env
|
272
|
+
end
|
273
|
+
DTR.info { "end of run suite(#{name}), test result status: #{result}, counter status: #{DRbTestRunner.counter}"}
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
module Rejection
|
280
|
+
def self.included(base)
|
281
|
+
base.class_eval do
|
282
|
+
remove_method :dtr_injected? if base.method_defined?(:dtr_injected?)
|
283
|
+
remove_method :run
|
284
|
+
alias_method :run, :__run__
|
285
|
+
remove_method :__run__
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,19 @@
|
|
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/service_provider'
|
16
|
+
require 'dtr/test_unit'
|
17
|
+
$argv_dup = ARGV.dup.delete_if{|f| f.include?('dtr/test_unit_injection')}
|
18
|
+
|
19
|
+
DTR.inject
|
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.4"
|
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
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xli-dtr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Li Xiao
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-13 00:00:00 -07:00
|
13
|
+
default_executable: dtr
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: daemons
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.7
|
23
|
+
version:
|
24
|
+
description:
|
25
|
+
email: iam@li-xiao.com
|
26
|
+
executables:
|
27
|
+
- dtr
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- lib/dtr/base.rb
|
34
|
+
- lib/dtr/raketasks.rb
|
35
|
+
- lib/dtr/runner.rb
|
36
|
+
- lib/dtr/service_provider.rb
|
37
|
+
- lib/dtr/test_unit.rb
|
38
|
+
- lib/dtr/test_unit_injection.rb
|
39
|
+
- lib/dtr.rb
|
40
|
+
- bin/dtr
|
41
|
+
- bin
|
42
|
+
- CHANGES
|
43
|
+
- doc
|
44
|
+
- dtr.gemspec
|
45
|
+
- install.rb
|
46
|
+
- lib
|
47
|
+
- LICENSE.TXT
|
48
|
+
- Rakefile
|
49
|
+
- README
|
50
|
+
- TODO
|
51
|
+
has_rdoc: false
|
52
|
+
homepage: http://github.com/xli/dtr/tree/master
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project: dtr
|
73
|
+
rubygems_version: 1.2.0
|
74
|
+
signing_key:
|
75
|
+
specification_version: 2
|
76
|
+
summary: DTR is a distributed test runner to run tests on distributed computers for decreasing build time.
|
77
|
+
test_files: []
|
78
|
+
|