jason-o-matic-deep_test 1.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/CHANGELOG +47 -0
  2. data/README.rdoc +228 -0
  3. data/Rakefile +211 -0
  4. data/bin/deep_test +15 -0
  5. data/lib/deep_test.rb +90 -0
  6. data/lib/deep_test/database/mysql_setup_listener.rb +113 -0
  7. data/lib/deep_test/database/setup_listener.rb +116 -0
  8. data/lib/deep_test/deadlock_detector.rb +7 -0
  9. data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
  10. data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
  11. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  12. data/lib/deep_test/distributed/master_test_server.rb +52 -0
  13. data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
  14. data/lib/deep_test/distributed/null_work_unit.rb +12 -0
  15. data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
  16. data/lib/deep_test/distributed/remote_worker_server.rb +111 -0
  17. data/lib/deep_test/distributed/rsync.rb +37 -0
  18. data/lib/deep_test/distributed/show_status.rhtml +41 -0
  19. data/lib/deep_test/distributed/test_server.rb +79 -0
  20. data/lib/deep_test/distributed/test_server_status.rb +9 -0
  21. data/lib/deep_test/distributed/test_server_workers.rb +24 -0
  22. data/lib/deep_test/distributed/throughput_runner.rb +42 -0
  23. data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
  24. data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
  25. data/lib/deep_test/extensions/drb_extension.rb +34 -0
  26. data/lib/deep_test/extensions/object_extension.rb +40 -0
  27. data/lib/deep_test/listener_list.rb +17 -0
  28. data/lib/deep_test/local_workers.rb +55 -0
  29. data/lib/deep_test/logger.rb +17 -0
  30. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  31. data/lib/deep_test/metrics/gatherer.rb +67 -0
  32. data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
  33. data/lib/deep_test/null_worker_listener.rb +65 -0
  34. data/lib/deep_test/option.rb +70 -0
  35. data/lib/deep_test/options.rb +112 -0
  36. data/lib/deep_test/process_orchestrator.rb +49 -0
  37. data/lib/deep_test/rake_tasks.rb +11 -0
  38. data/lib/deep_test/result_reader.rb +36 -0
  39. data/lib/deep_test/rspec_detector.rb +24 -0
  40. data/lib/deep_test/server.rb +75 -0
  41. data/lib/deep_test/spec.rb +12 -0
  42. data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
  43. data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
  44. data/lib/deep_test/spec/extensions/options.rb +43 -0
  45. data/lib/deep_test/spec/extensions/reporter.rb +29 -0
  46. data/lib/deep_test/spec/extensions/spec_task.rb +20 -0
  47. data/lib/deep_test/spec/runner.rb +57 -0
  48. data/lib/deep_test/spec/work_result.rb +33 -0
  49. data/lib/deep_test/spec/work_unit.rb +59 -0
  50. data/lib/deep_test/test.rb +10 -0
  51. data/lib/deep_test/test/extensions/error.rb +14 -0
  52. data/lib/deep_test/test/runner.rb +24 -0
  53. data/lib/deep_test/test/supervised_test_suite.rb +49 -0
  54. data/lib/deep_test/test/work_result.rb +34 -0
  55. data/lib/deep_test/test/work_unit.rb +40 -0
  56. data/lib/deep_test/test_task.rb +51 -0
  57. data/lib/deep_test/ui/console.rb +76 -0
  58. data/lib/deep_test/ui/null.rb +17 -0
  59. data/lib/deep_test/warlock.rb +134 -0
  60. data/lib/deep_test/worker.rb +57 -0
  61. data/script/internal/run_test_suite.rb +7 -0
  62. data/script/public/master_test_server.rb +24 -0
  63. data/script/public/test_server.rb +18 -0
  64. data/script/public/test_throughput.rb +29 -0
  65. data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
  66. data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
  67. data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
  68. data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
  69. data/test/deep_test/distributed/master_test_server_test.rb +32 -0
  70. data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
  71. data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
  72. data/test/deep_test/distributed/remote_worker_server_test.rb +107 -0
  73. data/test/deep_test/distributed/rsync_test.rb +67 -0
  74. data/test/deep_test/distributed/test_server_test.rb +94 -0
  75. data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
  76. data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
  77. data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
  78. data/test/deep_test/extensions/object_extension_test.rb +37 -0
  79. data/test/deep_test/listener_list_test.rb +20 -0
  80. data/test/deep_test/local_workers_test.rb +22 -0
  81. data/test/deep_test/logger_test.rb +11 -0
  82. data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
  83. data/test/deep_test/metrics/gatherer_test.rb +66 -0
  84. data/test/deep_test/process_orchestrator_test.rb +11 -0
  85. data/test/deep_test/result_reader_test.rb +128 -0
  86. data/test/deep_test/server_test.rb +58 -0
  87. data/test/deep_test/test/extensions/error_test.rb +40 -0
  88. data/test/deep_test/test/runner_test.rb +7 -0
  89. data/test/deep_test/test/supervised_test_suite_test.rb +79 -0
  90. data/test/deep_test/test/work_result_test.rb +81 -0
  91. data/test/deep_test/test/work_unit_test.rb +61 -0
  92. data/test/deep_test/test_task_test.rb +43 -0
  93. data/test/deep_test/ui/console_test.rb +9 -0
  94. data/test/deep_test/warlock_test.rb +38 -0
  95. data/test/deep_test/worker_test.rb +94 -0
  96. data/test/failing.rake +11 -0
  97. data/test/failing.rb +7 -0
  98. data/test/fake_deadlock_error.rb +12 -0
  99. data/test/simple_test_blackboard.rb +45 -0
  100. data/test/simple_test_blackboard_test.rb +33 -0
  101. data/test/test_factory.rb +74 -0
  102. data/test/test_helper.rb +15 -0
  103. data/test/test_task_test.rb +72 -0
  104. metadata +160 -0
