sippy_cup 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 91c172b68ed8bdeec0588dbf52f2810c3e34a6ef
4
+ data.tar.gz: f426a399e082eb65b1e6f951ca33a0c3275a78aa
5
+ SHA512:
6
+ metadata.gz: 908e81ae00679f9067498bf35c9f1a8bd539a7847d910734b5df8c4a9be7cb8fee3e176ed91886f263cf6032628c2d269fe873a10dcfda3a679aa0a54bcd575f
7
+ data.tar.gz: d4cf2de02b7e39e190de47572b8b0ce81b87fca01b07c8cbaa578bad4591042f48e2154d20329fb7fdf0859cf321513b8197211b8e06d9821f7a93a7db9b3a8e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # [0.2.2](https://github.com/bklang/sippy_cup/compare/v0.2.1...v0.2.2)
2
+ * Feature: Added support for REGISTER messages
3
+ * Bugfix: Enable testing through proxies
4
+ * Several enhancements to the test runner, including ability to specify a data file and the RTCP port
5
+
1
6
  # [0.2.1](https://github.com/bklang/sippy_cup/compare/v0.2.0...v0.2.1)
2
7
  * Bugfix: fix the name of the compiled files
3
8
  * Bugfix: Fix SIPp arg to enable trace stats
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  ENV['SKIP_RCOV'] = 'true'
2
2
 
3
3
  group 'rspec' do
4
- guard 'rspec', :cli => '--format documentation' do
4
+ guard 'rspec', :cli => '--format documentation', :all_on_start => true, :all_after_pass => true do
5
5
  watch(%r{^spec/.+_spec\.rb$})
6
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
7
  watch('spec/spec_helper.rb') { "spec/" }
data/README.markdown CHANGED
@@ -35,6 +35,8 @@ SippyCup relies on the following to generate scenarios and the associated media
35
35
 
