deep_test_pre 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 (98) hide show
  1. data/CHANGELOG +47 -0
  2. data/README.rdoc +199 -0
  3. data/Rakefile +137 -0
  4. data/lib/deep_test.rb +78 -0
  5. data/lib/deep_test/agent.rb +108 -0
  6. data/lib/deep_test/central_command.rb +165 -0
  7. data/lib/deep_test/cpu_info.rb +22 -0
  8. data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
  9. data/lib/deep_test/database/setup_listener.rb +116 -0
  10. data/lib/deep_test/deadlock_detector.rb +7 -0
  11. data/lib/deep_test/demon.rb +25 -0
  12. data/lib/deep_test/distributed/beachhead.rb +104 -0
  13. data/lib/deep_test/distributed/dispatch_controller.rb +60 -0
  14. data/lib/deep_test/distributed/establish_beachhead.rb +19 -0
  15. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  16. data/lib/deep_test/distributed/landing_fleet.rb +30 -0
  17. data/lib/deep_test/distributed/landing_ship.rb +60 -0
  18. data/lib/deep_test/distributed/remote_deployment.rb +56 -0
  19. data/lib/deep_test/distributed/rsync.rb +50 -0
  20. data/lib/deep_test/distributed/shell_environment.rb +50 -0
  21. data/lib/deep_test/distributed/ssh_client_connection_info.rb +14 -0
  22. data/lib/deep_test/extensions/object_extension.rb +40 -0
  23. data/lib/deep_test/failure_message.rb +19 -0
  24. data/lib/deep_test/lib_root.rb +4 -0
  25. data/lib/deep_test/listener_list.rb +17 -0
  26. data/lib/deep_test/local_deployment.rb +46 -0
  27. data/lib/deep_test/logger.rb +32 -0
  28. data/lib/deep_test/main.rb +41 -0
  29. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  30. data/lib/deep_test/metrics/data.rb +34 -0
  31. data/lib/deep_test/metrics/measurement.rb +39 -0
  32. data/lib/deep_test/null_listener.rb +62 -0
  33. data/lib/deep_test/options.rb +113 -0
  34. data/lib/deep_test/proxy_io.rb +77 -0
  35. data/lib/deep_test/rake_tasks.rb +13 -0
  36. data/lib/deep_test/result_reader.rb +40 -0
  37. data/lib/deep_test/rspec_detector.rb +21 -0
  38. data/lib/deep_test/spec.rb +17 -0
  39. data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
  40. data/lib/deep_test/spec/extensions/example_methods.rb +52 -0
  41. data/lib/deep_test/spec/extensions/options.rb +43 -0
  42. data/lib/deep_test/spec/extensions/spec_task.rb +21 -0
  43. data/lib/deep_test/spec/runner.rb +72 -0
  44. data/lib/deep_test/spec/work_result.rb +35 -0
  45. data/lib/deep_test/spec/work_unit.rb +59 -0
  46. data/lib/deep_test/test.rb +10 -0
  47. data/lib/deep_test/test/extensions/error.rb +14 -0
  48. data/lib/deep_test/test/run_test_suite.rb +5 -0
  49. data/lib/deep_test/test/runner.rb +24 -0
  50. data/lib/deep_test/test/supervised_test_suite.rb +48 -0
  51. data/lib/deep_test/test/work_result.rb +35 -0
  52. data/lib/deep_test/test/work_unit.rb +40 -0
  53. data/lib/deep_test/test_task.rb +47 -0
  54. data/lib/deep_test/ui/console.rb +74 -0
  55. data/lib/deep_test/ui/null.rb +17 -0
  56. data/lib/deep_test/warlock.rb +146 -0
  57. data/lib/telegraph.rb +29 -0
  58. data/lib/telegraph/ack_sequence.rb +14 -0
  59. data/lib/telegraph/logging.rb +20 -0
  60. data/lib/telegraph/message.rb +39 -0
  61. data/lib/telegraph/operator.rb +47 -0
  62. data/lib/telegraph/switchboard.rb +57 -0
  63. data/lib/telegraph/wire.rb +73 -0
  64. data/test/deep_test/agent_test.rb +175 -0
  65. data/test/deep_test/central_command_test.rb +147 -0
  66. data/test/deep_test/cpu_info_test.rb +33 -0
  67. data/test/deep_test/database/mysql_setup_listener_test.rb +18 -0
  68. data/test/deep_test/demon_test.rb +23 -0
  69. data/test/deep_test/distributed/beachhead_test.rb +67 -0
  70. data/test/deep_test/distributed/dispatch_controller_test.rb +162 -0
  71. data/test/deep_test/distributed/filename_resolver_test.rb +56 -0
  72. data/test/deep_test/distributed/landing_fleet_test.rb +55 -0
  73. data/test/deep_test/distributed/landing_ship_test.rb +48 -0
  74. data/test/deep_test/distributed/remote_deployment_test.rb +134 -0
  75. data/test/deep_test/distributed/rsync_test.rb +47 -0
  76. data/test/deep_test/distributed/shell_environment_test.rb +108 -0
  77. data/test/deep_test/distributed/ssh_client_connection_info_test.rb +34 -0
  78. data/test/deep_test/extensions/object_extension_test.rb +37 -0
  79. data/test/deep_test/listener_list_test.rb +22 -0
  80. data/test/deep_test/local_deployment_test.rb +19 -0
  81. data/test/deep_test/logger_test.rb +38 -0
  82. data/test/deep_test/main_test.rb +12 -0
  83. data/test/deep_test/marshallable_exception_wrapper_test.rb +46 -0
  84. data/test/deep_test/metrics/data_test.rb +22 -0
  85. data/test/deep_test/metrics/measurement_test.rb +18 -0
  86. data/test/deep_test/proxy_io_test.rb +104 -0
  87. data/test/deep_test/result_reader_test.rb +128 -0
  88. data/test/deep_test/test/extensions/error_test.rb +42 -0
  89. data/test/deep_test/test/runner_test.rb +11 -0
  90. data/test/deep_test/test/supervised_test_suite_test.rb +107 -0
  91. data/test/deep_test/test/work_result_test.rb +85 -0
  92. data/test/deep_test/test/work_unit_test.rb +63 -0
  93. data/test/deep_test/test_task_test.rb +15 -0
  94. data/test/deep_test/ui/console_test.rb +13 -0
  95. data/test/deep_test/warlock_test.rb +40 -0
  96. data/test/test_helper.rb +30 -0
  97. data/test/test_task_test.rb +75 -0
  98. metadata +156 -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,25 @@
