dtr 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
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/shared'
16
+
17
+ module DTR
18
+ module Master
19
+ def with_dtr_master(&block)
20
+ if defined?(ActiveRecord::Base)
21
+ ActiveRecord::Base.clear_active_connections! rescue nil
22
+ end
23
+
24
+ DTR.info {""}
25
+ DTR.info {"--------------------beautiful line--------------------------"}
26
+ DTR.info {"Master process started at #{Time.now}"}
27
+
28
+ DTR.configuration.with_rinda_server do
29
+ provide_working_env WorkingEnv.new
30
+ with_wakeup_agents(&block)
31
+ end
32
+ end
33
+
34
+ include Adapter::Master
35
+ include Service::WorkingEnv
36
+ include SyncCodebase::MasterExt
37
+ end
38
+
39
+ Configuration.send(:include, SyncLogger::Provider)
40
+ end
@@ -0,0 +1,95 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
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/master'
16
+
17
+ module DTR
18
+
19
+ # Monitor provides a way to monitor agent/master process working status.
20
+ class Monitor
21
+
22
+ class AgentsMonitor
23
+ include Adapter::Master
24
+
25
+ def start
26
+ DTR.fork_process do
27
+ monitor
28
+ end
29
+ end
30
+ def monitor
31
+ DTR.configuration.with_rinda_server do
32
+ with_wakeup_agents do
33
+ begin
34
+ sleep
35
+ rescue Interrupt
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ class MasterMonitor
43
+ include Adapter::Follower
44
+ include Service::Agent
45
+
46
+ def start
47
+ DTR.fork_process do
48
+ begin
49
+ loop do
50
+ monitor
51
+ end
52
+ rescue Errno::EADDRINUSE
53
+ puts "There is DTR agent started on this machine."
54
+ puts "Shutdown it for monitoring working DTR Master info."
55
+ rescue Interrupt
56
+ ensure
57
+ relax
58
+ end
59
+ end
60
+ end
61
+
62
+ def monitor
63
+ msg, from_host, group = listen
64
+ unless ["127.0.0.1:#{DTR.configuration.rinda_server_port}"].include?(from_host)
65
+ puts "Master process message from #{from_host}: #{msg} for group #{group}"
66
+ ip, port = from_host.split(':')
67
+ with_configuration(ip, port) do
68
+ start_service
69
+ puts "Agents working for #{from_host}: "
70
+ puts all_agents_info.collect{|i| " #{i.strip}"}.join("\n")
71
+ end
72
+ end
73
+ end
74
+
75
+ def with_configuration(ip, port)
76
+ my_port = DTR.configuration.rinda_server_port
77
+ my_broadcast_list = DTR.configuration.broadcast_list
78
+ DTR.configuration.rinda_server_port = port.to_i
79
+ DTR.configuration.broadcast_list = [ip]
80
+ yield
81
+ ensure
82
+ DTR.configuration.broadcast_list = my_broadcast_list
83
+ DTR.configuration.rinda_server_port = my_port
84
+ end
85
+ end
86
+
87
+ def start
88
+ MasterMonitor.new.start
89
+ AgentsMonitor.new.start
90
+ puts "Monitor process started at #{Time.now}"
91
+ Process.waitall
92
+ rescue Interrupt
93
+ end
94
+ end
95
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2007-2008 Li Xiao
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -13,21 +13,79 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "rubygems"
16
- require 'dtr'
16
+ require 'rake'
17
17
  require 'rake/testtask'
18
+ require 'rake/tasklib'
19
+
20
+ require 'dtr'
21
+ require 'dtr/shared/ruby_ext'
22
+ require 'dtr/shared/utils'
23
+ require 'dtr/shared/sync_codebase/package'
18
24
 
19
25
  module DTR
20
- class MPTask < Rake::TestTask
21
- attr_accessor :processes, :runner_options, :start_server
22
-
26
+ # Create tasks that run a set of tests with DTR injected.
27
+ # The TestTask will create the following targets:
28
+ #
29
+ # [<b>:dtr</b>]:
30
+ # Create a task that runs a set of tests by DTR master.
31
+ #
32
+ # [DTR::PackageTask]:
33
+ # Create a packaging task that will package the project into
34
+ # distributable files for running test on remote machine.
35
+ # All test files should be included.
36
+ #
37
+ # Example:
38
+ # require 'dtr/raketasks'
39
+ #
40
+ # DTR::TestTask.new do |t|
41
+ # t.libs << "test"
42
+ # t.test_files = FileList['test/test*.rb']
43
+ # t.verbose = true
44
+ # t.processes = 1 # default is 1
45
+ # t.package_files.include("lib/**/*") # default is FileList["**/*"]
46
+ # t.package_files.include("test/**/*")
47
+ # end
48
+ #
49
+ # This task inherits from Rake::TestTask, and adds 2 DTR specific
50
+ # options: processes and package_files.
51
+ #
52
+ class TestTask < Rake::TestTask
53
+
54
+ #
55
+ # The option processes is used to start an DTR agent in same directory
56
+ # with master process. The number of processes is the size of runners
57
+ # launched by agent for running tests. If processes is set to 0,
58
+ # then there is no agent started locally.
59
+ # Default is 1.
60
+ attr_accessor :processes
61
+
62
+ # List of files to be included in the package for running tests on
63
+ # remote agent.
64
+ # The agent, which starts in same directory on same machine with
65
+ # master process, would skip copying codebase.
66
+ # The default package files is Rake::FileList["**/*"].
67
+ attr_accessor :package_files
68
+
69
+ def initialize(name=:dtr)
70
+ @processes = 1
71
+ @package_files = Rake::FileList.new
72
+ super(name)
73
+ end
74
+
23
75
  def define
