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.
- data/CHANGELOG +15 -5
- data/README +149 -5
- data/Rakefile +131 -11
- data/bin/deep_test +15 -0
- data/lib/deep_test.rb +62 -4
- data/lib/deep_test/database/mysql_setup_listener.rb +109 -0
- data/lib/deep_test/database/setup_listener.rb +116 -0
- data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
- data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
- data/lib/deep_test/distributed/filename_resolver.rb +40 -0
- data/lib/deep_test/distributed/master_test_server.rb +52 -0
- data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
- data/lib/deep_test/distributed/null_work_unit.rb +12 -0
- data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
- data/lib/deep_test/distributed/remote_worker_server.rb +82 -0
- data/lib/deep_test/distributed/rsync.rb +37 -0
- data/lib/deep_test/distributed/show_status.rhtml +41 -0
- data/lib/deep_test/distributed/test_server.rb +78 -0
- data/lib/deep_test/distributed/test_server_status.rb +9 -0
- data/lib/deep_test/distributed/test_server_workers.rb +24 -0
- data/lib/deep_test/distributed/throughput_runner.rb +42 -0
- data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
- data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
- data/lib/deep_test/extensions/drb_extension.rb +34 -0
- data/lib/deep_test/extensions/object_extension.rb +8 -0
- data/lib/deep_test/listener_list.rb +17 -0
- data/lib/deep_test/local_workers.rb +55 -0
- data/lib/deep_test/logger.rb +10 -2
- data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
- data/lib/deep_test/metrics/gatherer.rb +67 -0
- data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
- data/lib/deep_test/null_worker_listener.rb +50 -0
- data/lib/deep_test/option.rb +60 -0
- data/lib/deep_test/options.rb +61 -24
- data/lib/deep_test/process_orchestrator.rb +31 -78
- data/lib/deep_test/rake_tasks.rb +3 -0
- data/lib/deep_test/result_reader.rb +36 -0
- data/lib/deep_test/rspec_detector.rb +15 -8
- data/lib/deep_test/server.rb +68 -4
- data/lib/deep_test/spec.rb +1 -0
- data/lib/deep_test/spec/extensions/example_group_methods.rb +37 -11
- data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
- data/lib/deep_test/spec/extensions/options.rb +35 -3
- data/lib/deep_test/spec/runner.rb +19 -10
- data/lib/deep_test/spec/work_result.rb +12 -9
- data/lib/deep_test/spec/work_unit.rb +12 -7
- data/lib/deep_test/test.rb +1 -1
- data/lib/deep_test/test/extensions/error.rb +7 -7
- data/lib/deep_test/test/runner.rb +1 -6
- data/lib/deep_test/test/supervised_test_suite.rb +19 -16
- data/lib/deep_test/test/work_result.rb +34 -0
- data/lib/deep_test/test/work_unit.rb +6 -2
- data/lib/deep_test/test_task.rb +15 -35
- data/lib/deep_test/ui/console.rb +76 -0
- data/lib/deep_test/ui/null.rb +17 -0
- data/lib/deep_test/warlock.rb +92 -17
- data/lib/deep_test/worker.rb +38 -2
- data/script/internal/run_test_suite.rb +7 -0
- data/script/public/master_test_server.rb +24 -0
- data/script/public/test_server.rb +18 -0
- data/script/public/test_throughput.rb +29 -0
- data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
- data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
- data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
- data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
- data/test/deep_test/distributed/master_test_server_test.rb +32 -0
- data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
- data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
- data/test/deep_test/distributed/remote_worker_server_test.rb +99 -0
- data/test/deep_test/distributed/rsync_test.rb +67 -0
- data/test/deep_test/distributed/test_server_test.rb +94 -0
- data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
- data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
- data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
- data/test/deep_test/listener_list_test.rb +20 -0
- data/test/deep_test/local_workers_test.rb +22 -0
- data/test/{logger_test.rb → deep_test/logger_test.rb} +2 -2
- data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
- data/test/deep_test/metrics/gatherer_test.rb +66 -0
- data/test/deep_test/process_orchestrator_test.rb +11 -0
- data/test/deep_test/result_reader_test.rb +128 -0
- data/test/deep_test/server_test.rb +58 -0
- data/test/deep_test/test/extensions/error_test.rb +40 -0
- data/test/deep_test/test/supervised_test_suite_test.rb +19 -29
- data/test/deep_test/test/work_result_test.rb +81 -0
- data/test/deep_test/test/work_unit_test.rb +15 -4
- data/test/deep_test/test_task_test.rb +10 -0
- data/test/deep_test/ui/console_test.rb +9 -0
- data/test/deep_test/warlock_test.rb +17 -0
- data/test/deep_test/worker_test.rb +32 -0
- data/test/simple_test_blackboard.rb +2 -1
- data/test/test_helper.rb +1 -0
- metadata +117 -59
- data/lib/deep_test/rinda_blackboard.rb +0 -26
- data/lib/deep_test/test/extensions/test_result.rb +0 -17
- data/lib/deep_test/tuple_space_factory.rb +0 -12
- data/script/run_test_suite.rb +0 -5
- data/test/deep_test/rinda_blackboard_test.rb +0 -15
- data/test/deep_test/test/extensions/test_result_test.rb +0 -71
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module UI
|
|
3
|
+
class Console
|
|
4
|
+
def initialize(options)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
METHOD_DESCRIPTIONS = {
|
|
8
|
+
:sync => "Synchronizing working copies on worker machines",
|
|
9
|
+
:spawn_worker_server => "Spawning test environment processes",
|
|
10
|
+
:load_files => "Loading test files for workers",
|
|
11
|
+
:start_all => "Starting workers",
|
|
12
|
+
:stop_all => "Stopping workers"
|
|
13
|
+
} unless defined?(METHOD_DESCRIPTIONS)
|
|
14
|
+
|
|
15
|
+
def distributed_failover_to_local(method, exception)
|
|
16
|
+
width = 70
|
|
17
|
+
puts " Distributed DeepTest Failure ".center(width, '*')
|
|
18
|
+
puts "* Failed during #{method}".ljust(width - 1) + "*"
|
|
19
|
+
puts "* #{exception.message}".ljust(width - 1) + "*"
|
|
20
|
+
puts "* Failing over to local run".ljust(width - 1) + "*"
|
|
21
|
+
puts "*" * width
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def dispatch_starting(method_name)
|
|
25
|
+
@spinner.stop if @spinner
|
|
26
|
+
@spinner = Spinner.new(label(method_name))
|
|
27
|
+
@spinner.start
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def label(method_name)
|
|
31
|
+
METHOD_DESCRIPTIONS[method_name.to_sym] || method_name.to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dispatch_finished(method_name)
|
|
35
|
+
@spinner.stop if @spinner
|
|
36
|
+
@spinner = nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Spinner
|
|
40
|
+
FRAMES = ['|', '/', '-', '\\'] unless defined?(FRAMES)
|
|
41
|
+
BACKSPACE = "\x08" unless defined?(BACKSPACE)
|
|
42
|
+
SECONDS_PER_FRAME = 0.5 / 4 unless defined?(SECONDS_PER_FRAME)
|
|
43
|
+
|
|
44
|
+
def initialize(label)
|
|
45
|
+
@label = label
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def start
|
|
49
|
+
@start_time = Time.now
|
|
50
|
+
show "#{@label}: "
|
|
51
|
+
@thread = Thread.new do
|
|
52
|
+
index = 0
|
|
53
|
+
loop do
|
|
54
|
+
show FRAMES[index]
|
|
55
|
+
sleep SECONDS_PER_FRAME
|
|
56
|
+
show BACKSPACE
|
|
57
|
+
index = (index + 1) % FRAMES.length
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def stop
|
|
63
|
+
@stop_time = Time.now
|
|
64
|
+
@thread.kill if @thread
|
|
65
|
+
show BACKSPACE
|
|
66
|
+
show("finished in %.2f seconds\n" % (@stop_time.to_f - @start_time.to_f))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def show(string)
|
|
70
|
+
$stdout.print string
|
|
71
|
+
$stdout.flush
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -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
|
data/lib/deep_test/warlock.rb
CHANGED
|
@@ -1,40 +1,91 @@
|
|
|
1
1
|
module DeepTest
|
|
2
2
|
class Warlock
|
|
3
3
|
def initialize
|
|
4
|
+
@demons_semaphore = Mutex.new
|
|
4
5
|
@demons = []
|
|
6
|
+
@reapers = []
|
|
5
7
|
end
|
|
6
|
-
|
|
8
|
+
|
|
7
9
|
def start(name, &block)
|
|
10
|
+
# Not synchronizing for the fork seems to cause
|
|
11
|
+
# random errors (Bus Error, Segfault, and GC non-object)
|
|
12
|
+
# in RemoteWorkerServer processes.
|
|
13
|
+
#
|
|
8
14
|
begin
|
|
9
|
-
pid =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
pid = nil
|
|
16
|
+
@demons_semaphore.synchronize do
|
|
17
|
+
pid = DeepTest.drb_safe_fork do
|
|
18
|
+
# Fork leaves the semaphore locked and we'll never make it
|
|
19
|
+
# to end of synchronize block.
|
|
20
|
+
#
|
|
21
|
+
# The Ruby 1.8.6 C mutex implementation automatically treats
|
|
22
|
+
# a mutex locked by a dead thread as unlocked and will raise
|
|
23
|
+
# an error if we try to unlock it from this thread.
|
|
24
|
+
#
|
|
25
|
+
@demons_semaphore.unlock if @demons_semaphore.locked?
|
|
26
|
+
|
|
27
|
+
begin
|
|
28
|
+
yield
|
|
29
|
+
rescue Exception => e
|
|
30
|
+
DeepTest.logger.debug "Exception in #{name} (#{Process.pid}): #{e.message}"
|
|
31
|
+
raise
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
raise "fatal: fork returned nil" if pid.nil?
|
|
38
|
+
add_demon name, pid
|
|
13
39
|
end
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
40
|
+
|
|
41
|
+
launch_reaper_thread name, pid
|
|
42
|
+
|
|
17
43
|
rescue => e
|
|
18
44
|
puts "exception starting #{name}: #{e}"
|
|
19
45
|
puts "\t" + e.backtrace.join("\n\t")
|
|
20
46
|
end
|
|
21
47
|
end
|
|
22
48
|
|
|
49
|
+
def demon_count
|
|
50
|
+
@demons_semaphore.synchronize do
|
|
51
|
+
@demons.size
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
23
55
|
def stop_all
|
|
24
|
-
|
|
56
|
+
DeepTest.logger.debug("stopping all demons")
|
|
57
|
+
receivers = @demons_semaphore.synchronize do
|
|
58
|
+
@demons.reverse
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
receivers.reverse.each do |demon|
|
|
25
62
|
name, pid = demon
|
|
26
63
|
if running?(pid)
|
|
27
|
-
|
|
64
|
+
DeepTest.logger.debug("Sending SIGTERM to #{name}, #{pid}")
|
|
65
|
+
Process.kill("TERM", pid)
|
|
28
66
|
end
|
|
29
67
|
end
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
DeepTest.logger.debug("Warlock: Stopped all receivers")
|
|
69
|
+
|
|
70
|
+
DeepTest.logger.debug("waiting for reapers")
|
|
71
|
+
@reapers.each {|r| r.join}
|
|
72
|
+
|
|
73
|
+
DeepTest.logger.debug("Warlock: done reaping processes")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def exit_when_none_running
|
|
77
|
+
Thread.new do
|
|
78
|
+
loop do
|
|
79
|
+
Thread.pass
|
|
80
|
+
exit(0) unless any_running?
|
|
81
|
+
sleep(0.01)
|
|
36
82
|
end
|
|
37
|
-
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def any_running?
|
|
87
|
+
@demons_semaphore.synchronize do
|
|
88
|
+
@demons.any? {|name, pid| running?(pid)}
|
|
38
89
|
end
|
|
39
90
|
end
|
|
40
91
|
|
|
@@ -55,5 +106,29 @@ module DeepTest
|
|
|
55
106
|
# return false
|
|
56
107
|
end
|
|
57
108
|
end
|
|
109
|
+
|
|
110
|
+
protected
|
|
111
|
+
|
|
112
|
+
def add_demon(name, pid)
|
|
113
|
+
DeepTest.logger.debug "Started: #{name} (#{pid})"
|
|
114
|
+
@demons << [name, pid]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def remove_demon(name, pid)
|
|
118
|
+
@demons.delete [name, pid]
|
|
119
|
+
DeepTest.logger.debug "Stopped: #{name} (#{pid})"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def launch_reaper_thread(name, pid)
|
|
124
|
+
@reapers << Thread.new do
|
|
125
|
+
Process.detach(pid).join
|
|
126
|
+
DeepTest.logger.debug("#{name} (#{pid}) reaped")
|
|
127
|
+
@demons_semaphore.synchronize do
|
|
128
|
+
DeepTest.logger.debug("Warlock Reaper: removing #{name} (#{pid}) from demon list")
|
|
129
|
+
remove_demon name, pid
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
58
133
|
end
|
|
59
134
|
end
|
data/lib/deep_test/worker.rb
CHANGED
|
@@ -10,11 +10,47 @@ module DeepTest
|
|
|
10
10
|
|
|
11
11
|
def run
|
|
12
12
|
@listener.starting(self)
|
|
13
|
-
while work_unit =
|
|
13
|
+
while work_unit = next_work_unit
|
|
14
14
|
@listener.starting_work(self, work_unit)
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
result = begin
|
|
17
|
+
work_unit.run
|
|
18
|
+
rescue Exception => error
|
|
19
|
+
Error.new(work_unit, error)
|
|
20
|
+
end
|
|
21
|
+
|
|
16
22
|
@listener.finished_work(self, work_unit, result)
|
|
17
23
|
@blackboard.write_result result
|
|
24
|
+
if ENV['DEEP_TEST_SHOW_WORKER_DOTS'] == 'yes'
|
|
25
|
+
$stdout.print '.'
|
|
26
|
+
$stdout.flush
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
rescue Server::NoWorkUnitsRemainingError
|
|
30
|
+
DeepTest.logger.debug("Worker #{number}: no more work to do")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def next_work_unit
|
|
34
|
+
@blackboard.take_work
|
|
35
|
+
rescue Server::NoWorkUnitsAvailableError
|
|
36
|
+
sleep 0.02
|
|
37
|
+
retry
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Error
|
|
41
|
+
attr_accessor :work_unit, :error
|
|
42
|
+
|
|
43
|
+
def initialize(work_unit, error)
|
|
44
|
+
@work_unit, @error = work_unit, error
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def ==(other)
|
|
48
|
+
work_unit == other.work_unit &&
|
|
49
|
+
error == other.error
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_s
|
|
53
|
+
"#{@work_unit}: #{@error}\n" + (@error.backtrace || []).join("\n")
|
|
18
54
|
end
|
|
19
55
|
end
|
|
20
56
|
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/../../lib/deep_test"
|
|
2
|
+
options = DeepTest::Options.from_command_line(ARGV[0])
|
|
3
|
+
DeepTest.init(options)
|
|
4
|
+
runner = DeepTest::Test::Runner.new(options)
|
|
5
|
+
workers = options.new_workers
|
|
6
|
+
workers.load_files Dir.glob(options.pattern)
|
|
7
|
+
DeepTest::ProcessOrchestrator.run(options, workers, runner)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/../../lib/deep_test"
|
|
2
|
+
require 'drb/drb'
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
uri = "drubyall://:4021"
|
|
6
|
+
slave_uris = OptionParser.new do |opts|
|
|
7
|
+
opts.banner = "Usage: deep_test master_test_server [options] <test_server_uris>"
|
|
8
|
+
|
|
9
|
+
opts.on("--uri URI", "DRb URI to bind server to") do |v|
|
|
10
|
+
uri = v
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
14
|
+
puts opts
|
|
15
|
+
exit
|
|
16
|
+
end
|
|
17
|
+
end.parse(ARGV)
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
DeepTest::Distributed::MasterTestServer.start(uri, slave_uris)
|
|
21
|
+
rescue Interrupt
|
|
22
|
+
DeepTest.logger.info "Exiting due to Interrupt"
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/../../lib/deep_test"
|
|
2
|
+
require 'drb/drb'
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
config = DeepTest::Distributed::TestServer.parse_args(ARGV)
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# Clear args so they won't be processed in any forked processes.
|
|
9
|
+
# - When specs are loaded by the RemoteWorkerServer, RSpec
|
|
10
|
+
# attempts to parse ARGV
|
|
11
|
+
#
|
|
12
|
+
ARGV.clear
|
|
13
|
+
|
|
14
|
+
begin
|
|
15
|
+
DeepTest::Distributed::TestServer.start(config)
|
|
16
|
+
rescue Interrupt
|
|
17
|
+
DeepTest.logger.info "Exiting due to Interrupt"
|
|
18
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../lib/deep_test'
|
|
2
|
+
|
|
3
|
+
unless ARGV.length == 2
|
|
4
|
+
puts "Usage: deep_test test_throughput <server uri> <test_count>"
|
|
5
|
+
exit(1)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
uri = ARGV[0]
|
|
9
|
+
test_count = ARGV[1].to_i
|
|
10
|
+
|
|
11
|
+
options = DeepTest::Options.new(:distributed_server => uri,
|
|
12
|
+
:sync_options => {:source => ""})
|
|
13
|
+
server = DeepTest::Distributed::TestServer.connect options
|
|
14
|
+
workers = DeepTest::Distributed::ThroughputWorkerClient.new(options, server)
|
|
15
|
+
runner = DeepTest::Distributed::ThroughputRunner.new(options, test_count) do |result|
|
|
16
|
+
$stdout.print "."
|
|
17
|
+
$stdout.flush
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
start_time = Time.now
|
|
21
|
+
DeepTest::ProcessOrchestrator.new(options, workers, runner).run(false)
|
|
22
|
+
end_time = Time.now
|
|
23
|
+
|
|
24
|
+
puts
|
|
25
|
+
puts runner.statistics.summary
|
|
26
|
+
|
|
27
|
+
run_time = end_time.to_f - start_time.to_f
|
|
28
|
+
puts "Total Run Time: #{run_time} seconds"
|
|
29
|
+
puts "Run Time Not Spent On Tests: #{run_time - runner.statistics.timespan_in_seconds}"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/../../test_helper"
|
|
2
|
+
|
|
3
|
+
unit_tests do
|
|
4
|
+
test "dump_schema includes procedures" do
|
|
5
|
+
listener = DeepTest::Database::MysqlSetupListener.new
|
|
6
|
+
listener.expects(:system).with do |command|
|
|
7
|
+
command =~ / -R /
|
|
8
|
+
end
|
|
9
|
+
listener.expects(:master_database_config).returns({})
|
|
10
|
+
listener.expects(:dump_file_name).returns("")
|
|
11
|
+
$?.expects(:success?).returns(true)
|
|
12
|
+
listener.dump_schema
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/../../test_helper"
|
|
2
|
+
|
|
3
|
+
unit_tests do
|
|
4
|
+
test "dispatch invokes each receiver once" do
|
|
5
|
+
receiver_1, receiver_2 = mock, mock
|
|
6
|
+
|
|
7
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
8
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null"}),
|
|
9
|
+
[receiver_1, receiver_2]
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
receiver_1.expects(:a_method).with(:args)
|
|
13
|
+
receiver_2.expects(:a_method).with(:args)
|
|
14
|
+
|
|
15
|
+
controller.dispatch(:a_method, :args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "dispatch returns array of all results" do
|
|
19
|
+
receiver_1, receiver_2 = mock, mock
|
|
20
|
+
|
|
21
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
22
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null"}),
|
|
23
|
+
[receiver_1, receiver_2]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
receiver_1.expects(:a_method).returns(:result_1)
|
|
27
|
+
receiver_2.expects(:a_method).returns(:result_2)
|
|
28
|
+
|
|
29
|
+
results = controller.dispatch(:a_method)
|
|
30
|
+
assert_equal 2, results.size
|
|
31
|
+
assert_equal [:result_1, :result_2].to_set, results.to_set
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test "dispatch calls all receivers in parallel" do
|
|
35
|
+
options = DeepTest::Options.new({:ui => "DeepTest::UI::Null"})
|
|
36
|
+
waiter = Waiter.new(tracker = Tracker.new)
|
|
37
|
+
|
|
38
|
+
Timeout.timeout(1) do
|
|
39
|
+
DeepTest::Distributed::DispatchController.new(options,[tracker, waiter]).
|
|
40
|
+
dispatch(:tracked_method)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
waiter = Waiter.new(tracker = Tracker.new)
|
|
44
|
+
|
|
45
|
+
Timeout.timeout(1) do
|
|
46
|
+
DeepTest::Distributed::DispatchController.new(options,[waiter, tracker]).
|
|
47
|
+
dispatch(:tracked_method)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test "dispatch omits results that are taking too long" do
|
|
52
|
+
receiver = Object.new
|
|
53
|
+
def receiver.__drburi; ""; end
|
|
54
|
+
def receiver.sleep_100_millis
|
|
55
|
+
sleep 0.1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
59
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null", :timeout_in_seconds => 0.05}),
|
|
60
|
+
[receiver]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
DeepTest.logger.expects(:error)
|
|
64
|
+
|
|
65
|
+
assert_equal [], controller.dispatch(:sleep_100_millis)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
test "after timeout, no further calls are sent to that receiver" do
|
|
69
|
+
receiver_1, receiver_2 = mock(:__drburi => ""), mock
|
|
70
|
+
receiver_1.expects(:method_call_1).raises(Timeout::Error.new("message"))
|
|
71
|
+
receiver_1.expects(:method_call_2).never
|
|
72
|
+
|
|
73
|
+
receiver_2.expects(:method_call_1)
|
|
74
|
+
receiver_2.expects(:method_call_2)
|
|
75
|
+
|
|
76
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
77
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null", :timeout_in_seconds => 0.05}),
|
|
78
|
+
[receiver_1, receiver_2]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
DeepTest.logger.expects(:error)
|
|
82
|
+
|
|
83
|
+
controller.dispatch(:method_call_1)
|
|
84
|
+
controller.dispatch(:method_call_2)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
test "receiver is dropped when connection is refused" do
|
|
88
|
+
receiver_1, receiver_2 = mock(:__drburi => ""), mock
|
|
89
|
+
receiver_1.expects(:method_call_1).raises(DRb::DRbConnError)
|
|
90
|
+
receiver_1.expects(:method_call_2).never
|
|
91
|
+
|
|
92
|
+
receiver_2.expects(:method_call_1).returns(:value)
|
|
93
|
+
receiver_2.expects(:method_call_2)
|
|
94
|
+
|
|
95
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
96
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null", :timeout_in_seconds => 0.05}),
|
|
97
|
+
[receiver_1, receiver_2]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
DeepTest.logger.expects(:error)
|
|
101
|
+
|
|
102
|
+
assert_equal [:value], controller.dispatch(:method_call_1)
|
|
103
|
+
|
|
104
|
+
controller.dispatch(:method_call_2)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
test "receiver is dropped when any exception occurs" do
|
|
108
|
+
receiver = mock(:__drburi => "")
|
|
109
|
+
receiver.expects(:method_call).raises(Exception)
|
|
110
|
+
|
|
111
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
112
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null", :timeout_in_seconds => 0.05}),
|
|
113
|
+
[receiver]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
DeepTest.logger.expects(:error)
|
|
117
|
+
|
|
118
|
+
controller.dispatch(:method_call)
|
|
119
|
+
assert_raises(DeepTest::Distributed::NoDispatchReceiversError) {controller.dispatch(:another_call)}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
test "no error is printed if dispatching without error" do
|
|
123
|
+
receiver_1 = mock
|
|
124
|
+
receiver_1.expects(:method_call_1).raises(DRb::DRbConnError)
|
|
125
|
+
|
|
126
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
127
|
+
DeepTest::Options.new({:ui => "DeepTest::UI::Null", :timeout_in_seconds => 0.05}),
|
|
128
|
+
[receiver_1]
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
DeepTest.logger.expects(:error).never
|
|
132
|
+
|
|
133
|
+
controller.dispatch_with_options(:method_call_1, :ignore_connection_error => true)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
test "dispatch calls notifies ui of start and stop of dispatch" do
|
|
137
|
+
options = DeepTest::Options.new({:ui => "DeepTest::UI::Null"})
|
|
138
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
139
|
+
options, [stub_everything]
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
options.ui_instance.expects(:dispatch_starting).with(:method_name)
|
|
143
|
+
options.ui_instance.expects(:dispatch_finished).with(:method_name)
|
|
144
|
+
|
|
145
|
+
controller.dispatch(:method_name)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
test "dispatch calls notifies ui dispatch end in case of an error" do
|
|
149
|
+
options = DeepTest::Options.new({:ui => "DeepTest::UI::Null"})
|
|
150
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
151
|
+
options, [receiver = mock(:__drburi => '')]
|
|
152
|
+
)
|
|
153
|
+
receiver.expects(:method_name).raises("An Error")
|
|
154
|
+
DeepTest.logger.expects(:error)
|
|
155
|
+
|
|
156
|
+
options.ui_instance.expects(:dispatch_starting).with(:method_name)
|
|
157
|
+
options.ui_instance.expects(:dispatch_finished).with(:method_name)
|
|
158
|
+
|
|
159
|
+
begin
|
|
160
|
+
controller.dispatch(:method_name)
|
|
161
|
+
rescue RuntimeError => e
|
|
162
|
+
raise unless e.message == "An Error"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
test "error is raised if dispatch to no receivers in attempted" do
|
|
167
|
+
options = DeepTest::Options.new({:ui => "DeepTest::UI::Null"})
|
|
168
|
+
controller = DeepTest::Distributed::DispatchController.new(
|
|
169
|
+
options, []
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
assert_raises(DeepTest::Distributed::NoDispatchReceiversError) do
|
|
173
|
+
controller.dispatch(:any_method)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class Tracker
|
|
178
|
+
def initialize
|
|
179
|
+
@mutex = Mutex.new
|
|
180
|
+
@condvar = ConditionVariable.new
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def tracked_method
|
|
184
|
+
@mutex.synchronize do
|
|
185
|
+
@tracked_method_called = true
|
|
186
|
+
@condvar.broadcast
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def wait_for_tracked_method
|
|
191
|
+
@mutex.synchronize do
|
|
192
|
+
loop do
|
|
193
|
+
return if @tracked_method_called
|
|
194
|
+
@condvar.wait(@mutex)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class Waiter
|
|
201
|
+
def initialize(tracker)
|
|
202
|
+
@tracker = tracker
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def tracked_method
|
|
206
|
+
@tracker.wait_for_tracked_method
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|