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.
Files changed (99) hide show
  1. data/CHANGELOG +15 -5
  2. data/README +149 -5
  3. data/Rakefile +131 -11
  4. data/bin/deep_test +15 -0
  5. data/lib/deep_test.rb +62 -4
  6. data/lib/deep_test/database/mysql_setup_listener.rb +109 -0
  7. data/lib/deep_test/database/setup_listener.rb +116 -0
  8. data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
  9. data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
  10. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  11. data/lib/deep_test/distributed/master_test_server.rb +52 -0
  12. data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
  13. data/lib/deep_test/distributed/null_work_unit.rb +12 -0
  14. data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
  15. data/lib/deep_test/distributed/remote_worker_server.rb +82 -0
  16. data/lib/deep_test/distributed/rsync.rb +37 -0
  17. data/lib/deep_test/distributed/show_status.rhtml +41 -0
  18. data/lib/deep_test/distributed/test_server.rb +78 -0
  19. data/lib/deep_test/distributed/test_server_status.rb +9 -0
  20. data/lib/deep_test/distributed/test_server_workers.rb +24 -0
  21. data/lib/deep_test/distributed/throughput_runner.rb +42 -0
  22. data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
  23. data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
  24. data/lib/deep_test/extensions/drb_extension.rb +34 -0
  25. data/lib/deep_test/extensions/object_extension.rb +8 -0
  26. data/lib/deep_test/listener_list.rb +17 -0
  27. data/lib/deep_test/local_workers.rb +55 -0
  28. data/lib/deep_test/logger.rb +10 -2
  29. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  30. data/lib/deep_test/metrics/gatherer.rb +67 -0
  31. data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
  32. data/lib/deep_test/null_worker_listener.rb +50 -0
  33. data/lib/deep_test/option.rb +60 -0
  34. data/lib/deep_test/options.rb +61 -24
  35. data/lib/deep_test/process_orchestrator.rb +31 -78
  36. data/lib/deep_test/rake_tasks.rb +3 -0
  37. data/lib/deep_test/result_reader.rb +36 -0
  38. data/lib/deep_test/rspec_detector.rb +15 -8
  39. data/lib/deep_test/server.rb +68 -4
  40. data/lib/deep_test/spec.rb +1 -0
  41. data/lib/deep_test/spec/extensions/example_group_methods.rb +37 -11
  42. data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
  43. data/lib/deep_test/spec/extensions/options.rb +35 -3
  44. data/lib/deep_test/spec/runner.rb +19 -10
  45. data/lib/deep_test/spec/work_result.rb +12 -9
  46. data/lib/deep_test/spec/work_unit.rb +12 -7
  47. data/lib/deep_test/test.rb +1 -1
  48. data/lib/deep_test/test/extensions/error.rb +7 -7
  49. data/lib/deep_test/test/runner.rb +1 -6
  50. data/lib/deep_test/test/supervised_test_suite.rb +19 -16
  51. data/lib/deep_test/test/work_result.rb +34 -0
  52. data/lib/deep_test/test/work_unit.rb +6 -2
  53. data/lib/deep_test/test_task.rb +15 -35
  54. data/lib/deep_test/ui/console.rb +76 -0
  55. data/lib/deep_test/ui/null.rb +17 -0
  56. data/lib/deep_test/warlock.rb +92 -17
  57. data/lib/deep_test/worker.rb +38 -2
  58. data/script/internal/run_test_suite.rb +7 -0
  59. data/script/public/master_test_server.rb +24 -0
  60. data/script/public/test_server.rb +18 -0
  61. data/script/public/test_throughput.rb +29 -0
  62. data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
  63. data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
  64. data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
  65. data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
  66. data/test/deep_test/distributed/master_test_server_test.rb +32 -0
  67. data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
  68. data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
  69. data/test/deep_test/distributed/remote_worker_server_test.rb +99 -0
  70. data/test/deep_test/distributed/rsync_test.rb +67 -0
  71. data/test/deep_test/distributed/test_server_test.rb +94 -0
  72. data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
  73. data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
  74. data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
  75. data/test/deep_test/listener_list_test.rb +20 -0
  76. data/test/deep_test/local_workers_test.rb +22 -0
  77. data/test/{logger_test.rb → deep_test/logger_test.rb} +2 -2
  78. data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
  79. data/test/deep_test/metrics/gatherer_test.rb +66 -0
  80. data/test/deep_test/process_orchestrator_test.rb +11 -0
  81. data/test/deep_test/result_reader_test.rb +128 -0
  82. data/test/deep_test/server_test.rb +58 -0
  83. data/test/deep_test/test/extensions/error_test.rb +40 -0
  84. data/test/deep_test/test/supervised_test_suite_test.rb +19 -29
  85. data/test/deep_test/test/work_result_test.rb +81 -0
  86. data/test/deep_test/test/work_unit_test.rb +15 -4
  87. data/test/deep_test/test_task_test.rb +10 -0
  88. data/test/deep_test/ui/console_test.rb +9 -0
  89. data/test/deep_test/warlock_test.rb +17 -0
  90. data/test/deep_test/worker_test.rb +32 -0
  91. data/test/simple_test_blackboard.rb +2 -1
  92. data/test/test_helper.rb +1 -0
  93. metadata +117 -59
  94. data/lib/deep_test/rinda_blackboard.rb +0 -26
  95. data/lib/deep_test/test/extensions/test_result.rb +0 -17
  96. data/lib/deep_test/tuple_space_factory.rb +0 -12
  97. data/script/run_test_suite.rb +0 -5
  98. data/test/deep_test/rinda_blackboard_test.rb +0 -15
  99. data/test/deep_test/test/extensions/test_result_test.rb +0 -71
