jperkins-deep_test 1.2.2

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 (117) hide show
  1. data/CHANGELOG +47 -0
  2. data/README.rdoc +228 -0
  3. data/Rakefile +247 -0
  4. data/bin/deep_test +15 -0
  5. data/lib/deep_test.rb +91 -0
  6. data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
  7. data/lib/deep_test/database/postgresql_setup_listener.rb +116 -0
  8. data/lib/deep_test/database/setup_listener.rb +125 -0
  9. data/lib/deep_test/deadlock_detector.rb +7 -0
  10. data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
  11. data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
  12. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  13. data/lib/deep_test/distributed/master_test_server.rb +52 -0
  14. data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
  15. data/lib/deep_test/distributed/null_work_unit.rb +12 -0
  16. data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
  17. data/lib/deep_test/distributed/remote_worker_server.rb +82 -0
  18. data/lib/deep_test/distributed/rsync.rb +37 -0
  19. data/lib/deep_test/distributed/show_status.rhtml +41 -0
  20. data/lib/deep_test/distributed/test_server.rb +78 -0
  21. data/lib/deep_test/distributed/test_server_status.rb +9 -0
  22. data/lib/deep_test/distributed/test_server_workers.rb +24 -0
  23. data/lib/deep_test/distributed/throughput_runner.rb +42 -0
  24. data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
  25. data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
  26. data/lib/deep_test/extensions/drb_extension.rb +34 -0
  27. data/lib/deep_test/extensions/object_extension.rb +40 -0
  28. data/lib/deep_test/listener_list.rb +17 -0
  29. data/lib/deep_test/local_workers.rb +55 -0
  30. data/lib/deep_test/logger.rb +17 -0
  31. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  32. data/lib/deep_test/metrics/gatherer.rb +67 -0
  33. data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
  34. data/lib/deep_test/null_worker_listener.rb +62 -0
  35. data/lib/deep_test/option.rb +60 -0
  36. data/lib/deep_test/options.rb +110 -0
  37. data/lib/deep_test/process_orchestrator.rb +49 -0
  38. data/lib/deep_test/rake_tasks.rb +11 -0
  39. data/lib/deep_test/result_reader.rb +36 -0
  40. data/lib/deep_test/rspec_detector.rb +21 -0
  41. data/lib/deep_test/server.rb +75 -0
  42. data/lib/deep_test/spec.rb +13 -0
  43. data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
  44. data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
  45. data/lib/deep_test/spec/extensions/options.rb +43 -0
  46. data/lib/deep_test/spec/extensions/reporter.rb +29 -0
  47. data/lib/deep_test/spec/extensions/spec_task.rb +20 -0
  48. data/lib/deep_test/spec/runner.rb +57 -0
  49. data/lib/deep_test/spec/work_result.rb +33 -0
  50. data/lib/deep_test/spec/work_unit.rb +59 -0
  51. data/lib/deep_test/test.rb +10 -0
  52. data/lib/deep_test/test/extensions/error.rb +14 -0
  53. data/lib/deep_test/test/runner.rb +24 -0
  54. data/lib/deep_test/test/supervised_test_suite.rb +49 -0
  55. data/lib/deep_test/test/work_result.rb +34 -0
  56. data/lib/deep_test/test/work_unit.rb +40 -0
  57. data/lib/deep_test/test_task.rb +47 -0
  58. data/lib/deep_test/ui/console.rb +76 -0
  59. data/lib/deep_test/ui/null.rb +17 -0
  60. data/lib/deep_test/warlock.rb +134 -0
  61. data/lib/deep_test/worker.rb +57 -0
  62. data/script/internal/run_test_suite.rb +7 -0
  63. data/script/public/master_test_server.rb +24 -0
  64. data/script/public/test_server.rb +18 -0
  65. data/script/public/test_throughput.rb +29 -0
  66. data/spec/deep_test/option_spec.rb +33 -0
  67. data/spec/deep_test/options_spec.rb +183 -0
  68. data/spec/deep_test/spec/extensions/example_group_methods_spec.rb +48 -0
  69. data/spec/deep_test/spec/extensions/example_methods_spec.rb +61 -0
  70. data/spec/deep_test/spec/extensions/options_spec.rb +23 -0
  71. data/spec/deep_test/spec/extensions/reporter_spec.rb +28 -0
  72. data/spec/deep_test/spec/extensions/spec_task_spec.rb +36 -0
  73. data/spec/deep_test/spec/runner_spec.rb +106 -0
  74. data/spec/deep_test/spec/work_result_spec.rb +14 -0
  75. data/spec/deep_test/spec/work_unit_spec.rb +78 -0
  76. data/spec/spec_helper.rb +59 -0
  77. data/spec/thread_worker.rb +25 -0
  78. data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
  79. data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
  80. data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
  81. data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
  82. data/test/deep_test/distributed/master_test_server_test.rb +32 -0
  83. data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
  84. data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
  85. data/test/deep_test/distributed/remote_worker_server_test.rb +99 -0
  86. data/test/deep_test/distributed/rsync_test.rb +67 -0
  87. data/test/deep_test/distributed/test_server_test.rb +94 -0
  88. data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
  89. data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
  90. data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
  91. data/test/deep_test/extensions/object_extension_test.rb +37 -0
  92. data/test/deep_test/listener_list_test.rb +20 -0
  93. data/test/deep_test/local_workers_test.rb +22 -0
  94. data/test/deep_test/logger_test.rb +11 -0
  95. data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
  96. data/test/deep_test/metrics/gatherer_test.rb +66 -0
  97. data/test/deep_test/process_orchestrator_test.rb +11 -0
  98. data/test/deep_test/result_reader_test.rb +128 -0
  99. data/test/deep_test/server_test.rb +58 -0
  100. data/test/deep_test/test/extensions/error_test.rb +40 -0
  101. data/test/deep_test/test/runner_test.rb +7 -0
  102. data/test/deep_test/test/supervised_test_suite_test.rb +79 -0
  103. data/test/deep_test/test/work_result_test.rb +81 -0
  104. data/test/deep_test/test/work_unit_test.rb +61 -0
  105. data/test/deep_test/test_task_test.rb +43 -0
  106. data/test/deep_test/ui/console_test.rb +9 -0
  107. data/test/deep_test/warlock_test.rb +38 -0
  108. data/test/deep_test/worker_test.rb +94 -0
  109. data/test/failing.rake +11 -0
  110. data/test/failing.rb +7 -0
  111. data/test/fake_deadlock_error.rb +12 -0
  112. data/test/simple_test_blackboard.rb +45 -0
  113. data/test/simple_test_blackboard_test.rb +33 -0
  114. data/test/test_factory.rb +74 -0
  115. data/test/test_helper.rb +15 -0
  116. data/test/test_task_test.rb +72 -0
  117. metadata +214 -0
