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
@@ -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