@@ -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,51 @@
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
+
11
+ @options.requires = []
12
+ @options.libs = ["lib"]
13
+
14
+ self.pattern = "test/**/*_test.rb"
15
+ yield self if block_given?
16
+ define
17
+ end
18
+
19
+ def define
20
+ require 'rake'
21
+ desc "Run '#{@name}' suite using DeepTest"
22
+ task @name do
23
+ lib_options = libs.any? ? "-I" + libs.join(File::PATH_SEPARATOR) : ""
24
+ require_options = requires.map {|f| "-r#{f}"}.join(" ")
25
+ ruby "#{lib_options} #{require_options} #{runner} '#{@options.to_command_line}'"
26
+ end
27
+ end
28
+
29
+ Options::VALID_OPTIONS.each do |option|
30
+ eval <<-end_src
31
+ def #{option.name}
32
+ @options.#{option.name}
33
+ end
34
+
35
+ def #{option.name}=(value)
36
+ @options.#{option.name} = value
37
+ end
38
+ end_src
39
+ end
40
+
41
+ def pattern=(pattern)
42
+ @options.pattern = Dir.pwd + "/" + pattern
43
+ end
44
+
45
+ private
46
+
47
+ def runner
48
+ File.expand_path(File.dirname(__FILE__) + "/../../script/internal/run_test_suite.rb")
49
+ end
50
+ end
51
+ 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
@@ -0,0 +1,57 @@
1
+ module DeepTest
2
+ class Worker
3
+ attr_reader :number
4
+
5
+ def initialize(number, blackboard, worker_listener)
6
+ @number = number
7
+ @blackboard = blackboard
8
+ @listener = worker_listener
9
+ end
10
+
11
+ def run
12
+ @listener.starting(self)
13
+ while work_unit = next_work_unit
14
+ @listener.starting_work(self, work_unit)
15
+
16
+ result = begin
17
+ work_unit.run
18
+ rescue Exception => error
19
+ Error.new(work_unit, error)
20
+ end
21
+
22
+ @listener.finished_work(self, work_unit, result)
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")
54
+ end
55
+ end
56
+ end
57
+ 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
+