@@ -0,0 +1,7 @@
1
+ module DeepTest
2
+ class DeadlockDetector
3
+ def self.due_to_deadlock?(error)
4
+ error && !error.message.to_s.match(/Deadlock found when trying to get lock/).nil?
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,53 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class DispatchController
4
+ def initialize(options, receivers)
5
+ @options = options
6
+ @receivers = receivers
7
+ end
8
+
9
+ def dispatch(method_name, *args)
10
+ dispatch_with_options(method_name, {}, *args)
11
+ end
12
+
13
+ def dispatch_with_options(method_name, options, *args)
14
+ raise NoDispatchReceiversError if @receivers.empty?
15
+
16
+ @options.ui_instance.dispatch_starting(method_name)
17
+
18
+ threads = @receivers.map do |r|
19
+ Thread.new do
20
+ Thread.current[:receiver] = r
21
+ Timeout.timeout(@options.timeout_in_seconds) do
22
+ r.send method_name, *args
23
+ end
24
+ end
25
+ end
26
+
27
+ results = []
28
+ threads.each do |t|
29
+ begin
30
+ results << t.value
31
+ rescue Timeout::Error
32
+ @receivers.delete t[:receiver]
33
+ DeepTest.logger.error "Timeout dispatching #{method_name} to #{t[:receiver].__drburi}"
34
+ rescue DRb::DRbConnError
35
+ @receivers.delete t[:receiver]
36
+ unless options[:ignore_connection_error]
37
+ DeepTest.logger.error "Connection Refused dispatching #{method_name} to #{t[:receiver].__drburi}"
38
+ end
39
+ rescue Exception => e
40
+ @receivers.delete t[:receiver]
41
+ DeepTest.logger.error "Exception while dispatching #{method_name} to #{t[:receiver].__drburi} #{e.message}"
42
+ end
43
+ end
44
+
45
+ results
46
+ ensure
47
+ @options.ui_instance.dispatch_finished(method_name)
48
+ end
49
+ end
50
+
51
+ class NoDispatchReceiversError < StandardError; end
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class DRbClientConnectionInfo
4
+ attr_reader :address
5
+
6
+ def initialize
7
+ info = Thread.current['DRb']
8
+ raise "No DRb client found" unless info && info['client']
9
+ peeraddr = info['client'].peeraddr
10
+ DeepTest.logger.debug("DRbClientConnection info: #{peeraddr.inspect}")
11
+ @address = peeraddr[3]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class FilenameResolver
4
+ def initialize(base_path)
5
+ @base_path = base_path
6
+ end
7
+
8
+ def resolve(filename)
9
+ return resolve("/" + filename) unless filename[0] == ?/
10
+
11
+ return filename.sub(@cached_replaced_path, @base_path) if @cached_replaced_path
12
+
13
+ each_potential_filename(filename) do |potential_filename|
14
+ if File.exist?(potential_filename)
15
+ cache_resolution(filename, potential_filename)
16
+ return potential_filename
17
+ end
18
+ end
19
+
20
+ raise "Filename resolution failed. Cannot resolve #{filename} within #{@base_path}"
21
+ end
22
+
23
+ def cache_resolution(original_filename, resolved_filename)
24
+ @cached_replaced_path = original_filename.sub(
25
+ resolved_filename.sub(@base_path, ""), ""
26
+ )
27
+ end
28
+
29
+ def each_potential_filename(filename)
30
+ basename = File.basename(filename)
31
+ dirs = File.dirname(filename).split('/')
32
+
33
+ begin
34
+ dirs.shift
35
+ yield [@base_path, dirs, basename].flatten.join('/')
36
+ end until dirs.empty?
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class MasterTestServer
4
+ include ERB::Util
5
+
6
+ STATUS_PORT = 4020 unless defined?(STATUS_PORT)
7
+
8
+ attr_reader :servers
9
+
10
+ def initialize(servers)
11
+ @servers = servers
12
+ end
13
+
14
+ def show_status(req, res)
15
+ template = File.read(File.dirname(__FILE__) + "/show_status.rhtml")
16
+ res.body = ERB.new(template).result(binding)
17
+ end
18
+
19
+ def test_server_statuses
20
+ @servers.map do |s|
21
+ status = begin
22
+ s.status
23
+ rescue Exception => e
24
+ e
25
+ end
26
+
27
+ [s.__drburi, status]
28
+ end
29
+ end
30
+
31
+ def self.start(uri, server_uris)
32
+ master = start_drb(uri, server_uris)
33
+ start_http(master)
34
+ DeepTest.logger.info "MasterTestServer listening at #{DRb.uri}"
35
+ DRb.thread.join
36
+ end
37
+
38
+ def self.start_drb(uri, server_uris)
39
+ servers = server_uris.map {|server_uri| DRbObject.new_with_uri server_uri}
40
+ master = DeepTest::Distributed::MasterTestServer.new(servers)
41
+ DRb.start_service(uri, master)
42
+ master
43
+ end
44
+
45
+ def self.start_http(master)
46
+ s = WEBrick::HTTPServer.new :Port => STATUS_PORT
47
+ s.mount_proc("/", &master.method(:show_status))
48
+ Thread.new {s.start}
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class MultiTestServerProxy
4
+ def initialize(options, slaves)
5
+ DeepTest.logger.debug "MultiTestServerProxy#initialize #{slaves.length} slaves"
6
+ @slave_controller = DispatchController.new(options, slaves)
7
+ end
8
+
9
+ def spawn_worker_server(options)
10
+ DeepTest.logger.debug "dispatch spawn_worker_server for #{options.origin_hostname}"
11
+ WorkerServerProxy.new options,
12
+ @slave_controller.dispatch(:spawn_worker_server,
13
+ options)
14
+ end
15
+
16
+ def sync(options)
17
+ DeepTest.logger.debug "dispatch sync for #{options.origin_hostname}"
18
+ @slave_controller.dispatch(:sync, options)
19
+ end
20
+
21
+ class WorkerServerProxy
22
+ def initialize(options, slaves)
23
+ DeepTest.logger.debug "WorkerServerProxy#initialize #{slaves.inspect}"
24
+ @slave_controller = DispatchController.new(options, slaves)
25
+ end
26
+
27
+ def load_files(files)
28
+ DeepTest.logger.debug "dispatch load_files"
29
+ @slave_controller.dispatch(:load_files, files)
30
+ end
31
+
32
+ def start_all
33
+ DeepTest.logger.debug "dispatch start_all"
34
+ @slave_controller.dispatch(:start_all)
35
+ end
36
+
37
+ def stop_all
38
+ DeepTest.logger.debug "dispatch stop_all"
39
+ @slave_controller.dispatch_with_options(:stop_all, :ignore_connection_error => true)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module DeepTest
2
+ module Distributed
3
+ #
4
+ # Work Unit used to measure throughput of workers.
5
+ #
6
+ class NullWorkUnit
7
+ def run
8
+ :null_work_unit_result
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RemoteWorkerClient
4
+ def initialize(options, test_server, failover_workers)
5
+ @failover_workers = failover_workers
6
+ @options = options
7
+ @test_server = test_server
8
+ end
9
+
10
+ def load_files(filelist)
11
+ @options.new_listener_list.before_sync
12
+
13
+ t = Thread.new do
14
+ @test_server.sync(@options)
15
+ @worker_server = @test_server.spawn_worker_server(@options)
16
+ @worker_server.load_files filelist
17
+ end
18
+
19
+ filelist.each {|f| load f}
20
+
21
+ begin
22
+ t.join
23
+ rescue => e
24
+ # The failover here doesn't invoke load_files on the failover_workers
25
+ # because they will be LocalWorkers, which fork from the current
26
+ # process. The fact that we depend in this here is damp...
27
+ #
28
+ fail_over("load_files", e)
29
+ end
30
+ end
31
+
32
+ def start_all
33
+ @worker_server.start_all
34
+ rescue => e
35
+ raise if failed_over?
36
+ fail_over("start_all", e)
37
+ retry
38
+ end
39
+
40
+ def stop_all
41
+ @worker_server.stop_all
42
+ end
43
+
44
+ def fail_over(method, exception)
45
+ @options.ui_instance.distributed_failover_to_local(method, exception)
46
+ @worker_server = @failover_workers
47
+ end
48
+
49
+ def failed_over?
50
+ @worker_server == @failover_workers
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,82 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RemoteWorkerServer
4
+ include DRb::DRbUndumped
5
+
6
+ MERCY_KILLING_GRACE_PERIOD = 10 * 60 unless defined?(MERCY_KILLING_GRACE_PERIOD)
7
+
8
+ def initialize(base_path, workers)
9
+ @base_path = base_path
10
+ @workers = workers
11
+ end
12
+
13
+ def launch_mercy_killer(grace_period)
14
+ Thread.new do
15
+ sleep grace_period
16
+ exit(0) unless workers_started?
17
+ end
18
+ end
19
+
20
+ def load_files(files)
21
+ Dir.chdir @base_path
22
+ resolver = FilenameResolver.new(@base_path)
23
+ files.each do |file|
24
+ load resolver.resolve(file)
25
+ end
26
+ end
27
+
28
+ def start_all
29
+ @workers_started = true
30
+ @workers.start_all
31
+ end
32
+
33
+ def stop_all
34
+ Thread.new do
35
+ @workers.stop_all
36
+ end
37
+ end
38
+
39
+ def workers_started?
40
+ @workers_started
41
+ end
42
+
43
+ def self.warlock
44
+ @warlock ||= DeepTest::Warlock.new
45
+ end
46
+
47
+ def self.running_server_count
48
+ @warlock.demon_count if @warlock
49
+ end
50
+
51
+ def self.stop_all
52
+ @warlock.stop_all if @warlock
53
+ end
54
+
55
+ def self.start(address, base_path, workers, grace_period = MERCY_KILLING_GRACE_PERIOD)
56
+ innie, outie = IO.pipe
57
+
58
+ warlock.start("RemoteWorkerServer") do
59
+ innie.close
60
+
61
+ server = new(base_path, workers)
62
+
63
+ DRb.start_service("drubyall://#{address}:0", server)
64
+ DeepTest.logger.info "RemoteWorkerServer started at #{DRb.uri}"
65
+
66
+ outie.write DRb.uri
67
+ outie.close
68
+
69
+ server.launch_mercy_killer(grace_period)
70
+
71
+ DRb.thread.join
72
+ end
73
+
74
+ outie.close
75
+ uri = innie.gets
76
+ innie.close
77
+ DRbObject.new_with_uri(uri)
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,37 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RSync
4
+ def self.sync(connection_info, options, destination)
5
+ command = Args.new(connection_info, options).command(destination)
6
+ DeepTest.logger.debug("rsycing: #{command}")
7
+ successful = system command
8
+ raise "RSync Failed!!" unless successful
9
+ end
10
+
11
+ class Args
12
+ def initialize(connection_info, options)
13
+ @connection_info = connection_info
14
+ @options = options
15
+ @sync_options = options.sync_options
16
+ end
17
+
18
+ def command(destination)
19
+ # The '/' after source tells rsync to copy the contents
20
+ # of source to destination, rather than the source directory
21
+ # itself
22
+ "rsync -az --delete #{@sync_options[:rsync_options]} #{source_location}/ #{destination}".strip.squeeze(" ")
23
+ end
24
+
25
+ def source_location
26
+ source = ""
27
+ unless @sync_options[:local]
28
+ source << @sync_options[:username] << '@' if @sync_options[:username]
29
+ source << @connection_info.address
30
+ source << (@sync_options[:daemon] ? '::' : ':')
31
+ end
32
+ source << @sync_options[:source]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ <html>
2
+ <head>
3
+ <title>DeepTest Test Server Status</title>
4
+ <style>
5
+ .ok {
6
+ background-color: #AAFFAA;
7
+ }
8
+
9
+ .error {
10
+ background-color: #FFAAAA;
11
+ }
12
+
13
+ </style>
14
+ </head>
15
+ <body>
16
+ <h1>DeepTest Test Server Status</h1>
17
+ <table>
18
+ <tr>
19
+ <th>Mirror Server</th>
20
+ <th>Bound To</th>
21
+ <th>Workers / Client</th>
22
+ <th>Clients</th>
23
+ </tr>
24
+ <% test_server_statuses.each do |uri, status|
25
+ if Exception === status %>
26
+ <tr class="error">
27
+ <td><%=h uri %></td>
28
+ <td colspan="3"><%=h status.message %></td>
29
+ </tr>
30
+ <% else %>
31
+ <tr class="ok">
32
+ <td><%=h uri %></td>
33
+ <td><%=h status.binding_uri %></td>
34
+ <td><%=h status.number_of_workers %></td>
35
+ <td><%=h status.remote_worker_server_count %></td>
36
+ </tr>
37
+ <% end
38
+ end %>
39
+ </table>
40
+ </body>
41
+ </html>