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.
- data/CHANGELOG +47 -0
- data/README.rdoc +199 -0
- data/Rakefile +137 -0
- data/lib/deep_test.rb +78 -0
- data/lib/deep_test/agent.rb +108 -0
- data/lib/deep_test/central_command.rb +165 -0
- data/lib/deep_test/cpu_info.rb +22 -0
- data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
- data/lib/deep_test/database/setup_listener.rb +116 -0
- data/lib/deep_test/deadlock_detector.rb +7 -0
- data/lib/deep_test/demon.rb +25 -0
- data/lib/deep_test/distributed/beachhead.rb +104 -0
- data/lib/deep_test/distributed/dispatch_controller.rb +60 -0
- data/lib/deep_test/distributed/establish_beachhead.rb +19 -0
- data/lib/deep_test/distributed/filename_resolver.rb +40 -0
- data/lib/deep_test/distributed/landing_fleet.rb +30 -0
- data/lib/deep_test/distributed/landing_ship.rb +60 -0
- data/lib/deep_test/distributed/remote_deployment.rb +56 -0
- data/lib/deep_test/distributed/rsync.rb +50 -0
- data/lib/deep_test/distributed/shell_environment.rb +50 -0
- data/lib/deep_test/distributed/ssh_client_connection_info.rb +14 -0
- data/lib/deep_test/extensions/object_extension.rb +40 -0
- data/lib/deep_test/failure_message.rb +19 -0
- data/lib/deep_test/lib_root.rb +4 -0
- data/lib/deep_test/listener_list.rb +17 -0
- data/lib/deep_test/local_deployment.rb +46 -0
- data/lib/deep_test/logger.rb +32 -0
- data/lib/deep_test/main.rb +41 -0
- data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
- data/lib/deep_test/metrics/data.rb +34 -0
- data/lib/deep_test/metrics/measurement.rb +39 -0
- data/lib/deep_test/null_listener.rb +62 -0
- data/lib/deep_test/options.rb +113 -0
- data/lib/deep_test/proxy_io.rb +77 -0
- data/lib/deep_test/rake_tasks.rb +13 -0
- data/lib/deep_test/result_reader.rb +40 -0
- data/lib/deep_test/rspec_detector.rb +21 -0
- data/lib/deep_test/spec.rb +17 -0
- data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
- data/lib/deep_test/spec/extensions/example_methods.rb +52 -0
- data/lib/deep_test/spec/extensions/options.rb +43 -0
- data/lib/deep_test/spec/extensions/spec_task.rb +21 -0
- data/lib/deep_test/spec/runner.rb +72 -0
- data/lib/deep_test/spec/work_result.rb +35 -0
- data/lib/deep_test/spec/work_unit.rb +59 -0
- data/lib/deep_test/test.rb +10 -0
- data/lib/deep_test/test/extensions/error.rb +14 -0
- data/lib/deep_test/test/run_test_suite.rb +5 -0
- data/lib/deep_test/test/runner.rb +24 -0
- data/lib/deep_test/test/supervised_test_suite.rb +48 -0
- data/lib/deep_test/test/work_result.rb +35 -0
- data/lib/deep_test/test/work_unit.rb +40 -0
- data/lib/deep_test/test_task.rb +47 -0
- data/lib/deep_test/ui/console.rb +74 -0
- data/lib/deep_test/ui/null.rb +17 -0
- data/lib/deep_test/warlock.rb +146 -0
- data/lib/telegraph.rb +29 -0
- data/lib/telegraph/ack_sequence.rb +14 -0
- data/lib/telegraph/logging.rb +20 -0
- data/lib/telegraph/message.rb +39 -0
- data/lib/telegraph/operator.rb +47 -0
- data/lib/telegraph/switchboard.rb +57 -0
- data/lib/telegraph/wire.rb +73 -0
- data/test/deep_test/agent_test.rb +175 -0
- data/test/deep_test/central_command_test.rb +147 -0
- data/test/deep_test/cpu_info_test.rb +33 -0
- data/test/deep_test/database/mysql_setup_listener_test.rb +18 -0
- data/test/deep_test/demon_test.rb +23 -0
- data/test/deep_test/distributed/beachhead_test.rb +67 -0
- data/test/deep_test/distributed/dispatch_controller_test.rb +162 -0
- data/test/deep_test/distributed/filename_resolver_test.rb +56 -0
- data/test/deep_test/distributed/landing_fleet_test.rb +55 -0
- data/test/deep_test/distributed/landing_ship_test.rb +48 -0
- data/test/deep_test/distributed/remote_deployment_test.rb +134 -0
- data/test/deep_test/distributed/rsync_test.rb +47 -0
- data/test/deep_test/distributed/shell_environment_test.rb +108 -0
- data/test/deep_test/distributed/ssh_client_connection_info_test.rb +34 -0
- data/test/deep_test/extensions/object_extension_test.rb +37 -0
- data/test/deep_test/listener_list_test.rb +22 -0
- data/test/deep_test/local_deployment_test.rb +19 -0
- data/test/deep_test/logger_test.rb +38 -0
- data/test/deep_test/main_test.rb +12 -0
- data/test/deep_test/marshallable_exception_wrapper_test.rb +46 -0
- data/test/deep_test/metrics/data_test.rb +22 -0
- data/test/deep_test/metrics/measurement_test.rb +18 -0
- data/test/deep_test/proxy_io_test.rb +104 -0
- data/test/deep_test/result_reader_test.rb +128 -0
- data/test/deep_test/test/extensions/error_test.rb +42 -0
- data/test/deep_test/test/runner_test.rb +11 -0
- data/test/deep_test/test/supervised_test_suite_test.rb +107 -0
- data/test/deep_test/test/work_result_test.rb +85 -0
- data/test/deep_test/test/work_unit_test.rb +63 -0
- data/test/deep_test/test_task_test.rb +15 -0
- data/test/deep_test/ui/console_test.rb +13 -0
- data/test/deep_test/warlock_test.rb +40 -0
- data/test/test_helper.rb +30 -0
- data/test/test_task_test.rb +75 -0
- metadata +156 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class Agent
|
|
3
|
+
include Demon
|
|
4
|
+
attr_reader :number
|
|
5
|
+
|
|
6
|
+
def initialize(number, options, listener)
|
|
7
|
+
@number = number
|
|
8
|
+
@listener = listener
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def connect(stream_to_parent_process)
|
|
13
|
+
DeepTest.logger.debug { "Agent: Connecting to #{@options.origin_hostname}:#{@options.server_port}" }
|
|
14
|
+
@options.connect_to_central_command do |wire|
|
|
15
|
+
stream_to_parent_process.puts "Connected"
|
|
16
|
+
stream_to_parent_process.close rescue nil
|
|
17
|
+
yield wire
|
|
18
|
+
end
|
|
19
|
+
ensure
|
|
20
|
+
stream_to_parent_process.close unless stream_to_parent_process.closed?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def execute(stream_from_child_process, stream_to_parent_process)
|
|
24
|
+
stream_from_child_process.close
|
|
25
|
+
connect(stream_to_parent_process) do |wire|
|
|
26
|
+
reseed_random_numbers
|
|
27
|
+
reconnect_to_database
|
|
28
|
+
|
|
29
|
+
@listener.starting(self)
|
|
30
|
+
wire.send_message CentralCommand::NeedWork
|
|
31
|
+
|
|
32
|
+
while work_unit_message = next_work_unit_message(wire)
|
|
33
|
+
@listener.starting_work(self, work_unit_message.body)
|
|
34
|
+
|
|
35
|
+
result = begin
|
|
36
|
+
Metrics::Measurement.send_home("Agents Performing Work", wire, @options) do
|
|
37
|
+
work_unit_message.body.run
|
|
38
|
+
end
|
|
39
|
+
rescue Exception => error
|
|
40
|
+
Error.new(work_unit_message.body, error)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@listener.finished_work(self, work_unit_message.body, result)
|
|
44
|
+
send_result wire, work_unit_message, result
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
rescue CentralCommand::NoWorkUnitsRemainingError
|
|
48
|
+
DeepTest.logger.debug { "Agent #{number}: no more work to do" }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def next_work_unit_message(wire)
|
|
52
|
+
Metrics::Measurement.send_home("Agents Retrieving Work", wire, @options) do
|
|
53
|
+
begin
|
|
54
|
+
message = wire.next_message(:timeout => 2)
|
|
55
|
+
next message.body == CentralCommand::NoMoreWork ? nil : message
|
|
56
|
+
rescue Telegraph::NoMessageAvailable
|
|
57
|
+
DeepTest.logger.debug { "Agent: NoMessageAvailable" }
|
|
58
|
+
retry
|
|
59
|
+
rescue Telegraph::LineDead
|
|
60
|
+
DeepTest.logger.debug { "Agent: LineDead" }
|
|
61
|
+
next nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def send_result(wire, work_unit_message, result)
|
|
67
|
+
wire.send_message result, :ack => work_unit_message
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def reconnect_to_database
|
|
71
|
+
ActiveRecord::Base.connection.reconnect! if defined?(ActiveRecord::Base)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def reseed_random_numbers
|
|
75
|
+
srand
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Error
|
|
80
|
+
include CentralCommand::Result
|
|
81
|
+
|
|
82
|
+
attr_accessor :work_unit, :error
|
|
83
|
+
|
|
84
|
+
def initialize(work_unit, error)
|
|
85
|
+
@work_unit, @error = work_unit, error
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def _dump(limit)
|
|
89
|
+
Marshal.dump([@work_unit, @error], limit)
|
|
90
|
+
rescue
|
|
91
|
+
Marshal.dump(["<< Undumpable >>", @error], limit)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self._load(string)
|
|
95
|
+
new *Marshal.load(string)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ==(other)
|
|
99
|
+
work_unit == other.work_unit &&
|
|
100
|
+
error == other.error
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def to_s
|
|
104
|
+
"#{@work_unit}: #{@error}\n" + (@error.backtrace || []).join("\n")
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module DeepTest
|
|
4
|
+
class CentralCommand
|
|
5
|
+
attr_reader :operator
|
|
6
|
+
attr_reader :switchboard
|
|
7
|
+
attr_reader :data
|
|
8
|
+
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
@work_queue = Queue.new
|
|
12
|
+
@results_mutex = Mutex.new
|
|
13
|
+
@results_condvar = ConditionVariable.new
|
|
14
|
+
@results = []
|
|
15
|
+
@data = Metrics::Data.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def done_with_work
|
|
19
|
+
@done_with_work = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def take_result
|
|
23
|
+
@results_mutex.synchronize do
|
|
24
|
+
loop do
|
|
25
|
+
if @results.any?
|
|
26
|
+
return @results.shift
|
|
27
|
+
else
|
|
28
|
+
@results_condvar.wait @results_mutex
|
|
29
|
+
raise NoAgentsRunningError unless @results.any? || @switchboard.any_live_wires?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def take_work
|
|
36
|
+
@work_queue.pop(true)
|
|
37
|
+
rescue ThreadError => e
|
|
38
|
+
if e.message == "queue empty"
|
|
39
|
+
raise NoWorkUnitsRemainingError if @done_with_work
|
|
40
|
+
raise NoWorkUnitsAvailableError
|
|
41
|
+
else
|
|
42
|
+
raise
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def write_result(result)
|
|
47
|
+
@results_mutex.synchronize do
|
|
48
|
+
@results << result
|
|
49
|
+
@results_condvar.signal
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def write_work(work_unit)
|
|
54
|
+
@work_queue.push work_unit
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def stdout
|
|
58
|
+
$stdout
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def stderr
|
|
62
|
+
$stderr
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.start(options)
|
|
66
|
+
central_command = new(options)
|
|
67
|
+
central_command.start
|
|
68
|
+
central_command
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def start
|
|
72
|
+
@switchboard = Telegraph::Switchboard.new
|
|
73
|
+
@operator = Telegraph::Operator.listen("0.0.0.0", 0, @switchboard)
|
|
74
|
+
@options.server_port = @operator.port
|
|
75
|
+
@process_messages_thread = Thread.new { process_messages }
|
|
76
|
+
|
|
77
|
+
DeepTest.logger.info { "Started DeepTest service on port #{@operator.port}" }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless defined?(NeedWork)
|
|
81
|
+
NeedWork = "NeedWork"
|
|
82
|
+
NoMoreWork = "NoMoreWork"
|
|
83
|
+
module Result; end
|
|
84
|
+
module Operation; end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def process_messages
|
|
88
|
+
loop do
|
|
89
|
+
begin
|
|
90
|
+
return if @stop_process_messages
|
|
91
|
+
wires_waiting_for_work.each { |w| send_work w }
|
|
92
|
+
|
|
93
|
+
@results_mutex.synchronize do
|
|
94
|
+
# make take_result wake up and check if any agents are running
|
|
95
|
+
@results_condvar.signal
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
message, wire = switchboard.next_message(:timeout => 0.5)
|
|
99
|
+
|
|
100
|
+
case message.body
|
|
101
|
+
when NeedWork
|
|
102
|
+
send_work wire
|
|
103
|
+
when Result
|
|
104
|
+
write_result message.body
|
|
105
|
+
send_work wire
|
|
106
|
+
when Operation
|
|
107
|
+
message.body.execute
|
|
108
|
+
when Metrics::Measurement
|
|
109
|
+
data.add message.body
|
|
110
|
+
else
|
|
111
|
+
raise UnexpectedMessageError, message.inspect
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
rescue Telegraph::NoMessageAvailable
|
|
115
|
+
retry
|
|
116
|
+
rescue Exception => e
|
|
117
|
+
raise unless @stop_process_messages
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def wires_waiting_for_work
|
|
123
|
+
@wires_waiting_for_work ||= Set.new
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def send_work(wire)
|
|
127
|
+
begin
|
|
128
|
+
wire.send_message take_work, :need_ack => true
|
|
129
|
+
wires_waiting_for_work.delete wire
|
|
130
|
+
|
|
131
|
+
rescue NoWorkUnitsAvailableError
|
|
132
|
+
put_abandonded_work_back_on_the_queue
|
|
133
|
+
wires_waiting_for_work.add wire
|
|
134
|
+
|
|
135
|
+
rescue NoWorkUnitsRemainingError
|
|
136
|
+
wire.send_message NoMoreWork
|
|
137
|
+
wires_waiting_for_work.delete wire
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def put_abandonded_work_back_on_the_queue
|
|
143
|
+
closed_wires = switchboard.using_wires { |wires| wires.select {|w| w.closed? }}
|
|
144
|
+
closed_wires.each do |wire|
|
|
145
|
+
wire.unacked_messages.each do |m|
|
|
146
|
+
write_work m.body
|
|
147
|
+
switchboard.drop_wire wire
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def stop
|
|
153
|
+
@stop_process_messages = true
|
|
154
|
+
operator.shutdown
|
|
155
|
+
@process_messages_thread.join
|
|
156
|
+
data.save @options.metrics_file if @options.gathering_metrics?
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
class NoWorkUnitsAvailableError < StandardError; end
|
|
160
|
+
class NoWorkUnitsRemainingError < StandardError; end
|
|
161
|
+
class NoAgentsRunningError < StandardError; end
|
|
162
|
+
class CheckIfAgentsAreStillRunning < StandardError; end
|
|
163
|
+
class UnexpectedMessageError < StandardError; end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class CpuInfo
|
|
3
|
+
attr_accessor :platform
|
|
4
|
+
|
|
5
|
+
def initialize(platform = RUBY_PLATFORM)
|
|
6
|
+
@platform = platform
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def count
|
|
10
|
+
case platform
|
|
11
|
+
when /darwin/
|
|
12
|
+
output = `sysctl -n hw.ncpu`
|
|
13
|
+
output.strip.to_i
|
|
14
|
+
when /linux/
|
|
15
|
+
File.readlines("/proc/cpuinfo").inject(0) do |count, line|
|
|
16
|
+
next count + 1 if line =~ /processor\s*:\s*\d+/
|
|
17
|
+
count
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Database
|
|
3
|
+
#
|
|
4
|
+
# SetupListener implementation for MySQL.
|
|
5
|
+
#
|
|
6
|
+
class MysqlSetupListener < SetupListener
|
|
7
|
+
class <<self
|
|
8
|
+
#
|
|
9
|
+
# ActiveRecord configuration to use when connecting to
|
|
10
|
+
# MySQL to create databases, drop database, and grant
|
|
11
|
+
# privileges. By default, connects to information_schema
|
|
12
|
+
# on localhost as root with no password.
|
|
13
|
+
#
|
|
14
|
+
attr_accessor :admin_configuration
|
|
15
|
+
end
|
|
16
|
+
self.admin_configuration = {
|
|
17
|
+
:adapter => "mysql",
|
|
18
|
+
:host => "localhost",
|
|
19
|
+
:username => "root",
|
|
20
|
+
:database => "information_schema"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Creates database and grants privileges (via +grant_privileges+)
|
|
25
|
+
# on it via ActiveRecord connection based on admin_configuration.
|
|
26
|
+
#
|
|
27
|
+
def create_database
|
|
28
|
+
admin_connection do |connection|
|
|
29
|
+
connection.create_database agent_database
|
|
30
|
+
grant_privileges connection
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Grants 'all' privilege on agent database to username and password
|
|
36
|
+
# specified by agent database config. If your application has
|
|
37
|
+
# special database privilege needs beyond 'all', you should override
|
|
38
|
+
# this method and grant them.
|
|
39
|
+
#
|
|
40
|
+
def grant_privileges(connection)
|
|
41
|
+
identified_by = if agent_database_config[:password]
|
|
42
|
+
%{identified by %s} % connection.quote(agent_database_config[:password])
|
|
43
|
+
else
|
|
44
|
+
""
|
|
45
|
+
end
|
|
46
|
+
sql = %{grant all on #{agent_database}.* to %s@'localhost' #{identified_by} ; } %
|
|
47
|
+
connection.quote(agent_database_config[:username])
|
|
48
|
+
|
|
49
|
+
connection.execute sql
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# Drops database via ActiveRecord connection based on admin_configuration
|
|
54
|
+
#
|
|
55
|
+
def drop_database
|
|
56
|
+
admin_connection do |connection|
|
|
57
|
+
connection.drop_database agent_database
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Dumps schema from master database using mysqldump command
|
|
63
|
+
#
|
|
64
|
+
def dump_schema
|
|
65
|
+
config = command_line_config(master_database_config)
|
|
66
|
+
system "mysqldump -R #{config} > #{dump_file_name}"
|
|
67
|
+
raise "Error Dumping schema" unless $?.success?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Loads dumpfile into agent database using mysql command
|
|
72
|
+
#
|
|
73
|
+
def load_schema
|
|
74
|
+
config = command_line_config(agent_database_config)
|
|
75
|
+
system "mysql #{config} < #{dump_file_name}"
|
|
76
|
+
raise "Error Loading schema" unless $?.success?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# Location to store dumpfile. The default assumes you are testing
|
|
81
|
+
# a Rails project. You should override this if you are not using Rails
|
|
82
|
+
# or would like the dump file to be something other than the default
|
|
83
|
+
#
|
|
84
|
+
def dump_file_name
|
|
85
|
+
"#{RAILS_ROOT}/db/deep_test_schema.sql"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def system(command) # :nodoc:
|
|
89
|
+
DeepTest.logger.info { command }
|
|
90
|
+
super command
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def command_line_config(config) # :nodoc:
|
|
94
|
+
command = ['-u', config[:username]]
|
|
95
|
+
command += ["-p#{config[:password]}"] if config[:password]
|
|
96
|
+
command += ['-h', config[:host]] if config[:host]
|
|
97
|
+
command += ['-P', config[:port]] if config[:port]
|
|
98
|
+
command += ['-S', config[:socket]] if config[:socket]
|
|
99
|
+
command += [config[:database]]
|
|
100
|
+
command.join(' ')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def admin_connection # :nodoc:
|
|
104
|
+
conn = ActiveRecord::Base.mysql_connection(self.class.admin_configuration)
|
|
105
|
+
yield conn
|
|
106
|
+
ensure
|
|
107
|
+
conn.disconnect! if conn
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Database
|
|
3
|
+
#
|
|
4
|
+
# Skeleton Listener to help with setting up a separate database
|
|
5
|
+
# for each agent. Calls +dump_schema+, +load_schema+, +create_database+,
|
|
6
|
+
# and +drop_database+ hooks provided by subclasses that implement database
|
|
7
|
+
# setup strategies for particular database flavors.
|
|
8
|
+
#
|
|
9
|
+
class SetupListener < NullListener
|
|
10
|
+
DUMPED_SCHEMAS = [] unless defined?(DUMPED_SCHEMAS)
|
|
11
|
+
|
|
12
|
+
def before_sync # :nodoc:
|
|
13
|
+
dump_schema_once
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def before_starting_agents # :nodoc:
|
|
17
|
+
dump_schema_once
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def dump_schema_once # :nodoc:
|
|
21
|
+
schema_name = master_database_config[:database]
|
|
22
|
+
dump_schema unless DUMPED_SCHEMAS.include?(schema_name)
|
|
23
|
+
DUMPED_SCHEMAS << schema_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def starting(agent) # :nodoc:
|
|
27
|
+
@agent = agent
|
|
28
|
+
|
|
29
|
+
at_exit do
|
|
30
|
+
DeepTest.logger.debug { "dropping database #{agent_database}" }
|
|
31
|
+
drop_database
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
drop_database
|
|
35
|
+
create_database
|
|
36
|
+
connect_to_database
|
|
37
|
+
load_schema
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# Called on each agent after creating database and before loading
|
|
42
|
+
# schema to initialize connections
|
|
43
|
+
#
|
|
44
|
+
def connect_to_database
|
|
45
|
+
ActiveRecord::Base.establish_connection(agent_database_config)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Called in each agent to create the database named by
|
|
50
|
+
# +agent_database+.
|
|
51
|
+
#
|
|
52
|
+
def create_database
|
|
53
|
+
raise "Subclass must implement"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
# Called in each agent to drop the database created by
|
|
58
|
+
# +create_database+. This method is called twice, once before
|
|
59
|
+
# +create_database+ to ensure that no database exists and once
|
|
60
|
+
# at exit to clean as the agent process exits. This method
|
|
61
|
+
# must not fail if the database does not exist when it is called.
|
|
62
|
+
#
|
|
63
|
+
def drop_database
|
|
64
|
+
raise "Subclass must implement"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# Called before any agents are spawned to dump the schema that
|
|
69
|
+
# will be used for testing. When running distributed, this method
|
|
70
|
+
# is called on the local machine providing the tests to run.
|
|
71
|
+
#
|
|
72
|
+
# For distributed testing to work, the schema must be dumped in
|
|
73
|
+
# location accessible by all agent machines. The easiest way to
|
|
74
|
+
# accomplish this is to dump it to a location within the working copy.
|
|
75
|
+
#
|
|
76
|
+
def dump_schema
|
|
77
|
+
raise "Subclass must implement"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# Called once in each agent as it is starting to load the schema
|
|
83
|
+
# dumped from dump_schema. Subclasses should load the schema definition
|
|
84
|
+
# into the +agent_database+
|
|
85
|
+
#
|
|
86
|
+
def load_schema
|
|
87
|
+
raise "Subclass must implement"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# ActiveRecord configuration for the agent database. By default,
|
|
92
|
+
# the same as +master_database_config+, except that points to
|
|
93
|
+
# +agent_database+ instead of the database named in the master config.
|
|
94
|
+
#
|
|
95
|
+
def agent_database_config
|
|
96
|
+
master_database_config.merge(:database => agent_database)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#
|
|
100
|
+
# ActiveRecord configuration for the master database, based on
|
|
101
|
+
# RAILS_ENV. If not running Rails, you'll need to override this
|
|
102
|
+
# to provide the correct configuration.
|
|
103
|
+
#
|
|
104
|
+
def master_database_config
|
|
105
|
+
ActiveRecord::Base.configurations[RAILS_ENV].with_indifferent_access
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
# Unique name for database on machine that agent is running on.
|
|
110
|
+
#
|
|
111
|
+
def agent_database
|
|
112
|
+
"deep_test_agent_#{@agent.number}_pid_#{Process.pid}"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|