rack-perftools_profiler 0.3.0 → 0.4.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/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
|