dtr 0.0.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. data/CHANGES +7 -0
  2. data/LICENSE.txt +203 -0
  3. data/README.rdoc +208 -0
  4. data/Rakefile +64 -205
  5. data/TODO +8 -2
  6. data/bin/dtr +27 -60
  7. data/dtr.gemspec +8 -11
  8. data/lib/dtr.rb +5 -94
  9. data/lib/dtr/agent.rb +38 -0
  10. data/lib/dtr/agent/brain.rb +57 -0
  11. data/lib/dtr/agent/herald.rb +60 -0
  12. data/lib/dtr/agent/runner.rb +87 -0
  13. data/lib/dtr/agent/sync_codebase.rb +44 -0
  14. data/lib/dtr/agent/sync_logger.rb +70 -0
  15. data/lib/dtr/agent/test_case.rb +53 -0
  16. data/lib/dtr/agent/test_unit.rb +40 -0
  17. data/lib/dtr/agent/worker.rb +89 -0
  18. data/lib/dtr/agent/working_env_ext.rb +47 -0
  19. data/lib/dtr/facade.rb +65 -0
  20. data/lib/dtr/master.rb +40 -0
  21. data/lib/dtr/monitor.rb +95 -0
  22. data/lib/dtr/raketasks.rb +155 -22
  23. data/lib/dtr/shared.rb +24 -0
  24. data/lib/dtr/shared/adapter.rb +115 -0
  25. data/lib/dtr/shared/configuration.rb +104 -0
  26. data/lib/dtr/shared/message_decorator.rb +28 -0
  27. data/lib/dtr/shared/ruby_ext.rb +129 -0
  28. data/lib/dtr/shared/service.rb +19 -0
  29. data/lib/dtr/shared/service/agent.rb +37 -0
  30. data/lib/dtr/shared/service/file.rb +28 -0
  31. data/lib/dtr/shared/service/rinda.rb +48 -0
  32. data/lib/dtr/shared/service/runner.rb +34 -0
  33. data/lib/dtr/shared/service/working_env.rb +28 -0
  34. data/lib/dtr/shared/sync_codebase.rb +18 -0
  35. data/lib/dtr/shared/sync_codebase/copiable_package.rb +40 -0
  36. data/lib/dtr/shared/sync_codebase/master_ext.rb +40 -0
  37. data/lib/dtr/shared/sync_codebase/package.rb +53 -0
  38. data/lib/dtr/shared/sync_codebase/sync_service.rb +36 -0
  39. data/lib/dtr/shared/sync_logger.rb +64 -0
  40. data/lib/dtr/shared/utils.rb +17 -0
  41. data/lib/dtr/shared/utils/cmd.rb +30 -0
  42. data/lib/dtr/shared/utils/env_store.rb +60 -0
  43. data/lib/dtr/shared/utils/logger.rb +87 -0
  44. data/lib/dtr/shared/working_env.rb +38 -0
  45. data/lib/dtr/test_unit.rb +9 -275
  46. data/lib/dtr/test_unit/drb_test_runner.rb +48 -0
  47. data/lib/dtr/test_unit/injection.rb +29 -0
  48. data/lib/dtr/test_unit/test_case_injection.rb +37 -0
  49. data/lib/dtr/test_unit/test_suite_injection.rb +24 -0
  50. data/lib/dtr/test_unit/testrunnermediator_injection.rb +72 -0
  51. data/lib/dtr/test_unit/thread_safe_test_result.rb +38 -0
  52. data/lib/dtr/test_unit/worker_club.rb +72 -0
  53. data/lib/dtr/test_unit_injection.rb +1 -2
  54. data/test/acceptance/agent_working_env_test.rb +86 -0
  55. data/test/acceptance/dtr_package_task_test.rb +36 -0
  56. data/test/acceptance/general_test.rb +331 -0
  57. data/test/acceptance/raketasks_test.rb +23 -0
  58. data/test/acceptance/sync_codebase_test.rb +66 -0
  59. data/test/acceptance/sync_logger_test.rb +32 -0
  60. data/test/agent_helper.rb +37 -0
  61. data/test/logger_stub.rb +34 -0
  62. data/test/test_helper.rb +71 -0
  63. data/test/unit/adapter_test.rb +149 -0
  64. data/test/unit/configuration_test.rb +44 -0
  65. data/test/unit/facade_test.rb +41 -0
  66. data/test/unit/logger_test.rb +72 -0
  67. data/test/unit/test_unit_test.rb +26 -0
  68. data/test/unit/working_env_test.rb +71 -0
  69. data/testdata/Rakefile +11 -0
  70. data/testdata/a_failed_test_case.rb +8 -0
  71. data/testdata/a_file_system_test_case.rb +8 -0
  72. data/testdata/a_test_case.rb +13 -0
  73. data/testdata/a_test_case2.rb +6 -0
  74. data/testdata/an_error_test_case.rb +9 -0
  75. data/testdata/another_project/Rakefile +6 -0
  76. data/testdata/another_project/passed_test_case.rb +7 -0
  77. data/testdata/hacked_run_method_test_case.rb +15 -0
  78. data/testdata/is_required_by_a_test.rb +9 -0
  79. data/testdata/lib/lib_test_case.rb +7 -0
  80. data/testdata/package_task_test_rakefile +8 -0
  81. data/testdata/raketasks/Rakefile +7 -0
  82. data/testdata/raketasks/success_test_case.rb +6 -0
  83. data/testdata/scenario_test_case.rb +34 -0
  84. data/testdata/setup_agent_env_test_case.rb +9 -0
  85. data/testdata/sleep_3_secs_test_case.rb +9 -0
  86. data/testdata/verify_dir_pwd/Rakefile +6 -0
  87. data/testdata/verify_dir_pwd/verify_dir_pwd_test_case.rb +10 -0
  88. metadata +101 -34
  89. data/README +0 -291
  90. data/install.rb +0 -88
  91. data/lib/dtr/base.rb +0 -172
  92. data/lib/dtr/runner.rb +0 -270
  93. data/lib/dtr/service_provider.rb +0 -160
