rack-perftools_profiler 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +26 -12
- data/VERSION +1 -1
- data/lib/rack/perftools_profiler.rb +2 -0
- data/lib/rack/perftools_profiler/profile_once.rb +9 -1
- data/lib/rack/perftools_profiler/profiler.rb +42 -6
- data/lib/rack/perftools_profiler/return_data.rb +1 -1
- data/lib/rack/perftools_profiler/start_profiling.rb +14 -1
- data/lib/rack/perftools_profiler/utils.rb +11 -0
- data/rack-perftools_profiler.gemspec +81 -0
- data/test/multiple_request_profiling_test.rb +352 -0
- data/test/rack-perftools-profiler_test.rb +14 -369
- data/test/single_request_profiling_test.rb +278 -0
- data/test/test_helper.rb +64 -0
- metadata +29 -7
- data/.gitignore +0 -6
@@ -1,66 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
ITERATIONS = case RUBY_VERSION
|
4
|
-
when /1\.9\.1/
|
5
|
-
350_000 # Ruby 1.9.1 is that we need to add extra iterations to get profiling data
|
6
|
-
else
|
7
|
-
35_000
|
8
|
-
end
|
9
|
-
|
10
|
-
# From the Rack spec (http://rack.rubyforge.org/doc/files/SPEC.html) :
|
11
|
-
# The Body must respond to each and must only yield String values. The Body should not be an instance of String.
|
12
|
-
# ... The Body commonly is an Array of Strings, the application instance itself, or a File-like object.
|
13
|
-
|
14
|
-
class RackResponseBody
|
15
|
-
include Test::Unit::Assertions
|
16
|
-
|
17
|
-
def initialize(body)
|
18
|
-
assert !body.instance_of?(String)
|
19
|
-
@body = body
|
20
|
-
end
|
21
|
-
|
22
|
-
def to_s
|
23
|
-
str = ""
|
24
|
-
@body.each do |part|
|
25
|
-
str << part
|
26
|
-
end
|
27
|
-
str
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
class TestApp
|
33
|
-
|
34
|
-
def call(env)
|
35
|
-
case env['PATH_INFO']
|
36
|
-
when /method1/
|
37
|
-
ITERATIONS.times do
|
38
|
-
self.class.new.method1
|
39
|
-
end
|
40
|
-
GC.start
|
41
|
-
when /method2/
|
42
|
-
ITERATIONS.times do
|
43
|
-
self.class.new.method2
|
44
|
-
end
|
45
|
-
GC.start
|
46
|
-
end
|
47
|
-
[200, {}, ['Done']]
|
48
|
-
end
|
49
|
-
|
50
|
-
def method1
|
51
|
-
100.times do
|
52
|
-
1+2+3+4+5
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def method2
|
57
|
-
100.times do
|
58
|
-
1+2+3+4+5
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
3
|
class RackPerftoolsProfilerTest < Test::Unit::TestCase
|
65
4
|
include Rack::PerftoolsProfiler
|
66
5
|
|
@@ -114,6 +53,19 @@ class RackPerftoolsProfilerTest < Test::Unit::TestCase
|
|
114
53
|
end
|
115
54
|
assert_match(/Invalid printer type\: badprinter/, error.message)
|
116
55
|
end
|
56
|
+
|
57
|
+
should 'raise error if mode is invalid' do
|
58
|
+
error = assert_raises ProfilerArgumentError do
|
59
|
+
Rack::PerftoolsProfiler.with_profiling_off(@app, :mode => 'badmode', :default_printer => 'gif')
|
60
|
+
end
|
61
|
+
assert_match(/Invalid mode\: badmode/, error.message)
|
62
|
+
end
|
63
|
+
|
64
|
+
should 'not raise error if mode is unchangeable' do
|
65
|
+
assert_nothing_raised do
|
66
|
+
Rack::PerftoolsProfiler.with_profiling_off(@app, :mode => 'walltime', :default_printer => 'gif')
|
67
|
+
end
|
68
|
+
end
|
117
69
|
|
118
70
|
should 'not modify options hash' do
|
119
71
|
options = {:mode => 'walltime', :default_printer => 'gif'}
|
@@ -122,7 +74,7 @@ class RackPerftoolsProfilerTest < Test::Unit::TestCase
|
|
122
74
|
assert_equal old_options, options
|
123
75
|
end
|
124
76
|
|
125
|
-
context '
|
77
|
+
context 'when not profiling' do
|
126
78
|
|
127
79
|
should 'call app directly' do
|
128
80
|
status, headers, body = Rack::PerftoolsProfiler.with_profiling_off(@app).call(@root_request_env)
|
@@ -141,313 +93,6 @@ class RackPerftoolsProfilerTest < Test::Unit::TestCase
|
|
141
93
|
|
142
94
|
end
|
143
95
|
|
144
|
-
context 'simple profiling mode' do
|
145
|
-
|
146
|
-
should 'default to text printer' do
|
147
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@app).call(@profiled_request_env)
|
148
|
-
assert_equal "text/plain", headers['Content-Type']
|
149
|
-
end
|
150
|
-
|
151
|
-
should "set CPUPROFILE_REALTIME to 1 if mode is 'walltime'" do
|
152
|
-
realtime = ENV['CPUPROFILE_REALTIME']
|
153
|
-
assert_nil realtime
|
154
|
-
app = lambda do |env|
|
155
|
-
realtime = ENV['CPUPROFILE_REALTIME']
|
156
|
-
[200, {}, ["hi"]]
|
157
|
-
end
|
158
|
-
Rack::PerftoolsProfiler.new(app, :mode => 'walltime').call(@profiled_request_env)
|
159
|
-
assert_equal '1', realtime
|
160
|
-
end
|
161
|
-
|
162
|
-
should "set CPUPROFILE_OBJECTS to 1 if mode is 'objects'" do
|
163
|
-
realtime = ENV['CPUPROFILE_OBJECTS']
|
164
|
-
assert_nil realtime
|
165
|
-
app = lambda do |env|
|
166
|
-
realtime = ENV['CPUPROFILE_OBJECTS']
|
167
|
-
[200, {}, ["hi"]]
|
168
|
-
end
|
169
|
-
Rack::PerftoolsProfiler.new(app, :mode => 'objects').call(@profiled_request_env)
|
170
|
-
assert_equal '1', realtime
|
171
|
-
end
|
172
|
-
|
173
|
-
should "not set CPUPROFILE_FREQUENCY by default" do
|
174
|
-
frequency = ENV['CPUPROFILE_FREQUENCY']
|
175
|
-
assert_nil frequency
|
176
|
-
app = lambda do |env|
|
177
|
-
frequency = ENV['CPUPROFILE_FREQUENCY']
|
178
|
-
[200, {}, ["hi"]]
|
179
|
-
end
|
180
|
-
Rack::PerftoolsProfiler.new(app).call(@profiled_request_env)
|
181
|
-
assert_nil frequency
|
182
|
-
end
|
183
|
-
|
184
|
-
should 'alter CPUPROFILE_FREQUENCY if frequency is set' do
|
185
|
-
frequency = ENV['CPUPROFILE_FREQUENCY']
|
186
|
-
assert_nil frequency
|
187
|
-
app = lambda do |env|
|
188
|
-
frequency = ENV['CPUPROFILE_FREQUENCY']
|
189
|
-
[200, {}, ["hi"]]
|
190
|
-
end
|
191
|
-
Rack::PerftoolsProfiler.new(app, :frequency => 500).call(@profiled_request_env)
|
192
|
-
assert_equal '500', frequency
|
193
|
-
end
|
194
|
-
|
195
|
-
context 'text printer' do
|
196
|
-
|
197
|
-
should 'return profiling data' do
|
198
|
-
_, _, body = Rack::PerftoolsProfiler.new(@slow_app, :default_printer => 'text').call(@profiled_request_env)
|
199
|
-
assert_match(/Total: \d+ samples/, RackResponseBody.new(body).to_s)
|
200
|
-
end
|
201
|
-
|
202
|
-
should 'have Content-Type text/plain' do
|
203
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'text').call(@profiled_request_env)
|
204
|
-
assert_equal "text/plain", headers['Content-Type']
|
205
|
-
end
|
206
|
-
|
207
|
-
should 'have Content-Length' do
|
208
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@slow_app, :default_printer => 'text').call(@profiled_request_env)
|
209
|
-
assert (headers.fetch('Content-Length').to_i > 500)
|
210
|
-
end
|
211
|
-
|
212
|
-
end
|
213
|
-
|
214
|
-
context 'gif printer' do
|
215
|
-
|
216
|
-
should 'gif printer has Content-Type image/gif' do
|
217
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'gif').call(@profiled_request_env)
|
218
|
-
assert_equal "image/gif", headers['Content-Type']
|
219
|
-
end
|
220
|
-
|
221
|
-
should 'gif printer has Content-Length' do
|
222
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@slow_app, :default_printer => 'gif').call(@profiled_request_env)
|
223
|
-
assert headers.fetch('Content-Length').to_i > 25_000
|
224
|
-
end
|
225
|
-
|
226
|
-
should 'pdf printer has Content-Type application/pdf' do
|
227
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(@profiled_request_env)
|
228
|
-
assert_equal "application/pdf", headers['Content-Type']
|
229
|
-
end
|
230
|
-
|
231
|
-
end
|
232
|
-
|
233
|
-
context 'pdf printer' do
|
234
|
-
|
235
|
-
should 'have default filename' do
|
236
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(@profiled_request_env)
|
237
|
-
assert_equal %q{attachment; filename="profile_data.pdf"}, headers['Content-Disposition']
|
238
|
-
end
|
239
|
-
|
240
|
-
end
|
241
|
-
|
242
|
-
should 'be able to call app multiple times' do
|
243
|
-
env = Rack::MockRequest.env_for('/', :params => 'profile=true×=3')
|
244
|
-
app = @app.clone
|
245
|
-
app.expects(:call).times(3)
|
246
|
-
Rack::PerftoolsProfiler.new(app, :default_printer => 'text').call(env)
|
247
|
-
end
|
248
|
-
|
249
|
-
should "allow 'printer' param override :default_printer option'" do
|
250
|
-
env = Rack::MockRequest.env_for('/', :params => 'profile=true&printer=gif')
|
251
|
-
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(env)
|
252
|
-
assert_equal 'image/gif', headers['Content-Type']
|
253
|
-
end
|
254
|
-
|
255
|
-
should 'give 400 if printer is invalid' do
|
256
|
-
env = Rack::MockRequest.env_for('/', :params => 'profile=true&printer=badprinter')
|
257
|
-
status, _, _ = Rack::PerftoolsProfiler.new(@app).call(env)
|
258
|
-
assert_equal 400, status
|
259
|
-
end
|
260
|
-
|
261
|
-
should 'send Rack environment to underlying application (minus special profiling GET params)' do
|
262
|
-
env = Rack::MockRequest.env_for('/', :params => 'profile=true×=1¶m=value&printer=gif&focus=foo&ignore=bar')
|
263
|
-
old_env = env.clone
|
264
|
-
expected_env = env.clone
|
265
|
-
expected_env["QUERY_STRING"] = 'param=value'
|
266
|
-
app = @app.clone
|
267
|
-
app.expects(:call).with(expected_env)
|
268
|
-
Rack::PerftoolsProfiler.new(app, :default_printer => 'gif').call(env)
|
269
|
-
assert_equal env, old_env
|
270
|
-
end
|
271
|
-
|
272
|
-
should "accept 'focus' param" do
|
273
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
274
|
-
custom_env = Rack::MockRequest.env_for('/method1', :params => 'profile=true&focus=method1')
|
275
|
-
status, headers, body = profiled_app.call(custom_env)
|
276
|
-
assert_no_match(/garbage/, RackResponseBody.new(body).to_s)
|
277
|
-
end
|
278
|
-
|
279
|
-
should "accept 'ignore' param" do
|
280
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
281
|
-
custom_env = Rack::MockRequest.env_for('/method1', :params => 'profile=true&ignore=method1')
|
282
|
-
status, headers, body = profiled_app.call(custom_env)
|
283
|
-
assert_match(/garbage/, RackResponseBody.new(body).to_s)
|
284
|
-
assert_no_match(/method1/, RackResponseBody.new(body).to_s)
|
285
|
-
end
|
286
|
-
|
287
|
-
end
|
288
|
-
|
289
|
-
context 'start/stop profiling' do
|
290
|
-
|
291
|
-
should "set CPUPROFILE_REALTIME to 1 if mode is 'walltime' " do
|
292
|
-
realtime = ENV['CPUPROFILE_REALTIME']
|
293
|
-
assert_nil realtime
|
294
|
-
app = lambda do |env|
|
295
|
-
realtime = ENV['CPUPROFILE_REALTIME']
|
296
|
-
[200, {}, ["hi"]]
|
297
|
-
end
|
298
|
-
profiled_app = Rack::PerftoolsProfiler.new(app, :mode => 'walltime')
|
299
|
-
profiled_app.call(@start_env)
|
300
|
-
profiled_app.call(@root_request_env)
|
301
|
-
profiled_app.call(@stop_env)
|
302
|
-
assert_equal '1', realtime
|
303
|
-
end
|
304
|
-
|
305
|
-
should 'alter CPUPROFILE_FREQUENCY if frequency is set' do
|
306
|
-
frequency = ENV['CPUPROFILE_FREQUENCY']
|
307
|
-
assert_nil frequency
|
308
|
-
app = lambda do |env|
|
309
|
-
frequency = ENV['CPUPROFILE_FREQUENCY']
|
310
|
-
[200, {}, ["hi"]]
|
311
|
-
end
|
312
|
-
profiled_app = Rack::PerftoolsProfiler.new(app, :frequency => 250)
|
313
|
-
profiled_app.call(@start_env)
|
314
|
-
profiled_app.call(@root_request_env)
|
315
|
-
assert_equal '250', frequency
|
316
|
-
end
|
317
|
-
|
318
|
-
context 'when profiling is on' do
|
319
|
-
|
320
|
-
should 'not provide profiling data when __data__ is called' do
|
321
|
-
Rack::PerftoolsProfiler.clear_data
|
322
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(@app, :default_printer => 'text')
|
323
|
-
profiled_app.call(@start_env)
|
324
|
-
profiled_app.call(@root_request_env)
|
325
|
-
status, _, body = profiled_app.call(@data_env)
|
326
|
-
assert_equal 400, status
|
327
|
-
assert_match(/No profiling data available./, RackResponseBody.new(body).to_s)
|
328
|
-
end
|
329
|
-
|
330
|
-
should 'pass on profiling params in environment' do
|
331
|
-
env = Rack::MockRequest.env_for('/', :params => 'times=2')
|
332
|
-
old_env = env.clone
|
333
|
-
app = @app.clone
|
334
|
-
expected_env = env.clone
|
335
|
-
expected_env['rack.request.query_string'] = 'times=2'
|
336
|
-
expected_env['rack.request.query_hash'] = {'times' => '2'}
|
337
|
-
app.expects(:call).with(expected_env)
|
338
|
-
profiled_app = Rack::PerftoolsProfiler.new(app, :default_printer => 'text')
|
339
|
-
profiled_app.call(@start_env)
|
340
|
-
profiled_app.call(env)
|
341
|
-
assert_equal env, old_env
|
342
|
-
end
|
343
|
-
|
344
|
-
should 'pass on non-profiling params in environment' do
|
345
|
-
env = Rack::MockRequest.env_for('/', :params => 'param=value')
|
346
|
-
old_env = env.clone
|
347
|
-
app = @app.clone
|
348
|
-
expected_env = env.clone
|
349
|
-
expected_env['rack.request.query_string'] = 'param=value'
|
350
|
-
expected_env['rack.request.query_hash'] = {'param' => 'value'}
|
351
|
-
app.expects(:call).with(expected_env)
|
352
|
-
profiled_app = Rack::PerftoolsProfiler.new(app, :default_printer => 'text')
|
353
|
-
profiled_app.call(@start_env)
|
354
|
-
profiled_app.call(env)
|
355
|
-
assert_equal env, old_env
|
356
|
-
end
|
357
|
-
|
358
|
-
should 'not alter regular calls' do
|
359
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(@app, :default_printer => 'gif')
|
360
|
-
profiled_app.call(@start_env)
|
361
|
-
status, headers, body = profiled_app.call(@root_request_env)
|
362
|
-
assert_equal 200, status
|
363
|
-
assert_equal 'text/plain', headers['Content-Type']
|
364
|
-
assert_equal 'Oh hai der', RackResponseBody.new(body).to_s
|
365
|
-
end
|
366
|
-
|
367
|
-
end
|
368
|
-
|
369
|
-
context 'after profiling is finished' do
|
370
|
-
|
371
|
-
should 'return profiling data when __data__ is called' do
|
372
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(@app, :default_printer => 'gif')
|
373
|
-
profiled_app.call(@start_env)
|
374
|
-
profiled_app.call(@root_request_env)
|
375
|
-
profiled_app.call(@stop_env)
|
376
|
-
status, headers, body = profiled_app.call(@data_env)
|
377
|
-
assert_equal 200, status
|
378
|
-
assert_equal "image/gif", headers['Content-Type']
|
379
|
-
end
|
380
|
-
|
381
|
-
end
|
382
|
-
|
383
|
-
should 'keeps data from multiple calls' do
|
384
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
385
|
-
profiled_app.call(@start_env)
|
386
|
-
profiled_app.call(Rack::MockRequest.env_for('/method1'))
|
387
|
-
profiled_app.call(Rack::MockRequest.env_for('/method2'))
|
388
|
-
profiled_app.call(@stop_env)
|
389
|
-
status, headers, body = profiled_app.call(@data_env)
|
390
|
-
assert_match(/method1/, RackResponseBody.new(body).to_s)
|
391
|
-
assert_match(/method2/, RackResponseBody.new(body).to_s)
|
392
|
-
end
|
393
|
-
|
394
|
-
should "allow 'printer' param to override :default_printer option'" do
|
395
|
-
profiled_app = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf')
|
396
|
-
profiled_app.call(@start_env)
|
397
|
-
profiled_app.call(@root_request_env)
|
398
|
-
profiled_app.call(@stop_env)
|
399
|
-
custom_data_env = Rack::MockRequest.env_for('__data__', :params => 'printer=gif')
|
400
|
-
_, headers, _ = profiled_app.call(custom_data_env)
|
401
|
-
assert_equal 'image/gif', headers['Content-Type']
|
402
|
-
end
|
403
|
-
|
404
|
-
should "accept 'focus' param" do
|
405
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
406
|
-
profiled_app.call(@start_env)
|
407
|
-
profiled_app.call(Rack::MockRequest.env_for('/method1'))
|
408
|
-
profiled_app.call(Rack::MockRequest.env_for('/method2'))
|
409
|
-
profiled_app.call(@stop_env)
|
410
|
-
custom_data_env = Rack::MockRequest.env_for('__data__', :params => 'focus=method1')
|
411
|
-
status, headers, body = profiled_app.call(custom_data_env)
|
412
|
-
assert_no_match(/method2/, RackResponseBody.new(body).to_s)
|
413
|
-
end
|
414
|
-
|
415
|
-
should "accept 'ignore' param" do
|
416
|
-
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
417
|
-
profiled_app.call(@start_env)
|
418
|
-
profiled_app.call(Rack::MockRequest.env_for('/method1'))
|
419
|
-
profiled_app.call(Rack::MockRequest.env_for('/method2'))
|
420
|
-
profiled_app.call(@stop_env)
|
421
|
-
custom_data_env = Rack::MockRequest.env_for('__data__', :params => 'ignore=method1')
|
422
|
-
status, headers, body = profiled_app.call(custom_data_env)
|
423
|
-
assert_no_match(/method1/, RackResponseBody.new(body).to_s)
|
424
|
-
end
|
425
|
-
|
426
|
-
end
|
427
|
-
|
428
|
-
context "when in bundler mode" do
|
429
|
-
|
430
|
-
should "call pprof.rb using 'bundle' command if bundler is set" do
|
431
|
-
status = stub_everything(:exitstatus => 0)
|
432
|
-
profiled_app = Rack::PerftoolsProfiler.new(@app, :bundler => true)
|
433
|
-
Open4.expects(:popen4).with(regexp_matches(/^bundle exec pprof\.rb/)).returns(status)
|
434
|
-
profiled_app.call(@profiled_request_env)
|
435
|
-
end
|
436
|
-
|
437
|
-
should "change directory into the current directory if custom Gemfile dir is not provided" do
|
438
|
-
profiled_app = Rack::PerftoolsProfiler.new(@app, :bundler => true, :gemfile_dir => 'bundler')
|
439
|
-
Dir.expects(:chdir).with('bundler').returns(["","",0])
|
440
|
-
profiled_app.call(@profiled_request_env)
|
441
|
-
end
|
442
|
-
|
443
|
-
should "change directory into custom Gemfile dir if provided" do
|
444
|
-
profiled_app = Rack::PerftoolsProfiler.new(@app, :bundler => true)
|
445
|
-
Dir.expects(:chdir).with('.').returns(["","",0])
|
446
|
-
profiled_app.call(@profiled_request_env)
|
447
|
-
end
|
448
|
-
|
449
|
-
end
|
450
|
-
|
451
96
|
end
|
452
97
|
|
453
98
|
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SingleRequestProfilingTest < Test::Unit::TestCase
|
4
|
+
include Rack::PerftoolsProfiler
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@app = lambda { |env| ITERATIONS.times {1+2+3+4+5}; [200, {'Content-Type' => 'text/plain'}, ['Oh hai der']] }
|
8
|
+
@slow_app = lambda { |env| ITERATIONS.times {1+2+3+4+5}; [200, {'Content-Type' => 'text/plain'}, ['slow app']] }
|
9
|
+
@root_request_env = Rack::MockRequest.env_for("/")
|
10
|
+
@profiled_request_env = Rack::MockRequest.env_for("/", :params => "profile=true")
|
11
|
+
@profiled_request_env_with_times = Rack::MockRequest.env_for("/", :params => "profile=true×=2")
|
12
|
+
end
|
13
|
+
|
14
|
+
context "(common behavior)" do
|
15
|
+
|
16
|
+
should 'default to text printer' do
|
17
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@app).call(@profiled_request_env)
|
18
|
+
assert_equal "text/plain", headers['Content-Type']
|
19
|
+
end
|
20
|
+
|
21
|
+
should "set CPUPROFILE_REALTIME to 1 if mode is 'walltime'" do
|
22
|
+
realtime = ENV['CPUPROFILE_REALTIME']
|
23
|
+
assert_nil realtime
|
24
|
+
app = lambda do |env|
|
25
|
+
realtime = ENV['CPUPROFILE_REALTIME']
|
26
|
+
[200, {}, ["hi"]]
|
27
|
+
end
|
28
|
+
Rack::PerftoolsProfiler.new(app, :mode => 'walltime').call(@profiled_request_env)
|
29
|
+
assert_equal '1', realtime
|
30
|
+
end
|
31
|
+
|
32
|
+
should "set CPUPROFILE_OBJECTS to 1 if mode is 'objects'" do
|
33
|
+
objects = ENV['CPUPROFILE_OBJECTS']
|
34
|
+
assert_nil objects
|
35
|
+
app = lambda do |env|
|
36
|
+
objects = ENV['CPUPROFILE_OBJECTS']
|
37
|
+
[200, {}, ["hi"]]
|
38
|
+
end
|
39
|
+
Rack::PerftoolsProfiler.new(app, :mode => 'objects').call(@profiled_request_env)
|
40
|
+
assert_equal '1', objects
|
41
|
+
end
|
42
|
+
|
43
|
+
should "set CPUPROFILE_METHODS to 1 if mode is 'methods'" do
|
44
|
+
methods = ENV['CPUPROFILE_METHODS']
|
45
|
+
assert_nil methods
|
46
|
+
app = lambda do |env|
|
47
|
+
methods = ENV['CPUPROFILE_METHODS']
|
48
|
+
[200, {}, ["hi"]]
|
49
|
+
end
|
50
|
+
status, headers, body = Rack::PerftoolsProfiler.new(app, :mode => 'methods').call(@profiled_request_env)
|
51
|
+
assert_equal '1', methods
|
52
|
+
end
|
53
|
+
|
54
|
+
should "not set CPUPROFILE_FREQUENCY by default" do
|
55
|
+
frequency = ENV['CPUPROFILE_FREQUENCY']
|
56
|
+
assert_nil frequency
|
57
|
+
app = lambda do |env|
|
58
|
+
frequency = ENV['CPUPROFILE_FREQUENCY']
|
59
|
+
[200, {}, ["hi"]]
|
60
|
+
end
|
61
|
+
Rack::PerftoolsProfiler.new(app).call(@profiled_request_env)
|
62
|
+
assert_nil frequency
|
63
|
+
end
|
64
|
+
|
65
|
+
should 'alter CPUPROFILE_FREQUENCY if frequency is set' do
|
66
|
+
frequency = ENV['CPUPROFILE_FREQUENCY']
|
67
|
+
assert_nil frequency
|
68
|
+
app = lambda do |env|
|
69
|
+
frequency = ENV['CPUPROFILE_FREQUENCY']
|
70
|
+
[200, {}, ["hi"]]
|
71
|
+
end
|
72
|
+
Rack::PerftoolsProfiler.new(app, :frequency => 500).call(@profiled_request_env)
|
73
|
+
assert_equal '500', frequency
|
74
|
+
end
|
75
|
+
|
76
|
+
should "allow 'printer' param override :default_printer option'" do
|
77
|
+
env = Rack::MockRequest.env_for('/', :params => 'profile=true&printer=gif')
|
78
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(env)
|
79
|
+
assert_equal 'image/gif', headers['Content-Type']
|
80
|
+
end
|
81
|
+
|
82
|
+
should 'give 400 if printer is invalid' do
|
83
|
+
env = Rack::MockRequest.env_for('/', :params => 'profile=true&printer=badprinter')
|
84
|
+
status, _, _ = Rack::PerftoolsProfiler.new(@app).call(env)
|
85
|
+
assert_equal 400, status
|
86
|
+
end
|
87
|
+
|
88
|
+
should "accept 'focus' param" do
|
89
|
+
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
90
|
+
custom_env = Rack::MockRequest.env_for('/method1', :params => 'profile=true&focus=method1')
|
91
|
+
status, headers, body = profiled_app.call(custom_env)
|
92
|
+
assert_no_match(/garbage/, RackResponseBody.new(body).to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
should "accept 'ignore' param" do
|
96
|
+
profiled_app = Rack::PerftoolsProfiler.with_profiling_off(TestApp.new, :default_printer => 'text', :mode => 'walltime')
|
97
|
+
custom_env = Rack::MockRequest.env_for('/method1', :params => 'profile=true&ignore=method1')
|
98
|
+
status, headers, body = profiled_app.call(custom_env)
|
99
|
+
assert_match(/garbage/, RackResponseBody.new(body).to_s)
|
100
|
+
assert_no_match(/method1/, RackResponseBody.new(body).to_s)
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when in bundler mode" do
|
104
|
+
|
105
|
+
should "call pprof.rb using 'bundle' command if bundler is set" do
|
106
|
+
status = stub_everything(:exitstatus => 0)
|
107
|
+
profiled_app = Rack::PerftoolsProfiler.new(@app, :bundler => true)
|
108
|
+
Open4.expects(:popen4).with(regexp_matches(/^bundle exec pprof\.rb/)).returns(status)
|
109
|
+
profiled_app.call(@profiled_request_env)
|
110
|
+
end
|
111
|
+
|
112
|
+
should "change directory into the current directory if custom Gemfile dir is not provided" do
|
113
|
+
profiled_app = Rack::PerftoolsProfiler.new(@app, :bundler => true, :gemfile_dir => 'bundler')
|
114
|
+
Dir.expects(:chdir).with('bundler').returns(["","",0])
|
115
|
+
profiled_app.call(@profiled_request_env)
|
116
|
+
end
|
117
|
+
|
118
|
+
should "change directory into custom Gemfile dir if provided" do
|
119
|
+
profiled_app = Rack::PerftoolsProfiler.new(@app, :bundler => true)
|
120
|
+
Dir.expects(:chdir).with('.').returns(["","",0])
|
121
|
+
profiled_app.call(@profiled_request_env)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when overriding profiling mode" do
|
127
|
+
|
128
|
+
should "default to configured mode if mode is empty string" do
|
129
|
+
realtime = ENV['CPUPROFILE_REALTIME']
|
130
|
+
assert_nil realtime
|
131
|
+
app = lambda do |env|
|
132
|
+
realtime = ENV['CPUPROFILE_REALTIME']
|
133
|
+
[200, {}, ["hi"]]
|
134
|
+
end
|
135
|
+
request = Rack::MockRequest.env_for("/", :params => 'profile=true&mode=')
|
136
|
+
Rack::PerftoolsProfiler.new(app, :mode => :walltime).call(request)
|
137
|
+
assert_equal '1', realtime
|
138
|
+
end
|
139
|
+
|
140
|
+
should "set CPUPROFILE_OBJECTS to 1 if mode is 'objects'" do
|
141
|
+
objects = ENV['CPUPROFILE_OBJECTS']
|
142
|
+
assert_nil objects
|
143
|
+
app = lambda do |env|
|
144
|
+
objects = ENV['CPUPROFILE_OBJECTS']
|
145
|
+
[200, {}, ["hi"]]
|
146
|
+
end
|
147
|
+
request = Rack::MockRequest.env_for("/", :params => 'profile=true&mode=objects')
|
148
|
+
Rack::PerftoolsProfiler.new(app, :mode => :cputime).call(request)
|
149
|
+
assert_equal '1', objects
|
150
|
+
end
|
151
|
+
|
152
|
+
should "set CPUPROFILE_METHODS to 1 if mode is 'methods'" do
|
153
|
+
methods = ENV['CPUPROFILE_METHODS']
|
154
|
+
assert_nil methods
|
155
|
+
app = lambda do |env|
|
156
|
+
methods = ENV['CPUPROFILE_METHODS']
|
157
|
+
[200, {}, ["hi"]]
|
158
|
+
end
|
159
|
+
request = Rack::MockRequest.env_for("/", :params => 'profile=true&mode=methods')
|
160
|
+
Rack::PerftoolsProfiler.new(app, :mode => :cputime).call(request)
|
161
|
+
assert_equal '1', methods
|
162
|
+
end
|
163
|
+
|
164
|
+
should "return to default mode if no mode is specified" do
|
165
|
+
objects = ENV['CPUPROFILE_OBJECTS']
|
166
|
+
assert_nil objects
|
167
|
+
app = lambda do |env|
|
168
|
+
objects = ENV['CPUPROFILE_OBJECTS']
|
169
|
+
[200, {}, ["hi"]]
|
170
|
+
end
|
171
|
+
|
172
|
+
request = Rack::MockRequest.env_for("/", :params => 'profile=true&mode=objects')
|
173
|
+
rack_profiler = Rack::PerftoolsProfiler.new(app, :mode => :cputime)
|
174
|
+
rack_profiler.call(request)
|
175
|
+
rack_profiler.call(@profiled_request_env)
|
176
|
+
assert_nil objects
|
177
|
+
end
|
178
|
+
|
179
|
+
should "return error message if mode is unrecognized" do
|
180
|
+
profiled_app = Rack::PerftoolsProfiler.new(@app)
|
181
|
+
mode = "foobar"
|
182
|
+
request = Rack::MockRequest.env_for("/", :params => "profile=true&mode=#{mode}")
|
183
|
+
status, _, body = profiled_app.call(request)
|
184
|
+
assert_equal 400, status
|
185
|
+
assert_match(/Cannot change mode to '#{mode}'.\nPer-request mode changes are only available for the following modes: 'methods', 'objects'/,
|
186
|
+
RackResponseBody.new(body).to_s)
|
187
|
+
end
|
188
|
+
|
189
|
+
should "return error message if mode is 'walltime'" do
|
190
|
+
profiled_app = Rack::PerftoolsProfiler.new(@app)
|
191
|
+
mode = "walltime"
|
192
|
+
request = Rack::MockRequest.env_for("/", :params => "profile=true&mode=#{mode}")
|
193
|
+
status, _, body = profiled_app.call(request)
|
194
|
+
assert_equal 400, status
|
195
|
+
assert_match(/Cannot change mode to '#{mode}'.\nPer-request mode changes are only available for the following modes: 'methods', 'objects'/,
|
196
|
+
RackResponseBody.new(body).to_s)
|
197
|
+
end
|
198
|
+
|
199
|
+
should "return error message if mode is 'cputime'" do
|
200
|
+
profiled_app = Rack::PerftoolsProfiler.new(@app)
|
201
|
+
mode = "cputime"
|
202
|
+
request = Rack::MockRequest.env_for("/", :params => "profile=true&mode=#{mode}")
|
203
|
+
status, _, body = profiled_app.call(request)
|
204
|
+
assert_equal 400, status
|
205
|
+
assert_match(/Cannot change mode to '#{mode}'.\nPer-request mode changes are only available for the following modes: 'methods', 'objects'/,
|
206
|
+
RackResponseBody.new(body).to_s)
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
context 'when using the text printer' do
|
214
|
+
|
215
|
+
should 'return profiling data' do
|
216
|
+
_, _, body = Rack::PerftoolsProfiler.new(@slow_app, :default_printer => 'text').call(@profiled_request_env)
|
217
|
+
assert_match(/Total: \d+ samples/, RackResponseBody.new(body).to_s)
|
218
|
+
end
|
219
|
+
|
220
|
+
should 'have Content-Type text/plain' do
|
221
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'text').call(@profiled_request_env)
|
222
|
+
assert_equal "text/plain", headers['Content-Type']
|
223
|
+
end
|
224
|
+
|
225
|
+
should 'have Content-Length' do
|
226
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@slow_app, :default_printer => 'text').call(@profiled_request_env)
|
227
|
+
assert (headers.fetch('Content-Length').to_i > 500)
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'when using the gif printer' do
|
233
|
+
|
234
|
+
should 'gif printer has Content-Type image/gif' do
|
235
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'gif').call(@profiled_request_env)
|
236
|
+
assert_equal "image/gif", headers['Content-Type']
|
237
|
+
end
|
238
|
+
|
239
|
+
should 'gif printer has Content-Length' do
|
240
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@slow_app, :default_printer => 'gif').call(@profiled_request_env)
|
241
|
+
assert headers.fetch('Content-Length').to_i > 25_000
|
242
|
+
end
|
243
|
+
|
244
|
+
should 'pdf printer has Content-Type application/pdf' do
|
245
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(@profiled_request_env)
|
246
|
+
assert_equal "application/pdf", headers['Content-Type']
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'when using the pdf printer' do
|
252
|
+
|
253
|
+
should 'have default filename' do
|
254
|
+
_, headers, _ = Rack::PerftoolsProfiler.new(@app, :default_printer => 'pdf').call(@profiled_request_env)
|
255
|
+
assert_equal %q{attachment; filename="profile_data.pdf"}, headers['Content-Disposition']
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
should 'be able to call app multiple times' do
|
261
|
+
env = Rack::MockRequest.env_for('/', :params => 'profile=true×=3')
|
262
|
+
app = @app.clone
|
263
|
+
app.expects(:call).times(3)
|
264
|
+
Rack::PerftoolsProfiler.new(app, :default_printer => 'text').call(env)
|
265
|
+
end
|
266
|
+
|
267
|
+
should 'send Rack environment to underlying application (minus special profiling GET params)' do
|
268
|
+
env = Rack::MockRequest.env_for('/', :params => 'profile=true×=1¶m=value&printer=gif&focus=foo&ignore=bar')
|
269
|
+
old_env = env.clone
|
270
|
+
expected_env = env.clone
|
271
|
+
expected_env["QUERY_STRING"] = 'param=value'
|
272
|
+
app = @app.clone
|
273
|
+
app.expects(:call).with(expected_env)
|
274
|
+
Rack::PerftoolsProfiler.new(app, :default_printer => 'gif').call(env)
|
275
|
+
assert_equal env, old_env
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|