76
+ PackageTask.new do |p|
77
+ p.package_files = package_files
78
+ if p.package_files.empty?
79
+ p.package_files.include("**/*")
80
+ end
81
+ end
82
+
24
83
  @libs.unshift DTR.lib_path
25
84
  lib_path = @libs.join(File::PATH_SEPARATOR)
26
85
 
27
- desc "Run tests" + (@name==:test ? "" : " for #{@name}")
86
+ desc "Run tests with DTR injected"
28
87
  task @name do
29
- DTR.start_server_daemon_mode if start_server?
30
- start_runners
88
+ @agent = start_agent
31
89
  run_code = ''
32
90
  begin
33
91
  RakeFileUtils.verbose(@verbose) do
@@ -41,29 +99,104 @@ module DTR
41
99
  " #{option_list}"
42
100
  end
43
101
  ensure
44
- DTR.stop_runners_daemon_mode rescue nil
45
- if start_server?
46
- DTR.stop_server_daemon_mode rescue nil
102
+ if defined?(@agent)
103
+ DTR.kill_process @agent
47
104
  end
48
105
  end
49
106
  end
50
107
  self
51
108
  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
-
109
+
61
110
  private
62
- def start_runners
111
+ def start_agent
63
112
  return if self.processes.to_i <= 0
64
113
  runner_names = []
65
114
  self.processes.to_i.times {|i| runner_names << "runner#{i}"}
