deep_test 1.1.4 → 1.2.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 (99) hide show
  1. data/CHANGELOG +15 -5
  2. data/README +149 -5
  3. data/Rakefile +131 -11
  4. data/bin/deep_test +15 -0
  5. data/lib/deep_test.rb +62 -4
  6. data/lib/deep_test/database/mysql_setup_listener.rb +109 -0
  7. data/lib/deep_test/database/setup_listener.rb +116 -0
  8. data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
  9. data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
  10. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  11. data/lib/deep_test/distributed/master_test_server.rb +52 -0
  12. data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
  13. data/lib/deep_test/distributed/null_work_unit.rb +12 -0
  14. data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
  15. data/lib/deep_test/distributed/remote_worker_server.rb +82 -0
  16. data/lib/deep_test/distributed/rsync.rb +37 -0
  17. data/lib/deep_test/distributed/show_status.rhtml +41 -0
  18. data/lib/deep_test/distributed/test_server.rb +78 -0
  19. data/lib/deep_test/distributed/test_server_status.rb +9 -0
  20. data/lib/deep_test/distributed/test_server_workers.rb +24 -0
  21. data/lib/deep_test/distributed/throughput_runner.rb +42 -0
  22. data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
  23. data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
  24. data/lib/deep_test/extensions/drb_extension.rb +34 -0
  25. data/lib/deep_test/extensions/object_extension.rb +8 -0
  26. data/lib/deep_test/listener_list.rb +17 -0
  27. data/lib/deep_test/local_workers.rb +55 -0
  28. data/lib/deep_test/logger.rb +10 -2
  29. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  30. data/lib/deep_test/metrics/gatherer.rb +67 -0
  31. data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
  32. data/lib/deep_test/null_worker_listener.rb +50 -0
  33. data/lib/deep_test/option.rb +60 -0
  34. data/lib/deep_test/options.rb +61 -24
  35. data/lib/deep_test/process_orchestrator.rb +31 -78
  36. data/lib/deep_test/rake_tasks.rb +3 -0
  37. data/lib/deep_test/result_reader.rb +36 -0
  38. data/lib/deep_test/rspec_detector.rb +15 -8
  39. data/lib/deep_test/server.rb +68 -4
  40. data/lib/deep_test/spec.rb +1 -0
  41. data/lib/deep_test/spec/extensions/example_group_methods.rb +37 -11
  42. data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
  43. data/lib/deep_test/spec/extensions/options.rb +35 -3
  44. data/lib/deep_test/spec/runner.rb +19 -10
  45. data/lib/deep_test/spec/work_result.rb +12 -9
  46. data/lib/deep_test/spec/work_unit.rb +12 -7
  47. data/lib/deep_test/test.rb +1 -1
  48. data/lib/deep_test/test/extensions/error.rb +7 -7
  49. data/lib/deep_test/test/runner.rb +1 -6
  50. data/lib/deep_test/test/supervised_test_suite.rb +19 -16
  51. data/lib/deep_test/test/work_result.rb +34 -0
  52. data/lib/deep_test/test/work_unit.rb +6 -2
  53. data/lib/deep_test/test_task.rb +15 -35
  54. data/lib/deep_test/ui/console.rb +76 -0
  55. data/lib/deep_test/ui/null.rb +17 -0
  56. data/lib/deep_test/warlock.rb +92 -17
  57. data/lib/deep_test/worker.rb +38 -2
  58. data/script/internal/run_test_suite.rb +7 -0
  59. data/script/public/master_test_server.rb +24 -0
  60. data/script/public/test_server.rb +18 -0
  61. data/script/public/test_throughput.rb +29 -0
  62. data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
  63. data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
  64. data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
  65. data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
  66. data/test/deep_test/distributed/master_test_server_test.rb +32 -0
  67. data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
  68. data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
  69. data/test/deep_test/distributed/remote_worker_server_test.rb +99 -0
  70. data/test/deep_test/distributed/rsync_test.rb +67 -0
  71. data/test/deep_test/distributed/test_server_test.rb +94 -0
  72. data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
  73. data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
  74. data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
  75. data/test/deep_test/listener_list_test.rb +20 -0
  76. data/test/deep_test/local_workers_test.rb +22 -0
  77. data/test/{logger_test.rb → deep_test/logger_test.rb} +2 -2
  78. data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
  79. data/test/deep_test/metrics/gatherer_test.rb +66 -0
  80. data/test/deep_test/process_orchestrator_test.rb +11 -0
  81. data/test/deep_test/result_reader_test.rb +128 -0
  82. data/test/deep_test/server_test.rb +58 -0
  83. data/test/deep_test/test/extensions/error_test.rb +40 -0
  84. data/test/deep_test/test/supervised_test_suite_test.rb +19 -29
  85. data/test/deep_test/test/work_result_test.rb +81 -0
  86. data/test/deep_test/test/work_unit_test.rb +15 -4
  87. data/test/deep_test/test_task_test.rb +10 -0
  88. data/test/deep_test/ui/console_test.rb +9 -0
  89. data/test/deep_test/warlock_test.rb +17 -0
  90. data/test/deep_test/worker_test.rb +32 -0
  91. data/test/simple_test_blackboard.rb +2 -1
  92. data/test/test_helper.rb +1 -0
  93. metadata +117 -59
  94. data/lib/deep_test/rinda_blackboard.rb +0 -26
  95. data/lib/deep_test/test/extensions/test_result.rb +0 -17
  96. data/lib/deep_test/tuple_space_factory.rb +0 -12
  97. data/script/run_test_suite.rb +0 -5
  98. data/test/deep_test/rinda_blackboard_test.rb +0 -15
  99. data/test/deep_test/test/extensions/test_result_test.rb +0 -71
