deep_test 1.1.2 → 1.1.3

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 (54) hide show
  1. data/CHANGELOG +6 -0
  2. data/README +39 -2
  3. data/Rakefile +17 -12
  4. data/lib/deep_test.rb +27 -18
  5. data/lib/deep_test/deadlock_detector.rb +7 -0
  6. data/lib/deep_test/logger.rb +9 -0
  7. data/lib/deep_test/null_worker_listener.rb +12 -0
  8. data/lib/deep_test/options.rb +72 -0
  9. data/lib/deep_test/process_orchestrator.rb +85 -0
  10. data/lib/deep_test/rake_tasks.rb +7 -0
  11. data/lib/deep_test/rinda_blackboard.rb +8 -7
  12. data/lib/deep_test/rspec_detector.rb +17 -0
  13. data/lib/deep_test/server.rb +3 -9
  14. data/lib/deep_test/spec.rb +9 -0
  15. data/lib/deep_test/spec/extensions/options.rb +11 -0
  16. data/lib/deep_test/spec/extensions/reporter.rb +17 -0
  17. data/lib/deep_test/spec/extensions/spec_task.rb +13 -0
  18. data/lib/deep_test/spec/runner.rb +48 -0
  19. data/lib/deep_test/spec/work_result.rb +26 -0
  20. data/lib/deep_test/spec/work_unit.rb +53 -0
  21. data/lib/deep_test/test.rb +9 -0
  22. data/lib/deep_test/{extensions → test/extensions}/error.rb +0 -0
  23. data/lib/deep_test/{extensions/testresult.rb → test/extensions/test_result.rb} +1 -1
  24. data/lib/deep_test/test/runner.rb +29 -0
  25. data/lib/deep_test/test/supervised_test_suite.rb +46 -0
  26. data/lib/deep_test/test/work_unit.rb +36 -0
  27. data/lib/deep_test/test_task.rb +36 -9
  28. data/lib/deep_test/tuple_space_factory.rb +3 -2
  29. data/lib/deep_test/warlock.rb +2 -2
  30. data/lib/deep_test/worker.rb +10 -20
  31. data/script/run_test_suite.rb +5 -34
  32. data/test/deep_test/rinda_blackboard_test.rb +5 -5
  33. data/test/{testresult_test.rb → deep_test/test/extensions/test_result_test.rb} +2 -2
  34. data/test/deep_test/test/runner_test.rb +7 -0
  35. data/test/deep_test/test/supervised_test_suite_test.rb +89 -0
  36. data/test/deep_test/test/work_unit_test.rb +50 -0
  37. data/test/deep_test/test_task_test.rb +23 -0
  38. data/test/deep_test/worker_test.rb +62 -0
  39. data/test/failing.rake +11 -0
  40. data/test/fake_deadlock_error.rb +12 -0
  41. data/test/logger_test.rb +11 -0
  42. data/test/simple_test_blackboard.rb +44 -0
  43. data/test/simple_test_blackboard_test.rb +5 -5
  44. data/test/test_factory.rb +74 -0
  45. data/test/test_helper.rb +5 -71
  46. metadata +32 -13
  47. data/lib/deep_test/loader.rb +0 -18
  48. data/lib/deep_test/simple_test_blackboard.rb +0 -24
  49. data/lib/deep_test/supervised_test_suite.rb +0 -19
  50. data/lib/deep_test/supervisor.rb +0 -27
  51. data/test/deep_test/loader_test.rb +0 -8
  52. data/test/supervised_test_suite_test.rb +0 -63
  53. data/test/supervisor_test.rb +0 -80
  54. data/test/worker_test.rb +0 -64
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ * 1.1.3 (March 10, 2008)
2
+
3
+ * Added Support for RSpec
4
+ * Added DeepTest::TestTask#timeout_in_seconds to configure Rinda IO timeouts
5
+ * Added worker_listener to configuration options
6
+
1
7
  * 1.1.2 (February 9, 2008)
2
8
 
3
9
  * Fix bug with run_test_suite script not exiting with correct exit code.
