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.
- data/CHANGELOG +15 -5
- data/README +149 -5
- data/Rakefile +131 -11
- data/bin/deep_test +15 -0
- data/lib/deep_test.rb +62 -4
- data/lib/deep_test/database/mysql_setup_listener.rb +109 -0
- data/lib/deep_test/database/setup_listener.rb +116 -0
- data/lib/deep_test/distributed/dispatch_controller.rb +53 -0
- data/lib/deep_test/distributed/drb_client_connection_info.rb +15 -0
- data/lib/deep_test/distributed/filename_resolver.rb +40 -0
- data/lib/deep_test/distributed/master_test_server.rb +52 -0
- data/lib/deep_test/distributed/multi_test_server_proxy.rb +44 -0
- data/lib/deep_test/distributed/null_work_unit.rb +12 -0
- data/lib/deep_test/distributed/remote_worker_client.rb +54 -0
- data/lib/deep_test/distributed/remote_worker_server.rb +82 -0
- data/lib/deep_test/distributed/rsync.rb +37 -0
- data/lib/deep_test/distributed/show_status.rhtml +41 -0
- data/lib/deep_test/distributed/test_server.rb +78 -0
- data/lib/deep_test/distributed/test_server_status.rb +9 -0
- data/lib/deep_test/distributed/test_server_workers.rb +24 -0
- data/lib/deep_test/distributed/throughput_runner.rb +42 -0
- data/lib/deep_test/distributed/throughput_statistics.rb +26 -0
- data/lib/deep_test/distributed/throughput_worker_client.rb +19 -0
- data/lib/deep_test/extensions/drb_extension.rb +34 -0
- data/lib/deep_test/extensions/object_extension.rb +8 -0
- data/lib/deep_test/listener_list.rb +17 -0
- data/lib/deep_test/local_workers.rb +55 -0
- data/lib/deep_test/logger.rb +10 -2
- data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
- data/lib/deep_test/metrics/gatherer.rb +67 -0
- data/lib/deep_test/metrics/queue_lock_wait_time_measurement.rb +133 -0
- data/lib/deep_test/null_worker_listener.rb +50 -0
- data/lib/deep_test/option.rb +60 -0
- data/lib/deep_test/options.rb +61 -24
- data/lib/deep_test/process_orchestrator.rb +31 -78
- data/lib/deep_test/rake_tasks.rb +3 -0
- data/lib/deep_test/result_reader.rb +36 -0
- data/lib/deep_test/rspec_detector.rb +15 -8
- data/lib/deep_test/server.rb +68 -4
- data/lib/deep_test/spec.rb +1 -0
- data/lib/deep_test/spec/extensions/example_group_methods.rb +37 -11
- data/lib/deep_test/spec/extensions/example_methods.rb +46 -0
- data/lib/deep_test/spec/extensions/options.rb +35 -3
- data/lib/deep_test/spec/runner.rb +19 -10
- data/lib/deep_test/spec/work_result.rb +12 -9
- data/lib/deep_test/spec/work_unit.rb +12 -7
- data/lib/deep_test/test.rb +1 -1
- data/lib/deep_test/test/extensions/error.rb +7 -7
- data/lib/deep_test/test/runner.rb +1 -6
- data/lib/deep_test/test/supervised_test_suite.rb +19 -16
- data/lib/deep_test/test/work_result.rb +34 -0
- data/lib/deep_test/test/work_unit.rb +6 -2
- data/lib/deep_test/test_task.rb +15 -35
- data/lib/deep_test/ui/console.rb +76 -0
- data/lib/deep_test/ui/null.rb +17 -0
- data/lib/deep_test/warlock.rb +92 -17
- data/lib/deep_test/worker.rb +38 -2
- data/script/internal/run_test_suite.rb +7 -0
- data/script/public/master_test_server.rb +24 -0
- data/script/public/test_server.rb +18 -0
- data/script/public/test_throughput.rb +29 -0
- data/test/deep_test/database/mysql_setup_listener_test.rb +14 -0
- data/test/deep_test/distributed/dispatch_controller_test.rb +209 -0
- data/test/deep_test/distributed/drb_client_connection_info_test.rb +42 -0
- data/test/deep_test/distributed/filename_resolver_test.rb +52 -0
- data/test/deep_test/distributed/master_test_server_test.rb +32 -0
- data/test/deep_test/distributed/multi_test_server_proxy_test.rb +96 -0
- data/test/deep_test/distributed/remote_worker_client_test.rb +180 -0
- data/test/deep_test/distributed/remote_worker_server_test.rb +99 -0
- data/test/deep_test/distributed/rsync_test.rb +67 -0
- data/test/deep_test/distributed/test_server_test.rb +94 -0
- data/test/deep_test/distributed/test_server_workers_test.rb +26 -0
- data/test/deep_test/distributed/throughput_runner_test.rb +68 -0
- data/test/deep_test/distributed/throughput_worker_client_test.rb +28 -0
- data/test/deep_test/listener_list_test.rb +20 -0
- data/test/deep_test/local_workers_test.rb +22 -0
- data/test/{logger_test.rb → deep_test/logger_test.rb} +2 -2
- data/test/deep_test/marshallable_exception_wrapper_test.rb +44 -0
- data/test/deep_test/metrics/gatherer_test.rb +66 -0
- data/test/deep_test/process_orchestrator_test.rb +11 -0
- data/test/deep_test/result_reader_test.rb +128 -0
- data/test/deep_test/server_test.rb +58 -0
- data/test/deep_test/test/extensions/error_test.rb +40 -0
- data/test/deep_test/test/supervised_test_suite_test.rb +19 -29
- data/test/deep_test/test/work_result_test.rb +81 -0
- data/test/deep_test/test/work_unit_test.rb +15 -4
- data/test/deep_test/test_task_test.rb +10 -0
- data/test/deep_test/ui/console_test.rb +9 -0
- data/test/deep_test/warlock_test.rb +17 -0
- data/test/deep_test/worker_test.rb +32 -0
- data/test/simple_test_blackboard.rb +2 -1
- data/test/test_helper.rb +1 -0
- metadata +117 -59
- data/lib/deep_test/rinda_blackboard.rb +0 -26
- data/lib/deep_test/test/extensions/test_result.rb +0 -17
- data/lib/deep_test/tuple_space_factory.rb +0 -12
- data/script/run_test_suite.rb +0 -5
- data/test/deep_test/rinda_blackboard_test.rb +0 -15
- data/test/deep_test/test/extensions/test_result_test.rb +0 -71
|
@@ -8,6 +8,14 @@ module DeepTest
|
|
|
8
8
|
ensure
|
|
9
9
|
$stdout = old_stdout if old_stdout
|
|
10
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
|
|
11
19
|
|
|
12
20
|
def retrying(description = nil, times = 5)
|
|
13
21
|
i = 0
|
|
@@ -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
|
+
NullWorkerListener.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,55 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class LocalWorkers
|
|
3
|
+
def initialize(options)
|
|
4
|
+
@options = options
|
|
5
|
+
@warlock = Warlock.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def load_files(files)
|
|
9
|
+
files.each {|f| load f}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def server
|
|
13
|
+
@options.server
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def start_all
|
|
17
|
+
each_worker do |worker_num|
|
|
18
|
+
start_worker(worker_num) do
|
|
19
|
+
reseed_random_numbers
|
|
20
|
+
reconnect_to_database
|
|
21
|
+
worker = DeepTest::Worker.new(worker_num,
|
|
22
|
+
server,
|
|
23
|
+
@options.new_listener_list)
|
|
24
|
+
worker.run
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def stop_all
|
|
30
|
+
@warlock.stop_all
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def number_of_workers
|
|
34
|
+
@options.number_of_workers
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def reconnect_to_database
|
|
40
|
+
ActiveRecord::Base.connection.reconnect! if defined?(ActiveRecord::Base)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def start_worker(worker_num, &blk)
|
|
44
|
+
@warlock.start("worker #{worker_num}", &blk)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def reseed_random_numbers
|
|
48
|
+
srand
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def each_worker
|
|
52
|
+
number_of_workers.to_i.times { |worker_num| yield worker_num }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/deep_test/logger.rb
CHANGED
|
@@ -2,8 +2,16 @@ module DeepTest
|
|
|
2
2
|
class Logger < ::Logger
|
|
3
3
|
def initialize(*args)
|
|
4
4
|
super
|
|
5
|
-
self.formatter = proc { |severity, time, progname, msg| "#{msg}\n" }
|
|
6
|
-
self.level =
|
|
5
|
+
self.formatter = proc { |severity, time, progname, msg| "[DeepTest] #{msg}\n" }
|
|
6
|
+
self.level = configured_log_level
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def configured_log_level
|
|
10
|
+
if ENV['DEEP_TEST_LOG_LEVEL']
|
|
11
|
+
Logger.const_get(ENV['DEEP_TEST_LOG_LEVEL'])
|
|
12
|
+
else
|
|
13
|
+
Logger::INFO
|
|
14
|
+
end
|
|
7
15
|
end
|
|
8
16
|
end
|
|
9
17
|
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,67 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Metrics
|
|
3
|
+
class Gatherer
|
|
4
|
+
def self.setup(options)
|
|
5
|
+
$metrics_gatherer = new(options)
|
|
6
|
+
|
|
7
|
+
at_exit do
|
|
8
|
+
$metrics_gatherer.write_file
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.enabled?
|
|
13
|
+
return false unless $metrics_gatherer
|
|
14
|
+
$metrics_gatherer.enabled?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.section(title, &block)
|
|
18
|
+
$metrics_gatherer.section(title, &block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(options)
|
|
22
|
+
@options = options
|
|
23
|
+
@sections = []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def enabled?
|
|
27
|
+
!@options.metrics_file.nil?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def section(title, &block)
|
|
31
|
+
@sections << Section.new(title, &block) if enabled?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def render
|
|
35
|
+
@sections.map {|s| s.render}.join("\n")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def write_file
|
|
39
|
+
return unless enabled?
|
|
40
|
+
File.open(@options.metrics_file, "w") do |io|
|
|
41
|
+
io << render
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Section
|
|
46
|
+
def initialize(title, &generate_measurements_block)
|
|
47
|
+
@title = title
|
|
48
|
+
@generate_measurements_block = generate_measurements_block
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def measurement(name, value)
|
|
52
|
+
@measurements << [name, value]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def gather_measurements
|
|
56
|
+
@measurements = []
|
|
57
|
+
@generate_measurements_block.call(self)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def render
|
|
61
|
+
gather_measurements
|
|
62
|
+
"[#{@title}]\n" + @measurements.map {|name, value| "#{name}: #{value}"}.join("\n")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module Metrics
|
|
3
|
+
module QueueLockWaitTimeMeasurement
|
|
4
|
+
attr_reader :total_pop_time, :total_push_time
|
|
5
|
+
|
|
6
|
+
def self.extended(o)
|
|
7
|
+
o.instance_eval do
|
|
8
|
+
alias pop_without_lock_wait_measurement pop
|
|
9
|
+
alias pop pop_with_lock_wait_measurement
|
|
10
|
+
|
|
11
|
+
alias push_without_lock_wait_measurement push
|
|
12
|
+
alias push push_with_lock_wait_measurement
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def pop_with_lock_wait_measurement(no_wait = false)
|
|
17
|
+
if no_wait
|
|
18
|
+
return measure(:add_pop_time) do
|
|
19
|
+
pop_without_lock_wait_measurement(no_wait)
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
begin
|
|
23
|
+
# Measure without waiting to minimize extra time added
|
|
24
|
+
# above locking time
|
|
25
|
+
#
|
|
26
|
+
return measure(:add_pop_time) do
|
|
27
|
+
pop_without_lock_wait_measurement(true)
|
|
28
|
+
end
|
|
29
|
+
rescue ThreadError => e
|
|
30
|
+
if e.message == "queue empty"
|
|
31
|
+
# Normally we would have waiting for a condvar signal,
|
|
32
|
+
# so don't penalize time for locking here again - hence
|
|
33
|
+
# no measure
|
|
34
|
+
return pop_without_lock_wait_measurement(false)
|
|
35
|
+
else
|
|
36
|
+
raise
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def push_with_lock_wait_measurement(value)
|
|
43
|
+
measure(:add_push_time) do
|
|
44
|
+
push_without_lock_wait_measurement(value)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def measure(accumulator)
|
|
49
|
+
start_time = Time.now
|
|
50
|
+
result = yield
|
|
51
|
+
send(accumulator, Time.now - start_time)
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def add_push_time(time)
|
|
56
|
+
Thread.exclusive do
|
|
57
|
+
@total_push_time ||= 0
|
|
58
|
+
@total_push_time += time
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def add_pop_time(time)
|
|
63
|
+
Thread.exclusive do
|
|
64
|
+
@total_pop_time ||= 0
|
|
65
|
+
@total_pop_time += time
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if $0 == __FILE__
|
|
74
|
+
require 'thread'
|
|
75
|
+
require 'timeout'
|
|
76
|
+
|
|
77
|
+
thread_count = (ARGV[0] || 10).to_i
|
|
78
|
+
action_count = (ARGV[1] || 100).to_i
|
|
79
|
+
|
|
80
|
+
def test_measurement(thread_count, action_count)
|
|
81
|
+
q = Queue.new
|
|
82
|
+
q.extend DeepTest::Metrics::QueueLockWaitTimeMeasurement
|
|
83
|
+
|
|
84
|
+
threads = []
|
|
85
|
+
thread_count.times do
|
|
86
|
+
threads << Thread.new do
|
|
87
|
+
action_count.times do |i|
|
|
88
|
+
show_progress ".", action_count, i
|
|
89
|
+
q.push 1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
thread_count.times do
|
|
95
|
+
threads << Thread.new do
|
|
96
|
+
action_count.times do |i|
|
|
97
|
+
show_progress "-", action_count, i
|
|
98
|
+
if rand(2) == 0
|
|
99
|
+
begin
|
|
100
|
+
q.pop(true)
|
|
101
|
+
rescue ThreadError
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
begin
|
|
105
|
+
Timeout.timeout(0.01) do
|
|
106
|
+
q.pop
|
|
107
|
+
end
|
|
108
|
+
rescue Timeout::Error
|
|
109
|
+
break
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
threads.each {|t| t.join}
|
|
117
|
+
|
|
118
|
+
puts
|
|
119
|
+
puts "Push Time: #{q.total_push_time}"
|
|
120
|
+
puts "Pop Time: #{q.total_pop_time}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def show_progress(s, total, current)
|
|
124
|
+
if (current % (total / 5)) == 0
|
|
125
|
+
$stdout.print s
|
|
126
|
+
$stdout.flush
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
start_time = Time.now
|
|
131
|
+
test_measurement(thread_count, action_count)
|
|
132
|
+
puts "Total Run Time: #{Time.now - start_time}"
|
|
133
|
+
end
|
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
module DeepTest
|
|
2
|
+
#
|
|
3
|
+
# Listener that implements no-ops for all callbacks that DeepTest supports.
|
|
4
|
+
#
|
|
2
5
|
class NullWorkerListener
|
|
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 workers, 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_workers
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# A separate listener instance is created in each worker process and
|
|
24
|
+
# notified that the worker is starting. The worker for the process is
|
|
25
|
+
# provided for the listener to use. If you are using 3 workers, 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 worker provided.
|
|
29
|
+
#
|
|
3
30
|
def starting(worker)
|
|
4
31
|
end
|
|
5
32
|
|
|
33
|
+
#
|
|
34
|
+
# Each time a worker 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 worker 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
|
+
#
|
|
6
44
|
def starting_work(worker, work_unit)
|
|
7
45
|
end
|
|
8
46
|
|
|
47
|
+
#
|
|
48
|
+
# Each time a worker 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 worker 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
|
+
#
|
|
9
59
|
def finished_work(worker, work_unit, result)
|
|
10
60
|
end
|
|
11
61
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class Option
|
|
3
|
+
attr_reader :name, :default
|
|
4
|
+
|
|
5
|
+
def initialize(name, type, default)
|
|
6
|
+
@name, @type, @default = name, type, default
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def from_command_line(command_line)
|
|
10
|
+
command_line =~ /--#{name} (\S+)(\s|$)/
|
|
11
|
+
@type.from_string($1) if $1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_command_line(value)
|
|
15
|
+
"--#{name} #{@type.to_string(value)}" if value && value != default
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module Hash
|
|
19
|
+
def self.to_string(hash)
|
|
20
|
+
pairs = []
|
|
21
|
+
hash.each do |key, value|
|
|
22
|
+
value = value.gsub(/ /,'%20') if (::String === value)
|
|
23
|
+
pairs << "#{key}:#{value.inspect}"
|
|
24
|
+
end
|
|
25
|
+
pairs.join(",")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_string(string)
|
|
29
|
+
hash = {}
|
|
30
|
+
string.split(/,/).each do |pair|
|
|
31
|
+
key, unevaled_value = pair.split(/:/)
|
|
32
|
+
value = eval(unevaled_value)
|
|
33
|
+
value = value.gsub(/%20/, " ") if ::String === value
|
|
34
|
+
hash[key.to_sym] = value
|
|
35
|
+
end
|
|
36
|
+
hash
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module Integer
|
|
41
|
+
def self.to_string(i)
|
|
42
|
+
i.to_s
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.from_string(s)
|
|
46
|
+
s.to_i
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module String
|
|
51
|
+
def self.to_string(s)
|
|
52
|
+
s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.from_string(s)
|
|
56
|
+
s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|