@@ -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
@@ -1,9 +1,41 @@
1
1
  module Spec
2
2
  module Runner
3
3
  class Options
4
- def run_one_example(file, line_number)
5
- @examples = [SpecParser.new.spec_name_for(file, line_number)]
6
- ExampleGroupRunner.new(self).run
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
7
39
  end
8
40
  end
9
41
  end
@@ -4,18 +4,24 @@ module DeepTest
4
4
  def initialize(options, deep_test_options, blackboard = nil)
5
5
  super(options)
6
6
  @deep_test_options = DeepTest::Options.from_command_line(deep_test_options)
7
+ DeepTest.init(@deep_test_options)
7
8
  @blackboard = blackboard
9
+ @workers = @deep_test_options.new_workers
8
10
  end
9
11
 
10
12
  def blackboard
11
13
  # Can't create blackboard as default argument to initialize
12
14
  # because ProcessOrchestrator hasn't been invoked at
13
15
  # instantiation time
14
- @blackboard ||= RindaBlackboard.new(@deep_test_options)
16
+ @blackboard ||= @deep_test_options.server
17
+ end
18
+
19
+ def load_files(files)
20
+ @workers.load_files files
15
21
  end
16
22
 
17
23
  def run
18
- ProcessOrchestrator.run(@deep_test_options, self)
24
+ ProcessOrchestrator.run(@deep_test_options, @workers, self)
19
25
  end
20
26
 
21
27
  def process_work_units
@@ -24,21 +30,24 @@ module DeepTest
24
30
  examples = example_groups.map {|g| g.send(:examples_to_run)}.flatten
25
31
  examples_by_location = {}
26
32
  examples.each do |example|
27
- file, line, *rest = example.implementation_backtrace.first.split(/:/)
28
- examples_by_location["#{file}:#{line}"] = example
29
- blackboard.write_work Spec::WorkUnit.new(file, line.to_i)
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)
30
36
  end
31
37
 
32
38
  success = true
33
- until examples_by_location.empty?
34
- result = blackboard.take_result
35
- next unless result
36
- print result.output if result.output
37
- example = examples_by_location.delete("#{result.file}:#{result.line}")
39
+
40
+ missing_exmaples = ResultReader.new(blackboard).read(examples_by_location) do |example, result|
38
41
  @options.reporter.example_finished(example, result.error)
39
42
  success &= result.success?
40
43
  end
41
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
+
42
51
  success
43
52
  ensure
44
53
  finish
@@ -1,17 +1,20 @@
1
1
  module DeepTest
2
2
  module Spec
3
3
  class WorkResult
4
- attr_reader :file, :line, :example_description, :error, :output
5
- def initialize(file, line, example_description, error, output)
6
- @file, @line, @example_description, @error, @output =
7
- file, line, example_description, error, output
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
8
13
  end
9
14
 
10
15
  def ==(other)
11
- file == other.file &&
12
- line == other.line &&
13
- example_description == other.example_description &&
14
- error == other.error
16
+ identifier == other.identifier &&
17
+ @error == other.instance_variable_get(:@error)
15
18
  end
16
19
 
17
20
  def failed_due_to_deadlock?
@@ -23,7 +26,7 @@ module DeepTest
23
26
  end
24
27
 
25
28
  def deadlock_result
26
- WorkResult.new(file, line, example_description, nil, '-deadlock-')
29
+ WorkResult.new(identifier, nil, '-deadlock-')
27
30
  end
28
31
  end
29
32
  end
@@ -1,15 +1,15 @@
1
1
  module DeepTest
