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,17 @@
1
+ module DeepTest
2
+ module UI
3
+ class Null
4
+ def initialize(options)
5
+ end
6
+
7
+ def distributed_failover_to_local(method, exception)
8
+ end
9
+
10
+ def dispatch_starting(method_name)
11
+ end
12
+
13
+ def dispatch_finished(method_name)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,146 @@
1
+ module DeepTest
2
+ class Warlock
3
+ def initialize(options)
4
+ @options = options
5
+ @demons_semaphore = Mutex.new
6
+ @demons = []
7
+ @reapers = []
8
+ end
9
+
10
+ def start(name, demon, *demon_args)
11
+ # Not synchronizing for the fork seems to cause
12
+ # random errors (Bus Error, Segfault, and GC non-object)
13
+ # in Beachhead processes.
14
+ #
15
+ begin
16
+ pid = nil
17
+ @demons_semaphore.synchronize do
18
+ pid = fork do
19
+ # Fork leaves the semaphore locked and we'll never make it
20
+ # to end of synchronize block.
21
+ #
22
+ # The Ruby 1.8.6 C mutex implementation automatically treats
23
+ # a mutex locked by a dead thread as unlocked and will raise
24
+ # an error if we try to unlock it from this thread.
25
+ #
26
+ @demons_semaphore.unlock if @demons_semaphore.locked?
27
+
28
+ close_open_network_connections
29
+ demon.forked name, @options, demon_args
30
+
31
+ exit
32
+ end
33
+
34
+ raise "fatal: fork returned nil" if pid.nil?
35
+ add_demon name, pid
36
+ end
37
+
38
+ launch_reaper_thread name, pid
39
+
40
+ rescue => e
41
+ puts "exception starting #{name}: #{e}"
42
+ puts "\t" + e.backtrace.join("\n\t")
43
+ end
44
+ end
45
+
46
+ def close_open_network_connections
47
+ ObjectSpace.each_object(BasicSocket) do |sock|
48
+ begin
49
+ sock.close
50
+ rescue IOError
51
+ end
52
+ end
53
+ end
54
+
55
+ def demon_count
56
+ @demons_semaphore.synchronize do
57
+ @demons.size
58
+ end
59
+ end
60
+
61
+ def stop_demons
62
+ DeepTest.logger.debug { "stopping all demons" }
63
+ receivers = @demons_semaphore.synchronize do
64
+ @demons.reverse
65
+ end
66
+
67
+ receivers.reverse.each do |demon|
68
+ name, pid = demon
69
+ if running?(pid)
70
+ DeepTest.logger.debug { "Sending SIGTERM to #{name}, #{pid}" }
71
+ Process.kill("TERM", pid)
72
+ end
73
+ end
74
+ DeepTest.logger.debug { "Warlock: Stopped all receivers" }
75
+
76
+ DeepTest.logger.debug { "waiting for reapers" }
77
+ @reapers.each {|r| r.join}
78
+
79
+ DeepTest.logger.debug { "Warlock: done reaping processes" }
80
+ end
81
+
82
+ def exit_when_none_running
83
+ Thread.new do
84
+ wait_for_all_to_finish
85
+ DeepTest.logger.debug { "exiting #{Process.pid} with all demons finished" }
86
+ exit(0)
87
+ end
88
+ end
89
+
90
+ def wait_for_all_to_finish
91
+ loop do
92
+ Thread.pass
93
+ return unless any_running?
94
+ sleep(0.01)
95
+ end
96
+ end
97
+
98
+ def any_running?
99
+ @demons_semaphore.synchronize do
100
+ @demons.any? {|name, pid| running?(pid)}
101
+ end
102
+ end
103
+
104
+ #stolen from daemons
105
+ def running?(pid)
106
+ # Check if process is in existence
107
+ # The simplest way to do this is to send signal '0'
108
+ # (which is a single system call) that doesn't actually
109
+ # send a signal
110
+ begin
111
+ Process.kill(0, pid)
112
+ return true
113
+ rescue Errno::ESRCH
114
+ return false
115
+ rescue ::Exception # for example on EPERM (process exists but does not belong to us)
116
+ return true
117
+ #rescue Errno::EPERM
118
+ # return false
119
+ end
120
+ end
121
+
122
+ protected
123
+
124
+ def add_demon(name, pid)
125
+ DeepTest.logger.debug { "Started: #{name} (#{pid})" }
126
+ @demons << [name, pid]
127
+ end
128
+
129
+ def remove_demon(name, pid)
130
+ @demons.delete [name, pid]
131
+ DeepTest.logger.debug { "Stopped: #{name} (#{pid})" }
132
+ end
133
+
134
+
135
+ def launch_reaper_thread(name, pid)
136
+ @reapers << Thread.new do
137
+ Process.detach(pid).join
138
+ DeepTest.logger.debug { "#{name} (#{pid}) reaped" }
139
+ @demons_semaphore.synchronize do
140
+ DeepTest.logger.debug { "Warlock Reaper: removing #{name} (#{pid}) from demon list" }
141
+ remove_demon name, pid
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
data/lib/telegraph.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'socket'
2
+ require File.dirname(__FILE__) + "/telegraph/logging"
3
+ require File.dirname(__FILE__) + "/telegraph/ack_sequence"
4
+ require File.dirname(__FILE__) + "/telegraph/message"
5
+ require File.dirname(__FILE__) + "/telegraph/wire"
6
+ require File.dirname(__FILE__) + "/telegraph/operator"
7
+ require File.dirname(__FILE__) + "/telegraph/switchboard"
8
+
9
+ module Telegraph
10
+ class Ping
11
+ attr_reader :value
12
+
13
+ def initialize(value)
14
+ @value = value
15
+ end
16
+ end
17
+
18
+ class Pong
19
+ attr_reader :value
20
+
21
+ def initialize(value)
22
+ @value = value
23
+ end
24
+ end
25
+
26
+ class NoMessageAvailable < StandardError; end
27
+ class LineDead < StandardError; end
28
+ end
29
+
@@ -0,0 +1,14 @@
1
+ module Telegraph
2
+ class AckSequence
3
+ def initialize
4
+ @value = 0
5
+ end
6
+
7
+ def next
8
+ Thread.exclusive do
9
+ @value += 1
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,20 @@
1
+ require 'logger'
2
+
3
+ module Telegraph
4
+ module Logging
5
+ def self.logger
6
+ @logger ||= begin
7
+ l = Logger.new($stdout)
8
+ l.level = Logger.const_get((ENV['TELEGRAPH_LOG_LEVEL'] || 'info').upcase)
9
+ l.formatter = proc do |sev, time, progmane, msg|
10
+ "[#{time.strftime "%T"}] (pid #{Process.pid}) #{msg}\n"
11
+ end
12
+ l
13
+ end
14
+ end
15
+
16
+ def debug
17
+ Logging.logger.debug { "#{self.class}: #{yield}" }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ module Telegraph
2
+ class Message
3
+ include Logging
4
+
5
+ attr_reader :body, :sequence_number, :sequence_ack
6
+
7
+ def initialize(body, sequence_number, sequence_ack)
8
+ @body = body
9
+ @sequence_number = sequence_number
10
+ @sequence_ack = sequence_ack
11
+ end
12
+
13
+ def write(stream)
14
+ message_data = Marshal.dump(body)
15
+ debug { "send #{message_data[4..20]}... (#{message_data.length} bytes)" }
16
+ stream.write [message_data.size, @sequence_number, @sequence_ack || 0].pack("NNN") + message_data
17
+ end
18
+
19
+ class <<self
20
+ include Logging
21
+
22
+ def read(stream)
23
+ header_data = read_data(stream, 12, "header")
24
+ size, sequence_number, sequence_ack = header_data.unpack("NNN")
25
+
26
+ message_data = read_data(stream, size, "message")
27
+ debug { "read #{message_data[4..20]}... (#{message_data.length} bytes)" }
28
+ Message.new Marshal.load(message_data), sequence_number, (sequence_ack == 0 ? nil : sequence_ack)
29
+ end
30
+
31
+ def read_data(stream, length, label)
32
+ data = stream.read(length)
33
+ raise IOError, "connection closed" unless data
34
+ raise IOError, "incomplete #{label} data" unless data.length == length
35
+ data
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ require 'thread'
2
+
3
+ module Telegraph
4
+ class Operator
5
+ include Logging
6
+
7
+ attr_reader :switchboard
8
+
9
+ def self.listen(host, port, switchboard = Switchboard.new)
10
+ new TCPServer.new(host, port), switchboard
11
+ end
12
+
13
+ def initialize(socket, switchboard)
14
+ @socket = socket
15
+ @switchboard = switchboard
16
+ @accept_thread = Thread.new do
17
+ @socket.listen 100
18
+ loop do
19
+ if @should_shutdown
20
+ @socket.close
21
+ @switchboard.close_all_wires
22
+ break
23
+ end
24
+
25
+ begin
26
+ client = @socket.accept_nonblock
27
+ debug { "Accepted connection: #{client.inspect}" }
28
+ @switchboard.add_wire Wire.new(client)
29
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
30
+ connection_ready, = IO.select([@socket], nil, nil, 0.25)
31
+ retry if connection_ready
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def port
38
+ @socket.addr[1]
39
+ end
40
+
41
+ def shutdown
42
+ debug { "Shutting down" }
43
+ @should_shutdown = true
44
+ @accept_thread.join
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,57 @@
1
+ module Telegraph
2
+ class Switchboard
3
+ include Logging
4
+
5
+ def process_messages(options = {:timeout => 0})
6
+ yield next_message(options) while true
7
+ rescue NoMessageAvailable
8
+ retry
9
+ end
10
+
11
+ def next_message(options = {:timeout => 0})
12
+ debug { "Waiting for next message on any of #{live_wires.size} wires for #{options[:timeout]} seconds" }
13
+
14
+ if live_wires.empty?
15
+ sleep 0.01
16
+ Thread.pass
17
+ raise NoMessageAvailable
18
+ end
19
+
20
+ readers, = IO.select live_wires.map {|w| w.stream}, nil, nil, options[:timeout]
21
+ raise NoMessageAvailable unless readers
22
+
23
+ wire = using_wires {|wires| wires.detect {|w| w.stream == readers.first} }
24
+ return wire.next_message(options.merge(:timeout => 0)), wire
25
+ rescue LineDead => e
26
+ debug { "LineDead: #{e.message} while reading message from wire" }
27
+ raise NoMessageAvailable
28
+ end
29
+
30
+ def any_live_wires?
31
+ live_wires.any?
32
+ end
33
+
34
+ def drop_wire(wire)
35
+ using_wires {|w| w.delete wire }
36
+ end
37
+
38
+ def add_wire(wire)
39
+ using_wires {|w| w << wire }
40
+ end
41
+
42
+ def close_all_wires
43
+ debug { "Closing all wires" }
44
+ using_wires {|w| w.each { |wire| wire.close rescue nil } }
45
+ end
46
+
47
+ def live_wires
48
+ using_wires {|w| w.select {|wire| !wire.closed?}}
49
+ end
50
+
51
+ def using_wires
52
+ @wires ||= []
53
+ @wires_mutex ||= Mutex.new
54
+ @wires_mutex.synchronize { yield @wires }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,73 @@
1
+ module Telegraph
2
+ class Wire
3
+ include Logging
4
+
5
+ attr_reader :stream
6
+
7
+ def self.connect(host, port)
8
+ wire = new TCPSocket.new(host, port)
9
+ return wire unless block_given?
10
+ begin
11
+ yield wire
12
+ ensure
13
+ wire.close
14
+ end
15
+ end
16
+
17
+ def initialize(stream)
18
+ @sequence = AckSequence.new
19
+ @stream = stream
20
+ end
21
+
22
+ def close
23
+ if @stream.closed?
24
+ debug { "stream already closed" }
25
+ else
26
+ debug { "closing stream #{@stream.inspect}" }
27
+ @stream.close
28
+ end
29
+ end
30
+
31
+ def closed?
32
+ @stream.closed?
33
+ end
34
+
35
+ def send_message(body, options = {})
36
+ sequence_ack = options[:ack] ? options[:ack].sequence_number : nil
37
+ message = Message.new(body, @sequence.next, sequence_ack)
38
+ message.write stream
39
+ unacked_sequences_numbers[message.sequence_number] = message if options[:need_ack]
40
+ rescue IOError, Errno::EPIPE, Errno::ECONNRESET => e
41
+ close rescue nil
42
+ raise LineDead, e.message
43
+ end
44
+
45
+ def process_messages(options = {:timeout => 0})
46
+ yield next_message(options) while true
47
+ rescue NoMessageAvailable
48
+ retry
49
+ end
50
+
51
+ def next_message(options = {:timeout => 0})
52
+ begin
53
+ raise NoMessageAvailable unless IO.select [@stream], nil, nil, options[:timeout]
54
+ message = Message.read(@stream)
55
+ unacked_sequences_numbers.delete message.sequence_ack if message.sequence_ack
56
+ return message
57
+ rescue IOError, Errno::ECONNRESET => e
58
+ raise LineDead, e.message
59
+ end
60
+ rescue LineDead
61
+ close rescue nil
62
+ raise
63
+ end
64
+
65
+ def unacked_sequences_numbers
66
+ @unacked_sequences_numbers ||= {}
67
+ end
68
+
69
+ def unacked_messages
70
+ unacked_sequences_numbers.values
71
+ end
72
+ end
73
+ end