deep_test_pre 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 (98) hide show
  1. data/CHANGELOG +47 -0
  2. data/README.rdoc +199 -0
  3. data/Rakefile +137 -0
  4. data/lib/deep_test.rb +78 -0
  5. data/lib/deep_test/agent.rb +108 -0
  6. data/lib/deep_test/central_command.rb +165 -0
  7. data/lib/deep_test/cpu_info.rb +22 -0
  8. data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
  9. data/lib/deep_test/database/setup_listener.rb +116 -0
  10. data/lib/deep_test/deadlock_detector.rb +7 -0
  11. data/lib/deep_test/demon.rb +25 -0
  12. data/lib/deep_test/distributed/beachhead.rb +104 -0
  13. data/lib/deep_test/distributed/dispatch_controller.rb +60 -0
  14. data/lib/deep_test/distributed/establish_beachhead.rb +19 -0
  15. data/lib/deep_test/distributed/filename_resolver.rb +40 -0
  16. data/lib/deep_test/distributed/landing_fleet.rb +30 -0
  17. data/lib/deep_test/distributed/landing_ship.rb +60 -0
  18. data/lib/deep_test/distributed/remote_deployment.rb +56 -0
  19. data/lib/deep_test/distributed/rsync.rb +50 -0
  20. data/lib/deep_test/distributed/shell_environment.rb +50 -0
  21. data/lib/deep_test/distributed/ssh_client_connection_info.rb +14 -0
  22. data/lib/deep_test/extensions/object_extension.rb +40 -0
  23. data/lib/deep_test/failure_message.rb +19 -0
  24. data/lib/deep_test/lib_root.rb +4 -0
  25. data/lib/deep_test/listener_list.rb +17 -0
  26. data/lib/deep_test/local_deployment.rb +46 -0
  27. data/lib/deep_test/logger.rb +32 -0
  28. data/lib/deep_test/main.rb +41 -0
  29. data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
  30. data/lib/deep_test/metrics/data.rb +34 -0
  31. data/lib/deep_test/metrics/measurement.rb +39 -0
  32. data/lib/deep_test/null_listener.rb +62 -0
  33. data/lib/deep_test/options.rb +113 -0
  34. data/lib/deep_test/proxy_io.rb +77 -0
  35. data/lib/deep_test/rake_tasks.rb +13 -0
  36. data/lib/deep_test/result_reader.rb +40 -0
  37. data/lib/deep_test/rspec_detector.rb +21 -0
  38. data/lib/deep_test/spec.rb +17 -0
  39. data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
  40. data/lib/deep_test/spec/extensions/example_methods.rb +52 -0
  41. data/lib/deep_test/spec/extensions/options.rb +43 -0
  42. data/lib/deep_test/spec/extensions/spec_task.rb +21 -0
  43. data/lib/deep_test/spec/runner.rb +72 -0
  44. data/lib/deep_test/spec/work_result.rb +35 -0
  45. data/lib/deep_test/spec/work_unit.rb +59 -0
  46. data/lib/deep_test/test.rb +10 -0
  47. data/lib/deep_test/test/extensions/error.rb +14 -0
  48. data/lib/deep_test/test/run_test_suite.rb +5 -0
  49. data/lib/deep_test/test/runner.rb +24 -0
  50. data/lib/deep_test/test/supervised_test_suite.rb +48 -0
  51. data/lib/deep_test/test/work_result.rb +35 -0
  52. data/lib/deep_test/test/work_unit.rb +40 -0
  53. data/lib/deep_test/test_task.rb +47 -0
  54. data/lib/deep_test/ui/console.rb +74 -0
  55. data/lib/deep_test/ui/null.rb +17 -0
  56. data/lib/deep_test/warlock.rb +146 -0
  57. data/lib/telegraph.rb +29 -0
  58. data/lib/telegraph/ack_sequence.rb +14 -0
  59. data/lib/telegraph/logging.rb +20 -0
  60. data/lib/telegraph/message.rb +39 -0
  61. data/lib/telegraph/operator.rb +47 -0
  62. data/lib/telegraph/switchboard.rb +57 -0
  63. data/lib/telegraph/wire.rb +73 -0
  64. data/test/deep_test/agent_test.rb +175 -0
  65. data/test/deep_test/central_command_test.rb +147 -0
  66. data/test/deep_test/cpu_info_test.rb +33 -0
  67. data/test/deep_test/database/mysql_setup_listener_test.rb +18 -0
  68. data/test/deep_test/demon_test.rb +23 -0
  69. data/test/deep_test/distributed/beachhead_test.rb +67 -0
  70. data/test/deep_test/distributed/dispatch_controller_test.rb +162 -0
  71. data/test/deep_test/distributed/filename_resolver_test.rb +56 -0
  72. data/test/deep_test/distributed/landing_fleet_test.rb +55 -0
  73. data/test/deep_test/distributed/landing_ship_test.rb +48 -0
  74. data/test/deep_test/distributed/remote_deployment_test.rb +134 -0
  75. data/test/deep_test/distributed/rsync_test.rb +47 -0
  76. data/test/deep_test/distributed/shell_environment_test.rb +108 -0
  77. data/test/deep_test/distributed/ssh_client_connection_info_test.rb +34 -0
  78. data/test/deep_test/extensions/object_extension_test.rb +37 -0
  79. data/test/deep_test/listener_list_test.rb +22 -0
  80. data/test/deep_test/local_deployment_test.rb +19 -0
  81. data/test/deep_test/logger_test.rb +38 -0
  82. data/test/deep_test/main_test.rb +12 -0
  83. data/test/deep_test/marshallable_exception_wrapper_test.rb +46 -0
  84. data/test/deep_test/metrics/data_test.rb +22 -0
  85. data/test/deep_test/metrics/measurement_test.rb +18 -0
  86. data/test/deep_test/proxy_io_test.rb +104 -0
  87. data/test/deep_test/result_reader_test.rb +128 -0
  88. data/test/deep_test/test/extensions/error_test.rb +42 -0
  89. data/test/deep_test/test/runner_test.rb +11 -0
  90. data/test/deep_test/test/supervised_test_suite_test.rb +107 -0
  91. data/test/deep_test/test/work_result_test.rb +85 -0
  92. data/test/deep_test/test/work_unit_test.rb +63 -0
  93. data/test/deep_test/test_task_test.rb +15 -0
  94. data/test/deep_test/ui/console_test.rb +13 -0
  95. data/test/deep_test/warlock_test.rb +40 -0
  96. data/test/test_helper.rb +30 -0
  97. data/test/test_task_test.rb +75 -0
  98. metadata +156 -0