@@ -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>
@@ -0,0 +1,78 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class TestServer
4
+ DEFAULT_CONFIG = {
5
+ :work_dir => "/tmp",
6
+ :uri => "drubyall://:4022",
7
+ :number_of_workers => 2
8
+ } unless defined?(DEFAULT_CONFIG)
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ def spawn_worker_server(options)
15
+ DeepTest.logger.debug("mirror spawn_worker_server for #{options.origin_hostname}")
16
+ RemoteWorkerServer.start(URI.parse(@config[:uri]).host,
17
+ options.mirror_path(@config[:work_dir]),
18
+ TestServerWorkers.new(options, @config, DRbClientConnectionInfo.new))
19
+ end
20
+
21
+ def status
22
+ TestServerStatus.new(
23
+ DRb.uri,
24
+ @config[:number_of_workers],
25
+ RemoteWorkerServer.running_server_count
26
+ )
27
+ end
28
+
29
+ def sync(options)
30
+ DeepTest.logger.debug "mirror sync for #{options.origin_hostname}"
31
+ path = options.mirror_path(@config[:work_dir])
32
+ DeepTest.logger.debug "Syncing #{options.sync_options[:source]} to #{path}"
33
+ RSync.sync(DRbClientConnectionInfo.new, options, path)
34
+ end
35
+
36
+ def servers
37
+ [DRbObject.new_with_uri(DRb.uri)]
38
+ end
39
+
40
+ def self.start(config)
41
+ server = DeepTest::Distributed::TestServer.new(config)
42
+ DRb.start_service(config[:uri], server)
43
+ DeepTest.logger.info "TestServer listening at #{DRb.uri}"
44
+ DRb.thread.join
45
+ end
46
+
47
+ def self.parse_args(args)
48
+ options = DeepTest::Distributed::TestServer::DEFAULT_CONFIG.dup
49
+ OptionParser.new do |opts|
50
+ opts.banner = "Usage: deep_test test_server [options]"
51
+
52
+ opts.on("--work_dir PATH", "Absolute path to keep mirror working copies at") do |v|
53
+ options[:work_dir] = v
54
+ end
55
+
56
+ opts.on("--uri URI", "DRb URI to bind server to") do |v|
57
+ options[:uri] = v
58
+ end
59
+
60
+ opts.on("--number_of_workers NUM", "Number of workers to start when running tests") do |v|
61
+ options[:number_of_workers] = v.to_i
62
+ end
63
+
64
+ opts.on_tail("-h", "--help", "Show this message") do
65
+ puts opts
66
+ exit
67
+ end
68
+ end.parse(args)
69
+ options
70
+ end
71
+
72
+ def self.connect(options)
73
+ servers = DRbObject.new_with_uri(options.distributed_server).servers
74
+ MultiTestServerProxy.new(options, servers)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ module DeepTest
2
+ module Distributed
3
+ TestServerStatus = Struct.new(
4
+ :binding_uri,
5
+ :number_of_workers,
6
+ :remote_worker_server_count
7
+ ) unless defined?(TestServerStatus)
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class TestServerWorkers < LocalWorkers
4
+ def initialize(options, test_server_config, connection_info)
5
+ super(options)
6
+ @test_server_config = test_server_config
7
+ @connection_info = connection_info
8
+ end
9
+
10
+ def number_of_workers
11
+ @test_server_config[:number_of_workers]
12
+ end
13
+
14
+ def server
15
+ Server.remote_reference(@connection_info.address, @options.server_port)
16
+ end
17
+
18
+ def start_all
19
+ super
20
+ @warlock.exit_when_none_running
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class ThroughputRunner
4
+ def initialize(options, test_count, blackboard = nil, &block)
5
+ @options = options
6
+ @test_count = test_count
7
+ @blackboard = blackboard
8
+ @progress_block = block
9
+ end
10
+
11
+ def blackboard
12
+ @blackboard ||= @options.server
13
+ end
14
+
15
+ def statistics
16
+ ThroughputStatistics.new(@test_count, @start_time, @end_time)
17
+ end
18
+
19
+ def process_work_units
20
+ @start_time = Time.now
21
+
22
+ @test_count.times do
23
+ blackboard.write_work NullWorkUnit.new
24
+ end
25
+
26
+ results_read = 0
27
+ until results_read == @test_count
28
+ Thread.pass
29
+ result = blackboard.take_result
30
+ if result
31
+ results_read += 1
32
+ @progress_block.call(result) if @progress_block
33
+ end
34
+ end
35
+
36
+ @end_time = Time.now
37
+
38
+ true
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class ThroughputStatistics
4
+ attr_reader :test_count, :start_time, :end_time
5
+
6
+ def initialize(test_count, start_time, end_time)
7
+ @test_count, @start_time, @end_time = test_count, start_time, end_time
8
+ end
9
+
10
+ def timespan_in_seconds
11
+ @end_time.to_f - @start_time.to_f
12
+ end
13
+
14
+ def tests_per_second
15
+ @test_count / timespan_in_seconds
16
+ end
17
+
18
+ def summary
19
+ <<-end_summary
20
+ #{test_count} tests run in #{timespan_in_seconds} seconds
21
+ (#{tests_per_second} tests / second)
22
+ end_summary
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class ThroughputWorkerClient
4
+ def initialize(options, test_server)
5
+ @options = options
6
+ @test_server = test_server
7
+ end
8
+
9
+ def start_all
10
+ @worker_server = @test_server.spawn_worker_server(@options)
11
+ @worker_server.start_all
12
+ end
13
+
14
+ def stop_all
15
+ @worker_server.stop_all
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module DeepTest
2
+ class DRbBindAllTCPSocket < DRb::DRbTCPSocket
3
+ def self.parse_uri(uri)
4
+ if uri =~ /^drubyall:\/\/(.*?):(\d+)(\?(.*))?$/
5
+ host = $1
6
+ port = $2.to_i
7
+ option = $4
8
+ [host, port, option]
9
+ else
10
+ raise(DRb::DRbBadScheme, uri) unless uri =~ /^drubyall:/
11
+ raise(DRb::DRbBadURI, 'can\'t parse uri:' + uri)
12
+ end
13
+ end
14
+
15
+ # Open a server listening for connections at +uri+ using
16
+ # configuration +config+.
17
+ def self.open_server(uri, config)
18
+ uri = 'drubyall://:0' unless uri
19
+ host, port, opt = parse_uri(uri)
20
+
21
+ if host.size == 0
22
+ host = getservername
23
+ end
24
+
25
+ DeepTest.logger.debug("Listening on port #{port}, all addresses.")
26
+ soc = TCPServer.open('0.0.0.0', port)
27
+ port = soc.addr[1] if port == 0
28
+ uri = "druby://#{host}:#{port}"
29
+ self.new(uri, soc, config)
30
+ end
31
+ end
32
+ end
33
+
34
+ DRb::DRbProtocol.add_protocol DeepTest::DRbBindAllTCPSocket