sippy_cup 0.2.1 → 0.2.2

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.
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: