xli-dtr 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+