sippy_cup 0.2.3 → 0.3.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,103 +1,457 @@
1
- require 'sippy_cup/runner'
1
+ require 'spec_helper'
2
2
 
3
3
  describe SippyCup::Runner do
4
+ before do
5
+ Dir.chdir "/tmp"
6
+ end
7
+
8
+ let(:settings) { {} }
9
+ let(:default_settings) { { logger: logger } }
10
+ let(:command) { "sudo sipp -i 127.0.0.1" }
11
+ let(:pid) { '1234' }
12
+
13
+ let(:logger) { double }
14
+
15
+ before { logger.stub :info }
16
+
17
+ let(:manifest) do
18
+ <<-MANIFEST
19
+ name: foobar
20
+ source: 'doo@dah.com'
21
+ destination: 'foo@bar.com'
22
+ max_concurrent: 5
23
+ calls_per_second: 2
24
+ number_of_calls: 10
25
+ steps:
26
+ - invite
27
+ - wait_for_answer
28
+ - ack_answer
29
+ - sleep 3
30
+ - send_digits 'abc'
31
+ - sleep 5
32
+ - send_digits '#'
33
+ - wait_for_hangup
34
+ MANIFEST
35
+ end
36
+ let(:scenario) { SippyCup::Scenario.from_manifest manifest }
37
+
38
+ subject { SippyCup::Runner.new scenario, default_settings.merge(settings) }
39
+
40
+ def expect_command_execution(command = anything)
41
+ Process.stub :wait2
42
+ subject.stub :process_exit_status
43
+
44
+ subject.should_receive(:spawn).with(command, anything)
45
+ end
46
+
4
47
  describe '#run' do