@@ -1,270 +0,0 @@
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_agent_env_cmd=nil)
43
- DTR.with_monitor do
44
- new(runner_names, setup_agent_env_cmd).launch
45
- end
46
- end
47
-
48
- def initialize(runner_names, setup_agent_env_cmd)
49
- @runner_names = runner_names.is_a?(Array) ? runner_names : [runner_names.to_s]
50
- @setup_agent_env_cmd = setup_agent_env_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_agent_env_cmd || working_env[:setup_agent_env_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.blank?
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].blank?
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].blank?
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 reboot
257
- DTR.info "#{self} is rebooting. Ran #{@started.size} tests, finished #{@run_finished.size}."
258
- @provider.provide(self)
259
- end
260
-
261
- def shutdown
262
- DTR.info "#{self} is shutting down. Ran #{@started.size} tests, finished #{@run_finished.size}."
263
- @provider.stop_service rescue exit!
264
- end
265
-
266
- def to_s
267
- "Runner #{@name}"
268
- end
269
- end
270
- end
@@ -1,160 +0,0 @@
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 'drb'
17
- require 'rinda/ring'
18
- require 'rinda/tuplespace'
19
- require 'socket'
20
-
21
- module DTR
22
- def decorate_error_message(msg, source=nil)
23
- source ? "#{source} from #{Socket.gethostname}: #{msg}" : "From #{Socket.gethostname}: #{msg}"
24
- end
25
-
26
- module_function :decorate_error_message
27
-
28
- class RunnerRuntimeException < StandardError
29
- def initialize(e)
30
- super(DTR.decorate_error_message(e.message, e.class.name))
31
- set_backtrace(e.backtrace)
32
- end
33
- end
34
-
35
- class ServiceProvider
36
-
37
- def self.broadcast_list=(list)
38
- EnvStore.new[:broadcast_list] = list
39
- end
40
-
41
- def self.port=(port)
42
- EnvStore.new[:port] = port
43
- end
44
-
45
- PORT = 3344
46
- BROADCAST_LIST = []
47
-
48
- def initialize
49
- DTR.info "-- Initializing drb service..."
50
- env_store = EnvStore.new
51
- (env_store[:broadcast_list] || ['localhost']).each do |broadcast|
52
- BROADCAST_LIST << broadcast.untaint
53
- DTR.info "-- Added broadcast: #{broadcast}"
54
- end
55
- DTR.info "-- Server port: #{server_port}"
56
- DRb.start_service
57
- end
58
-
59
- # start DTR server
60
- def start
61
- env_store = EnvStore.new
62
- DTR.info '-- Booting DTR server...'
63
- Rinda::RingServer.new Rinda::TupleSpace.new, server_port
64
- DTR.info "-- DTR server started on port #{server_port}"
65
- #set safe level to 1 here, now, runner can't set to 1, cause test should can do anything
66
- #......
67
- $SAFE = 1 unless $DEBUG # disable eval() and friends
68
- # Wait until the user explicitly kills the server.
69
- DRb.thread.join
70
- end
71
-
72
- def provide(runner)
73
- renewer = Rinda::SimpleRenewer.new
74
- tuple = [:name, 'DTR::Runner'.to_sym, runner.freeze, "DTR remote runner #{Process.pid}-#{runner.name}"]
75
- lookup_ring.write(tuple, renewer)
76
- end
77
-
78
- def send_message(message)
79
- lookup_ring.write [:agent_heartbeat, Socket.gethostname, message, Time.now], 2
80
- end
81
-
82
- def lookup_runner
83
- lookup_ring.take([:name, 'DTR::Runner'.to_sym, nil, nil])[2]
84
- end
85
-
86
- def runners
87
- lookup_ring.read_all([:name, 'DTR::Runner'.to_sym, nil, nil]).collect {|rt| rt[2]}
88
- end
89
-
90
- def monitor
91
- working_env_monitor = lookup_ring.notify(nil, [:working_env, nil])
92
- Thread.start do
93
- DTR.info("Current work environment: #{working_env.inspect}")
94
- working_env_monitor.each { |t| DTR.info t.inspect }
95
- end
96
- if DTROPTIONS[:log_level] == Logger::DEBUG
97
- runner_monitor = lookup_ring.notify(nil, [:name, 'DTR::Runner'.to_sym, nil, nil])
98
- Thread.start do
99
- runner_monitor.each { |t| DTR.debug t.inspect }
100
- end
101
- end
102
- agent_heartbeat_monitor = lookup_ring.notify("write", [:agent_heartbeat, nil, nil, nil])
103
- Thread.start do
104
- colors = {}
105
- base = 30
106
- agent_heartbeat_monitor.each do |t|
107
- host, message, time = t[1][1..3]
108
- colors[host] = base+=1 unless colors[host]
109
- message = "\e[1;31m#{message}\e[0m" if message =~ /-ERROR\]/
110
- DTR.info "#{time.strftime("[%I:%M:%S%p]")} \e[1;#{colors[host]};1m#{host}\e[0m: #{message}"
111
- end
112
- end
113
- DRb.thread.join
114
- end
115
-
116
- def wait_until_teardown
117
- lookup_ring.notify(nil, [:working_env, nil]).pop
118
- end
119
-
120
- def working_env
121
- lookup_ring.read([:working_env, nil])[1]
122
- end
123
-
124
- def setup_working_env(env)
125
- clear_workspace
126
- lookup_ring.write [:working_env, env]
127
- end
128
-
129
- def teardown_working_env
130
- clear_workspace
131
- end
132
-
133
- def start_service
134
- DRb.start_service
135
- end
136
-
137
- def stop_service
138
- DRb.stop_service
139
- end
140
-
141
- def clear_workspace
142
- lookup_ring.read_all([:working_env, nil]).size.times do
143
- lookup_ring.take [:working_env, nil] rescue nil
144
- end rescue nil
145
- runners.size.times do
146
- lookup_runner.shutdown rescue nil
147
- end
148
- end
149
-
150
- private
151
- def server_port
152
- env_store = EnvStore.new
153
- env_store[:port].to_i > 0 ? env_store[:port].to_i : PORT
154
- end
155
-
156
- def lookup_ring
157
- Rinda::TupleSpaceProxy.new(Rinda::RingFinger.new(BROADCAST_LIST, server_port).lookup_ring_any)
158
- end
159
- end
160
- end