2
2
  module Spec
3
3
  class WorkUnit
4
- def initialize(file, line)
5
- @file, @line = file, line
4
+ def initialize(identifier)
5
+ @identifier = identifier
6
6
  end
7
7
 
8
8
  def run
9
9
  # Dup options here to avoid clobbering the reporter on someone
10
10
  # elses options reference (Such as ExampleGroupRunner)
11
11
  original_options, $rspec_options = $rspec_options, $rspec_options.dup
12
- rspec_options.reporter = ResultReporter.new(@file, @line)
12
+ rspec_options.reporter = ResultReporter.new(@identifier)
13
13
  result = run_without_deadlock_protection
14
14
  result = run_without_deadlock_protection if result.failed_due_to_deadlock?
15
15
  result = result.deadlock_result if result.failed_due_to_deadlock?
@@ -18,19 +18,24 @@ module DeepTest
18
18
  $rspec_options = original_options
19
19
  end
20
20
 
21
+ def to_s
22
+ "#{@identifier.group_description}: #{@identifier.description}"
23
+ end
24
+
21
25
  protected
22
26
 
23
27
  def run_without_deadlock_protection
24
28
  output = capture_stdout do
25
- rspec_options.run_one_example(@file, @line)
29
+ rspec_options.run_one_example(@identifier)
26
30
  end
27
31
  rspec_options.reporter.result(output)
28
32
  end
29
33
 
30
34
  class ResultReporter
31
35
  attr_reader :result
32
- def initialize(file,line)
33
- @file, @line = file, line
36
+
37
+ def initialize(identifier)
38
+ @identifier = identifier
34
39
  end
35
40
 
36
41
  def add_example_group(example_group); end
@@ -43,7 +48,7 @@ module DeepTest
43
48
  end
44
49
 
45
50
  def result(output)
46
- Spec::WorkResult.new(@file, @line, @example.description, @error, output)
51
+ Spec::WorkResult.new(@identifier, @error, output)
47
52
  end
48
53
 
49
54
  def start(example); end
@@ -2,8 +2,8 @@ require "test/unit/testresult"
2
2
  require "test/unit/error"
3
3
  require 'test/unit/failure'
4
4
 
5
- require File.dirname(__FILE__) + "/test/extensions/test_result"
6
5
  require File.dirname(__FILE__) + "/test/extensions/error"
7
6
  require File.dirname(__FILE__) + "/test/runner"
8
7
  require File.dirname(__FILE__) + "/test/supervised_test_suite"
9
8
  require File.dirname(__FILE__) + "/test/work_unit"
9
+ require File.dirname(__FILE__) + "/test/work_result"
@@ -1,14 +1,14 @@
1
1
  module Test
2
2
  module Unit
3
3
  class Error
4
- def initialize_with_exception_wrapping(test_name, exception)
5
- wrap = Exception.new "#{exception.class}: #{exception.message}"
6
- wrap.set_backtrace exception.backtrace
7
- @test_name = test_name
8
- @exception = wrap
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)
9
11
  end
10
- alias_method :initialize_without_exception_wrapping, :initialize
11
- alias_method :initialize, :initialize_with_exception_wrapping
12
12
  end
13
13
  end
14
14
  end
@@ -12,14 +12,9 @@ module DeepTest
12
12
  @options = options
13
13
  end
14
14
 
15
- def load_files
16
- Dir.glob(@options.pattern).each { |file| load file }
17
- ::Test::Unit.run = true
18
- end
19
-
20
15
  def process_work_units
21
16
  suite = ::Test::Unit::AutoRunner::COLLECTORS[:objectspace].call NO_FILTERS
22
- supervised_suite = DeepTest::Test::SupervisedTestSuite.new(suite, RindaBlackboard.new(@options))
17
+ supervised_suite = DeepTest::Test::SupervisedTestSuite.new(suite, @options.server)
23
18
  require 'test/unit/ui/console/testrunner'
24
19
  result = ::Test::Unit::UI::Console::TestRunner.run(supervised_suite, ::Test::Unit::UI::NORMAL)
25
20
  result.passed?
@@ -8,8 +8,9 @@ module DeepTest
8
8
 
9
9
  def run(result, &progress_block)
10
10
  yield ::Test::Unit::TestSuite::STARTED, @suite.name
11
- count = add_tests @suite
12
- read_results result, count, &progress_block
11
+ tests_by_name = {}
12
+ add_tests @suite, tests_by_name
13
+ read_results result, tests_by_name, &progress_block
13
14
  yield ::Test::Unit::TestSuite::FINISHED, @suite.name
14
15
  end