1
+ module DeepTest
2
+ module Demon
3
+ def forked(name, options, demon_args)
4
+ options.connect_to_central_command do |wire|
5
+ ProxyIO.replace_stdout_stderr!(wire) do
6
+ begin
7
+ catch(:exit_demon) do
8
+ Signal.trap("TERM") { throw :exit_demon }
9
+ execute *demon_args
10
+ end
11
+ rescue SystemExit => e
12
+ raise
13
+ rescue Exception => e
14
+ FailureMessage.show self.class.name, "Process #{Process.pid} exiting with excetion: #{e.class}: #{e.message}"
15
+ raise
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def execute(*args)
22
+ raise "#{self.class} must implement the execute method to be a Demon"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,104 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class Beachhead < LocalDeployment
4
+ include Demon
5
+
6
+ MERCY_KILLING_GRACE_PERIOD = 10 * 60 unless defined?(MERCY_KILLING_GRACE_PERIOD)
7
+
8
+ def initialize(base_path, options)
9
+ super options
10
+ @base_path = base_path
11
+ end
12
+
13
+ def launch_mercy_killer(grace_period)
14
+ Thread.new do
15
+ sleep grace_period
16
+ exit(0) unless agents_deployed?
17
+ end
18
+ end
19
+
20
+ def load_files(files)
21
+ spec_support_path = File.expand_path(File.dirname(__FILE__) + "/../spec")
22
+ Dir.chdir @base_path
23
+ resolver = FilenameResolver.new(@base_path)
24
+ files.each do |file|
25
+ load resolver.resolve(file)
26
+ end
27
+
28
+ # Load rspec support if rspec is available now that we've loaded the host project files
29
+ #
30
+ DeepTest::RSpecDetector.if_rspec_available do
31
+ require spec_support_path
32
+ end
33
+ end
34
+
35
+ def deploy_agents
36
+ @agents_deployed = true
37
+ super
38
+ warlock.exit_when_none_running
39
+ end
40
+
41
+ def agents_deployed?
42
+ @agents_deployed
43
+ end
44
+
45
+ def forked(*args)
46
+ $stdout.reopen("/dev/null")
47
+ $stderr.reopen("/dev/null")
48
+ super
49
+ end
50
+
51
+ def execute(innie, outie, grace_period)
52
+ innie.close
53
+
54
+ switchboard = Telegraph::Switchboard.new
55
+ operator = Telegraph::Operator.listen "0.0.0.0", 0, switchboard
56
+
57
+ DeepTest.logger.debug { "Beachhead started on port #{operator.port}" }
58
+
59
+ outie.write operator.port
60
+ outie.close
61
+
62
+ launch_mercy_killer grace_period
63
+
64
+ loop do
65
+ begin
66
+ switchboard.process_messages :timeout => 1 do |message, wire|
67
+ case message.body
68
+ when LoadFiles
69
+ load_files message.body.files
70
+ when DeployAgents
71
+ deploy_agents
72
+ wire.send_message Done
73
+ operator.shutdown
74
+ break
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def daemonize(grace_period = MERCY_KILLING_GRACE_PERIOD)
82
+ innie, outie = IO.pipe
83
+
84
+ warlock.start "Beachhead", self, innie, outie, grace_period
85
+
86
+ outie.close
87
+ port = innie.gets
88
+ innie.close
89
+ port.to_i
90
+ end
91
+
92
+ unless defined? DeployAgents
93
+ DeployAgents = "DeployAgents"
94
+ Done = "Done"
95
+ class LoadFiles
96
+ attr_reader :files
97
+ def initialize(files)
98
+ @files = files
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,60 @@
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
+ begin
21
+ Thread.current[:receiver] = r
22
+ r.send method_name, *args
23
+ rescue Exception => e
24
+ Thread.current[:original_exception] = e
25
+ raise
26
+ end
27
+ end
28
+ end
29
+
30
+ results = []
31
+ threads.each do |t|
32
+ begin
33
+ results << t.value
34
+ rescue Timeout::Error
35
+ @receivers.delete t[:receiver]
36
+ DeepTest.logger.error { "Timeout dispatching #{method_name} to #{description t[:receiver]}" }
37
+ rescue Exception => this_exception
38
+ @receivers.delete t[:receiver]
39
+ DeepTest.logger.error { "Exception while dispatching #{method_name} to #{description t[:receiver]}:" }
40
+
41
+ e = t[:original_exception] || this_exception
42
+ DeepTest.logger.error { "#{e.class}: #{e.message}" }
43
+ e.backtrace.each {|l| DeepTest.logger.error { l } }
44
+ end
45
+ end
46
+
47
+ results
48
+ ensure
49
+ @options.ui_instance.dispatch_finished(method_name)
50
+ end
51
+
52
+ def description(receiver)
53
+ receiver.inspect
54
+ end
55
+
56
+ end
57
+
58
+ class NoDispatchReceiversError < StandardError; end
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + "/../../deep_test"
2
+ options = DeepTest::Options.from_command_line(ENV['OPTIONS'])
3
+ ENV['DEEP_TEST_LOG_LEVEL'] = options.environment_log_level
4
+ options.ssh_client_connection_info = DeepTest::Distributed::SshClientConnectionInfo.new
5
+
6
+ DeepTest.logger.debug { "mirror establish_beachhead for #{options.origin_hostname}" }
7
+
8
+ STDIN.close
9
+
10
+ beachhead_port = DeepTest::Distributed::Beachhead.new(
11
+ File.join(options.mirror_path, File.basename(options.sync_options[:source])),
12
+ options
13
+ ).daemonize
14
+
15
+ puts "Beachhead port: #{beachhead_port}"
16
+ $stdout.flush
17
+
18
+ exit!(0)
19
+
@@ -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,30 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class LandingFleet
4
+ def initialize(options, slaves)
5
+ DeepTest.logger.debug { "LandingFleet#initialize #{slaves.length} slaves" }
6
+ @slave_controller = DispatchController.new(options, slaves)
7
+ end
8
+
9
+ def establish_beachhead(options)
10
+ DeepTest.logger.debug { "dispatch establish_beachhead for #{options.origin_hostname}" }
11
+ @slave_controller.dispatch :establish_beachhead, options
12
+ end
13
+
14
+ def push_code(options)
15
+ DeepTest.logger.debug { "dispatch push_code for #{options.origin_hostname}" }
16
+ @slave_controller.dispatch :push_code, options
17
+ end
18
+
19
+ def load_files(files)
20
+ DeepTest.logger.debug { "dispatch load_files" }
21
+ @slave_controller.dispatch :load_files, files
22
+ end
23
+
24
+ def deploy_agents
25
+ DeepTest.logger.debug { "dispatch deploy_agents" }
26
+ @slave_controller.dispatch :deploy_agents
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class LandingShip
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def push_code(options)
9
+ RSync.push(@config[:address], options.sync_options, options.mirror_path)
10
+ end
11
+
12
+ def establish_beachhead(options)
13
+ command = "#{ssh_command(options)} '#{spawn_command(options)}' 2>&1"
14
+ DeepTest.logger.debug { "Establishing Beachhead: #{command}" }
15
+
16
+ output = `#{command}`
17
+ output.each do |line|
18
+ if DeepTest.logger.level == Logger::DEBUG
19
+ puts output
20
+ end
21
+ if line =~ /Beachhead port: (.+)/
22
+ @wire = Telegraph::Wire.connect(@config[:address], $1.to_i)
23
+ end
24
+ end
25
+ raise "LandingShip unable to establish Beachhead. Output from #{@config[:address]} was:\n#{output}" unless @wire
26
+ end
27
+
28
+ def load_files(files)
29
+ @wire.send_message Beachhead::LoadFiles.new(files)
30
+ end
31
+
32
+ def deploy_agents
33
+ @wire.send_message Beachhead::DeployAgents
34
+ begin
35
+ message = @wire.next_message :timeout => 1
36
+ raise "Unexpected message from Beachhead: #{message.inspect}" unless message.body == Beachhead::Done
37
+ rescue Telegraph::NoMessageAvailable
38
+ retry
39
+ end
40
+ end
41
+
42
+ def ssh_command(options)
43
+ username_option = if options.sync_options[:username]
44
+ " -l #{options.sync_options[:username]}"
45
+ else
46
+ ""
47
+ end
48
+
49
+ "ssh -4 #{@config[:address]}#{username_option}"
50
+ end
51
+
52
+ def spawn_command(options)
53
+ "#{ShellEnvironment.like_login} && " +
54
+ "cd #{options.mirror_path} && " +
55
+ "OPTIONS=#{options.to_command_line} " +
56
+ "ruby lib/deep_test/distributed/establish_beachhead.rb"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,56 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RemoteDeployment
4
+ def initialize(options, landing_fleet, failover_deployment)
5
+ @failover_deployment = failover_deployment
6
+ @options = options
7
+ @landing_fleet = landing_fleet
8
+ end
9
+
10
+ def load_files(filelist)
11
+ # load one file before calling listeners to make sure environment has
12
+ # been initialized as expected
13
+ #
14
+ load filelist.first
15
+ @options.new_listener_list.before_sync
16
+
17
+ t = Thread.new do
18
+ @landing_fleet.push_code(@options)
19
+ @landing_fleet.establish_beachhead(@options)
20
+ @landing_fleet.load_files filelist
21
+ end
22
+
23
+ filelist[1..-1].each {|f| load f}
24
+
25
+ begin
26
+ t.join
27
+ rescue => e
28
+ # The failover here doesn't invoke load_files on the failover_deployment
29
+ # because it will be a LocalDeployment, which forks from the current
30
+ # process. The fact that we depend in this here is damp...
31
+ #
32
+ fail_over("load_files", e)
33
+ end
34
+ end
35
+
36
+ def deploy_agents
37
+ DeepTest.logger.debug { "RemoteDeployment deploying agents with #{@landing_fleet}" }
38
+ @landing_fleet.deploy_agents
39
+ rescue => e
40
+ raise if failed_over?
41
+ fail_over("deploy_agents", e)
42
+ retry
43
+ end
44
+
45
+ def fail_over(method, exception)
46
+ DeepTest.logger.debug { "RemoteDeployment failing over on #{method}." }
47
+ @options.ui_instance.distributed_failover_to_local(method, exception)
48
+ @landing_fleet = @failover_deployment
49
+ end
50
+
51
+ def failed_over?
52
+ @landing_fleet == @failover_deployment
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,50 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class RSync
4
+ def self.push(address, sync_options, destination)
5
+ sync(:push, address, sync_options, destination)
6
+ end
7
+
8
+ def self.sync(operation, address, sync_options, destination)
9
+ command = Args.new(address, sync_options).send("#{operation}_command",
10
+ destination)
11
+
12
+ DeepTest.logger.debug { "rsycing: #{command}" }
13
+ successful = system command
14
+ raise "RSync Failed!!" unless successful
15
+ end
16
+
17
+ class Args
18
+ def initialize(address, sync_options)
19
+ @address = address
20
+ @sync_options = sync_options
21
+ end
22
+
23
+ def pull_command(destination)
24
+ command remote_location(@sync_options[:source]), destination
25
+ end
26
+
27
+ def push_command(destination)
28
+ command @sync_options[:source], remote_location(destination)
29
+ end
30
+
31
+ def command(source, destination)
32
+ # The '/' after source tells rsync to copy the contents
33
+ # of source to destination, rather than the source directory
34
+ # itself
35
+ "rsync -az --delete #{@sync_options[:rsync_options]} #{DeepTest::LIB_ROOT} #{source} #{destination}".strip.squeeze(" ")
36
+ end
37
+
38
+ def remote_location(path)
39
+ source = ""
40
+ unless @sync_options[:local]
41
+ source << @sync_options[:username] << '@' if @sync_options[:username]
42
+ source << @address
43
+ source << (@sync_options[:daemon] ? '::' : ':')
44
+ end
45
+ source << path
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end