deep_test 1.1.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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