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,10 @@
|
|
|
1
|
+
require "test/unit/testresult"
|
|
2
|
+
require "test/unit/error"
|
|
3
|
+
require 'test/unit/failure'
|
|
4
|
+
require 'test/unit/autorunner'
|
|
5
|
+
|
|
6
|
+
require File.dirname(__FILE__) + "/test/extensions/error"
|
|
7
|
+
require File.dirname(__FILE__) + "/test/runner"
|
|
8
|
+
require File.dirname(__FILE__) + "/test/supervised_test_suite"
|
|
9
|
+
require File.dirname(__FILE__) + "/test/work_unit"
|
|
10
|
+
require File.dirname(__FILE__) + "/test/work_result"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Test
|
|
2
|
+
module Unit
|
|
3
|
+
class Error
|
|
4
|
+
def resolve_marshallable_exception
|
|
5
|
+
@exception = @exception.resolve if @exception.kind_of?(DeepTest::MarshallableExceptionWrapper)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def make_exception_marshallable
|
|
9
|
+
return if @exception.kind_of?(DeepTest::MarshallableExceptionWrapper)
|
|
10
|
+
@exception = DeepTest::MarshallableExceptionWrapper.new(@exception)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Test
|
|
3
|
+
class Runner
|
|
4
|
+
unless defined?(NO_FILTERS)
|
|
5
|
+
NO_FILTERS = Object.new.instance_eval do
|
|
6
|
+
def filters; []; end;
|
|
7
|
+
self
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(options)
|
|
12
|
+
@options = options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def process_work_units
|
|
16
|
+
suite = ::Test::Unit::AutoRunner::COLLECTORS[:objectspace].call NO_FILTERS
|
|
17
|
+
supervised_suite = DeepTest::Test::SupervisedTestSuite.new(suite, @options.server)
|
|
18
|
+
require 'test/unit/ui/console/testrunner'
|
|
19
|
+
result = ::Test::Unit::UI::Console::TestRunner.run(supervised_suite, ::Test::Unit::UI::NORMAL)
|
|
20
|
+
result.passed?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Test
|
|
3
|
+
class SupervisedTestSuite
|
|
4
|
+
def initialize(suite, blackboard)
|
|
5
|
+
@suite = suite
|
|
6
|
+
@blackboard = blackboard
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def run(result, &progress_block)
|
|
10
|
+
yield ::Test::Unit::TestSuite::STARTED, @suite.name
|
|
11
|
+
tests_by_name = {}
|
|
12
|
+
add_tests @suite, tests_by_name
|
|
13
|
+
read_results result, tests_by_name, &progress_block
|
|
14
|
+
yield ::Test::Unit::TestSuite::FINISHED, @suite.name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def size
|
|
18
|
+
@suite.size
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_tests(test_suite, tests_by_name)
|
|
22
|
+
if test_suite.respond_to? :tests
|
|
23
|
+
test_suite.tests.each do |test|
|
|
24
|
+
add_tests(test, tests_by_name)
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
tests_by_name[test_suite.name] = test_suite
|
|
28
|
+
@blackboard.write_work Test::WorkUnit.new(test_suite)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def read_results(result, tests_by_name)
|
|
33
|
+
DeepTest.logger.debug("SupervisedTestSuite: going to read #{tests_by_name.size} results")
|
|
34
|
+
|
|
35
|
+
missing_tests =
|
|
36
|
+
ResultReader.new(@blackboard).read(tests_by_name) do |test, remote_result|
|
|
37
|
+
remote_result.add_to result
|
|
38
|
+
yield ::Test::Unit::TestCase::FINISHED, test.name if block_given?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
missing_tests.each do |name, test_case|
|
|
42
|
+
result.add_error ::Test::Unit::Error.new(name, WorkUnitNeverReceivedError.new)
|
|
43
|
+
end
|
|
44
|
+
ensure
|
|
45
|
+
DeepTest.logger.debug("SupervisedTestSuite: exiting with #{missing_tests.size} results left")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Test
|
|
3
|
+
class WorkResult < ::Test::Unit::TestResult
|
|
4
|
+
attr_reader :identifier
|
|
5
|
+
attr_accessor :output
|
|
6
|
+
|
|
7
|
+
def initialize(identifier)
|
|
8
|
+
super()
|
|
9
|
+
@identifier = identifier
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_to(result)
|
|
13
|
+
@failures.each {|e| result.add_failure(e)}
|
|
14
|
+
|
|
15
|
+
@errors.each do |e|
|
|
16
|
+
e.resolve_marshallable_exception
|
|
17
|
+
result.add_error(e)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
assertion_count.times {result.add_assertion}
|
|
21
|
+
run_count.times {result.add_run}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add_error(error)
|
|
25
|
+
error.make_exception_marshallable
|
|
26
|
+
super(error)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def failed_due_to_deadlock?
|
|
30
|
+
@errors.any? && DeepTest::DeadlockDetector.due_to_deadlock?(@errors.last)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Test
|
|
3
|
+
class WorkUnit
|
|
4
|
+
def initialize(test_case)
|
|
5
|
+
@test_case = test_case
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def run
|
|
9
|
+
result = run_without_deadlock_protection
|
|
10
|
+
result = run_without_deadlock_protection if result.failed_due_to_deadlock?
|
|
11
|
+
if result.failed_due_to_deadlock?
|
|
12
|
+
result = WorkResult.new(@test_case.name)
|
|
13
|
+
result.add_run
|
|
14
|
+
result.output = "-deadlock-"
|
|
15
|
+
end
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ==(other)
|
|
20
|
+
return false unless other.class == self.class
|
|
21
|
+
@test_case == other.instance_variable_get(:@test_case)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
@test_case.to_s
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
|
|
30
|
+
def run_without_deadlock_protection
|
|
31
|
+
result = WorkResult.new(@test_case.name)
|
|
32
|
+
output = capture_stdout do
|
|
33
|
+
@test_case.run(result) {|channel,event|}
|
|
34
|
+
end
|
|
35
|
+
result.output = output
|
|
36
|
+
result
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class TestTask
|
|
3
|
+
attr_accessor :libs, :requires
|
|
4
|
+
|
|
5
|
+
def initialize(name = :deep_test)
|
|
6
|
+
@requires = []
|
|
7
|
+
@name = name
|
|
8
|
+
@libs = ["lib"]
|
|
9
|
+
@options = Options.new({})
|
|
10
|
+
self.pattern = "test/**/*_test.rb"
|
|
11
|
+
yield self if block_given?
|
|
12
|
+
define
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def define
|
|
16
|
+
require 'rake'
|
|
17
|
+
desc "Run '#{@name}' suite using DeepTest"
|
|
18
|
+
task @name do
|
|
19
|
+
lib_options = @libs.any? ? "-I" + @libs.join(File::PATH_SEPARATOR) : ""
|
|
20
|
+
require_options = requires.map {|f| "-r#{f}"}.join(" ")
|
|
21
|
+
ruby "#{lib_options} #{require_options} #{runner} '#{@options.to_command_line}'"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Options::VALID_OPTIONS.each do |option|
|
|
26
|
+
eval <<-end_src
|
|
27
|
+
def #{option.name}
|
|
28
|
+
@options.#{option.name}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def #{option.name}=(value)
|
|
32
|
+
@options.#{option.name} = value
|
|
33
|
+
end
|
|
34
|
+
end_src
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pattern=(pattern)
|
|
38
|
+
@options.pattern = Dir.pwd + "/" + pattern
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def runner
|
|
44
|
+
File.expand_path(File.dirname(__FILE__) + "/../../script/internal/run_test_suite.rb")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class Warlock
|
|
3
|
+
def initialize
|
|
4
|
+
@demons_semaphore = Mutex.new
|
|
5
|
+
@demons = []
|
|
6
|
+
@reapers = []
|
|
7
|
+
end
|
|
8
|
+
|
|
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
|
+
#
|
|
14
|
+
begin
|
|
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
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
launch_reaper_thread name, pid
|
|
42
|
+
|
|
43
|
+
rescue => e
|
|
44
|
+
puts "exception starting #{name}: #{e}"
|
|
45
|
+
puts "\t" + e.backtrace.join("\n\t")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def demon_count
|
|
50
|
+
@demons_semaphore.synchronize do
|
|
51
|
+
@demons.size
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def stop_all
|
|
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|
|
|
62
|
+
name, pid = demon
|
|
63
|
+
if running?(pid)
|
|
64
|
+
DeepTest.logger.debug("Sending SIGTERM to #{name}, #{pid}")
|
|
65
|
+
Process.kill("TERM", pid)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
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)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def any_running?
|
|
87
|
+
@demons_semaphore.synchronize do
|
|
88
|
+
@demons.any? {|name, pid| running?(pid)}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#stolen from daemons
|
|
93
|
+
def running?(pid)
|
|
94
|
+
# Check if process is in existence
|
|
95
|
+
# The simplest way to do this is to send signal '0'
|
|
96
|
+
# (which is a single system call) that doesn't actually
|
|
97
|
+
# send a signal
|
|
98
|
+
begin
|
|
99
|
+
Process.kill(0, pid)
|
|
100
|
+
return true
|
|
101
|
+
rescue Errno::ESRCH
|
|
102
|
+
return false
|
|
103
|
+
rescue ::Exception # for example on EPERM (process exists but does not belong to us)
|
|
104
|
+
return true
|
|
105
|
+
#rescue Errno::EPERM
|
|
106
|
+
# return false
|
|
107
|
+
end
|
|
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
|
|
133
|
+
end
|
|
134
|
+
end
|