15
16
 
@@ -17,29 +18,31 @@ module DeepTest
17
18
  @suite.size
18
19
  end
19
20
 
20
- def add_tests(test_suite)
21
- count = 0
21
+ def add_tests(test_suite, tests_by_name)
22
22
  if test_suite.respond_to? :tests
23
23
  test_suite.tests.each do |test|
24
- count += add_tests(test)
24
+ add_tests(test, tests_by_name)
25
25
  end
26
26
  else
27
- count += 1
27
+ tests_by_name[test_suite.name] = test_suite
28
28
  @blackboard.write_work Test::WorkUnit.new(test_suite)
29
29
  end
30
- count
31
30
  end
32
31
 
33
- def read_results(result, count)
34
- while count > 0
35
- remote_result = @blackboard.take_result
36
- next unless remote_result
37
- count -= 1
38
- remote_result.add_to result
39
- # TODO: is this the right place for this next line? -Dan
40
- print remote_result.output if remote_result.output
41
- yield ::Test::Unit::TestCase::FINISHED, nil if block_given?
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)
42
43
  end
44
+ ensure
45
+ DeepTest.logger.debug("SupervisedTestSuite: exiting with #{missing_tests.size} results left")
43
46
  end
44
47
  end
45
48
  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
@@ -9,7 +9,7 @@ module DeepTest
9
9
  result = run_without_deadlock_protection
10
10
  result = run_without_deadlock_protection if result.failed_due_to_deadlock?
11
11
  if result.failed_due_to_deadlock?
12
- result = ::Test::Unit::TestResult.new
12
+ result = WorkResult.new(@test_case.name)
13
13
  result.add_run
14
14
  result.output = "-deadlock-"
15
15
  end
@@ -21,10 +21,14 @@ module DeepTest
21
21
  @test_case == other.instance_variable_get(:@test_case)
22
22
  end
23
23
 
24
+ def to_s
25
+ @test_case.to_s
26
+ end
27
+
24
28
  protected
25
29
 
26
30
  def run_without_deadlock_protection
27
- result = ::Test::Unit::TestResult.new
31
+ result = WorkResult.new(@test_case.name)
28
32
  output = capture_stdout do
29
33
  @test_case.run(result) {|channel,event|}
30
34
  end
@@ -1,6 +1,9 @@
1
1
  module DeepTest
2
2
  class TestTask
3
+ attr_accessor :requires
4
+
3
5
  def initialize(name = :deep_test)
6
+ @requires = []
4
7
  @name = name
5
8
  @options = Options.new({})
6
9
  self.pattern = "test/**/*_test.rb"
@@ -11,54 +14,31 @@ module DeepTest
11
14
  def define
12
15
  desc "Run '#{@name}' suite using DeepTest"
13
16
  task @name do
14
- ruby "#{runner} '#{@options.to_command_line}'"
17
+ require_options = requires.map {|f| "-r#{f}"}.join(" ")
18
+ ruby "#{require_options} #{runner} '#{@options.to_command_line}'"
15
19
  end
16
20
  end
17
21
 
18
- def number_of_workers
19
- @options.number_of_workers
20
- end
21
-
22
- def number_of_workers=(num)
23
- @options.number_of_workers = num
24
- end
22
+ Options::VALID_OPTIONS.each do |option|
23
+ eval <<-end_src
24
+ def #{option.name}
25
+ @options.#{option.name}
26
+ end
25
27
 
26
- def pattern
27
- @options.pattern
28
+ def #{option.name}=(value)
29
+ @options.#{option.name} = value
30
+ end
31
+ end_src
28
32
  end
29
33
 
30
34
  def pattern=(pattern)
31
35
  @options.pattern = Dir.pwd + "/" + pattern
32
36
  end
33
37
 
34
- def server_port=(port)
35
- @options.server_port = port
36
- end
37
-
38
- def server_port
39
- @options.server_port
40
- end
41
-
42
- def timeout_in_seconds=(seconds)
43
- @options.timeout_in_seconds = seconds
44
- end
45
-
46
- def timeout_in_seconds
47
- @options.timeout_in_seconds
48
- end
49
-
50
- def worker_listener=(listener)
51
- @options.worker_listener = listener
52
- end
53
-
54
- def worker_listener
55
- @options.worker_listener
56
- end
57
-
58
38
  private
59
39
 
60
40
  def runner
61
- File.expand_path(File.dirname(__FILE__) + "/../../script/run_test_suite.rb")
41
+ File.expand_path(File.dirname(__FILE__) + "/../../script/internal/run_test_suite.rb")
62
42
  end
63
43
  end
64
44
  end