data/README CHANGED
@@ -12,16 +12,53 @@ In your Rakefile:
12
12
  # sample DeepTest task
13
13
 
14
14
  DeepTest::TestTask.new "task_name" do |t|
15
- t.number_of_workers = 2 # optional, defaults to 2
15
+ t.number_of_workers = 2 # optional, defaults to 2
16
+ t.timeout_in_seconds = 30 # optional, defaults to 30
16
17
  t.pattern = "test/**/*_test.rb"
17
18
  end
18
19
 
20
+ # Sample SpecTask using DeepTest
21
+
22
+ Spec::Rake::SpecTask.new(:deep_spec) do |t|
23
+ t.spec_files = FileList['spec/**/*_spec.rb']
24
+ t.deep_test :number_of_workers => 2, # optional, defaults to 2
25
+ :timeout_in_seconds => 30, # optional, defaults to 30
26
+ end
27
+
28
+
29
+ If you want to specify a listener to be notified of worker events:
30
+
31
+ In my_worker_listener.rb
32
+ class MyWorkerListener
33
+ def starting(worker)
34
+ end
35
+
36
+ def starting_work(worker, work_unit)
37
+ end
38
+
39
+ def finished_work(worker, work_unit, result)
40
+ end
41
+ end
42
+
43
+ In your Rakefile
44
+
45
+ DeepTest::TestTask.new "task_name" do |t|
46
+ t.pattern = "test/**/*_test.rb"
47
+ t.worker_listener = "MyWorkerListener"
48
+ end
49
+
50
+ An instance of MyWorkerListener will be created for each worker that is
51
+ started, and will receive events from that worker. However, you must ensure
52
+ that the MyWorkerListener class is loaded at the time that the test files are
53
+ loaded, so it is available to be instantiated.
54
+
19
55
  == Contributors
20
56
 
21
57
  +* anonymous z
22
58
  +* Alex Chaffee
