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.
@@ -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 'without profiling' do
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&times=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&times=1&param=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&times=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&times=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&times=1&param=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