36
36
  If you do not have Ruby 1.9.3 available (check using `ruby --version`), we recommend installing Ruby with [RVM](http://rvm.io)
37
37
 
38
+ ### Install via gem (production)
39
+
38
40
  Once Ruby is installed, install SippyCup:
39
41
 
40
42
  ```
@@ -43,6 +45,24 @@ gem install sippy_cup
43
45
 
44
46
  Now you can start creating scenario files like in the examples below.
45
47
 
48
+ ### Install from repository (development)
49
+
50
+ You use `bundle` command (from the "[bundler](http://bundler.io/)" package) to install from the source directly. First, clone the repository into a working directory.
51
+
52
+ Install `bundle` via gem:
53
+
54
+ ```
55
+ gem install bundler --no-ri --no-rdoc
56
+ ```
57
+
58
+ Then build the `sippy_cup` application with `bundle`.
59
+
60
+ ```
61
+ bundle install
62
+ ```
63
+
64
+ Using `bundle` will then install the gem dependencies and allow you to run `sippy_cup` from your working directory.
65
+
46
66
  ## Examples
47
67
 
48
68
  ### Simple Example
@@ -62,7 +82,7 @@ steps:
62
82
  - send_digits '3125551234'
63
83
  - sleep 5
64
84
  - send_digits '#'
65
- - wait_for_bye
85
+ - wait_for_hangup
66
86
  ```
67
87
 
68
88
  Both `source` and `destination` above may be optionally supplied with a port number, eg. `192.0.2.200:5061`
@@ -92,7 +112,7 @@ scenario = SippyCup::Scenario.new 'Sippy Cup', source: '192.168.5.5:10001', dest
92
112
  s.sleep 5
93
113
  s.send_digits '#'
94
114
 
95
- s.wait_for_bye
115
+ s.wait_for_hangup
96
116
  end
97
117
 
98
118
  # Create the scenario XML and PCAP media. File will be named after the scenario name, in our case:
@@ -118,6 +138,7 @@ Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/re
118
138
 
119
139
  * `sleep <seconds>` Wait a specified number of seconds
120
140
  * `invite` Send a SIP INVITE to the specified target
141
+ * `register <username> [password]` Register the specified user to the target with an optional password
121
142
  * `receive_trying` Expect to receive a `100 Trying` response from the target
122
143
  * `receive_ringing` Expect to receive a `180 Ringing` response from the target
123
144
  * `receive_progress` Expect to receive a `183 Progress` response from the target
@@ -127,7 +148,7 @@ Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/re
127
148
  * `send_digits <string>` Send a DTMF string. May send one or many digits, including `0-9`, `*`, `#`, and `A-D`
128
149
  * `send_bye` Send a `BYE` (hangup request)
129
150
  * `receive_bye` Expect to receive a `BYE` from the target
130
- * `ack_bye` Send an `ACK` in response to a `BYE`
151
+ * `ack_bye` Send a `200 OK` response to a `BYE`
131
152
  * `wait_for_hangup` Convenient shortcut for `receive_bye; ack_bye`
132
153
 
133
154
  ### Alternate Output File Path
@@ -168,6 +189,12 @@ Each parameter has an impact on the test, and may either be changed once the YAM
168
189
 
169
190
  <dt>full_sipp_output</dt>
170
191
  <dd>By default, SippyCup will hide SIPp's command line output while running a scenario. Set this parameter to `true` to see full command line output</dd>
192
+
193
+ <dt>rtcp_port</dt>
194
+ <dd>By default, SIPp assigns RTCP ports dynamically. However, if there is a need for a static RTCP port (say, for data collection purposes), it can be done by supplying a port number here.</dd>
195
+
196
+ <dt>scenario_variables</dt>
197
+ <dd>If you're using sippy_cup to run a SIPp XML file, there may be CSV fields in the scenario ([field0], [field1], etc.). Specify a path to a CSV file containing the required information using this option. (File is semicolon delimeted, information can be found [here](http://sipp.sourceforge.net/doc/reference.html#inffile).)</dd>
171
198
  </dl>
172
199
 
173
200
  ### Additional SIPp Scenario Attributes
data/bin/sippy_cup CHANGED
@@ -10,7 +10,7 @@ def usage
10
10
  puts "--run, -r Run the scenario. If used without -c, must supply a previously"
11
11
  puts " compiled SippyCup file"
12
12
  puts "--help, -h Print this usage information"
13
- puts "--version, -v Print SippyCup version"
13
+ puts "--version, -V Print SippyCup version"
14
14
  end
15
15
 
16
16
  opts = GetoptLong.new(
@@ -34,6 +34,7 @@ opts.each do |opt, arg|
34
34
  usage
35
35
  exit 0
36
36
  when '--version'
37
+ require 'sippy_cup/version'
37
38
  puts "SippyCup version #{SippyCup::VERSION}"
38
39
  exit 0
39
40
  end
@@ -3,13 +3,20 @@ require 'active_support/core_ext/hash'
3
3
 
4
4
  module SippyCup
5
5
  class Runner
6
+ attr_accessor :sipp_pid
7
+ attr_accessor :logger
8
+
6
9
  def initialize(opts = {})
7
10
  @options = ActiveSupport::HashWithIndifferentAccess.new opts
11
+ @logger = @options[:logger] || Logger.new(STDOUT)
8
12
  end
9
13
 
10
14
  def compile
11
15
  raise ArgumentError, "Must provide scenario steps" unless @options[:steps]
12
- scenario = SippyCup::Scenario.new @options[:name].titleize, source: @options[:source], destination: @options[:destination]
16
+
17
+ scenario_opts = {source: @options[:source], destination: @options[:destination]}
18
+ scenario_opts[:filename] = @options[:filename] if @options[:filename]
19
+ scenario = SippyCup::Scenario.new @options[:name].titleize, scenario_opts
13
20
  @options[:steps].each do |step|
14
21
  instruction, arg = step.split ' ', 2
15
22
  if arg && !arg.empty?
@@ -30,26 +37,38 @@ module SippyCup
30
37
  command = "sudo sipp"
31
38
  source_port = @options[:source_port] || '8836'
32
39
  sip_user = @options[:sip_user] || '1'
33
- command << " -i #{@options[:source]} -p #{source_port} -sf #{File.expand_path @options[:scenario]}"
40
+ command << " -i #{@options[:source]} -p #{source_port} -sf #{File.expand_path @options[:scenario]}.xml"
34
41
  command << " -l #{@options[:max_concurrent]} -m #{@options[:number_of_calls]} -r #{@options[:calls_per_second]}"
35
42
  command << " -s #{sip_user}"
36
43
  if @options[:stats_file]
37
44
  stats_interval = @options[:stats_interval] || 1
38
45
  command << " -trace_stat -stf #{@options[:stats_file]} -fd #{stats_interval}"
39
46
  end
40
- command << "#{@options[:destination]}"
47
+ command << " -inf #{@options[:scenario_variables]}" if @options[:scenario_variables]
48
+ command << " #{@options[:destination]}"
41
49
  command << " > /dev/null 2>&1" unless @options[:full_sipp_output]
42
50
  command
43
51
  end
44
52
 
45
53
  def run
46
54
  command = prepare_command
47
- p "Preparing to run SIPp command: #{command}"
48
- result = system command
49
- raise "SIPp failed! Try running the scenario with the full_sipp_output enabled for more information" unless result
50
- p "Test completed successfully!"
51
- p "Statistics logged at #{File.expand_path @options[:stats_file]}" if @options[:stats_file]
55
+ @logger.info "Preparing to run SIPp command: #{command}"
56
+
57
+ begin
58
+ @sipp_pid = spawn command
59
+ Process.wait @sipp_pid
60
+ rescue Exception => e
61
+ raise RuntimeError, "Command #{command} failed"
62
+ end
63
+
64
+ @logger.info "Test completed successfully!"
65
+ @logger.info "Statistics logged at #{File.expand_path @options[:stats_file]}" if @options[:stats_file]
52
66
  end
53
67
 
68
+ def stop
69
+ Process.kill "KILL", @sipp_pid if @sipp_pid
70
+ rescue Exception => e
71
+ raise RuntimeError, "Killing #{@sipp_pid} failed"
72
+ end
54
73
  end
55
74
  end
@@ -3,6 +3,7 @@ require 'yaml'
3
3
 
4
4
  module SippyCup
5
5
  class Scenario
6
+ USER_AGENT = "SIPp/sippy_cup"
6
7
  VALID_DTMF = %w{0 1 2 3 4 5 6 7 8 9 0 * # A B C D}.freeze
7
8
  MSEC = 1_000
8
9
 
@@ -12,6 +13,7 @@ module SippyCup
12
13
  end
13
14
 
14
15
  parse_args args
16
+ @rtcp_port = args[:rtcp_port]
15
17
  @filename = args[:filename] || name.downcase.gsub(/\W+/, '_')
16
18
  @filename = File.expand_path @filename
17
19
  @doc = builder.doc
@@ -53,18 +55,20 @@ module SippyCup
53
55
 
54
56
  def invite(opts = {})
55
57
  opts[:retrans] ||= 500
58
+ rtp_string = @static_rtcp ? "m=audio #{@rtcp_port.to_i - 1} RTP/AVP 0 101\na=rtcp:#{@rtcp_port}\n" : "m=audio [media_port] RTP/AVP 0 101\n"
56
59
  # FIXME: The DTMF mapping (101) is hard-coded. It would be better if we could
57
60
  # get this from the DTMF payload generator
58
61
  msg = <<-INVITE
59
62
 
60
63
  INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
61
64
  Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
62
- From: sipp <sip:#{@from_user}@[local_ip]>;tag=[call_number]
65
+ From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
63
66
  To: <sip:[service]@[remote_ip]:[remote_port]>
64
67
  Call-ID: [call_id]
65
68
  CSeq: [cseq] INVITE
66
- Contact: sip:#{@from_user}@[local_ip]:[local_port]
69
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
67
70
  Max-Forwards: 100
71
+ User-Agent: #{USER_AGENT}
68
72
  Content-Type: application/sdp
69
73
  Content-Length: [len]
70
74
 
@@ -73,7 +77,7 @@ module SippyCup
73
77
  s=-
74
78
  c=IN IP[media_ip_type] [media_ip]
75
79
  t=0 0
76
- m=audio [media_port] RTP/AVP 0 101
80
+ #{rtp_string}
77
81
  a=rtpmap:0 PCMU/8000
78
82
  a=rtpmap:101 telephone-event/8000
79
83
  a=fmtp:101 0-15
@@ -82,6 +86,54 @@ module SippyCup
82
86
  @scenario << send
83
87
  end
84
88
 
89
+ def register(user, password = nil, opts = {})
90
+ opts[:retrans] ||= 500
91
+ user, domain = parse_user user
92
+ msg = register_message user, domain: domain
93
+ send = new_send msg, opts
94
+ @scenario << send
95
+ register_auth(user, password, domain: domain) if password
96
+ end
97
+
98
+ def register_message(user, opts = {})
99
+ <<-REGISTER
100
+
101
+ REGISTER sip:#{opts[:domain]} SIP/2.0
102
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
103
+ From: <sip:#{user}@#{opts[:domain]}>;tag=[call_number]
104
+ To: <sip:#{user}@#{opts[:domain]}>
105
+ Call-ID: [call_id]
106
+ CSeq: [cseq] REGISTER
107
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
108
+ Max-Forwards: 10
109
+ Expires: 120
110
+ User-Agent: #{USER_AGENT}
111
+ Content-Length: 0
112
+ REGISTER
113
+ end
114
+
115
+ def register_auth(user, password, opts = {})
116
+ opts[:retrans] ||= 500
117
+ @scenario << new_recv(response: '401', auth: true, optional: false)
118
+ msg = <<-AUTH
119
+
120
+ REGISTER sip:#{opts[:domain]} SIP/2.0
121
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
122
+ From: <sip:#{user}@#{opts[:domain]}>;tag=[call_number]
123
+ To: <sip:#{user}@#{opts[:domain]}>
124
+ Call-ID: [call_id]
125
+ CSeq: [cseq] REGISTER
126
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
127
+ Max-Forwards: 20
128
+ Expires: 3600
129
+ [authentication username=#{user} password=#{password}]
130
+ User-Agent: #{USER_AGENT}
131
+ Content-Length: 0
132
+ AUTH
133
+ send = new_send msg, opts
134
+ @scenario << send
135
+ end
136
+
85
137
  def receive_trying(opts = {})
86
138
  opts[:optional] = true if opts[:optional].nil?
87
139
  opts.merge! response: 100
@@ -108,6 +160,8 @@ module SippyCup
108
160
  recv = new_recv opts
109
161
  # Record Record Set: Make the Route headers available via [route] later
110
162
  recv['rrs'] = true
163
+ # Response Time Duration: Record the response time
164
+ recv['rtd'] = true
111
165
  @scenario << recv
112
166
  end
113
167
  alias :receive_200 :receive_answer
@@ -127,14 +181,15 @@ module SippyCup
127
181
 
128
182
  ACK [next_url] SIP/2.0
129
183
  Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
130
- From: <sip:#{@from_user}@[local_ip]>;tag=[call_number]
184
+ From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
131
185
  [last_To:]
132
- [routes]
133
186
  Call-ID: [call_id]
134
187
  CSeq: [cseq] ACK
135
- Contact: sip:#{@from_user}@[local_ip]:[local_port]
188
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
136
189
  Max-Forwards: 100
190
+ User-Agent: #{USER_AGENT}
137
191
  Content-Length: 0
192
+ [routes]
138
193
  ACK
139
194
  @scenario << new_send(msg, opts)
140
195
  start_media
@@ -167,15 +222,17 @@ module SippyCup
167
222
  def send_bye(opts = {})
168
223
  msg = <<-MSG
169
224
 
170
- BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
225
+ BYE [next_url] SIP/2.0
171
226
  [last_Via:]
172
- [last_From:]
227
+ From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
173
228
  [last_To:]
174
229
  [last_Call-ID]
175
230
  CSeq: [cseq] BYE
176
- Contact: <sip:[local_ip]:[local_port];transport=[transport]>
231
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
177
232
  Max-Forwards: 100
233
+ User-Agent: #{USER_AGENT}
178
234
  Content-Length: 0
235
+ [routes]
179
236
  MSG
180
237
  @scenario << new_send(msg, opts)
181
238
  end
@@ -200,12 +257,13 @@ module SippyCup
200
257
  [last_Via:]
201
258
  [last_From:]
202
259
  [last_To:]
203
- [routes]
204
260
  [last_Call-ID:]
205
261
  [last_CSeq:]
206
- Contact: <sip:[local_ip]:[local_port];transport=[transport]>
262
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
207
263
  Max-Forwards: 100
264
+ User-Agent: #{USER_AGENT}
208
265
  Content-Length: 0
266
+ [routes]
209
267
  ACK
210
268
  @scenario << new_send(msg, opts)
211
269
  end
@@ -226,6 +284,15 @@ module SippyCup
226
284
  puts "done."
227
285
  end
228
286
 
287
+ #TODO: SIPS support?
288
+ def parse_user(user)
289
+ user.slice! 0, 4 if user =~ /sip:/
290
+ user = user.split(":")[0]
291
+ user, domain = user.split("@")
292
+ domain ||= "[remote_ip]"
293
+ [user, domain]
294
+ end
295
+
229
296
  private
230
297
  def pause(msec)
231
298
  pause = Nokogiri::XML::Node.new 'pause', @doc
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module SippyCup
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.2'
5
5
  end
data/sippy_cup.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "https://github.com/bklang/sippy_cup"
12
12
  s.summary = "SIPp profile and RTP stream generator"
13
13
  s.description = "This tool makes it easier to generate SIPp load tests with DTMF interactions."
14
+ s.license = 'MIT'
14
15
 
15
16
  s.files = `git ls-files`.split("\n")
16
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -5,18 +5,22 @@ describe SippyCup::Runner do
5
5
  context "System call fails/doesn't fail" do
6
6
  let(:settings) { Hash.new }
7
7
  let(:command) { "sudo sipp -i 127.0.0.1" }
8
+ let(:pid) { '1234' }
9
+
8
10
  subject { SippyCup::Runner.new settings }
9
11
  it 'should raise an error when the system call fails' do
10
- subject.stub :p
12
+ subject.logger.stub :info
11
13
  subject.should_receive(:prepare_command).and_return command
12
- subject.should_receive(:system).with(command).and_return false
14
+ subject.should_receive(:spawn).with(command).and_raise(Errno::ENOENT)
15
+ Process.stub :wait
13
16
  lambda {subject.run}.should raise_error RuntimeError
14
17
  end
15
18
 
16
19
  it 'should not raise an error when the system call is successful' do
17
- subject.stub :p
20
+ subject.logger.stub :info
18
21
  subject.should_receive(:prepare_command).and_return command
19
- subject.should_receive(:system).with(command).and_return true
22
+ subject.should_receive(:spawn).with(command).and_return pid
23
+ Process.stub :wait
20
24
  lambda {subject.run}.should_not raise_error
21
25
  end
22
26
  end
@@ -24,12 +28,15 @@ describe SippyCup::Runner do
24
28
  context "specifying a stats file" do
25
29
  let(:settings) { { stats_file: 'stats.csv' } }
26
30
  let(:command) { "sudo sipp -i 127.0.0.1 -trace_stats -stf stats.csv" }
31
+ let(:pid) { '1234' }
32
+
27
33
  subject { SippyCup::Runner.new settings }
28
34
  it 'should display the path to the csv file when one is specified' do
29
- subject.should_receive(:p).twice
35
+ subject.logger.should_receive(:info).twice
30
36
  subject.should_receive(:prepare_command).and_return command
31
- subject.should_receive(:system).with(command).and_return true
32
- subject.should_receive(:p).with "Statistics logged at #{File.expand_path settings[:stats_file]}"
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]}"
33
40
  subject.run
34
41
  end
35
42
  end
@@ -37,15 +44,60 @@ describe SippyCup::Runner do
37
44
  context "no stats file" do
38
45
  let(:settings) { Hash.new }
39
46
  let(:command) { "sudo sipp -i 127.0.0.1" }
47
+ let(:pid) { '1234' }
48
+
40
49
  subject { SippyCup::Runner.new settings }
41
50
  it 'should not display a csv file path if none is specified' do
42
- subject.should_receive(:p).ordered.with(/Preparing to run SIPp command/)
43
- subject.should_receive(:p).ordered.with(/Test completed successfully/)
51
+ subject.logger.should_receive(:info).ordered.with(/Preparing to run SIPp command/)
52
+ subject.logger.should_receive(:info).ordered.with(/Test completed successfully/)
44
53
  subject.should_receive(:prepare_command).and_return command
45
- subject.should_receive(:system).with(command).and_return true
54
+ subject.should_receive(:spawn).with(command).and_return pid
55
+ Process.stub :wait
56
+ subject.run
57
+ end
58
+ end
59
+
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" }
65
+
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
46
72
  subject.run
47
73
  end
48
74
  end
49
75
 
50
76
  end
77
+
78
+ describe '#stop' do
79
+ let(:settings) { Hash.new }
80
+ let(:command) { "sudo sipp -i 127.0.0.1" }
81
+ let(:pid) { '1234' }
82
+
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
87
+ Process.should_receive(:kill).with("KILL", pid)
88
+ subject.stop
89
+ end
90
+
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
95
+ end
96
+
97
+ it "should raise a RuntimeError if the PID does not exist" do
98
+ subject.sipp_pid = pid
99
+ Process.should_receive(:kill).with("KILL", pid).and_raise(Errno::ESRCH)
100
+ expect { subject.stop }.to raise_error RuntimeError
101
+ end
102
+ end
51
103
  end
@@ -48,7 +48,7 @@ describe SippyCup::Scenario do
48
48
  end
49
49
 
50
50
  describe 'media-dependent operations' do
51
- let(:media) { mock :media }
51
+ let(:media) { double :media }
52
52
  let(:scenario) do
53
53
  SippyCup::Media.should_receive(:new).once.and_return media
54
54
  scenario = SippyCup::Scenario.new 'Test', source: '127.0.0.1:5061', destination: '127.0.0.1:5060'
@@ -69,4 +69,74 @@ describe SippyCup::Scenario do
69
69
  scenario.send_digits '136'
70
70
  end
71
71
  end
72
+
73
+ describe "#register" do
74
+ let(:scenario) { SippyCup::Scenario.new 'Test', source: '127.0.0.1:5061', destination: '127.0.0.1:5060' }
75
+
76
+ it %q{should only call #register_message if only user is passed} do
77
+ scenario.should_receive(:register_message).with 'foo', domain: "example.com"
78
+ scenario.should_not_receive(:register_auth)
79
+ scenario.register 'foo@example.com'
80
+ end
81
+
82
+ it %q{should call #register_auth if user and password are passed} do
83
+ scenario.should_receive(:register_auth).with 'sally', 'seekrut', domain: "[remote_ip]"
84
+ scenario.register 'sally', 'seekrut'
85
+ end
86
+
87
+ it %q{should not modify the passed in user if a domain is given} do
88
+ scenario.register 'foo@example.com'
89
+
90
+ xml = scenario.to_xml
91
+ xml.should =~ %r{foo@example\.com}
92
+ end
93
+
94
+ it %q{should interpolate the target IP if no domain is given} do
95
+ scenario.register 'sally'
96
+
97
+ xml = scenario.to_xml
98
+ xml.should =~ %r{sally@\[remote_ip\]}
99
+ end
100
+
101
+ it %q{should add an auth to registers which specify a password} do
102
+ scenario.register 'foo@example.com', 'seekrut'
103
+
104
+ xml = scenario.to_xml
105
+ xml.should =~ %r{recv response="401" optional="false" auth="true"}
106
+ xml.should =~ %r{\[authentication username=foo password=seekrut\]}
107
+ end
108
+ end
109
+
110
+ describe "#parse_user" do
111
+ let(:scenario) { SippyCup::Scenario.new 'Test', source: '127.0.0.1:5061', destination: '127.0.0.1:5060' }
112
+
113
+ context "sip: prefix" do
114
+
115
+ it %q{should return user and domain for addresses in the sip:user@domain:port format} do
116
+ scenario.parse_user('sip:foo@example.com:1337').should == ['foo', 'example.com']
117
+ end
118
+
119
+ it %q{should return user and domain for addresses in the sip:user@domain format} do
120
+ scenario.parse_user('sip:foo@example.com').should == ['foo', 'example.com']
121
+ end
122
+
123
+ it %q{should return user and [remote_ip] for addresses in the sip:user format} do
124
+ scenario.parse_user('sip:foo').should == ['foo', '[remote_ip]']
125
+ end
126
+ end
127
+
128
+ context "no prefix" do
129
+ it %q{should return user and domain for addresses in the user@domain:port format} do
130
+ scenario.parse_user('foo@example.com:1337').should == ['foo', 'example.com']
131
+ end
132
+
133
+ it %q{should return user and domain for addresses in the user@domain format} do
134
+ scenario.parse_user('foo@example.com').should == ['foo', 'example.com']
135
+ end
136
+
137
+ it %q{should return user and [remote_ip] for a standalone username} do
138
+ scenario.parse_user('sally').should == ['sally', '[remote_ip]']
139
+ end
140
+ end
141
+ end
72
142
  end
@@ -0,0 +1 @@
1
+
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sippy_cup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
5
- prerelease:
4
+ version: 0.2.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ben Klang
@@ -10,28 +9,25 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2013-08-27 00:00:00.000000000 Z
12
+ date: 2013-09-06 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: packetfu
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
- - - ! '>='
18
+ - - '>='
21
19
  - !ruby/object:Gem::Version
22
20
  version: '0'
23
21
  type: :runtime
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
- - - ! '>='
25
+ - - '>='
29
26
  - !ruby/object:Gem::Version
30
27
  version: '0'
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: nokogiri
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
32
  - - ~>
37
33
  - !ruby/object:Gem::Version
@@ -39,7 +35,6 @@ dependencies:
39
35
  type: :runtime
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
39
  - - ~>
45
40
  - !ruby/object:Gem::Version
@@ -47,39 +42,34 @@ dependencies:
47
42
  - !ruby/object:Gem::Dependency
48
43
  name: activesupport
49
44
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
45
  requirements:
52
- - - ! '>'
46
+ - - '>'
53
47
  - !ruby/object:Gem::Version
54
48
  version: '3.0'
55
49
  type: :runtime
56
50
  prerelease: false
57
51
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
52
  requirements:
60
- - - ! '>'
53
+ - - '>'
61
54
  - !ruby/object:Gem::Version
62
55
  version: '3.0'
63
56
  - !ruby/object:Gem::Dependency
64
57
  name: guard-rspec
65
58
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
59
  requirements:
68
- - - ! '>='
60
+ - - '>='
69
61
  - !ruby/object:Gem::Version
70
62
  version: '0'
71
63
  type: :development
72
64
  prerelease: false
73
65
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
66
  requirements:
76
- - - ! '>='
67
+ - - '>='
77
68
  - !ruby/object:Gem::Version
78
69
  version: '0'
79
70
  - !ruby/object:Gem::Dependency
80
71
  name: rspec
81
72
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
73
  requirements:
84
74
  - - ~>
85
75
  - !ruby/object:Gem::Version
@@ -87,7 +77,6 @@ dependencies:
87
77
  type: :development
88
78
  prerelease: false
89
79
  version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
80
  requirements:
92
81
  - - ~>
93
82
  - !ruby/object:Gem::Version
@@ -95,49 +84,43 @@ dependencies:
95
84
  - !ruby/object:Gem::Dependency
96
85
  name: simplecov
97
86
  requirement: !ruby/object:Gem::Requirement
98
- none: false
99
87
  requirements:
100
- - - ! '>='
88
+ - - '>='
101
89
  - !ruby/object:Gem::Version
102
90
  version: '0'
103
91
  type: :development
104
92
  prerelease: false
105
93
  version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
94
  requirements:
108
- - - ! '>='
95
+ - - '>='
109
96
  - !ruby/object:Gem::Version
110
97
  version: '0'
111
98
  - !ruby/object:Gem::Dependency
112
99
  name: simplecov-rcov
113
100
  requirement: !ruby/object:Gem::Requirement
114
- none: false
115
101
  requirements:
116
- - - ! '>='
102
+ - - '>='
117
103
  - !ruby/object:Gem::Version
118
104
  version: '0'
119
105
  type: :development
120
106
  prerelease: false
121
107
  version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
108
  requirements:
124
- - - ! '>='
109
+ - - '>='
125
110
  - !ruby/object:Gem::Version
126
111
  version: '0'
127
112
  - !ruby/object:Gem::Dependency
128
113
  name: ci_reporter
129
114
  requirement: !ruby/object:Gem::Requirement
130
- none: false
131
115
  requirements:
132
- - - ! '>='
116
+ - - '>='
133
117
  - !ruby/object:Gem::Version
134
118
  version: '0'
135
119
  type: :development
136
120
  prerelease: false
137
121
  version_requirements: !ruby/object:Gem::Requirement
138
- none: false
139
122
  requirements:
140
- - - ! '>='
123
+ - - '>='
141
124
  - !ruby/object:Gem::Version
142
125
  version: '0'
143
126
  description: This tool makes it easier to generate SIPp load tests with DTMF interactions.
@@ -174,33 +157,33 @@ files:
174
157
  - spec/sippy_cup/runner_spec.rb
175
158
  - spec/sippy_cup/scenario_spec.rb
176
159
  - spec/spec_helper.rb
160
+ - tmp/rspec_guard_result
177
161
  homepage: https://github.com/bklang/sippy_cup
178
- licenses: []
162
+ licenses:
163
+ - MIT
164
+ metadata: {}
179
165
  post_install_message:
180
166
  rdoc_options: []
181
167
  require_paths:
182
168
  - lib
183
169
  required_ruby_version: !ruby/object:Gem::Requirement
184
- none: false
185
170
  requirements:
186
- - - ! '>='
171
+ - - '>='
187
172
  - !ruby/object:Gem::Version
188
173
  version: '0'
189
174
  required_rubygems_version: !ruby/object:Gem::Requirement
190
- none: false
191
175
  requirements:
192
- - - ! '>='
176
+ - - '>='
193
177
  - !ruby/object:Gem::Version
194
178
  version: '0'
195
179
  requirements: []
196
180
  rubyforge_project:
197
- rubygems_version: 1.8.25
181
+ rubygems_version: 2.0.3
198
182
  signing_key:
199
- specification_version: 3
183
+ specification_version: 4
200
184
  summary: SIPp profile and RTP stream generator
201
185
  test_files:
202
186
  - spec/sippy_cup/media_spec.rb
203
187
  - spec/sippy_cup/runner_spec.rb
204
188
  - spec/sippy_cup/scenario_spec.rb
205
189
  - spec/spec_helper.rb
206
- has_rdoc: