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.
Files changed (117) hide show
  1. data/CHANGELOG +47 -0
  2. data/README.rdoc +228 -0
  3. data/Rakefile +247 -0
  4. data/bin/deep_test +15 -0
  5. data/lib/deep_test.rb +91 -0
  6. data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
  7. data/lib/deep_test/database/postgresql_setup_listener.rb +116 -0
  8. data/lib/deep_test/database/setup_listener.rb +125 -0
  9. data/lib/deep_test/deadlock_detector.rb +7 -0
  10. data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
  11. data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
  12. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  13. data/lib/deep_test/distributed/master_test_server.rb +52 -0
  14. data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
  15. data/lib/deep_test/distributed/null_work_unit.rb +12 -0
  16. data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
  17. data/lib/deep_test/distributed/remote_worker_server.rb +82 -0
  18. data/lib/deep_test/distributed/rsync.rb +37 -0
  19. data/lib/deep_test/distributed/show_status.rhtml +41 -0
  20. data/lib/deep_test/distributed/test_server.rb +78 -0
  21. data/lib/deep_test/distributed/test_server_status.rb +9 -0
  22. data/lib/deep_test/distributed/test_server_workers.rb +24 -0
  23. data/lib/deep_test/distributed/throughput_runner.rb +42 -0
  24. data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
  25. data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
  26. data/lib/deep_test/extensions/drb_extension.rb +34 -0
  27. data/lib/deep_test/extensions/object_extension.rb +40 -0
  28. data/lib/deep_test/listener_list.rb +17 -0
  29. data/lib/deep_test/local_workers.rb +55 -0
  30. data/lib/deep_test/logger.rb +17 -0
  31. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  32. data/lib/deep_test/metrics/gatherer.rb +67 -0
  33. data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
  34. data/lib/deep_test/null_worker_listener.rb +62 -0
  35. data/lib/deep_test/option.rb +60 -0
  36. data/lib/deep_test/options.rb +110 -0
  37. data/lib/deep_test/process_orchestrator.rb +49 -0
  38. data/lib/deep_test/rake_tasks.rb +11 -0
  39. data/lib/deep_test/result_reader.rb +36 -0
  40. data/lib/deep_test/rspec_detector.rb +21 -0
  41. data/lib/deep_test/server.rb +75 -0
  42. data/lib/deep_test/spec.rb +13 -0
  43. data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
  44. data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
  45. data/lib/deep_test/spec/extensions/options.rb +43 -0
  46. data/lib/deep_test/spec/extensions/reporter.rb +29 -0
  47. data/lib/deep_test/spec/extensions/spec_task.rb +20 -0
  48. data/lib/deep_test/spec/runner.rb +57 -0
  49. data/lib/deep_test/spec/work_result.rb +33 -0
  50. data/lib/deep_test/spec/work_unit.rb +59 -0
  51. data/lib/deep_test/test.rb +10 -0
  52. data/lib/deep_test/test/extensions/error.rb +14 -0
  53. data/lib/deep_test/test/runner.rb +24 -0
  54. data/lib/deep_test/test/supervised_test_suite.rb +49 -0
  55. data/lib/deep_test/test/work_result.rb +34 -0
  56. data/lib/deep_test/test/work_unit.rb +40 -0
  57. data/lib/deep_test/test_task.rb +47 -0
  58. data/lib/deep_test/ui/console.rb +76 -0
  59. data/lib/deep_test/ui/null.rb +17 -0
  60. data/lib/deep_test/warlock.rb +134 -0
  61. data/lib/deep_test/worker.rb +57 -0
  62. data/script/internal/run_test_suite.rb +7 -0
  63. data/script/public/master_test_server.rb +24 -0
  64. data/script/public/test_server.rb +18 -0
  65. data/script/public/test_throughput.rb +29 -0
  66. data/spec/deep_test/option_spec.rb +33 -0
  67. data/spec/deep_test/options_spec.rb +183 -0
  68. data/spec/deep_test/spec/extensions/example_group_methods_spec.rb +48 -0
  69. data/spec/deep_test/spec/extensions/example_methods_spec.rb +61 -0
  70. data/spec/deep_test/spec/extensions/options_spec.rb +23 -0
  71. data/spec/deep_test/spec/extensions/reporter_spec.rb +28 -0
  72. data/spec/deep_test/spec/extensions/spec_task_spec.rb +36 -0
  73. data/spec/deep_test/spec/runner_spec.rb +106 -0
  74. data/spec/deep_test/spec/work_result_spec.rb +14 -0
  75. data/spec/deep_test/spec/work_unit_spec.rb +78 -0
  76. data/spec/spec_helper.rb +59 -0
  77. data/spec/thread_worker.rb +25 -0
  78. data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
  79. data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
  80. data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
  81. data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
  82. data/test/deep_test/distributed/master_test_server_test.rb +32 -0
  83. data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
  84. data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
  85. data/test/deep_test/distributed/remote_worker_server_test.rb +99 -0
  86. data/test/deep_test/distributed/rsync_test.rb +67 -0
  87. data/test/deep_test/distributed/test_server_test.rb +94 -0
  88. data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
  89. data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
  90. data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
  91. data/test/deep_test/extensions/object_extension_test.rb +37 -0
  92. data/test/deep_test/listener_list_test.rb +20 -0
  93. data/test/deep_test/local_workers_test.rb +22 -0
  94. data/test/deep_test/logger_test.rb +11 -0
  95. data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
  96. data/test/deep_test/metrics/gatherer_test.rb +66 -0
  97. data/test/deep_test/process_orchestrator_test.rb +11 -0
  98. data/test/deep_test/result_reader_test.rb +128 -0
  99. data/test/deep_test/server_test.rb +58 -0
  100. data/test/deep_test/test/extensions/error_test.rb +40 -0
  101. data/test/deep_test/test/runner_test.rb +7 -0
  102. data/test/deep_test/test/supervised_test_suite_test.rb +79 -0
  103. data/test/deep_test/test/work_result_test.rb +81 -0
  104. data/test/deep_test/test/work_unit_test.rb +61 -0
  105. data/test/deep_test/test_task_test.rb +43 -0
  106. data/test/deep_test/ui/console_test.rb +9 -0
  107. data/test/deep_test/warlock_test.rb +38 -0
  108. data/test/deep_test/worker_test.rb +94 -0
  109. data/test/failing.rake +11 -0
  110. data/test/failing.rb +7 -0
  111. data/test/fake_deadlock_error.rb +12 -0
  112. data/test/simple_test_blackboard.rb +45 -0
  113. data/test/simple_test_blackboard_test.rb +33 -0
  114. data/test/test_factory.rb +74 -0
  115. data/test/test_helper.rb +15 -0
  116. data/test/test_task_test.rb +72 -0
  117. metadata +214 -0
@@ -0,0 +1,21 @@
1
+ module DeepTest
2
+ class RSpecDetector
3
+ def self.if_rspec_available
4
+ if defined?(::Spec)
5
+ require 'spec/version'
6
+ if ::Spec::VERSION::MAJOR == 1 &&
7
+ ::Spec::VERSION::MINOR == 1 &&
8
+ ::Spec::VERSION::TINY == 8
9
+ yield
10
+ else
11
+ require 'spec/rake/spectask'
12
+ ::Spec::Rake::SpecTask.class_eval do
13
+ def deep_test(options)
14
+ raise "* DeepTest RSpec support requires RSpec 1.1.8"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,75 @@
1
+ module DeepTest
2
+ class Server
3
+ def self.start(options)
4
+ server = new(options)
5
+ DRb.start_service("druby://0.0.0.0:#{options.server_port}", server)
6
+ DeepTest.logger.info "Started DeepTest service at #{DRb.uri}"
7
+ server
8
+ end
9
+
10
+ def self.stop
11
+ DRb.stop_service
12
+ end
13
+
14
+ def self.remote_reference(address, port)
15
+ DRb.start_service
16
+ blackboard = DRbObject.new_with_uri("druby://#{address}:#{port}")
17
+ DeepTest.logger.debug "Connecting to DeepTest server at #{blackboard.__drburi}"
18
+ blackboard
19
+ end
20
+
21
+ def initialize(options)
22
+ @options = options
23
+ @work_queue = Queue.new
24
+ @result_queue = Queue.new
25
+
26
+ if Metrics::Gatherer.enabled?
27
+ require File.dirname(__FILE__) + "/metrics/queue_lock_wait_time_measurement"
28
+ @work_queue.extend Metrics::QueueLockWaitTimeMeasurement
29
+ @result_queue.extend Metrics::QueueLockWaitTimeMeasurement
30
+ Metrics::Gatherer.section("server queue lock wait times") do |s|
31
+ s.measurement("work queue total pop wait time", @work_queue.total_pop_time)
32
+ s.measurement("work queue total push wait time", @work_queue.total_push_time)
33
+ s.measurement("result queue total pop wait time", @result_queue.total_pop_time)
34
+ s.measurement("result queue total push wait time", @result_queue.total_push_time)
35
+ end
36
+ end
37
+ end
38
+
39
+ def done_with_work
40
+ @done_with_work = true
41
+ end
42
+
43
+ def take_result
44
+ Timeout.timeout(@options.timeout_in_seconds, ResultOverdueError) do
45
+ @result_queue.pop
46
+ end
47
+ end
48
+
49
+ def take_work
50
+ raise NoWorkUnitsRemainingError if @done_with_work
51
+
52
+ @work_queue.pop(true)
53
+ rescue ThreadError => e
54
+ if e.message == "queue empty"
55
+ raise NoWorkUnitsAvailableError
56
+ else
57
+ raise
58
+ end
59
+ end
60
+
61
+ def write_result(result)
62
+ @result_queue.push result
63
+ nil
64
+ end
65
+
66
+ def write_work(work_unit)
67
+ @work_queue.push work_unit
68
+ nil
69
+ end
70
+
71
+ class NoWorkUnitsAvailableError < StandardError; end
72
+ class NoWorkUnitsRemainingError < StandardError; end
73
+ class ResultOverdueError < StandardError; end
74
+ end
75
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'spec/runner/example_group_runner'
3
+ require 'spec/example/example_group_methods'
4
+ require 'spec/rake/spectask'
5
+
6
+ require File.dirname(__FILE__) + "/spec/extensions/example_group_methods"
7
+ require File.dirname(__FILE__) + "/spec/extensions/example_methods"
8
+ require File.dirname(__FILE__) + "/spec/extensions/spec_task"
9
+ require File.dirname(__FILE__) + "/spec/extensions/options"
10
+ require File.dirname(__FILE__) + "/spec/extensions/reporter"
11
+ require File.dirname(__FILE__) + "/spec/runner"
12
+ require File.dirname(__FILE__) + "/spec/work_unit"
13
+ require File.dirname(__FILE__) + "/spec/work_result"
@@ -0,0 +1,64 @@
1
+ module Spec
2
+ module Example
3
+ module ExampleGroupMethods
4
+ class << self
5
+ def assign_instance_method_to_constant(proposed_constant)
6
+ method_sym = proposed_constant.to_s.downcase
7
+
8
+ unless const_defined?(proposed_constant)
9
+ const_set(proposed_constant, instance_method(method_sym))
10
+ end
11
+ end
12
+
13
+ private :assign_instance_method_to_constant
14
+ end
15
+
16
+ assign_instance_method_to_constant :PREPEND_BEFORE
17
+ assign_instance_method_to_constant :APPEND_BEFORE
18
+ assign_instance_method_to_constant :PREPEND_AFTER
19
+ assign_instance_method_to_constant :APPEND_AFTER
20
+
21
+ def prepend_before(*args, &block)
22
+ check_filter_args(args)
23
+ call_regular_instance_method :prepend_before, *args, &block
24
+ end
25
+
26
+ def append_before(*args, &block)
27
+ check_filter_args(args)
28
+ call_regular_instance_method :append_before, *args, &block
29
+ end
30
+
31
+ alias_method :before, :append_before
32
+
33
+ def prepend_after(*args, &block)
34
+ check_filter_args(args)
35
+ call_regular_instance_method :prepend_after, *args, &block
36
+ end
37
+
38
+ def append_after(*args, &block)
39
+ check_filter_args(args)
40
+ call_regular_instance_method :append_after, *args, &block
41
+ end
42
+
43
+ alias_method :after, :append_after
44
+
45
+ private
46
+
47
+ DeepTestAllBlockWarning =
48
+ "Warning: DeepTest will run before(:all) and after(:all) blocks for *every* test that is run. To remove this warning either convert all before/after blocks to each blocks or set $show_deep_test_all_block_warning to false" unless defined?(DeepTestAllBlockWarning)
49
+
50
+ $show_deep_test_all_block_warning = true
51
+
52
+ def check_filter_args(args)
53
+ if args.first == :all && $show_deep_test_all_block_warning
54
+ $show_deep_test_all_block_warning = false
55
+ $stderr.puts DeepTestAllBlockWarning
56
+ end
57
+ end
58
+
59
+ def call_regular_instance_method(sym, *args, &block)
60
+ ExampleGroupMethods.const_get(sym.to_s.upcase).bind(self).call(*args, &block)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ module Spec
2
+ module Example
3
+ module ExampleMethods
4
+ def identifier
5
+ file, line = implementation_backtrace.first.split(/:/)
6
+ Identifier.new(file, line.to_i, self.class.description, description)
7
+ end
8
+
9
+ class Identifier
10
+ attr_reader :file, :line, :group_description, :description
11
+ def initialize(file, line, group_description, description)
12
+ @file, @line, @group_description, @description =
13
+ file, line, group_description, description
14
+ end
15
+
16
+ def ==(other)
17
+ eql?(other)
18
+ end
19
+
20
+ def eql?(other)
21
+ File.basename(file) == File.basename(other.file) &&
22
+ line == other.line &&
23
+ group_description == other.group_description &&
24
+ description == other.description
25
+ end
26
+
27
+ def hash
28
+ description.hash
29
+ end
30
+
31
+ def locate(groups)
32
+ groups.each do |group|
33
+ group.examples.each do |example|
34
+ return example if example.identifier == self
35
+ end
36
+ end
37
+ raise "Unable to locate example #{self}"
38
+ end
39
+
40
+ def to_s
41
+ "#{group_description} #{description}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ module Spec
2
+ module Runner
3
+ class Options
4
+ def run_one_example(identifier)
5
+ example = identifier.locate(example_groups)
6
+ SingleExampleRunner.new(self, example).run
7
+ end
8
+
9
+ class SingleExampleRunner < ExampleGroupRunner
10
+ def initialize(options, example)
11
+ super(options)
12
+ @example = example
13
+ example_group.extend ExampleGroupHelper
14
+ end
15
+
16
+ def example_group
17
+ @example.class
18
+ end
19
+
20
+ def example_groups
21
+ [example_group]
22
+ end
23
+
24
+ def run
25
+ example_group.with_example_objects([@example]) do
26
+ super
27
+ end
28
+ end
29
+
30
+ module ExampleGroupHelper
31
+ def with_example_objects(example_objects)
32
+ original_example_objects = @example_objects
33
+ @example_objects = example_objects
34
+ yield
35
+ ensure
36
+ @example_objects = original_example_objects
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,29 @@
1
+ require 'spec/runner/reporter'
2
+ module Spec
3
+ module Runner
4
+ class Reporter
5
+ def example_finished(example, error=nil)
6
+ @examples << example
7
+
8
+ if error.nil?
9
+ example_passed(example)
10
+ elsif Spec::Example::ExamplePendingError === error
11
+ example_pending(example, error.message)
12
+ else
13
+ example_failed(example, error)
14
+ end
15
+ end
16
+
17
+ def failure(example, error)
18
+ backtrace_tweaker.tweak_backtrace(error)
19
+ example_name = "#{example.class.description} #{example.description}"
20
+ failure = Failure.new(example, error)
21
+ @failures << failure
22
+ formatters.each do |f|
23
+ f.example_failed(example, @failures.length, failure)
24
+ end
25
+ end
26
+ alias_method :example_failed, :failure
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ module Spec
2
+ module Rake
3
+ class SpecTask
4
+ def deep_test(options)
5
+ deep_test_options = DeepTest::Options.new(options)
6
+ deep_test_path = File.expand_path(File.dirname(__FILE__) +
7
+ "/../../../deep_test")
8
+ @deep_test_spec_opts = [
9
+ "--require #{deep_test_path}",
10
+ "--runner 'DeepTest::Spec::Runner:#{deep_test_options.to_command_line}'"
11
+ ]
12
+ spec_opts.concat @deep_test_spec_opts
13
+ end
14
+
15
+ def spec_opts=(options)
16
+ @spec_opts = (@deep_test_spec_opts || []) + options
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ module DeepTest
2
+ module Spec
3
+ class Runner < ::Spec::Runner::ExampleGroupRunner
4
+ def initialize(options, deep_test_options, blackboard = nil)
5
+ super(options)
6
+ @deep_test_options = DeepTest::Options.from_command_line(deep_test_options)
7
+ DeepTest.init(@deep_test_options)
8
+ @blackboard = blackboard
9
+ @workers = @deep_test_options.new_workers
10
+ end
11
+
12
+ def blackboard
13
+ # Can't create blackboard as default argument to initialize
14
+ # because ProcessOrchestrator hasn't been invoked at
15
+ # instantiation time
16
+ @blackboard ||= @deep_test_options.server
17
+ end
18
+
19
+ def load_files(files)
20
+ @workers.load_files files
21
+ end
22
+
23
+ def run
24
+ ProcessOrchestrator.run(@deep_test_options, @workers, self)
25
+ end
26
+
27
+ def process_work_units
28
+ prepare
29
+
30
+ examples = example_groups.map {|g| g.send(:examples_to_run)}.flatten
31
+ examples_by_location = {}
32
+ examples.each do |example|
33
+ raise "duplicate example: #{example.identifier}" if examples_by_location[example.identifier]
34
+ examples_by_location[example.identifier] = example
35
+ blackboard.write_work Spec::WorkUnit.new(example.identifier)
36
+ end
37
+
38
+ success = true
39
+
40
+ missing_exmaples = ResultReader.new(blackboard).read(examples_by_location) do |example, result|
41
+ @options.reporter.example_finished(example, result.error)
42
+ success &= result.success?
43
+ end
44
+
45
+ success &= missing_exmaples.empty?
46
+
47
+ missing_exmaples.each do |identifier, example|
48
+ @options.reporter.example_finished(example, WorkUnitNeverReceivedError.new)
49
+ end
50
+
51
+ success
52
+ ensure
53
+ finish
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ module DeepTest
2
+ module Spec
3
+ class WorkResult
4
+ attr_reader :identifier, :output
5
+
6
+ def initialize(identifier, error, output)
7
+ @identifier, @output = identifier, output
8
+ @error = MarshallableExceptionWrapper.new error if error
9
+ end
10
+
11
+ def error
12
+ @error.resolve if @error
13
+ end
14
+
15
+ def ==(other)
16
+ identifier == other.identifier &&
17
+ @error == other.instance_variable_get(:@error)
18
+ end
19
+
20
+ def failed_due_to_deadlock?
21
+ DeadlockDetector.due_to_deadlock?(@error)
22
+ end
23
+
24
+ def success?
25
+ error.nil? || ::Spec::Example::ExamplePendingError === error
26
+ end
27
+
28
+ def deadlock_result
29
+ WorkResult.new(identifier, nil, '-deadlock-')
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ module DeepTest
2
+ module Spec
3
+ class WorkUnit
4
+ def initialize(identifier)
5
+ @identifier = identifier
6
+ end
7
+
8
+ def run
9
+ # Dup options here to avoid clobbering the reporter on someone
10
+ # elses options reference (Such as ExampleGroupRunner)
11
+ original_options = ::Spec::Runner.options
12
+ ::Spec::Runner.use ::Spec::Runner.options.dup
13
+ ::Spec::Runner.options.reporter = ResultReporter.new(@identifier)
14
+ result = run_without_deadlock_protection
15
+ result = run_without_deadlock_protection if result.failed_due_to_deadlock?
16
+ result = result.deadlock_result if result.failed_due_to_deadlock?
17
+ result
18
+ ensure
19
+ ::Spec::Runner.use original_options
20
+ end
21
+
22
+ def to_s
23
+ "#{@identifier.group_description}: #{@identifier.description}"
24
+ end
25
+
26
+ protected
27
+
28
+ def run_without_deadlock_protection
29
+ output = capture_stdout do
30
+ ::Spec::Runner.options.run_one_example(@identifier)
31
+ end
32
+ ::Spec::Runner.options.reporter.result(output)
33
+ end
34
+
35
+ class ResultReporter
36
+ attr_reader :result
37
+
38
+ def initialize(identifier)
39
+ @identifier = identifier
40
+ end
41
+
42
+ def add_example_group(example_group); end
43
+ def dump; end
44
+ def end; end
45
+ def example_started(name); end
46
+
47
+ def example_finished(example, error)
48
+ @example, @error = example, error
49
+ end
50
+
51
+ def result(output)
52
+ Spec::WorkResult.new(@identifier, @error, output)
53
+ end
54
+
55
+ def start(example); end
56
+ end
57
+ end
58
+ end
59
+ end