66
- %x[dtr -r #{runner_names.join(',')} -D #{runner_options}]
115
+
116
+ DTR.fork_process do
117
+ DTR_AGENT_OPTIONS[:runners] = runner_names if DTR_AGENT_OPTIONS[:runners].empty?
118
+ DTR.start_agent
119
+ end
120
+ end
121
+ end
122
+
123
+ # Create a packaging task that will package the project into
124
+ # distributable files for running test on remote machine.
125
+ # It uses zip and unzip to package and unpackage files.
126
+ # All test files should be included.
127
+ #
128
+ # The PackageTask will create the following targets:
129
+ #
130
+ # [<b>:dtr_package</b>]
131
+ # Create all the requested package files.
132
+ #
133
+ # [<b>:dtr_clobber_package</b>]
134
+ # Delete all the package files. This target is automatically
135
+ # added to the main clobber target.
136
+ #
137
+ # [<b>:dtr_repackage</b>]
138
+ # Rebuild the package files from scratch, even if they are not out
139
+ # of date.
140
+ #
141
+ # Example:
142
+ #
143
+ # DTR::PackageTask.new do |p|
144
+ # p.package_files.include("lib/**/*.rb")
145
+ # p.package_files.include("test/**/*.rb")
146
+ # end
147
+ #
148
+ class PackageTask < Rake::TaskLib
149
+ include SyncCodebase::Package
150
+ # List of files to be included in the package.
151
+ attr_accessor :package_files
152
+
153
+ # Create a Package Task with the given name and version.
154
+ def initialize
155
+ @package_files = Rake::FileList.new
156
+ yield self if block_given?
157
+ define
158
+ end
159
+
160
+ # Create the tasks defined by this task library.
161
+ def define
162
+ desc "Build packages for dtr task"
163
+ task :dtr_package
164
+
165
+ desc "Force a rebuild of the package files for dtr task"
166
+ task :dtr_repackage => [:dtr_clobber_package, :dtr_package]
167
+
168
+ desc "Remove package for dtr task"
169
+ task :dtr_clobber_package do
170
+ rm_r package_dir rescue nil
171
+ end
172
+
173
+ file, flag = package_file, 'j'
174
+ task :dtr_package => ["#{package_dir}/#{file}"]
175
+
176
+ file "#{package_dir}/#{file}" => [package_dir_path] do
177
+ chdir(package_dir) do
178
+ do_work(package_cmd)
179
+ end
180
+ end
181
+
182
+ directory package_dir
183
+
184
+ file package_dir_path do
185
+ mkdir_p package_dir rescue nil
186
+ @package_files.exclude(package_dir)
187
+ @package_files.each do |fn|
188
+ f = File.join(package_dir_path, fn)
189
+ fdir = File.dirname(f)
190
+ mkdir_p(fdir) if !File.exist?(fdir)
191
+ if File.directory?(fn)
192
+ mkdir_p(f)
193
+ else
194
+ rm_f f
195
+ safe_ln(fn, f)
196
+ end
197
+ end
198
+ end
199
+ self
67
200
  end
68
201
  end
69
202
  end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
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 'fileutils'
16
+ require 'dtr/shared/ruby_ext'
17
+ require 'dtr/shared/utils'
18
+ require 'dtr/shared/message_decorator'
19
+ require 'dtr/shared/working_env'
20
+ require 'dtr/shared/service'
21
+ require 'dtr/shared/configuration'
22
+ require 'dtr/shared/sync_logger'
23
+ require 'dtr/shared/adapter'
24
+ require 'dtr/shared/sync_codebase'
@@ -0,0 +1,115 @@
1
+ # Copyright (c) 2007-2008 Li Xiao <iam@li-xiao.com>
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 'timeout'
16
+
17
+ module DTR
18
+ module Adapter
19
+ WAKEUP_MESSAGE = 'wakeup'
20
+ SLEEP_MESSAGE = 'sleep'
21
+
22
+ module Follower
23
+ def wakeup?
24
+ msg, host, group = listen
25
+ if group == DTR.configuration.group && msg == Adapter::WAKEUP_MESSAGE
26
+ ip, port = host.split(':')
27
+ DTR.configuration.rinda_server_port = port.to_i
28
+ DTR.configuration.broadcast_list = [ip]
29
+ @wakeup_for_host = host
30
+ true
31
+ end
32
+ end
33
+
34
+ def sleep?
35
+ return true unless defined?(@wakeup_for_host)
36
+ Timeout.timeout(DTR.configuration.follower_listen_heartbeat_timeout) do
37
+ until (msg, host = listen) && host == @wakeup_for_host; end
38
+ msg == Adapter::SLEEP_MESSAGE
39
+ end
40
+ rescue Timeout::Error => e
41
+ DTR.info {"Timeout while listening command"}
42
+ true
43
+ end
44
+
45
+ def relax
46
+ if defined?(@soc) && @soc
47
+ @soc.close rescue nil
48
+ end
49
+ end
50
+
51
+ private
52
+ def listen
53
+ unless defined?(@soc)
54
+ @soc = UDPSocket.open
55
+ @soc.bind('', DTR.configuration.agent_listen_port)
56
+ DTR.info("DTR Agent is listening on port #{DTR.configuration.agent_listen_port}")
57
+ end
58
+ message, client_address = @soc.recvfrom(400)
59
+ cmd, port, group = message.split
60
+
61
+ hostname = client_address[2]
62
+ host_ip = client_address[3]
63
+ DTR.info {"Received: #{cmd} for group #{group} from #{hostname}(#{host_ip}):#{port}"}
64
+ [cmd, "#{host_ip}:#{port}", group]
65
+ end
66
+ end
67
+
68
+ module Master
69
+ def hypnotize_agents
70
+ yell_agents("#{Adapter::SLEEP_MESSAGE} #{DTR.configuration.rinda_server_port}")
71
+ end
72
+
73
+ def with_wakeup_agents(&block)
74
+ heartbeat = Thread.new do
75
+ loop do
76
+ do_wakeup_agents
77
+ sleep(DTR.configuration.master_heartbeat_interval)
78
+ end
79
+ end
80
+ #heartbeat thread should have high priority for agent is listening
81
+ heartbeat.priority = Thread.current.priority + 10
82
+ block.call
83
+ ensure
84
+ #kill heartbeat first, so that agents wouldn't be wakeup after hypnotized
85
+ Thread.kill heartbeat rescue nil
86
+ hypnotize_agents rescue nil
87
+ end
88
+
89
+ def do_wakeup_agents
90
+ yell_agents("#{Adapter::WAKEUP_MESSAGE} #{DTR.configuration.rinda_server_port} #{DTR.configuration.group}")
91
+ end
92
+
93
+ private
94
+ def yell_agents(msg)
95
+ DTR.info {"yell agents #{msg}: #{DTR.configuration.broadcast_list.inspect}"}
96
+ DTR.configuration.broadcast_list.each do |it|
97
+ broadcast(it, msg)
98
+ end
99
+ end
100
+
101
+ def broadcast(it, msg)
102
+ soc = UDPSocket.open
103
+ begin
104
+ soc.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
105
+ DTR.debug {"broadcast sending #{msg} to #{it}"}
106
+ soc.send(msg, 0, it, DTR.configuration.agent_listen_port)
107
+ rescue
108
+ nil
109
+ ensure
110
+ soc.close
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end