@@ -0,0 +1,50 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class ShellEnvironment
4
+ def self.like_login
5
+ login_env = new
6
+ login_env.include_first '/etc/profile'
7
+ login_env.include_first '~/.profile', '~/.bash_profile', '~/.bashrc'
8
+ login_env
9
+ end
10
+
11
+ def initialize
12
+ @source_file_lists = []
13
+ end
14
+
15
+ def include_first(*filenames)
16
+ source_file_lists << SourceFileList.new(filenames)
17
+ end
18
+
19
+ def to_s
20
+ source_file_lists.join(" && ")
21
+ end
22
+
23
+ def ==(other)
24
+ source_file_lists == other.source_file_lists
25
+ end
26
+
27
+ attr_reader :source_file_lists
28
+ protected :source_file_lists
29
+
30
+ class SourceFileList
31
+ def initialize(filenames)
32
+ @filenames = filenames
33
+ end
34
+
35
+ def to_s
36
+ "if" +
37
+ filenames.map {|f| " [[ -f #{f} ]]; then . #{f}; "}.join("elif") +
38
+ "fi"
39
+ end
40
+
41
+ def ==(other)
42
+ filenames == other.filenames
43
+ end
44
+
45
+ attr_reader :filenames
46
+ protected :filenames
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ module DeepTest
2
+ module Distributed
3
+ class SshClientConnectionInfo
4
+ attr_reader :address
5
+
6
+ def initialize
7
+ raise "SSH_CLIENT environment variable not set" unless ENV['SSH_CLIENT']
8
+ ENV['SSH_CLIENT'] =~ /^(.+) \d+ \d+$/
9
+ raise "Unable to extract address from SSH_CLIENT (#{ENV['SSH_CLIENT']})" unless $1
10
+ @address = $1
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ require "stringio"
2
+ module DeepTest
3
+ module ObjectExtension
4
+ def capture_stdout
5
+ old_stdout, $stdout = $stdout, StringIO.new
6
+ yield
7
+ $stdout.string
8
+ ensure
9
+ $stdout = old_stdout if old_stdout
10
+ end
11
+
12
+ def capture_stderr
13
+ old_stderr, $stderr = $stderr, StringIO.new
14
+ yield
15
+ $stderr.string
16
+ ensure
17
+ $stderr = old_stderr if old_stderr
18
+ end
19
+
20
+ def retrying(description = nil, times = 5)
21
+ i = 0
22
+ loop do
23
+ begin
24
+ return yield
25
+ rescue => e
26
+ i += 1
27
+ print "#{description} received exception #{e}. "
28
+ if i < times
29
+ puts "Retrying..."
30
+ sleep 0.5
31
+ else
32
+ puts "Aborting."
33
+ raise e
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ Object.send :include, DeepTest::ObjectExtension
@@ -0,0 +1,19 @@
1
+ module DeepTest
2
+ module FailureMessage
3
+ def self.show(title, message, width = 70)
4
+ lines = [" #{title} ".center(width, '*')]
5
+ message.each do |line|
6
+ lines << "* #{line.strip}".ljust(width - 1) + "*"
7
+ end
8
+ lines << "*" * width
9
+ string = lines.join("\n")
10
+ begin
11
+ puts string
12
+ rescue
13
+ IO.new(2) do |err|
14
+ err.puts string
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module DeepTest
2
+ LIB_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '/..')) unless defined?(DeepTest::LIB_ROOT)
3
+ end
4
+
@@ -0,0 +1,17 @@
1
+ module DeepTest
2
+ class ListenerList
3
+ attr_reader :listeners
4
+
5
+ def initialize(listeners)
6
+ @listeners = listeners
7
+ end
8
+
9
+ NullListener.instance_methods(false).each do |event|
10
+ eval <<-end_src
11
+ def #{event}(*args)
12
+ @listeners.each {|l| l.#{event}(*args)}
13
+ end
14
+ end_src
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ module DeepTest
2
+ class LocalDeployment
3
+ attr_reader :warlock
4
+
5
+ def initialize(options, agent_class = DeepTest::Agent)
6
+ @options = options
7
+ @agent_class = agent_class
8
+ end
9
+
10
+ def warlock
11
+ @warlock ||= Warlock.new @options
12
+ end
13
+
14
+ def load_files(files)
15
+ files.each {|f| load f}
16
+ end
17
+
18
+ def deploy_agents
19
+ DeepTest.logger.debug { "Deploying #{number_of_agents} #{@agent_class}s" }
20
+ wait_for_connect_threads = []
21
+ each_agent do |agent_num|
22
+ stream_from_child_process, stream_to_parent_process = IO.pipe
23
+ warlock.start "agent #{agent_num}", @agent_class.new(agent_num, @options, @options.new_listener_list),
24
+ stream_from_child_process, stream_to_parent_process
25
+ wait_for_connect_threads << Thread.new do
26
+ stream_to_parent_process.close
27
+ message = stream_from_child_process.read
28
+ stream_from_child_process.close
29
+ raise "Agent was not able to connect: #{message}" unless message == "Connected\n"
30
+ end
31
+ end
32
+
33
+ wait_for_connect_threads.each { |t| t.join }
34
+ end
35
+
36
+ def number_of_agents
37
+ @options.number_of_agents
38
+ end
39
+
40
+ private
41
+
42
+ def each_agent
43
+ number_of_agents.to_i.times { |agent_num| yield agent_num }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ module DeepTest
2
+ class Logger < ::Logger
3
+ def initialize(*args)
4
+ super
5
+ hostname = Socket.gethostname
6
+ self.formatter = proc { |severity, time, progname, msg| "[DeepTest@#{hostname}] #{time.strftime "%F %T"} #{msg}\n" }
7
+ self.level = configured_log_level
8
+ end
9
+
10
+ def io_stream
11
+ @logdev.dev
12
+ end
13
+
14
+ def configured_log_level
15
+ if ENV['DEEP_TEST_LOG_LEVEL']
16
+ Logger.const_get(ENV['DEEP_TEST_LOG_LEVEL'].upcase)
17
+ else
18
+ Logger::INFO
19
+ end
20
+ end
21
+
22
+ Severity.constants.each do |severity|
23
+ eval <<-end_src
24
+ def #{severity.downcase}
25
+ super
26
+ rescue Exception => e
27
+ super "\#{e.class}: \#{e} occurred logging on \#{caller[0]}", &nil
28
+ end
29
+ end_src
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ module DeepTest
2
+ class Main
3
+ attr_reader :central_command
4
+
5
+ def initialize(options, deployment, runner, central_command = nil)
6
+ @options = options
7
+ @deployment = deployment
8
+ @runner = runner
9
+ @central_command = central_command || CentralCommand.start(options)
10
+ end
11
+
12
+ def load_files(files)
13
+ @deployment.load_files files
14
+ end
15
+
16
+ def run(exit_when_done = true)
17
+ passed = false
18
+
19
+ begin
20
+ @options.new_listener_list.before_starting_agents
21
+ @deployment.deploy_agents
22
+ begin
23
+ DeepTest.logger.debug { "Main: About to process work units (#{$$})" }
24
+ passed = @runner.process_work_units(central_command)
25
+ ensure
26
+ shutdown
27
+ end
28
+ ensure
29
+ DeepTest.logger.debug { "Main: Stopping CentralCommand" }
30
+ @central_command.stop
31
+ end
32
+
33
+ Kernel.exit(passed ? 0 : 1) if exit_when_done
34
+ end
35
+
36
+ def shutdown
37
+ DeepTest.logger.debug { "Main: Shutting Down" }
38
+ @central_command.done_with_work
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ module DeepTest
2
+ class MarshallableExceptionWrapper
3
+ attr_reader :classname, :message, :backtrace
4
+
5
+ def initialize(exception)
6
+ @classname = exception.class.name
7
+ @message = exception.message
8
+ @backtrace = exception.backtrace
9
+ end
10
+
11
+ def ==(other)
12
+ classname == other.classname &&
13
+ message == other.message &&
14
+ backtrace == other.backtrace
15
+ end
16
+
17
+ def resolve
18
+ begin
19
+ klass = eval("::" + classname)
20
+ resolved_message = message
21
+ rescue => e
22
+ DeepTest.logger.debug { "Unable to load exception class: #{classname}: #{e.message}" }
23
+ DeepTest.logger.debug { e.backtrace.join("\n") }
24
+
25
+ klass = UnloadableException
26
+ resolved_message = "#{classname}: #{message}"
27
+ end
28
+
29
+ begin
30
+ resolved_exception = klass.new resolved_message
31
+ rescue => e
32
+ DeepTest.logger.debug { "Unable to instantiation exception class: #{classname}: #{e.message}" }
33
+ DeepTest.logger.debug { e.backtrace.join("\n") }
34
+
35
+ resolved_exception = UnloadableException.new resolved_message
36
+ end
37
+
38
+ resolved_exception.set_backtrace backtrace
39
+ resolved_exception
40
+ end
41
+ end
42
+
43
+ class UnloadableException < StandardError; end
44
+ end
@@ -0,0 +1,34 @@
1
+ module DeepTest
2
+ module Metrics
3
+ class Data
4
+ def initialize
5
+ @measurements_by_category = {}
6
+ end
7
+
8
+ def add(measurement)
9
+ categories = @measurements_by_category[measurement.category] ||= []
10
+ categories << measurement
11
+ end
12
+
13
+ def summary
14
+ summary = []
15
+ summary << "Metrics Data\n"
16
+ summary << "------------\n"
17
+
18
+ @measurements_by_category.keys.sort.map do |category|
19
+ measurements = @measurements_by_category[category]
20
+ units = measurements.first.units
21
+ summary << "#{category}: #{Measurement.average(measurements)} avg / #{Measurement.total(measurements)} total #{units}\n"
22
+ end
23
+
24
+ summary.join
25
+ end
26
+
27
+ def save(file)
28
+ File.open(file, "w") do |f|
29
+ f << summary
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ module DeepTest
2
+ module Metrics
3
+ class Measurement
4
+ attr_reader :category, :value, :units
5
+
6
+ def initialize(category, value, units)
7
+ @category = category
8
+ @value = value
9
+ @units = units
10
+ end
11
+
12
+ def self.average(measurements)
13
+ total(measurements).to_f / measurements.size
14
+ end
15
+
16
+ def self.total(measurements)
17
+ measurements.inject(0) { |sum, m| sum + m.value }
18
+ end
19
+
20
+ def self.of_time_taken(category)
21
+ start = Time.now
22
+ yield
23
+ Measurement.new category, Time.now - start, "seconds"
24
+ end
25
+
26
+ def self.send_home(category, wire, options)
27
+ result = nil
28
+ measurement = of_time_taken(category) { result = yield }
29
+ if options.gathering_metrics?
30
+ begin
31
+ wire.send_message measurement
32
+ rescue Telegraph::LineDead
33
+ end
34
+ end
35
+ result
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ module DeepTest
2
+ #
3
+ # Listener that implements no-ops for all callbacks that DeepTest supports.
4
+ #
5
+ class NullListener
6
+ #
7
+ # Before DeepTest synchronizes any code during a distributed run,
8
+ # before_sync is called. If DeepTest is not running distributed,
9
+ # before_sync is never called.
10
+ #
11
+ def before_sync
12
+ end
13
+
14
+ #
15
+ # Before DeepTest starts any agents, it instantiates a listener and
16
+ # invokes this method. No other callbacks are made to the listener
17
+ # instance receiving this message.
18
+ #
19
+ def before_starting_agents
20
+ end
21
+
22
+ #
23
+ # A separate listener instance is created in each agent process and
24
+ # notified that the agent is starting. The agent for the process is
25
+ # provided for the listener to use. If you are using 3 agents, this
26
+ # method is invoked 3 times on 3 distinct instances. These instances
27
+ # will also receive the starting_work and finished_work callbacks for
28
+ # the agent provided.
29
+ #
30
+ def starting(agent)
31
+ end
32
+
33
+ #
34
+ # Each time a agent takes a work unit, it calls this method before
35
+ # doing the work. In total, this method will be called as many times
36
+ # as there are work units. The listener instance that received the
37
+ # starting callback with the agent provided here is the same instance
38
+ # that receives this message.
39
+ #
40
+ # Because each work processes work units in a serial fashion, the
41
+ # listener will receive a finished_work message before another
42
+ # starting_work message.
43
+ #
44
+ def starting_work(agent, work_unit)
45
+ end
46
+
47
+ #
48
+ # Each time a agent finishes computing a result for a work unit,
49
+ # it calls this method before sending that result back to the server.
50
+ # In total, this method will be called as many times
51
+ # as there are work units. The listener instance that received the
52
+ # starting callback with the agent provided here is the same instance
53
+ # that receives this message.
54
+ #
55
+ # Because each work processes work units in a serial fashion, the
56
+ # listener will receive a starting_work message before another
57
+ # finished_work message.
58
+ #
59
+ def finished_work(agent, work_unit, result)
60
+ end
61
+ end
62
+ end