48
+ it "executes the correct command to invoke SIPp" do
49
+ full_scenario_path = File.join(Dir.tmpdir, '/scenario.*')
50
+ expect_command_execution %r{sudo sipp -i doo@dah.com -p 8836 -sf #{full_scenario_path} -l 5 -m 10 -r 2 -s 1 foo@bar.com}
51
+ subject.run
52
+ end
53
+
54
+ it "ensures that input files are not left on the filesystem" do
55
+ FakeFS do
56
+ Dir.mkdir("/tmp") unless Dir.exist?("/tmp")
57
+ expect_command_execution.and_raise
58
+ expect { subject.run }.to raise_error
59
+ Dir.entries(Dir.tmpdir).should eql(['.', '..'])
60
+ end
61
+ end
62
+
5
63
  context "System call fails/doesn't fail" do
6
- let(:settings) { Hash.new }
7
- let(:command) { "sudo sipp -i 127.0.0.1" }
8
- let(:pid) { '1234' }
9
-
10
- subject { SippyCup::Runner.new settings }
11
- it 'should raise an error when the system call fails' do
12
- subject.logger.stub :info
13
- subject.should_receive(:prepare_command).and_return command
14
- subject.should_receive(:spawn).with(command).and_raise(Errno::ENOENT)
15
- Process.stub :wait
16
- lambda {subject.run}.should raise_error RuntimeError
17
- end
18
-
19
- it 'should not raise an error when the system call is successful' do
20
- subject.logger.stub :info
21
- subject.should_receive(:prepare_command).and_return command
22
- subject.should_receive(:spawn).with(command).and_return pid
23
- Process.stub :wait
24
- lambda {subject.run}.should_not raise_error
25
- end
26
- end
27
-
28
- context "specifying a stats file" do
29
- let(:settings) { { stats_file: 'stats.csv' } }
30
- let(:command) { "sudo sipp -i 127.0.0.1 -trace_stats -stf stats.csv" }
31
- let(:pid) { '1234' }
32
-
33
- subject { SippyCup::Runner.new settings }
34
- it 'should display the path to the csv file when one is specified' do
35
- subject.logger.should_receive(:info).twice
36
- subject.should_receive(:prepare_command).and_return command
37
- subject.should_receive(:spawn).with(command).and_return pid
38
- Process.stub :wait
39
- subject.logger.should_receive(:info).with "Statistics logged at #{File.expand_path settings[:stats_file]}"
64
+ it 'raises an error when the system call fails' do
65
+ expect_command_execution.and_raise(Errno::ENOENT)
66
+ expect { subject.run }.to raise_error Errno::ENOENT
67
+ end
68
+
69
+ it 'does not raise an error when the system call is successful' do
70
+ expect_command_execution
71
+ expect { subject.run }.not_to raise_error
72
+ end
73
+ end
74
+
75
+ context "specifying a source port in the manifest" do
76
+ let(:manifest) do
77
+ <<-MANIFEST
78
+ name: foobar
79
+ source: 'doo@dah.com'
80
+ destination: 'foo@bar.com'
81
+ max_concurrent: 5
82
+ calls_per_second: 2
83
+ number_of_calls: 10
84
+ source_port: 1234
85
+ steps:
86
+ - invite
87
+ - wait_for_answer
88
+ - ack_answer
89
+ - sleep 3
90
+ - send_digits 'abc'
91
+ - sleep 5
92
+ - send_digits '#'
93
+ - wait_for_hangup
94
+ MANIFEST
95
+ end
96
+
97
+ it 'should set the -p option' do
98
+ expect_command_execution(/-p 1234/)
99
+ subject.run
100
+ end
101
+ end
102
+
103
+ context "specifying a from_user in the Scenario" do
104
+ let(:manifest) do
105
+ <<-MANIFEST
106
+ name: foobar
107
+ source: 'doo@dah.com'
108
+ destination: 'foo@bar.com'
109
+ max_concurrent: 5
110
+ calls_per_second: 2
111
+ number_of_calls: 10
112
+ from_user: frank
113
+ steps:
114
+ - invite
115
+ - wait_for_answer
116
+ - ack_answer
117
+ - sleep 3
118
+ - send_digits 'abc'
119
+ - sleep 5
120
+ - send_digits '#'
121
+ - wait_for_hangup
122
+ MANIFEST
123
+ end
124
+
125
+ it 'should set the -s option' do
126
+ expect_command_execution(/-s frank/)
127
+ subject.run
128
+ end
129
+ end
130
+
131
+ context "specifying a media port" do
132
+ let(:manifest) do
133
+ <<-MANIFEST
134
+ name: foobar
135
+ source: 'doo@dah.com'
136
+ destination: 'foo@bar.com'
137
+ max_concurrent: 5
138
+ calls_per_second: 2
139
+ number_of_calls: 10
140
+ media_port: 6000
141
+ steps:
142
+ - invite
143
+ - wait_for_answer
144
+ - ack_answer
145
+ - sleep 3
146
+ - send_digits 'abc'
147
+ - sleep 5
148
+ - send_digits '#'
149
+ - wait_for_hangup
150
+ MANIFEST
151
+ end
152
+
153
+ it 'should set the -mp option' do
154
+ expect_command_execution(/-mp 6000/)
155
+ subject.run
156
+ end
157
+ end
158
+
159
+ context "specifying a stats file in the manifest" do
160
+ let(:manifest) do
161
+ <<-MANIFEST
162
+ name: foobar
163
+ source: 'doo@dah.com'
164
+ destination: 'foo@bar.com'
165
+ max_concurrent: 5
166
+ calls_per_second: 2
167
+ number_of_calls: 10
168
+ stats_file: stats.csv
169
+ steps:
170
+ - invite
171
+ - wait_for_answer
172
+ - ack_answer
173
+ - sleep 3
174
+ - send_digits 'abc'
175
+ - sleep 5
176
+ - send_digits '#'
177
+ - wait_for_hangup
178
+ MANIFEST
179
+ end
180
+
181
+ it 'should turn on -trace_stats, set the -stf option to the filename provided, and set the stats interval to 1 second' do
182
+ expect_command_execution(/-trace_stat -stf stats.csv -fd 1/)
183
+ subject.run
184
+ end
185
+
186
+ context 'with a stats interval provided' do
187
+ let(:manifest) do
188
+ <<-MANIFEST
189
+ name: foobar
190
+ source: 'doo@dah.com'
191
+ destination: 'foo@bar.com'
192
+ max_concurrent: 5
193
+ calls_per_second: 2
194
+ number_of_calls: 10
195
+ stats_file: stats.csv
196
+ stats_interval: 3
197
+ steps:
198
+ - invite
199
+ - wait_for_answer
200
+ - ack_answer
201
+ - sleep 3
202
+ - send_digits 'abc'
203
+ - sleep 5
204
+ - send_digits '#'
205
+ - wait_for_hangup
206
+ MANIFEST
207
+ end
208
+
209
+ it "passes the interval to the -fd option" do
210
+ expect_command_execution(/-fd 3/)
211
+ subject.run
212
+ end
213
+ end
214
+
215
+ it 'logs the path to the csv file' do
216
+ expect_command_execution
217
+ logger.should_receive(:info).with "Statistics logged at #{File.expand_path('stats.csv')}"
40
218
  subject.run
41
219
  end
42
220
  end
43
221
 
44
222
  context "no stats file" do
45
- let(:settings) { Hash.new }
46
- let(:command) { "sudo sipp -i 127.0.0.1" }
47
- let(:pid) { '1234' }
48
-
49
- subject { SippyCup::Runner.new settings }
50
- it 'should not display a csv file path if none is specified' do
51
- subject.logger.should_receive(:info).ordered.with(/Preparing to run SIPp command/)
52
- subject.logger.should_receive(:info).ordered.with(/Test completed successfully/)
53
- subject.should_receive(:prepare_command).and_return command
54
- subject.should_receive(:spawn).with(command).and_return pid
55
- Process.stub :wait
223
+ it 'does not log a statistics file path' do
224
+ logger.should_receive(:info).with(/Statistics logged at/).never
225
+ expect_command_execution
226
+ subject.run
227
+ end
228
+ end
229
+
230
+ context "specifying a variables file" do
231
+ let(:manifest) do
232
+ <<-MANIFEST
233
+ name: foobar
234
+ source: 'doo@dah.com'
235
+ destination: 'foo@bar.com'
236
+ max_concurrent: 5
237
+ calls_per_second: 2
238
+ number_of_calls: 10
239
+ scenario_variables: /path/to/vars.csv
240
+ steps:
241
+ - invite
242
+ - wait_for_answer
243
+ - ack_answer
244
+ - sleep 3
245
+ - send_digits 'abc'
246
+ - sleep 5
247
+ - send_digits '#'
248
+ - wait_for_hangup
249
+ MANIFEST
250
+ end
251
+
252
+ it 'uses CSV in the test run' do
253
+ logger.should_receive(:info).ordered.with(/Preparing to run SIPp command/)
254
+ logger.should_receive(:info).ordered.with(/Test completed successfully/)
255
+ expect_command_execution(%r{-inf /path/to/vars.csv})
56
256
  subject.run
57
257
  end
58
258
  end
59
259
 
60
- context "CSV file" do
61
- let(:settings) { {scenario_variables: "/path/to/csv", scenario: "/path/to/scenario", source: "127.0.0.1",
62
- destination: "127.0.0.1", max_concurrent: 5, calls_per_second: 5,
63
- number_of_calls: 5} }
64
- let(:pid) { "1234" }
260
+ context 'with a transport mode specified' do
261
+ let(:manifest) do
262
+ <<-MANIFEST
263
+ name: foobar
264
+ source: 'doo@dah.com'
265
+ destination: 'foo@bar.com'
266
+ max_concurrent: 5
267
+ calls_per_second: 2
268
+ number_of_calls: 10
269
+ transport_mode: t1
270
+ steps:
271
+ - invite
272
+ - wait_for_answer
273
+ - ack_answer
274
+ - sleep 3
275
+ - send_digits 'abc'
276
+ - sleep 5
277
+ - send_digits '#'
278
+ - wait_for_hangup
279
+ MANIFEST
280
+ end
65
281
 
66
- subject { SippyCup::Runner.new settings }
67
- it 'should use CSV into the test run' do
68
- subject.logger.should_receive(:info).ordered.with(/Preparing to run SIPp command/)
69
- subject.logger.should_receive(:info).ordered.with(/Test completed successfully/)
70
- subject.should_receive(:spawn).with(/\-inf \/path\/to\/csv/)
71
- Process.stub :wait
282
+ it "passes the transport mode to the -t option" do
283
+ expect_command_execution(/-t t1/)
72
284
  subject.run
73
285
  end
74
286
  end
75
287
 
288
+ describe 'SIPp exit status handling' do
289
+ let(:error_string) { "Some error" }
290
+ let(:exit_code) { 255 }
291
+ let(:command) { "sh -c 'echo \"#{error_string}\" 1>&2; exit #{exit_code}'" }
292
+
293
+ let(:settings) { { command: command } }
294
+
295
+ context "with normal operation" do
296
+ let(:exit_code) { 0 }
297
+
298
+ it "doesn't raise anything if SIPp returns 0" do
299
+ quietly do
300
+ subject.run.should be_true
301
+ end
302
+ end
303
+ end
304
+
305
+ context "with at least one call failure" do
306
+ let(:exit_code) { 1 }
307
+
308
+ it "returns false if SIPp returns 1" do
309
+ quietly do
310
+ logger.should_receive(:info).ordered.with(/Test completed successfully but some calls failed./)
311
+ subject.run.should be_false
312
+ end
313
+ end
314
+ end
315
+
316
+ context "with an exit from inside SIPp" do
317
+ let(:exit_code) { 97 }
318
+
319
+ it "raises a ExitOnInternalCommand error if SIPp returns 97" do
320
+ quietly do
321
+ expect { subject.run }.to raise_error SippyCup::ExitOnInternalCommand, error_string
322
+ end
323
+ end
324
+ end
325
+
326
+ context "with no calls processed" do
327
+ let(:exit_code) { 99 }
328
+
329
+ it "raises a NoCallsProcessed error if SIPp returns 99" do
330
+ quietly do
331
+ expect { subject.run }.to raise_error SippyCup::NoCallsProcessed, error_string
332
+ end
333
+ end
334
+ end
335
+
336
+ context "with a fatal error" do
337
+ let(:exit_code) { 255 }
338
+
339
+ it "raises a FatalError error if SIPp returns 255" do
340
+ quietly do
341
+ expect { subject.run }.to raise_error SippyCup::FatalError, error_string
342
+ end
343
+ end
344
+ end
345
+
346
+ context "with a socket binding fatal error" do
347
+ let(:exit_code) { 254 }
348
+
349
+ it "raises a FatalSocketBindingError error if SIPp returns 254" do
350
+ quietly do
351
+ expect { subject.run }.to raise_error SippyCup::FatalSocketBindingError, error_string
352
+ end
353
+ end
354
+ end
355
+
356
+ context "with a generic undocumented fatal error" do
357
+ let(:exit_code) { 128 }
358
+
359
+ it "raises a SippGenericError error if SIPp returns 255" do
360
+ quietly do
361
+ expect { subject.run }.to raise_error SippyCup::SippGenericError, error_string
362
+ end
363
+ end
364
+
365
+ it "raises a SippGenericError error with the appropriate message" do
366
+ quietly do
367
+ expect { subject.run }.to raise_error SippyCup::SippGenericError, error_string
368
+ end
369
+ end
370
+ end
371
+ end
372
+
373
+ describe "SIPp stdout/stderr" do
374
+ let(:output_string) { "Some output" }
375
+ let(:error_string) { "Some error" }
376
+ let(:command) { "sh -c 'echo \"#{output_string}\"' && sh -c 'echo \"#{error_string}\" 1>&2'" }
377
+
378
+ let(:settings) { { command: command } }
379
+
380
+ def active_thread_count
381
+ Thread.list.select { |t| t.status != 'aborting' }.size
382
+ end
383
+
384
+ context "by default" do
385
+ it "proxies stdout to the terminal" do
386
+ quietly do
387
+ capture(:stdout) { subject.run }.strip.should == output_string
388
+ end
389
+ end
390
+
391
+ it "proxies stderr to the terminal" do
392
+ quietly do
393
+ capture(:stderr) { subject.run }.strip.should == error_string
394
+ end
395
+ end
396
+
397
+ it "does not leak threads" do
398
+ original_thread_count = active_thread_count
399
+ quietly do
400
+ subject.run
401
+ end
402
+ sleep 0.1
403
+ active_thread_count.should == original_thread_count
404
+ end
405
+ end
406
+
407
+ context "with :full_sipp_output disabled" do
408
+ let(:settings) { { command: command, full_sipp_output: false } }
409
+
410
+ it "swallows stdout from SIPp" do
411
+ capture(:stdout) { subject.run }.should == ''
412
+ end
413
+
414
+ it "swallows stderr from SIPp" do
415
+ capture(:stderr) { subject.run }.should == ''
416
+ end
417
+
418
+ it "does not leak threads" do
419
+ quietly do
420
+ original_thread_count = active_thread_count
421
+ subject.run
422
+ sleep 0.1
423
+ active_thread_count.should == original_thread_count
424
+ end
425
+ end
426
+ end
427
+ end
76
428
  end
77
429
 
78
430
  describe '#stop' do
79
- let(:settings) { Hash.new }
80
- let(:command) { "sudo sipp -i 127.0.0.1" }
81
- let(:pid) { '1234' }
431
+ before { subject.sipp_pid = pid }
82
432
 
83
- subject { SippyCup::Runner.new settings }
84
-
85
- it "should try to kill the SIPp process if there is a PID" do
86
- subject.sipp_pid = pid
433
+ it "tries to kill the SIPp process if there is a PID" do
87
434
  Process.should_receive(:kill).with("KILL", pid)
88
435
  subject.stop
89
436
  end
90
437
 
91
- it "should not try to kill the SIPp process if there is a PID" do
92
- subject.sipp_pid = nil
93
- Process.should_receive(:kill).never
94
- subject.stop
438
+ context "if there is no PID available" do
439
+ let(:pid) { nil }
440
+
441
+ it "doesn't try to kill the SIPp process" do
442
+ Process.should_receive(:kill).never
443
+ subject.stop
444
+ end
95
445
  end
96
446
 
97
- it "should raise a RuntimeError if the PID does not exist" do
98
- subject.sipp_pid = pid
447
+ it "raises a Errno::ESRCH if the PID does not exist" do
99
448
  Process.should_receive(:kill).with("KILL", pid).and_raise(Errno::ESRCH)
100
- expect { subject.stop }.to raise_error RuntimeError
449
+ expect { subject.stop }.to raise_error Errno::ESRCH
450
+ end
451
+
452
+ it "raises a Errno::EPERM if the user has no permission to kill the process" do
453
+ Process.should_receive(:kill).with("KILL", pid).and_raise(Errno::EPERM)
454
+ expect { subject.stop }.to raise_error Errno::EPERM
101
455
  end
102
456
  end
103
457
  end