deep_test 1.1.2 → 1.1.3

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