jperkins-deep_test 1.2.2
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 +228 -0
- data/Rakefile +247 -0
- data/bin/deep_test +15 -0
- data/lib/deep_test.rb +91 -0
- data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
- data/lib/deep_test/database/postgresql_setup_listener.rb +116 -0
- data/lib/deep_test/database/setup_listener.rb +125 -0
- data/lib/deep_test/deadlock_detector.rb +7 -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 +40 -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 +17 -0
- 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 +62 -0
- data/lib/deep_test/option.rb +60 -0
- data/lib/deep_test/options.rb +110 -0
- data/lib/deep_test/process_orchestrator.rb +49 -0
- data/lib/deep_test/rake_tasks.rb +11 -0
- data/lib/deep_test/result_reader.rb +36 -0
- data/lib/deep_test/rspec_detector.rb +21 -0
- data/lib/deep_test/server.rb +75 -0
- data/lib/deep_test/spec.rb +13 -0
- data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
- data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
- data/lib/deep_test/spec/extensions/options.rb +43 -0
- data/lib/deep_test/spec/extensions/reporter.rb +29 -0
- data/lib/deep_test/spec/extensions/spec_task.rb +20 -0
- data/lib/deep_test/spec/runner.rb +57 -0
- data/lib/deep_test/spec/work_result.rb +33 -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/runner.rb +24 -0
- data/lib/deep_test/test/supervised_test_suite.rb +49 -0
- data/lib/deep_test/test/work_result.rb +34 -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 +76 -0
- data/lib/deep_test/ui/null.rb +17 -0
- data/lib/deep_test/warlock.rb +134 -0
- data/lib/deep_test/worker.rb +57 -0
- 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/spec/deep_test/option_spec.rb +33 -0
- data/spec/deep_test/options_spec.rb +183 -0
- data/spec/deep_test/spec/extensions/example_group_methods_spec.rb +48 -0
- data/spec/deep_test/spec/extensions/example_methods_spec.rb +61 -0
- data/spec/deep_test/spec/extensions/options_spec.rb +23 -0
- data/spec/deep_test/spec/extensions/reporter_spec.rb +28 -0
- data/spec/deep_test/spec/extensions/spec_task_spec.rb +36 -0
- data/spec/deep_test/spec/runner_spec.rb +106 -0
- data/spec/deep_test/spec/work_result_spec.rb +14 -0
- data/spec/deep_test/spec/work_unit_spec.rb +78 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/thread_worker.rb +25 -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/extensions/object_extension_test.rb +37 -0
- data/test/deep_test/listener_list_test.rb +20 -0
- data/test/deep_test/local_workers_test.rb +22 -0
- data/test/deep_test/logger_test.rb +11 -0
- 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/runner_test.rb +7 -0
- data/test/deep_test/test/supervised_test_suite_test.rb +79 -0
- data/test/deep_test/test/work_result_test.rb +81 -0
- data/test/deep_test/test/work_unit_test.rb +61 -0
- data/test/deep_test/test_task_test.rb +43 -0
- data/test/deep_test/ui/console_test.rb +9 -0
- data/test/deep_test/warlock_test.rb +38 -0
- data/test/deep_test/worker_test.rb +94 -0
- data/test/failing.rake +11 -0
- data/test/failing.rb +7 -0
- data/test/fake_deadlock_error.rb +12 -0
- data/test/simple_test_blackboard.rb +45 -0
- data/test/simple_test_blackboard_test.rb +33 -0
- data/test/test_factory.rb +74 -0
- data/test/test_helper.rb +15 -0
- data/test/test_task_test.rb +72 -0
- metadata +214 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Metrics
|
|
3
|
+
module QueueLockWaitTimeMeasurement
|
|
4
|
+
attr_reader :total_pop_time, :total_push_time
|
|
5
|
+
|
|
6
|
+
def self.extended(o)
|
|
7
|
+
o.instance_eval do
|
|
8
|
+
alias pop_without_lock_wait_measurement pop
|
|
9
|
+
alias pop pop_with_lock_wait_measurement
|
|
10
|
+
|
|
11
|
+
alias push_without_lock_wait_measurement push
|
|
12
|
+
alias push push_with_lock_wait_measurement
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def pop_with_lock_wait_measurement(no_wait = false)
|
|
17
|
+
if no_wait
|
|
18
|
+
return measure(:add_pop_time) do
|
|
19
|
+
pop_without_lock_wait_measurement(no_wait)
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
begin
|
|
23
|
+
# Measure without waiting to minimize extra time added
|
|
24
|
+
# above locking time
|
|
25
|
+
#
|
|
26
|
+
return measure(:add_pop_time) do
|
|
27
|
+
pop_without_lock_wait_measurement(true)
|
|
28
|
+
end
|
|
29
|
+
rescue ThreadError => e
|
|
30
|
+
if e.message == "queue empty"
|
|
31
|
+
# Normally we would have waiting for a condvar signal,
|
|
32
|
+
# so don't penalize time for locking here again - hence
|
|
33
|
+
# no measure
|
|
34
|
+
return pop_without_lock_wait_measurement(false)
|
|
35
|
+
else
|
|
36
|
+
raise
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def push_with_lock_wait_measurement(value)
|
|
43
|
+
measure(:add_push_time) do
|
|
44
|
+
push_without_lock_wait_measurement(value)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def measure(accumulator)
|
|
49
|
+
start_time = Time.now
|
|
50
|
+
result = yield
|
|
51
|
+
send(accumulator, Time.now - start_time)
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def add_push_time(time)
|
|
56
|
+
Thread.exclusive do
|
|
57
|
+
@total_push_time ||= 0
|
|
58
|
+
@total_push_time += time
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def add_pop_time(time)
|
|
63
|
+
Thread.exclusive do
|
|
64
|
+
@total_pop_time ||= 0
|
|
65
|
+
@total_pop_time += time
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if $0 == __FILE__
|
|
74
|
+
require 'thread'
|
|
75
|
+
require 'timeout'
|
|
76
|
+
|
|
77
|
+
thread_count = (ARGV[0] || 10).to_i
|
|
78
|
+
action_count = (ARGV[1] || 100).to_i
|
|
79
|
+
|
|
80
|
+
def test_measurement(thread_count, action_count)
|
|
81
|
+
q = Queue.new
|
|
82
|
+
q.extend DeepTest::Metrics::QueueLockWaitTimeMeasurement
|
|
83
|
+
|
|
84
|
+
threads = []
|
|
85
|
+
thread_count.times do
|
|
86
|
+
threads << Thread.new do
|
|
87
|
+
action_count.times do |i|
|
|
88
|
+
show_progress ".", action_count, i
|
|
89
|
+
q.push 1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
thread_count.times do
|
|
95
|
+
threads << Thread.new do
|
|
96
|
+
action_count.times do |i|
|
|
97
|
+
show_progress "-", action_count, i
|
|
98
|
+
if rand(2) == 0
|
|
99
|
+
begin
|
|
100
|
+
q.pop(true)
|
|
101
|
+
rescue ThreadError
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
begin
|
|
105
|
+
Timeout.timeout(0.01) do
|
|
106
|
+
q.pop
|
|
107
|
+
end
|
|
108
|
+
rescue Timeout::Error
|
|
109
|
+
break
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
threads.each {|t| t.join}
|
|
117
|
+
|
|
118
|
+
puts
|
|
119
|
+
puts "Push Time: #{q.total_push_time}"
|
|
120
|
+
puts "Pop Time: #{q.total_pop_time}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def show_progress(s, total, current)
|
|
124
|
+
if (current % (total / 5)) == 0
|
|
125
|
+
$stdout.print s
|
|
126
|
+
$stdout.flush
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
start_time = Time.now
|
|
131
|
+
test_measurement(thread_count, action_count)
|
|
132
|
+
puts "Total Run Time: #{Time.now - start_time}"
|
|
133
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
#
|
|
3
|
+
# Listener that implements no-ops for all callbacks that DeepTest supports.
|
|
4
|
+
#
|
|
5
|
+
class NullWorkerListener
|
|
6
|
+
#
|
|
7
|
+
# Before DeepTest synchronizes any code during a distributed run,
|
|
8
|
+
# before_sync is called. If DeepTest is not running distributed,
|
|
9
|
+
# before_sync is never called.
|
|
10
|
+
#
|
|
11
|
+
def before_sync
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# Before DeepTest starts any workers, it instantiates a listener and
|
|
16
|
+
# invokes this method. No other callbacks are made to the listener
|
|
17
|
+
# instance receiving this message.
|
|
18
|
+
#
|
|
19
|
+
def before_starting_workers
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# A separate listener instance is created in each worker process and
|
|
24
|
+
# notified that the worker is starting. The worker for the process is
|
|
25
|
+
# provided for the listener to use. If you are using 3 workers, this
|
|
26
|
+
# method is invoked 3 times on 3 distinct instances. These instances
|
|
27
|
+
# will also receive the starting_work and finished_work callbacks for
|
|
28
|
+
# the worker provided.
|
|
29
|
+
#
|
|
30
|
+
def starting(worker)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
# Each time a worker takes a work unit, it calls this method before
|
|
35
|
+
# doing the work. In total, this method will be called as many times
|
|
36
|
+
# as there are work units. The listener instance that received the
|
|
37
|
+
# starting callback with the worker provided here is the same instance
|
|
38
|
+
# that receives this message.
|
|
39
|
+
#
|
|
40
|
+
# Because each work processes work units in a serial fashion, the
|
|
41
|
+
# listener will receive a finished_work message before another
|
|
42
|
+
# starting_work message.
|
|
43
|
+
#
|
|
44
|
+
def starting_work(worker, work_unit)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# Each time a worker finishes computing a result for a work unit,
|
|
49
|
+
# it calls this method before sending that result back to the server.
|
|
50
|
+
# In total, this method will be called as many times
|
|
51
|
+
# as there are work units. The listener instance that received the
|
|
52
|
+
# starting callback with the worker provided here is the same instance
|
|
53
|
+
# that receives this message.
|
|
54
|
+
#
|
|
55
|
+
# Because each work processes work units in a serial fashion, the
|
|
56
|
+
# listener will receive a starting_work message before another
|
|
57
|
+
# finished_work message.
|
|
58
|
+
#
|
|
59
|
+
def finished_work(worker, work_unit, result)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class Option
|
|
3
|
+
attr_reader :name, :default
|
|
4
|
+
|
|
5
|
+
def initialize(name, type, default)
|
|
6
|
+
@name, @type, @default = name, type, default
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def from_command_line(command_line)
|
|
10
|
+
command_line =~ /--#{name} (\S+)(\s|$)/
|
|
11
|
+
@type.from_string($1) if $1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_command_line(value)
|
|
15
|
+
"--#{name} #{@type.to_string(value)}" if value && value != default
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module Hash
|
|
19
|
+
def self.to_string(hash)
|
|
20
|
+
pairs = []
|
|
21
|
+
hash.each do |key, value|
|
|
22
|
+
value = value.gsub(/ /,'%20') if (::String === value)
|
|
23
|
+
pairs << "#{key}:#{value.inspect}"
|
|
24
|
+
end
|
|
25
|
+
pairs.join(",")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_string(string)
|
|
29
|
+
hash = {}
|
|
30
|
+
string.split(/,/).each do |pair|
|
|
31
|
+
key, unevaled_value = pair.split(/:/)
|
|
32
|
+
value = eval(unevaled_value)
|
|
33
|
+
value = value.gsub(/%20/, " ") if ::String === value
|
|
34
|
+
hash[key.to_sym] = value
|
|
35
|
+
end
|
|
36
|
+
hash
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Integer
|
|
41
|
+
def self.to_string(i)
|
|
42
|
+
i.to_s
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.from_string(s)
|
|
46
|
+
s.to_i
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module String
|
|
51
|
+
def self.to_string(s)
|
|
52
|
+
s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.from_string(s)
|
|
56
|
+
s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class Options
|
|
3
|
+
unless defined?(VALID_OPTIONS)
|
|
4
|
+
VALID_OPTIONS = [
|
|
5
|
+
Option.new(:distributed_server, Option::String, nil),
|
|
6
|
+
Option.new(:number_of_workers, Option::Integer, 2),
|
|
7
|
+
Option.new(:metrics_file, Option::String, nil),
|
|
8
|
+
Option.new(:pattern, Option::String, nil),
|
|
9
|
+
Option.new(:server_port, Option::Integer, 6969),
|
|
10
|
+
Option.new(:sync_options, Option::Hash, {}),
|
|
11
|
+
Option.new(:timeout_in_seconds, Option::Integer, 30),
|
|
12
|
+
Option.new(:ui, Option::String, "DeepTest::UI::Console"),
|
|
13
|
+
Option.new(:worker_listener, Option::String, "DeepTest::NullWorkerListener"),
|
|
14
|
+
]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_accessor *VALID_OPTIONS.map {|o| o.name}
|
|
18
|
+
|
|
19
|
+
def ui=(value)
|
|
20
|
+
@ui = value.to_s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def worker_listener=(value)
|
|
24
|
+
@worker_listener = value.to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_command_line(command_line)
|
|
28
|
+
hash = {}
|
|
29
|
+
VALID_OPTIONS.each do |option|
|
|
30
|
+
hash[option.name] = option.from_command_line(command_line)
|
|
31
|
+
end
|
|
32
|
+
new(hash)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize(hash)
|
|
36
|
+
@origin_hostname = Socket.gethostname
|
|
37
|
+
check_option_keys(hash)
|
|
38
|
+
VALID_OPTIONS.each do |option|
|
|
39
|
+
send("#{option.name}=", hash[option.name] || option.default)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def gathering_metrics?
|
|
44
|
+
!@metrics_file.nil?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def new_listener_list
|
|
48
|
+
listeners = worker_listener.split(',').map do |listener|
|
|
49
|
+
eval(listener).new
|
|
50
|
+
end
|
|
51
|
+
ListenerList.new(listeners)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def origin_hostname
|
|
55
|
+
(Socket.gethostname == @origin_hostname) ? 'localhost' : @origin_hostname
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Don't store UI instances in the options instance, which will
|
|
59
|
+
# need to be dumped over DRb. UI instances may not be dumpable
|
|
60
|
+
# and we don't want to have to start yet another DRb Server
|
|
61
|
+
#
|
|
62
|
+
UI_INSTANCES = {} unless defined?(UI_INSTANCES)
|
|
63
|
+
def ui_instance
|
|
64
|
+
UI_INSTANCES[self] ||= eval(ui).new(self)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def to_command_line
|
|
68
|
+
command_line = []
|
|
69
|
+
VALID_OPTIONS.each do |option|
|
|
70
|
+
value = send(option.name)
|
|
71
|
+
command_line << option.to_command_line(value)
|
|
72
|
+
end
|
|
73
|
+
command_line.compact.join(' ')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def mirror_path(base)
|
|
77
|
+
raise "No source directory specified in sync_options" unless sync_options[:source]
|
|
78
|
+
relative_mirror_path = origin_hostname + sync_options[:source].gsub('/','_')
|
|
79
|
+
"#{base}/#{relative_mirror_path}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def new_workers
|
|
83
|
+
if distributed_server.nil?
|
|
84
|
+
LocalWorkers.new self
|
|
85
|
+
else
|
|
86
|
+
begin
|
|
87
|
+
server = Distributed::TestServer.connect(self)
|
|
88
|
+
Distributed::RemoteWorkerClient.new(self, server, LocalWorkers.new(self))
|
|
89
|
+
rescue => e
|
|
90
|
+
ui_instance.distributed_failover_to_local("connect", e)
|
|
91
|
+
LocalWorkers.new self
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def server
|
|
97
|
+
Server.remote_reference(origin_hostname, server_port)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
protected
|
|
101
|
+
|
|
102
|
+
def check_option_keys(hash)
|
|
103
|
+
hash.keys.each do |key|
|
|
104
|
+
raise InvalidOptionError.new(key) unless VALID_OPTIONS.any? {|o| o.name == key.to_sym}
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class InvalidOptionError < StandardError; end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class ProcessOrchestrator
|
|
3
|
+
def self.run(options, workers, runner)
|
|
4
|
+
new(options, workers, runner).run
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize(options, workers, runner)
|
|
8
|
+
@options = options
|
|
9
|
+
@runner = runner
|
|
10
|
+
@workers = workers
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(exit_when_done = true)
|
|
14
|
+
passed = false
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
server = Server.start(@options)
|
|
18
|
+
@options.new_listener_list.before_starting_workers
|
|
19
|
+
@workers.start_all
|
|
20
|
+
begin
|
|
21
|
+
DeepTest.logger.debug "Loader Starting (#{$$})"
|
|
22
|
+
passed = @runner.process_work_units
|
|
23
|
+
ensure
|
|
24
|
+
shutdown(server)
|
|
25
|
+
end
|
|
26
|
+
ensure
|
|
27
|
+
DeepTest.logger.debug "ProcessOrchestrator: Stopping Server"
|
|
28
|
+
Server.stop
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Kernel.exit(passed ? 0 : 1) if exit_when_done
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def shutdown(server)
|
|
35
|
+
DeepTest.logger.debug "ProcessOrchestrator: Shutting Down"
|
|
36
|
+
server.done_with_work
|
|
37
|
+
|
|
38
|
+
first_exception = $!
|
|
39
|
+
begin
|
|
40
|
+
DeepTest.logger.debug "ProcessOrchestrator: Stopping Workers"
|
|
41
|
+
@workers.stop_all
|
|
42
|
+
rescue DRb::DRbConnError
|
|
43
|
+
# Workers must have already stopped
|
|
44
|
+
rescue Exception => e
|
|
45
|
+
raise first_exception || e
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require "socket"
|
|
2
|
+
|
|
3
|
+
require File.dirname(__FILE__) + "/option"
|
|
4
|
+
require File.dirname(__FILE__) + "/options"
|
|
5
|
+
require File.dirname(__FILE__) + "/test_task"
|
|
6
|
+
require File.dirname(__FILE__) + "/rspec_detector"
|
|
7
|
+
|
|
8
|
+
DeepTest::RSpecDetector.if_rspec_available do
|
|
9
|
+
require 'spec/rake/spectask'
|
|
10
|
+
require File.dirname(__FILE__) + "/spec/extensions/spec_task"
|
|
11
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class ResultReader
|
|
3
|
+
def initialize(blackboard)
|
|
4
|
+
@blackboard = blackboard
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def read(original_work_units_by_id)
|
|
8
|
+
work_units_by_id = original_work_units_by_id.dup
|
|
9
|
+
errors = 0
|
|
10
|
+
|
|
11
|
+
begin
|
|
12
|
+
until errors == work_units_by_id.size
|
|
13
|
+
Thread.pass
|
|
14
|
+
result = @blackboard.take_result
|
|
15
|
+
next if result.nil?
|
|
16
|
+
|
|
17
|
+
if Worker::Error === result
|
|
18
|
+
puts result
|
|
19
|
+
errors += 1
|
|
20
|
+
else
|
|
21
|
+
if result.respond_to?(:output) && (output = result.output)
|
|
22
|
+
print output
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
work_unit = work_units_by_id.delete(result.identifier)
|
|
26
|
+
yield [work_unit, result]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
rescue Server::ResultOverdueError
|
|
30
|
+
DeepTest.logger.error("Results are overdue from server, ending run")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
work_units_by_id
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|