rereplay 0.1 → 0.2
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/.infinity_test +25 -0
- data/Gemfile +4 -10
- data/Gemfile.lock +12 -6
- data/README.md +4 -4
- data/lib/rereplay.rb +1 -5
- data/lib/rereplay/monitors/request_time_monitor.rb +1 -1
- data/lib/rereplay/monitors/timeout_failer.rb +2 -1
- data/lib/rereplay/ruby_runner.rb +250 -0
- data/lib/rereplay/version.rb +1 -1
- data/rereplay.gemspec +0 -3
- data/spec/{advanced.rb → advanced_spec.rb} +4 -4
- data/spec/{basic.rb → basic_spec.rb} +3 -3
- data/spec/{monitor.rb → monitor_spec.rb} +4 -8
- data/spec/profile_spec.rb +61 -0
- metadata +16 -61
- data/lib/rereplay/runner.rb +0 -200
- data/spec/profile.rb +0 -90
data/.infinity_test
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
infinity_test do
|
|
2
|
+
|
|
3
|
+
notifications :lib_notify do
|
|
4
|
+
show_images# :mode => :mario_bros
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
use :rubies => %w(1.8.7 1.9.2 ruby-head jruby), :test_framework => :rspec
|
|
8
|
+
|
|
9
|
+
before(:each_ruby) do |environment|
|
|
10
|
+
# ...
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after(:each_ruby) do |environment|
|
|
14
|
+
# ...
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
before_run do
|
|
18
|
+
clear :terminal
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
after_run do
|
|
22
|
+
# ...
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
data/Gemfile
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
source "http://rubygems.org"
|
|
2
2
|
|
|
3
|
-
gem "
|
|
4
|
-
gem "
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
gem "rspec", "~>2.0"
|
|
8
|
-
gem "webmock", "~>1.4.0"
|
|
9
|
-
gem "activesupport", "~>3.0.1"
|
|
10
|
-
# gem "autotest"
|
|
11
|
-
# gem "test_notifier"
|
|
12
|
-
end
|
|
3
|
+
gem "infinity_test", :git => "git://github.com/tomas-stefano/infinity_test.git"
|
|
4
|
+
gem "rspec", "~>2.0"
|
|
5
|
+
gem "webmock", "~>1.4.0"
|
|
6
|
+
gem "activesupport", "~>3.0.1"
|
|
13
7
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
GIT
|
|
2
|
+
remote: git://github.com/tomas-stefano/infinity_test.git
|
|
3
|
+
revision: c732c6bcc54e8745fba4dbd4565e068a2a329e1a
|
|
4
|
+
specs:
|
|
5
|
+
infinity_test (0.2.0)
|
|
6
|
+
notifiers (>= 1.1.0)
|
|
7
|
+
watchr (>= 0.7)
|
|
8
|
+
|
|
1
9
|
GEM
|
|
2
10
|
remote: http://rubygems.org/
|
|
3
11
|
specs:
|
|
@@ -5,10 +13,7 @@ GEM
|
|
|
5
13
|
addressable (2.2.2)
|
|
6
14
|
crack (0.1.8)
|
|
7
15
|
diff-lcs (1.1.2)
|
|
8
|
-
|
|
9
|
-
addressable (>= 2.0.0)
|
|
10
|
-
eventmachine (>= 0.12.9)
|
|
11
|
-
eventmachine (0.12.10)
|
|
16
|
+
notifiers (1.1.0)
|
|
12
17
|
rspec (2.0.1)
|
|
13
18
|
rspec-core (~> 2.0.1)
|
|
14
19
|
rspec-expectations (~> 2.0.1)
|
|
@@ -19,16 +24,17 @@ GEM
|
|
|
19
24
|
rspec-mocks (2.0.1)
|
|
20
25
|
rspec-core (~> 2.0.1)
|
|
21
26
|
rspec-expectations (~> 2.0.1)
|
|
27
|
+
watchr (0.7)
|
|
22
28
|
webmock (1.4.0)
|
|
23
29
|
addressable (>= 2.2.2)
|
|
24
30
|
crack (>= 0.1.7)
|
|
25
31
|
|
|
26
32
|
PLATFORMS
|
|
33
|
+
java
|
|
27
34
|
ruby
|
|
28
35
|
|
|
29
36
|
DEPENDENCIES
|
|
30
37
|
activesupport (~> 3.0.1)
|
|
31
|
-
|
|
32
|
-
eventmachine (~> 0.12.10)
|
|
38
|
+
infinity_test!
|
|
33
39
|
rspec (~> 2.0)
|
|
34
40
|
webmock (~> 1.4.0)
|
data/README.md
CHANGED
|
@@ -14,17 +14,17 @@ There are a couple other main features as well. You can provide Request Monitor
|
|
|
14
14
|
[0.5, :get, "http://www.microsoft.com/"],
|
|
15
15
|
[0.9, :get, "http://www.amazon.com/"]
|
|
16
16
|
]
|
|
17
|
-
r = ReReplay.new(input)
|
|
17
|
+
r = ReReplay::Runner.new(input)
|
|
18
18
|
r.run
|
|
19
19
|
# and done!
|
|
20
20
|
|
|
21
21
|
Of course, this doesn't actually track any output, so...let's monitor the request time using the request_time_monitor:
|
|
22
22
|
|
|
23
23
|
## Request Monitor
|
|
24
|
-
require "rereplay/monitors
|
|
24
|
+
require "rereplay/monitors"
|
|
25
25
|
input = [same as in Simple]
|
|
26
|
-
mon = RequestTimeMonitor.new
|
|
27
|
-
r = ReReplay.new(input)
|
|
26
|
+
mon = ReReplay::RequestTimeMonitor.new
|
|
27
|
+
r = ReReplay::Runner.new(input)
|
|
28
28
|
r.request_monitors << mon
|
|
29
29
|
r.run
|
|
30
30
|
puts mon.results.inspect
|
data/lib/rereplay.rb
CHANGED
|
@@ -7,7 +7,7 @@ module ReReplay
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def finish(request)
|
|
10
|
-
@results[request.index] =
|
|
10
|
+
@results[request.index] = {:url => request.url, :duration => request.finish - request.actual_start, :scheduled_start => request.scheduled_start, :actual_start => request.actual_start}
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module ReReplay
|
|
2
2
|
class TimeoutFailer
|
|
3
|
+
class Timeout < StandardError; end
|
|
3
4
|
def initialize(max_timeouts=1)
|
|
4
5
|
@max_timeouts = max_timeouts
|
|
5
6
|
@timeouts = 0
|
|
@@ -10,7 +11,7 @@ module ReReplay
|
|
|
10
11
|
@timeouts += 1
|
|
11
12
|
end
|
|
12
13
|
if(@timeouts >= @max_timeouts)
|
|
13
|
-
raise "TimeoutFailer triggered because timeout limit #{@max_timeouts} was reached"
|
|
14
|
+
raise Timeout, "TimeoutFailer triggered because timeout limit #{@max_timeouts} was reached"
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
end
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
require 'thread'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'monitor'
|
|
6
|
+
|
|
7
|
+
module ReReplay
|
|
8
|
+
class TestDurationExceeded < StandardError; end
|
|
9
|
+
class Runner
|
|
10
|
+
|
|
11
|
+
attr_accessor :periodic_monitors
|
|
12
|
+
attr_accessor :request_monitors
|
|
13
|
+
|
|
14
|
+
def initialize(input=nil)
|
|
15
|
+
if(!input.nil?)
|
|
16
|
+
self.input = input
|
|
17
|
+
end
|
|
18
|
+
@periodic_monitors = []
|
|
19
|
+
@request_monitors = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def input=(input)
|
|
23
|
+
if(input.is_a? Array)
|
|
24
|
+
@input = input
|
|
25
|
+
elsif(input.respond_to? :readlines)
|
|
26
|
+
@input = input.readlines
|
|
27
|
+
elsif(input.respond_to? :split)
|
|
28
|
+
@input = input.split("\n").map do |i|
|
|
29
|
+
i = i.strip.split(",").map {|j| j.strip}
|
|
30
|
+
i[0] = i[0].to_f
|
|
31
|
+
i[1] = i[1].to_sym
|
|
32
|
+
i
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
raise "Invalid input, expected Array, #readlines, or #split"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def validate_input
|
|
40
|
+
if(@input.nil? || @input.empty?)
|
|
41
|
+
raise ArgumentError, "Nothing to process (input was empty)"
|
|
42
|
+
end
|
|
43
|
+
valid_methods = [:get, :head]
|
|
44
|
+
@input.each_with_index do |a, i|
|
|
45
|
+
if(!a[0].is_a? Numeric)
|
|
46
|
+
raise ArgumentError, "Expected element at index 0 of input #{i+1} to be Numeric; was #{a[0]}"
|
|
47
|
+
end
|
|
48
|
+
if(!a[1].is_a?(Symbol) || !valid_methods.include?(a[1]))
|
|
49
|
+
raise ArgumentError, "Expected element at index 1 of input #{i+1} to be a symbol in #{valid_methods.inspect}; was #{a[1].inspect}"
|
|
50
|
+
end
|
|
51
|
+
if(!a[2].is_a? String)
|
|
52
|
+
raise ArgumentError, "Expected element at index 2 of input #{i+1} to be a String; was #{a[2]}"
|
|
53
|
+
end
|
|
54
|
+
if(!a[3].nil? && !a[3].is_a?(Hash))
|
|
55
|
+
raise ArgumentError, "Expected element at index 3 of input #{i+1} to be nil or a Hash; was #{a[3]}"
|
|
56
|
+
end
|
|
57
|
+
# TODO post data
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def prepare
|
|
62
|
+
p = profile
|
|
63
|
+
|
|
64
|
+
max_time = @input.max {|a, b| a[0] <=> b[0]}[0]
|
|
65
|
+
|
|
66
|
+
loop_count = 1
|
|
67
|
+
if(p[:when_input_consumed] == :loop)
|
|
68
|
+
if(max_time < p[:run_for])
|
|
69
|
+
loop_count = (p[:run_for].to_f / max_time).ceil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
if(loop_count > 1)
|
|
73
|
+
new_inputs = []
|
|
74
|
+
2.upto(loop_count) do |loop|
|
|
75
|
+
new_input = @input.map do |i|
|
|
76
|
+
new_i = i.dup
|
|
77
|
+
new_i[0] += max_time * (loop - 1)
|
|
78
|
+
new_i
|
|
79
|
+
end
|
|
80
|
+
new_inputs << new_input
|
|
81
|
+
end
|
|
82
|
+
new_inputs.each {|input| @input += input}
|
|
83
|
+
end
|
|
84
|
+
real_max_time = [max_time * loop_count, p[:run_for]].min
|
|
85
|
+
if(p[:rampup][0] != p[:rampup][1] || p[:rampup][0] != 1.0)
|
|
86
|
+
case p[:rampup_method]
|
|
87
|
+
when :linear
|
|
88
|
+
sr = 1.0 / p[:rampup][0]
|
|
89
|
+
fr = 1.0 / p[:rampup][1]
|
|
90
|
+
prev_time = 0
|
|
91
|
+
new_prev_time = 0
|
|
92
|
+
@input.map! do |a|
|
|
93
|
+
time = a[0].to_f
|
|
94
|
+
percent = time / real_max_time
|
|
95
|
+
fraction = sr + (fr - sr)*(time / real_max_time)
|
|
96
|
+
tmp = a[0]
|
|
97
|
+
a[0] = (time - prev_time)*fraction + new_prev_time
|
|
98
|
+
prev_time = tmp
|
|
99
|
+
new_prev_time = a[0]
|
|
100
|
+
a
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def run
|
|
107
|
+
validate_input
|
|
108
|
+
p = profile
|
|
109
|
+
done_count = 0
|
|
110
|
+
# request monitors with a start method
|
|
111
|
+
request_monitors_start = request_monitors.select {|mon| mon.respond_to? :start}
|
|
112
|
+
# request monitors with a finish method
|
|
113
|
+
request_monitors_finish = request_monitors.select {|mon| mon.respond_to? :finish}
|
|
114
|
+
tg = ThreadGroup.new
|
|
115
|
+
q = Queue.new
|
|
116
|
+
q.extend MonitorMixin
|
|
117
|
+
waiters_cond = q.new_cond
|
|
118
|
+
|
|
119
|
+
prepare
|
|
120
|
+
start_time = nil
|
|
121
|
+
index = 0
|
|
122
|
+
requests_to_make = @input.map do |r|
|
|
123
|
+
a = r.dup
|
|
124
|
+
a[3] = index
|
|
125
|
+
index += 1
|
|
126
|
+
a
|
|
127
|
+
end
|
|
128
|
+
thread_count = 20
|
|
129
|
+
done = 0
|
|
130
|
+
total_requests = @input.length
|
|
131
|
+
max_delay = 0
|
|
132
|
+
parent_thread = Thread.current
|
|
133
|
+
Thread.abort_on_exception = true
|
|
134
|
+
ready_for_processing = false
|
|
135
|
+
gatekeeper = Thread.new do
|
|
136
|
+
q.synchronize do
|
|
137
|
+
waiters_cond.wait_until { ready_for_processing }
|
|
138
|
+
end
|
|
139
|
+
until requests_to_make.empty?
|
|
140
|
+
task = requests_to_make.shift
|
|
141
|
+
since_start = Time.new - start_time
|
|
142
|
+
time_until_next_task = task[0] - since_start
|
|
143
|
+
|
|
144
|
+
if(time_until_next_task > 0)
|
|
145
|
+
sleep time_until_next_task
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
q << task
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
thread_count.times do
|
|
153
|
+
t = Thread.new do
|
|
154
|
+
while true
|
|
155
|
+
task = q.pop
|
|
156
|
+
now = Time.new
|
|
157
|
+
since_start = now - start_time
|
|
158
|
+
delay = since_start - task[0]
|
|
159
|
+
if(delay > max_delay) then max_delay = delay; end
|
|
160
|
+
url = URI.parse(task[2])
|
|
161
|
+
req = Net::HTTP::Get.new(url.path)
|
|
162
|
+
request = OpenStruct.new(:url => task[2], :scheduled_start => task[0], :index => task[3], :http_method => task[1])
|
|
163
|
+
# this connection can actually take ~300ms...is there a better way?
|
|
164
|
+
Net::HTTP.start(url.host, url.port) do |http|
|
|
165
|
+
http.read_timeout = p[:timeout]
|
|
166
|
+
status = nil
|
|
167
|
+
begin
|
|
168
|
+
#request.actual_start = Time.now - start_time
|
|
169
|
+
request.actual_start = now - start_time
|
|
170
|
+
resp = http.request(req)
|
|
171
|
+
request_monitors_start.each {|mon| mon.start(request)}
|
|
172
|
+
rescue Timeout::Error
|
|
173
|
+
status = :timeout
|
|
174
|
+
end
|
|
175
|
+
if status.nil?
|
|
176
|
+
status = resp.code
|
|
177
|
+
end
|
|
178
|
+
time_finished = Time.now - start_time
|
|
179
|
+
request.finish = time_finished
|
|
180
|
+
request.status = status
|
|
181
|
+
begin
|
|
182
|
+
request_monitors_finish.each {|mon| mon.finish(request)}
|
|
183
|
+
rescue => e
|
|
184
|
+
parent_thread.raise e
|
|
185
|
+
end
|
|
186
|
+
q.synchronize do
|
|
187
|
+
done += 1
|
|
188
|
+
waiters_cond.broadcast
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
tg.add t
|
|
194
|
+
end
|
|
195
|
+
test_duration_exceeded = false
|
|
196
|
+
q.synchronize do
|
|
197
|
+
ready_for_processing = true
|
|
198
|
+
start_time = Time.now
|
|
199
|
+
waiters_cond.broadcast
|
|
200
|
+
end
|
|
201
|
+
timeout_thread = Thread.new do
|
|
202
|
+
sleep_duration = start_time + p[:run_for] - Time.now
|
|
203
|
+
sleep sleep_duration
|
|
204
|
+
q.synchronize do
|
|
205
|
+
test_duration_exceeded = true
|
|
206
|
+
waiters_cond.broadcast
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
periodic_monitor_threads = []
|
|
210
|
+
periodic_monitors.each do |mon|
|
|
211
|
+
interval = mon.respond_to?(:interval) ? mon.interval : 5
|
|
212
|
+
periodic_monitor_threads << Thread.new do
|
|
213
|
+
i = 0
|
|
214
|
+
while true
|
|
215
|
+
mon.tick(Time.now - start_time)
|
|
216
|
+
i += 1
|
|
217
|
+
time_to_next = start_time + (interval * i) - Time.now
|
|
218
|
+
sleep time_to_next if time_to_next > 0
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
q.synchronize do
|
|
223
|
+
waiters_cond.wait_while { done < total_requests && !test_duration_exceeded }
|
|
224
|
+
end
|
|
225
|
+
ensure
|
|
226
|
+
gatekeeper.kill if gatekeeper
|
|
227
|
+
tg.list.each {|t| t.kill} if tg
|
|
228
|
+
periodic_monitor_threads.each {|t| t.kill} if periodic_monitor_threads
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def profile=(new_profile)
|
|
232
|
+
@profile = {
|
|
233
|
+
:run_for => 5,
|
|
234
|
+
:when_input_consumed => :stop,
|
|
235
|
+
:timeout => 1,
|
|
236
|
+
:rampup => [1.0, 1.0],
|
|
237
|
+
:rampup_method => :linear
|
|
238
|
+
}
|
|
239
|
+
if(new_profile.is_a? Hash)
|
|
240
|
+
@profile.merge!(new_profile)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
def profile
|
|
244
|
+
if(@profile.nil?)
|
|
245
|
+
self.profile = {}
|
|
246
|
+
end
|
|
247
|
+
@profile
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
data/lib/rereplay/version.rb
CHANGED
data/rereplay.gemspec
CHANGED
|
@@ -15,9 +15,6 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
|
|
16
16
|
s.required_rubygems_version = ">= 1.3.6"
|
|
17
17
|
|
|
18
|
-
s.add_runtime_dependency "eventmachine", ">= 0.12.10", "< 0.13"
|
|
19
|
-
s.add_runtime_dependency "em-http-request", ">= 0.2.14", "< 0.3"
|
|
20
|
-
|
|
21
18
|
s.add_development_dependency "rspec", ">= 2.0", "< 3"
|
|
22
19
|
s.add_development_dependency "webmock", ">= 1.4.0", "< 1.5"
|
|
23
20
|
s.add_development_dependency "active_support", ">= 3.0.1", "< 3.1"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ReReplay, "advanced functions" do
|
|
4
4
|
it "should support linear rampup" do
|
|
@@ -9,7 +9,7 @@ describe ReReplay, "advanced functions" do
|
|
|
9
9
|
:rampup => [1.0, 2.0]
|
|
10
10
|
}
|
|
11
11
|
r.profile = profile
|
|
12
|
-
lambda { r.run }.should take_between(
|
|
12
|
+
lambda { r.run }.should take_between(0.65.seconds).and(0.8.seconds)
|
|
13
13
|
validate_input(10)
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ describe ReReplay, "advanced functions" do
|
|
|
21
21
|
:rampup => [0.5, 2]
|
|
22
22
|
}
|
|
23
23
|
r.profile = profile
|
|
24
|
-
lambda { r.run }.should take_between(
|
|
24
|
+
lambda { r.run }.should take_between(1.05.seconds).and(1.2.seconds)
|
|
25
25
|
validate_input(10)
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -35,7 +35,7 @@ describe ReReplay, "advanced functions" do
|
|
|
35
35
|
:when_input_consumed => :loop
|
|
36
36
|
}
|
|
37
37
|
r.profile = profile
|
|
38
|
-
lambda { r.run }.should take_between(
|
|
38
|
+
lambda { r.run }.should take_between(2.seconds).and(2.2.seconds)
|
|
39
39
|
validate_input(10, 2)
|
|
40
40
|
end
|
|
41
41
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ReReplay, "basic functions" do
|
|
4
4
|
it "should process input from array" do
|
|
@@ -10,7 +10,7 @@ describe ReReplay, "basic functions" do
|
|
|
10
10
|
input = urls.map {|i| [interval += 0.1, :get, i]}
|
|
11
11
|
|
|
12
12
|
r = ReReplay::Runner.new(input)
|
|
13
|
-
lambda { r.run }.should take_between(
|
|
13
|
+
lambda { r.run }.should take_between(0.seconds).and(1.second)
|
|
14
14
|
|
|
15
15
|
urls.each {|url| WebMock.should have_requested(:get, url)}
|
|
16
16
|
end
|
|
@@ -25,7 +25,7 @@ EOF
|
|
|
25
25
|
stub_request(:get, "http://www.amazon.com/")
|
|
26
26
|
|
|
27
27
|
r = ReReplay::Runner.new(input)
|
|
28
|
-
lambda { r.run }.should take_between(
|
|
28
|
+
lambda { r.run }.should take_between(0.seconds).and(1.second)
|
|
29
29
|
|
|
30
30
|
WebMock.should have_requested(:get, "http://www.google.com/")
|
|
31
31
|
WebMock.should have_requested(:get, "http://www.amazon.com/")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ReReplay, "periodic monitors" do
|
|
4
4
|
it "should work" do
|
|
@@ -9,9 +9,6 @@ describe ReReplay, "periodic monitors" do
|
|
|
9
9
|
r = ReReplay::Runner.new(input)
|
|
10
10
|
r.periodic_monitors << mem_monitor
|
|
11
11
|
|
|
12
|
-
# the periodic monitor will start around `time_for_setup` (1 second)
|
|
13
|
-
# and run once every ~0.23s thereafter.
|
|
14
|
-
# Because the final request will finish around 1.75, this run four times
|
|
15
12
|
r.run
|
|
16
13
|
validate_input(3)
|
|
17
14
|
mem_monitor.results.length.should == 4
|
|
@@ -26,9 +23,7 @@ describe ReReplay, "request monitors" do
|
|
|
26
23
|
input = generate_input(3, :interval => 0.25)
|
|
27
24
|
r = ReReplay::Runner.new(input)
|
|
28
25
|
r.request_monitors << req_mon << delay_mon
|
|
29
|
-
|
|
30
|
-
# and run once every ~0.23s thereafter.
|
|
31
|
-
# Because the final request will finish around 1.75, this run four times
|
|
26
|
+
|
|
32
27
|
r.run
|
|
33
28
|
validate_input(3)
|
|
34
29
|
req_mon.results.length.should == 3
|
|
@@ -48,7 +43,8 @@ started request 1:\\(http://microsoft.com/\\) at [\\d\\.]+
|
|
|
48
43
|
started request 2:\\(http://amazon.com/\\) at [\\d\\.]+
|
|
49
44
|
- finished request 2, status 200
|
|
50
45
|
EOF
|
|
51
|
-
capture_stdout { r.run }
|
|
46
|
+
output = capture_stdout { r.run }
|
|
47
|
+
output.should match(expected)
|
|
52
48
|
validate_input(3)
|
|
53
49
|
end
|
|
54
50
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ReReplay, "profile options" do
|
|
4
|
+
it "respects 'run_for' parameter" do
|
|
5
|
+
input = generate_input(3, :interval => 0.3)
|
|
6
|
+
r = ReReplay::Runner.new(input)
|
|
7
|
+
profile = {
|
|
8
|
+
:run_for => 0.5
|
|
9
|
+
}
|
|
10
|
+
r.profile = profile
|
|
11
|
+
|
|
12
|
+
lambda { r.run }.should take_between(0.5.seconds).and(0.6.seconds)
|
|
13
|
+
validate_input(1)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "respects 'when_input_consumed' parameter when :stop" do
|
|
17
|
+
input = generate_input(3, :interval => 0.1)
|
|
18
|
+
r = ReReplay::Runner.new(input)
|
|
19
|
+
profile = {
|
|
20
|
+
:run_for => 10,
|
|
21
|
+
:when_input_consumed => :stop
|
|
22
|
+
}
|
|
23
|
+
r.profile = profile
|
|
24
|
+
|
|
25
|
+
lambda { r.run }.should take_between(0.3.seconds).and(0.5.seconds)
|
|
26
|
+
validate_input(3)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "respects 'when_input_consumed' parameter when :loop" do
|
|
30
|
+
input = generate_input(1, :interval => 0.25)
|
|
31
|
+
r = ReReplay::Runner.new(input)
|
|
32
|
+
profile = {
|
|
33
|
+
:run_for => 0.6,
|
|
34
|
+
:when_input_consumed => :loop
|
|
35
|
+
}
|
|
36
|
+
r.profile = profile
|
|
37
|
+
req_mon = ReReplay::RequestTimeMonitor.new
|
|
38
|
+
r.request_monitors << req_mon
|
|
39
|
+
|
|
40
|
+
# normally this would take 1.5 seconds with :stop, but we're forcing it to loop and take 2 seconds
|
|
41
|
+
lambda { r.run }.should take_between(0.6.seconds).and(0.7.seconds)
|
|
42
|
+
req_mon.results.length.should == 2
|
|
43
|
+
validate_input(1, 2)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# eh, this isn't that good a test because WebMock causes #to_timeout requests to timeout immediately
|
|
47
|
+
# but it tests that timeout works
|
|
48
|
+
it "works with timeouts" do
|
|
49
|
+
input = generate_input(1, :interval => 0.25, :timeout => true)
|
|
50
|
+
r = ReReplay::Runner.new(input)
|
|
51
|
+
profile = {
|
|
52
|
+
:timeout => 10
|
|
53
|
+
}
|
|
54
|
+
r.profile = profile
|
|
55
|
+
r.request_monitors << ReReplay::TimeoutFailer.new
|
|
56
|
+
|
|
57
|
+
lambda { lambda { r.run }.should raise_error(StandardError, /TimeoutFailer/) }.should take_between(0.25.seconds).and(0.5.seconds)
|
|
58
|
+
validate_input(1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
metadata
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rereplay
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 15
|
|
5
5
|
prerelease: false
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
|
-
-
|
|
9
|
-
version: "0.
|
|
8
|
+
- 2
|
|
9
|
+
version: "0.2"
|
|
10
10
|
platform: ruby
|
|
11
11
|
authors:
|
|
12
12
|
- Max Aller
|
|
@@ -14,59 +14,13 @@ autorequire:
|
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
16
|
|
|
17
|
-
date: 2010-10-
|
|
17
|
+
date: 2010-10-30 00:00:00 -07:00
|
|
18
18
|
default_executable:
|
|
19
19
|
dependencies:
|
|
20
|
-
- !ruby/object:Gem::Dependency
|
|
21
|
-
name: eventmachine
|
|
22
|
-
prerelease: false
|
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
-
none: false
|
|
25
|
-
requirements:
|
|
26
|
-
- - ">="
|
|
27
|
-
- !ruby/object:Gem::Version
|
|
28
|
-
hash: 59
|
|
29
|
-
segments:
|
|
30
|
-
- 0
|
|
31
|
-
- 12
|
|
32
|
-
- 10
|
|
33
|
-
version: 0.12.10
|
|
34
|
-
- - <
|
|
35
|
-
- !ruby/object:Gem::Version
|
|
36
|
-
hash: 17
|
|
37
|
-
segments:
|
|
38
|
-
- 0
|
|
39
|
-
- 13
|
|
40
|
-
version: "0.13"
|
|
41
|
-
type: :runtime
|
|
42
|
-
version_requirements: *id001
|
|
43
|
-
- !ruby/object:Gem::Dependency
|
|
44
|
-
name: em-http-request
|
|
45
|
-
prerelease: false
|
|
46
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
|
47
|
-
none: false
|
|
48
|
-
requirements:
|
|
49
|
-
- - ">="
|
|
50
|
-
- !ruby/object:Gem::Version
|
|
51
|
-
hash: 11
|
|
52
|
-
segments:
|
|
53
|
-
- 0
|
|
54
|
-
- 2
|
|
55
|
-
- 14
|
|
56
|
-
version: 0.2.14
|
|
57
|
-
- - <
|
|
58
|
-
- !ruby/object:Gem::Version
|
|
59
|
-
hash: 13
|
|
60
|
-
segments:
|
|
61
|
-
- 0
|
|
62
|
-
- 3
|
|
63
|
-
version: "0.3"
|
|
64
|
-
type: :runtime
|
|
65
|
-
version_requirements: *id002
|
|
66
20
|
- !ruby/object:Gem::Dependency
|
|
67
21
|
name: rspec
|
|
68
22
|
prerelease: false
|
|
69
|
-
requirement: &
|
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
70
24
|
none: false
|
|
71
25
|
requirements:
|
|
72
26
|
- - ">="
|
|
@@ -83,11 +37,11 @@ dependencies:
|
|
|
83
37
|
- 3
|
|
84
38
|
version: "3"
|
|
85
39
|
type: :development
|
|
86
|
-
version_requirements: *
|
|
40
|
+
version_requirements: *id001
|
|
87
41
|
- !ruby/object:Gem::Dependency
|
|
88
42
|
name: webmock
|
|
89
43
|
prerelease: false
|
|
90
|
-
requirement: &
|
|
44
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
91
45
|
none: false
|
|
92
46
|
requirements:
|
|
93
47
|
- - ">="
|
|
@@ -106,11 +60,11 @@ dependencies:
|
|
|
106
60
|
- 5
|
|
107
61
|
version: "1.5"
|
|
108
62
|
type: :development
|
|
109
|
-
version_requirements: *
|
|
63
|
+
version_requirements: *id002
|
|
110
64
|
- !ruby/object:Gem::Dependency
|
|
111
65
|
name: active_support
|
|
112
66
|
prerelease: false
|
|
113
|
-
requirement: &
|
|
67
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
114
68
|
none: false
|
|
115
69
|
requirements:
|
|
116
70
|
- - ">="
|
|
@@ -129,7 +83,7 @@ dependencies:
|
|
|
129
83
|
- 1
|
|
130
84
|
version: "3.1"
|
|
131
85
|
type: :development
|
|
132
|
-
version_requirements: *
|
|
86
|
+
version_requirements: *id003
|
|
133
87
|
description: Replay or script traffic in order to track performance of your site over time.
|
|
134
88
|
email:
|
|
135
89
|
- nanodeath@gmail.com
|
|
@@ -141,6 +95,7 @@ extra_rdoc_files: []
|
|
|
141
95
|
|
|
142
96
|
files:
|
|
143
97
|
- .gitignore
|
|
98
|
+
- .infinity_test
|
|
144
99
|
- Gemfile
|
|
145
100
|
- Gemfile.lock
|
|
146
101
|
- LICENSE
|
|
@@ -153,13 +108,13 @@ files:
|
|
|
153
108
|
- lib/rereplay/monitors/request_time_monitor.rb
|
|
154
109
|
- lib/rereplay/monitors/timeout_failer.rb
|
|
155
110
|
- lib/rereplay/monitors/verbose_monitor.rb
|
|
156
|
-
- lib/rereplay/
|
|
111
|
+
- lib/rereplay/ruby_runner.rb
|
|
157
112
|
- lib/rereplay/version.rb
|
|
158
113
|
- rereplay.gemspec
|
|
159
|
-
- spec/
|
|
160
|
-
- spec/
|
|
161
|
-
- spec/
|
|
162
|
-
- spec/
|
|
114
|
+
- spec/advanced_spec.rb
|
|
115
|
+
- spec/basic_spec.rb
|
|
116
|
+
- spec/monitor_spec.rb
|
|
117
|
+
- spec/profile_spec.rb
|
|
163
118
|
- spec/spec_custom_matchers.rb
|
|
164
119
|
- spec/spec_helper.rb
|
|
165
120
|
has_rdoc: true
|
data/lib/rereplay/runner.rb
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
module ReReplay
|
|
2
|
-
class Runner
|
|
3
|
-
attr_accessor :periodic_monitors
|
|
4
|
-
attr_accessor :request_monitors
|
|
5
|
-
|
|
6
|
-
def initialize(input=nil)
|
|
7
|
-
if(!input.nil?)
|
|
8
|
-
self.input = input
|
|
9
|
-
end
|
|
10
|
-
@periodic_monitors = []
|
|
11
|
-
@request_monitors = []
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def input=(input)
|
|
15
|
-
if(input.is_a? Array)
|
|
16
|
-
@input = input
|
|
17
|
-
elsif(input.respond_to? :readlines)
|
|
18
|
-
@input = input.readlines
|
|
19
|
-
elsif(input.respond_to? :split)
|
|
20
|
-
@input = input.split("\n").map do |i|
|
|
21
|
-
i = i.strip.split(",").map {|j| j.strip}
|
|
22
|
-
i[0] = i[0].to_f
|
|
23
|
-
i[1] = i[1].to_sym
|
|
24
|
-
i
|
|
25
|
-
end
|
|
26
|
-
else
|
|
27
|
-
raise "Invalid input, expected Array, #readlines, or #split"
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def validate_input
|
|
32
|
-
if(@input.nil? || @input.empty?)
|
|
33
|
-
raise ArgumentError, "Nothing to process (input was empty)"
|
|
34
|
-
end
|
|
35
|
-
valid_methods = [:get, :head]
|
|
36
|
-
@input.each_with_index do |a, i|
|
|
37
|
-
if(!a[0].is_a? Numeric)
|
|
38
|
-
raise ArgumentError, "Expected element at index 0 of input #{i+1} to be Numeric; was #{a[0]}"
|
|
39
|
-
end
|
|
40
|
-
if(!a[1].is_a?(Symbol) || !valid_methods.include?(a[1]))
|
|
41
|
-
raise ArgumentError, "Expected element at index 1 of input #{i+1} to be a symbol in #{valid_methods.inspect}; was #{a[1].inspect}"
|
|
42
|
-
end
|
|
43
|
-
if(!a[2].is_a? String)
|
|
44
|
-
raise ArgumentError, "Expected element at index 2 of input #{i+1} to be a String; was #{a[2]}"
|
|
45
|
-
end
|
|
46
|
-
if(!a[3].nil? && !a[3].is_a?(Hash))
|
|
47
|
-
raise ArgumentError, "Expected element at index 3 of input #{i+1} to be nil or a Hash; was #{a[3]}"
|
|
48
|
-
end
|
|
49
|
-
# TODO post data
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def run
|
|
54
|
-
validate_input
|
|
55
|
-
p = profile
|
|
56
|
-
done_count = 0
|
|
57
|
-
# request monitors with a start method
|
|
58
|
-
request_monitors_start = request_monitors.select {|mon| mon.respond_to? :start}
|
|
59
|
-
# request monitors with a finish method
|
|
60
|
-
request_monitors_finish = request_monitors.select {|mon| mon.respond_to? :finish}
|
|
61
|
-
EM::run do
|
|
62
|
-
EM.set_quantum(p[:timer_granularity])
|
|
63
|
-
start = Time.now
|
|
64
|
-
setup_time = p[:time_for_setup]
|
|
65
|
-
actual_start = start + setup_time
|
|
66
|
-
|
|
67
|
-
loop_count = 1
|
|
68
|
-
|
|
69
|
-
max_time = @input.max {|a,b| a[0] <=> b[0]}[0]
|
|
70
|
-
#avg_time = @input.inject(0){|memo, i| i[0] + memo}.to_f / @input.length
|
|
71
|
-
if(p[:when_input_consumed] == :loop)
|
|
72
|
-
if(max_time < p[:run_for])
|
|
73
|
-
loop_count = (p[:run_for].to_f / max_time).ceil
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
if(loop_count > 1)
|
|
78
|
-
# If we need to have multiple iterations of the data,
|
|
79
|
-
# we pad that on here
|
|
80
|
-
new_inputs = []
|
|
81
|
-
2.upto(loop_count) do |loop|
|
|
82
|
-
new_input = @input.map do |i|
|
|
83
|
-
new_i = i.dup
|
|
84
|
-
new_i[0] += max_time * (loop - 1)
|
|
85
|
-
new_i
|
|
86
|
-
end
|
|
87
|
-
new_inputs << new_input
|
|
88
|
-
end
|
|
89
|
-
new_inputs.each {|input| @input += input}
|
|
90
|
-
end
|
|
91
|
-
real_max_time = [max_time * loop_count, p[:run_for]].min
|
|
92
|
-
if(p[:rampup][0] != p[:rampup][1] || p[:rampup][0] != 1.0)
|
|
93
|
-
case p[:rampup_method]
|
|
94
|
-
when :linear
|
|
95
|
-
sr = 1.0 / p[:rampup][0]
|
|
96
|
-
fr = 1.0 / p[:rampup][1]
|
|
97
|
-
prev_time = 0
|
|
98
|
-
new_prev_time = 0
|
|
99
|
-
@input.map! do |a|
|
|
100
|
-
time = a[0].to_f
|
|
101
|
-
percent = time / real_max_time
|
|
102
|
-
fraction = sr + (fr - sr)*(time / real_max_time)
|
|
103
|
-
tmp = a[0]
|
|
104
|
-
a[0] = (time - prev_time)*fraction + new_prev_time
|
|
105
|
-
prev_time = tmp
|
|
106
|
-
new_prev_time = a[0]
|
|
107
|
-
a
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
total_urls = @input.length
|
|
112
|
-
|
|
113
|
-
requests = []
|
|
114
|
-
# pregenerate requests
|
|
115
|
-
@input.each do |a|
|
|
116
|
-
requests << EventMachine::HttpRequest.new(a[2])
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
@input.each_with_index do |a, i|
|
|
120
|
-
scheduled_start = a[0]
|
|
121
|
-
request = OpenStruct.new(:url => a[2], :scheduled_start => scheduled_start, :index => i, :http_method => a[1])
|
|
122
|
-
delay = actual_start + scheduled_start
|
|
123
|
-
if(delay < Time.now)
|
|
124
|
-
raise "Not enough time allotted for setup! Try increasing time_for_setup in your profile."
|
|
125
|
-
end
|
|
126
|
-
delay -= Time.now
|
|
127
|
-
EM::add_timer(delay) do
|
|
128
|
-
EM.defer do
|
|
129
|
-
begin
|
|
130
|
-
request.actual_start = Time.now - actual_start
|
|
131
|
-
http = requests[i].send(request.http_method, :timeout => p[:timeout])
|
|
132
|
-
request_monitors_start.each {|mon| mon.start(request)}
|
|
133
|
-
rescue => e
|
|
134
|
-
EM.next_tick do
|
|
135
|
-
raise e
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
callback = lambda {
|
|
139
|
-
time_finished = Time.now - actual_start
|
|
140
|
-
request.finish = time_finished
|
|
141
|
-
request.status = http.response_header.status
|
|
142
|
-
if(request.status == 0)
|
|
143
|
-
request.status = :timeout
|
|
144
|
-
end
|
|
145
|
-
begin
|
|
146
|
-
request_monitors_finish.each {|mon| mon.finish(request)}
|
|
147
|
-
rescue => e
|
|
148
|
-
EM.next_tick do
|
|
149
|
-
raise e
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
done_count += 1
|
|
153
|
-
if(done_count == total_urls && p[:when_input_consumed] == :stop)
|
|
154
|
-
EM.stop
|
|
155
|
-
end
|
|
156
|
-
}
|
|
157
|
-
http.errback { callback.call }
|
|
158
|
-
http.callback { callback.call }
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
periodic_monitors.each do |mon|
|
|
163
|
-
interval = mon.respond_to?(:interval) ? mon.interval : 5
|
|
164
|
-
EM::add_timer(actual_start - Time.now - interval) do
|
|
165
|
-
EM::add_periodic_timer(interval) do
|
|
166
|
-
mon.tick(Time.now - actual_start)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
run_for = actual_start + p[:run_for] - Time.now
|
|
171
|
-
EM::add_timer(run_for) do
|
|
172
|
-
#puts "run_for hit (#{run_for})"
|
|
173
|
-
EM.stop
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def profile=(new_profile)
|
|
179
|
-
@profile = {
|
|
180
|
-
:time_for_setup => 1,
|
|
181
|
-
:timer_granularity => 50,
|
|
182
|
-
:run_for => 5,
|
|
183
|
-
:when_input_consumed => :stop,
|
|
184
|
-
:timeout => 1,
|
|
185
|
-
:rampup => [1.0, 1.0],
|
|
186
|
-
:rampup_method => :linear
|
|
187
|
-
}
|
|
188
|
-
if(new_profile.is_a? Hash)
|
|
189
|
-
@profile.merge!(new_profile)
|
|
190
|
-
end
|
|
191
|
-
# TODO validate profile
|
|
192
|
-
end
|
|
193
|
-
def profile
|
|
194
|
-
if(@profile.nil?)
|
|
195
|
-
self.profile = {}
|
|
196
|
-
end
|
|
197
|
-
@profile
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|
data/spec/profile.rb
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
require 'spec/spec_helper'
|
|
2
|
-
|
|
3
|
-
describe ReReplay, "profile options" do
|
|
4
|
-
it "respects 'time_for_setup' parameter" do
|
|
5
|
-
input = generate_input(2)
|
|
6
|
-
|
|
7
|
-
r = ReReplay::Runner.new(input)
|
|
8
|
-
profile = {
|
|
9
|
-
:time_for_setup => 0.25
|
|
10
|
-
}
|
|
11
|
-
r.profile = profile
|
|
12
|
-
lambda { r.run }.should take_between(0.45.seconds).and(0.6.seconds)
|
|
13
|
-
validate_input(2)
|
|
14
|
-
end
|
|
15
|
-
it "respects 'timer_granularity' parameter" do
|
|
16
|
-
input = generate_input(2)
|
|
17
|
-
|
|
18
|
-
r = ReReplay::Runner.new(input)
|
|
19
|
-
profile = {
|
|
20
|
-
:timer_granularity => 1000
|
|
21
|
-
}
|
|
22
|
-
r.profile = profile
|
|
23
|
-
|
|
24
|
-
# normally this would finish at around 1.2 seconds, but with such a high
|
|
25
|
-
# timer resolution, it rounds up from 0.1 to 1
|
|
26
|
-
lambda { r.run }.should take_between(2.seconds).and(3.seconds)
|
|
27
|
-
validate_input(2)
|
|
28
|
-
end
|
|
29
|
-
it "respects 'run_for' parameter" do
|
|
30
|
-
input = generate_input(3, :interval => 1)
|
|
31
|
-
r = ReReplay::Runner.new(input)
|
|
32
|
-
profile = {
|
|
33
|
-
:run_for => 1
|
|
34
|
-
}
|
|
35
|
-
r.profile = profile
|
|
36
|
-
|
|
37
|
-
# normally this would run for the full 4 seconds, but with run_for fixed at 2,
|
|
38
|
-
# it will stop then
|
|
39
|
-
lambda { r.run }.should take_between(2.seconds).and(2.1.seconds)
|
|
40
|
-
validate_input(1)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it "respects 'when_input_consumed' parameter when :stop" do
|
|
44
|
-
input = generate_input(3, :interval => 0.5)
|
|
45
|
-
r = ReReplay::Runner.new(input)
|
|
46
|
-
profile = {
|
|
47
|
-
:run_for => 10,
|
|
48
|
-
:when_input_consumed => :stop
|
|
49
|
-
}
|
|
50
|
-
r.profile = profile
|
|
51
|
-
|
|
52
|
-
lambda { r.run }.should take_between(2.5.seconds).and(2.7.seconds)
|
|
53
|
-
validate_input(3)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it "respects 'when_input_consumed' parameter when :loop" do
|
|
57
|
-
input = generate_input(1, :interval => 0.5)
|
|
58
|
-
r = ReReplay::Runner.new(input)
|
|
59
|
-
profile = {
|
|
60
|
-
:run_for => 1.1,
|
|
61
|
-
:when_input_consumed => :loop
|
|
62
|
-
}
|
|
63
|
-
r.profile = profile
|
|
64
|
-
req_mon = ReReplay::RequestTimeMonitor.new
|
|
65
|
-
r.request_monitors << req_mon
|
|
66
|
-
|
|
67
|
-
# normally this would take 1.5 seconds with :stop, but we're forcing it to loop and take 2 seconds
|
|
68
|
-
lambda { r.run }.should take_between(2.seconds).and(2.2.seconds)
|
|
69
|
-
req_mon.results.length.should == 2
|
|
70
|
-
validate_input(1, 2)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# eh, this isn't that good a test because WebMock causes #to_timeout requests to timeout immediately
|
|
74
|
-
it "respects 'timeout' parameter" do
|
|
75
|
-
input = generate_input(1, :interval => 0.25, :timeout => true)
|
|
76
|
-
r = ReReplay::Runner.new(input)
|
|
77
|
-
profile = {
|
|
78
|
-
:timeout => 10,
|
|
79
|
-
:time_for_setup => 0.25
|
|
80
|
-
}
|
|
81
|
-
r.profile = profile
|
|
82
|
-
r.request_monitors << ReReplay::TimeoutFailer.new
|
|
83
|
-
|
|
84
|
-
# normally this would take 4 seconds with :stop, but after 2 seconds we hit the timeout
|
|
85
|
-
|
|
86
|
-
lambda { lambda { r.run }.should raise_error(StandardError, /TimeoutFailer/) }.should take_between(0.5.seconds).and(0.7.seconds)
|
|
87
|
-
validate_input(1)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
end
|