23
59
  +* Dan[http://www.dcmanges.com/blog] Manges[http://www.dcmanges.com/blog]
24
- +* David Vollbracht
60
+ +* David Vollbracht[http://davidvollbracht.com/]
61
+ +* Scott Taylor
25
62
 
26
63
  == License
27
64
  Released under Ruby's[http://www.ruby-lang.org/en/LICENSE.txt] license[http://www.ruby-lang.org/en/LICENSE.txt]
data/Rakefile CHANGED
@@ -6,32 +6,37 @@ require 'rake/contrib/sshpublisher'
6
6
  $LOAD_PATH << File.dirname(__FILE__) + "/lib"
7
7
  require "deep_test/rake_tasks"
8
8
 
9
- task :default => %w[test failing_test deep_test]
9
+ task :default => %w[test spec failing_test deep_test deep_spec]
10
10
 
11
11
  Rake::TestTask.new do |t|
12
12
  t.pattern = "test/**/*_test.rb"
13
13
  t.libs += ['test', 'lib']
14
14
  end
15
15
 
16
+ Spec::Rake::SpecTask.new(:spec) do |t|
17
+ t.spec_files = FileList['spec/**/*_spec.rb']
18
+ end
19
+
16
20
  DeepTest::TestTask.new :deep_test do |t|
17
21
  t.number_of_workers = 2
18
22
  t.pattern = "test/**/*_test.rb"
19
23
  end
20
24
 
21
- DeepTest::TestTask.new :deep_test_failing do |t|
22
- t.number_of_workers = 1
23
- t.pattern = "test/failing.rb"
25
+ Spec::Rake::SpecTask.new(:deep_spec) do |t|
26
+ t.spec_files = FileList['spec/**/*_spec.rb']
27
+ t.deep_test :number_of_workers => 2
24
28
  end
25
29
 
26
- # TODO: figure out a better way to test this that doesn't
27
- # involve having a failing test in the output of 'rake test'
28
30
  task :failing_test do
29
- exception = nil
30
- begin
31
- Rake::Task[:deep_test_failing].invoke
32
- rescue => exception
31
+ command = "rake --rakefile test/failing.rake 2>&1"
32
+ puts command
33
+ `#{command}`
34
+ if $?.success?
35
+ puts "F"
36
+ fail "****\ntest/failing.rake should have failed\n****"
37
+ else
38
+ puts "."
33
39
  end
34
- fail "exception should not be nil" if exception.nil?
35
40
  end
36
41
 
37
42
  desc "Generate documentation"
@@ -53,7 +58,7 @@ specification = Gem::Specification.new do |s|
53
58
  s.platform = Gem::Platform::RUBY
54
59
  s.name = "deep_test"
55
60
  s.summary = "DeepTest runs tests in multiple processes."
56
- s.version = "1.1.2"
61
+ s.version = "1.1.3"
57
62
  s.author = "anonymous z, Dan Manges, David Vollbracht"
58
63
  s.description = s.summary
59
64
  s.email = "daniel.manges@gmail.com"
@@ -1,23 +1,32 @@
1
1
  module DeepTest
2
+ class << self
3
+ def logger
4
+ @logger ||= DeepTest::Logger.new($stdout)
5
+ end
6
+ end
2
7
  end
3
- require 'rinda/ring'
4
- require 'rinda/tuplespace'
5
8
 
6
- require "test/unit/testresult"
7
- require "test/unit/error"
8
- require 'test/unit/failure'
9
+ require "logger"
10
+ require "rinda/ring"
11
+ require "rinda/tuplespace"
9
12
 
10
- require "deep_test/extensions/testresult"
11
- require "deep_test/extensions/error"
12
- require "deep_test/extensions/object_extension"
13
+ require File.dirname(__FILE__) + "/deep_test/extensions/object_extension"
14
+
15
+ require File.dirname(__FILE__) + "/deep_test/deadlock_detector"
16
+ require File.dirname(__FILE__) + "/deep_test/tuple_space_factory"
17
+ require File.dirname(__FILE__) + "/deep_test/rinda_blackboard"
18
+ require File.dirname(__FILE__) + "/deep_test/logger"
19
+ require File.dirname(__FILE__) + "/deep_test/null_worker_listener"
20
+ require File.dirname(__FILE__) + "/deep_test/options"
21
+ require File.dirname(__FILE__) + "/deep_test/process_orchestrator"
22
+ require File.dirname(__FILE__) + "/deep_test/rspec_detector"
23
+ require File.dirname(__FILE__) + "/deep_test/server"
24
+ require File.dirname(__FILE__) + "/deep_test/test_task"
25
+ require File.dirname(__FILE__) + "/deep_test/worker"
26
+ require File.dirname(__FILE__) + "/deep_test/warlock"
27
+
28
+ DeepTest::RSpecDetector.if_rspec_available do
29
+ require File.dirname(__FILE__) + "/deep_test/spec"
30
+ end
31
+ require File.dirname(__FILE__) + "/deep_test/test"
13
32
 
14
- require "deep_test/tuple_space_factory"
15
- require "deep_test/rinda_blackboard"
16
- require "deep_test/loader"
17
- require "deep_test/server"
18
- require "deep_test/simple_test_blackboard"
19
- require "deep_test/supervised_test_suite"
20
- require "deep_test/supervisor"
21
- require "deep_test/test_task"
22
- require "deep_test/worker"
23
- require "deep_test/warlock"
@@ -0,0 +1,7 @@
1
+ module DeepTest
2
+ class DeadlockDetector
3
+ def self.due_to_deadlock?(error)
4
+ error && !error.message.to_s.match(/Deadlock found when trying to get lock/).nil?
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module DeepTest
2
+ class Logger < ::Logger
3
+ def initialize(*args)
4
+ super
5
+ self.formatter = proc { |severity, time, progname, msg| "#{msg}\n" }
6
+ self.level = Logger::INFO
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module DeepTest
2
+ class NullWorkerListener
3
+ def starting(worker)
4
+ end
5
+
6
+ def starting_work(worker, work_unit)
7
+ end
8
+
9
+ def finished_work(worker, work_unit, result)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,72 @@
1
+ module DeepTest
2
+ class Options
3
+ class Option
4
+ attr_reader :name, :default
5
+
6
+ def initialize(name, conversion, default)
7
+ @name, @conversion, @default = name, conversion, default
8
+ end
9
+
10
+ def from_command_line(command_line)
11
+ command_line =~ /--#{name} (\S+)(\s|$)/
12
+ $1.send(@conversion) if $1
13
+ end
14
+
15
+ def to_command_line(value)
16
+ "--#{name} #{value}" if value && value != default
17
+ end
18
+ end
19
+
20
+ unless defined?(VALID_OPTIONS)
21
+ VALID_OPTIONS = [
22
+ Option.new(:number_of_workers, :to_i, 2),
23
+ Option.new(:pattern, :to_s, nil),
24
+ Option.new(:timeout_in_seconds, :to_i, 30),
25
+ Option.new(:worker_listener, :to_s, "DeepTest::NullWorkerListener"),
26
+ ]
27
+ end
28
+
29
+ attr_accessor *VALID_OPTIONS.map {|o| o.name}
30
+ def worker_listener=(value)
31
+ @worker_listener = value.to_s
32
+ end
33
+
34
+ def self.from_command_line(command_line)
35
+ hash = {}
36
+ VALID_OPTIONS.each do |option|
37
+ hash[option.name] = option.from_command_line(command_line)
38
+ end
39
+ new(hash)
40
+ end
41
+
42
+ def initialize(hash)
43
+ check_option_keys(hash)
44
+ VALID_OPTIONS.each do |option|
45
+ send("#{option.name}=", hash[option.name] || option.default)
46
+ end
47
+ end
48
+
49
+ def new_worker_listener
50
+ eval(worker_listener).new
51
+ end
52
+
53
+ def to_command_line
54
+ command_line = []
55
+ VALID_OPTIONS.each do |option|
56
+ value = send(option.name)
57
+ command_line << option.to_command_line(value)
58
+ end
59
+ command_line.compact.join(' ')
60
+ end
61
+
62
+ protected
63
+
64
+ def check_option_keys(hash)
65
+ hash.keys.each do |key|
66
+ raise InvalidOptionError.new(key) unless VALID_OPTIONS.any? {|o| o.name == key.to_sym}
67
+ end
68
+ end
69
+
70
+ class InvalidOptionError < StandardError; end
71
+ end
72
+ end
@@ -0,0 +1,85 @@
1
+ module DeepTest
2
+ class ProcessOrchestrator
3
+ def self.run(options, runner)
4
+ new(options, runner).run
5
+ end
6
+
7
+ def initialize(options, runner)
8
+ @options = options
9
+ @runner = runner
10
+ end
11
+
12
+ def run
13
+ stop_zombie_warlocks
14
+ start_warlock_server
15
+ start_all_workers
16
+ start_loader
17
+ wait_for_loader_to_be_done
18
+ exit_process
19
+ ensure
20
+ stop_all_warlocks
21
+ end
22
+
23
+ private
24
+
25
+ def exit_process
26
+ Kernel.exit($?.success? ? 0 : 1)
27
+ end
28
+
29
+ def start_all_workers
30
+ each_worker do |worker_num|
31
+ start_worker(worker_num) do
32
+ reseed_random_numbers
33
+ reconnect_to_database
34
+ worker = DeepTest::Worker.new(worker_num,
35
+ RindaBlackboard.new(@options),
36
+ @options.new_worker_listener)
37
+ worker.run
38
+ end
39
+ end
40
+ end
41
+
42
+ def reconnect_to_database
43
+ ActiveRecord::Base.connection.reconnect! if defined?(ActiveRecord::Base)
44
+ end
45
+
46
+ def start_worker(worker_num, &blk)
47
+ @warlock.start("worker #{worker_num}", &blk)
48
+ end
49
+
50
+ def reseed_random_numbers
51
+ srand
52
+ end
53
+
54
+ def wait_for_loader_to_be_done
55
+ Process.wait(@loader_pid)
56
+ end
57
+
58
+ def start_warlock_server
59
+ @warlock.start("server") { DeepTest::Server.start }
60
+ sleep 0.5 # TODO: change from sleep to something better... Process.wait?
61
+ end
62
+
63
+ def stop_all_warlocks
64
+ @warlock.stop_all if @warlock
65
+ end
66
+
67
+ def each_worker
68
+ @options.number_of_workers.to_i.times { |worker_num| yield worker_num }
69
+ end
70
+
71
+ def stop_zombie_warlocks
72
+ @warlock = DeepTest::Warlock.new
73
+ Signal.trap("HUP") { warlock.stop_all; exit 0 }
74
+ end
75
+
76
+ def start_loader
77
+ @loader_pid = fork do
78
+ DeepTest.logger.debug "Loader Starting (#{$$})"
79
+ passed = @runner.process_work_units
80
+ exit(passed ? 0 : 1)
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -1 +1,8 @@
1
+ require File.dirname(__FILE__) + "/options"
1
2
  require File.dirname(__FILE__) + "/test_task"
3
+ require File.dirname(__FILE__) + "/rspec_detector"
4
+
5
+ DeepTest::RSpecDetector.if_rspec_available do
6
+ require 'spec/rake/spectask'
7
+ require File.dirname(__FILE__) + "/spec/extensions/spec_task"
8
+ end
@@ -1,25 +1,26 @@
1
1
  module DeepTest
2
2
  class RindaBlackboard
3
- def initialize(tuple_space = TupleSpaceFactory.tuple_space)
3
+ def initialize(options, tuple_space = TupleSpaceFactory.tuple_space)
4
+ @options = options
4
5
  @tuple_space = tuple_space
5
6
  end
6
7
 
7
8
  def take_result
8
- result = @tuple_space.take ["test_result", nil], 30
9
+ result = @tuple_space.take ["test_result", nil], @options.timeout_in_seconds
9
10
  result[1]
10
11
  end
11
12
 
12
- def take_test
13
- tuple = @tuple_space.take ["run_test", nil, nil], 30
14
- eval(tuple[1]).new(tuple[2])
13
+ def take_work
14
+ tuple = @tuple_space.take ["deep_work", nil], @options.timeout_in_seconds
15
+ tuple[1]
15
16
  end
16
17
 
17
18
  def write_result(result)
18
19
  @tuple_space.write ["test_result", result]
19
20
  end
20
21
 
21
- def write_test(test_case)
22
- @tuple_space.write ["run_test", test_case.class.to_s, test_case.method_name]
22
+ def write_work(work_unit)
23
+ @tuple_space.write ["deep_work", work_unit]
23
24
  end
24
25
  end
25
26
  end
@@ -0,0 +1,17 @@
1
+ module DeepTest
2
+ class RSpecDetector
3
+ def self.if_rspec_available
4
+ begin
5
+ require 'rubygems'
6
+ rescue LoadError
7
+ else
8
+ begin
9
+ gem 'rspec'
10
+ rescue Gem::LoadError
11
+ else
12
+ yield
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,17 +1,11 @@
1
1
  module DeepTest
2
2
  class Server
3
- PORT = 6969
3
+ PORT = 6969 unless defined?(PORT)
4
4
 
5
5
  def self.start
6
- require 'rinda/ring'
7
- require 'rinda/tuplespace'
8
- require 'test/unit'
9
- require 'test/unit/testresult'
10
- require 'deep_test'
11
6
  DRb.start_service
12
- ts = Rinda::TupleSpace.new
13
- Rinda::RingServer.new(ts, PORT)
14
- puts "Started DeepTest service at #{DRb.uri}"
7
+ Rinda::RingServer.new(Rinda::TupleSpace.new, PORT)
8
+ DeepTest.logger.info "Started DeepTest service at #{DRb.uri}"
15
9
  DRb.thread.join
16
10
  end
17
11
  end