Where was I ???

Ok, its been long since I had my last blog post. Where was I? Well, I have been busy! Last few months have been the most happening months of my life.

First of all, I am a proud father of my 1 month old son now. Although I haven’t managed to stop watching cartoons and start acting like a dad yet, its really amazing how you feel!

Again, the product “Vantage Contact Center” that I have been working for last 1.5 years is in market now!

The product is built on top of Broadwork’s 3rd party call control api (OCI-P / CCC2) and Broadworks SIP stack.This is the biggest project that I know of built on top of broadworks 3rd party api. We had a product owner who had spent last 10 year with callcenter solutions. So you can say it was a smooth sail. Once again It proves that for any successful software, most important thing is ‘Domain Knowledge’. We emphasized heavily on Domain Driven Design, unit testing , functional automation testing and we didn’t forget that none of these can replace the manual testing. At the end, it all paid off. Except for some minor integration issues at deployment time (which is natural because the solution depends on a number of servers talking to each other, a typical pattern for telephony apps) , we didn’t have a single major bug that is a showstopper! We had to change some labels on customer demand and add some more validation, but thats about it!

So here I am , feeling all good about the year 2009 and praying it would go the same for year 2010! And hoping I will update my blog regularly! Wish me luck!

Automate SIP Flow testing with Sipper

If you are into VOIP development and want to automate your sip call flows for testing, you might be interested in this project SIPr.

I have been involved in a project for a few months now which provides an advanced callcenter service leveraging the Broadworks Platform. Like any callcenter, the customer calls the CallCenter , Callcenter Queues the Call and Selects an agent and delivers the Call to the Agent. The agent is a registered user of Broadworks.

I have been looking for the right tool to automate the SIP Call Flows. After playing a while with sippr, I end up writing these test cases:

The CUSTOMER controller:
It justs sends the INVITE to a broadworks number which is the QUEUE. After the call gets received, it waits and sends BYE to terminate the call.

require 'sip_test_driver_controller'

class CustomerController < SIP::SipTestDriverController

  transaction_usage :use_transactions=>false
  start_on_load false

  def initialize
    logd('Controller created')
  end

  def on_provisional_res(session)
  end

  def start
    session = create_udp_session(SipperConfigurator[:DefaultRIP], SipperConfigurator[:DefaultRP])
    session.request_with('INVITE', 'sip:sajidqueue1@bwas.broadworkslab.com')
    puts 'Sending INVITE Dest: ',  'sip:sajidqueue1@bwas.broadworkslab.com';
 end

 def on_invite(session)
 puts 'customer got re-invite : '
 session.respond_with(200)
 session.schedule_timer_for("send 200!", 15000)
 end

 def on_timer(session , task)
 puts 'sending bye'
 session.request_with('BYE')
 session.invalidate(true)
 end

 def on_success_res(session)
 puts 'customer ---> on_success_res'
 session.request_with('ACK')
 end
end

Here is the Agent Controller:
It registers with Broadworks and waits for calls from the queue and receives it after 10 sec Ring and then waits for the BYE from the customer.

require 'sip_test_driver_controller'

class AgentController < SIP::SipTestDriverController

  transaction_usage :use_transactions=>false

  # change the directive below to true to start after loading.
  start_on_load false

  def specified_transport
    [SipperConfigurator[:LocalSipperIP],SipperConfigurator[:LocalSipperPort][1]]
  end   

  def initialize
    logd('Controller created')
  end

  def on_invite(session)
    
 
    if !session['invite']
      puts 'Agent got call from Queue '
      session.respond_with(100)
      session.respond_with(180)
      session['invite']=1
      session.schedule_timer_for("send 200!", 5000)
    else
      puts 'agent got re-invite'
      session.respond_with(200)
    end
    
  end
  
  def on_timer(session , task) 
    puts 'Agent is going to accept the call from customer!', task
    session.respond_with(200)
  end

  def on_bye(session)
    puts 'Agent received BYE from Customer!'
    session.respond_with(200)
    session.invalidate(true)
    session.flow_completed_for("CustomerAgentTest")
  end

  def on_provisional_res(session)
  end

  def start
    contact = SipperConfigurator[:LocalSipperIP]+":"
    r = Request.create_initial("REGISTER", "sip:" + "bwas.broadworkslab.com'",
         :from=>"sip:2401120075@bwas.dhaka.vantage.com", :to=>"sip:2401120075@bwas.broadworkslab.com'",
         :contact=>"sip:sajidagent1@"+ SipperConfigurator[:LocalSipperIP]+":"+SipperConfigurator[:LocalSipperPort][1].to_s(),
         :expires=>"300",
         :cseq=>"1 REGISTER",
         :p_session_record=>"msg-info")
    r.contact.q="0.9"       
    session = create_udp_session(SipperConfigurator[:DefaultRIP], SipperConfigurator[:DefaultRP])
    #session.create_register_request('sip:bwas.dhaka.vantage.com', '<sip:sajidagent1@bwas.broadworkslab.com'>')
    session.send(r)    
    #print "Sent a new REGISTER from "+name+"\n"
  end
  
  def on_success_res_for_register(s)
    s.invalidate(true);
  end

  def on_success_res(session)
    puts 'on_success_res->'
  end

  def on_ack(session)
  end

  def on_failure_res(session)
    if session.iresponse.code == 401
     r = session.create_request_with_response_to_challenge(session.iresponse.www_authenticate, false,"a", "b")     
     session.send r
     session[:auth] = r.authorization
    end
  end
end

And Here is the Test Case that runs both controller and performs Validation/Assertion for the call flow.

$:.unshift File.join(ENV['SIPPER_HOME'],'sipper_test')
require 'driven_sip_test_case'

class CustomerAgentTest < DrivenSipTestCase
  def setup
    super
    SipperConfigurator&#91;:SessionRecord&#93;='msg-info'
    puts '------------------------------------'
  end
  
  def test_case1
    start_named_controller_non_blocking("AgentController")
    sleep 1
    start_named_controller_non_blocking("CustomerController")    

    #it waits for Agent & Customer flow complete. The agent controller triggers the completion with session.flow_completed_for invocation. 
    wait_for_signaling()
            
    self.expected_flow = &#91;'< INVITE', '> 100', '> 180', '> 200', '< ACK ' , '< INVITE', '> 200', '< ACK',  '< INVITE', '> 200', '< ACK' , '< BYE', '> 200']
    verify_call_flow(:in,0)

    self.expected_flow = ['> INVITE', '< 100 {0,}', '< 200', '> ACK', '< INVITE', '> 200', '< ACK', '< INVITE', '> 200', '< ACK', '> BYE'  ]
    verify_call_flow(:out, 1)   
    puts "done tests"
  end
end

Ain’t this cool! Just a few lines of Code and you get a test case with Both UAC & UAS simulation!. I wish the Sipper Guys posted some more examples. We are planning to integrate this testcases with Watir & FunFX to automate the Full VOICE + WEB Mash up.

Asterisk as Sip User Agent emulator

Most common use case of Asterisk is that of a Sip Server. User Agents register to it and can call each other. But have anyone though of using it as a sip user agent emulator? This is exactly what we are doing ! Our main product is written in java which needs to register to a Sip Service Provider, receive calls, play automated answers/forward calls and all kind of play around with the Call. We looked into various open source sip stack written in C / java. We kind of overlooked Asterisk as its known as a sip server! We were just exploring asterisk sip stack to see if we can somehow reuse it, but we were surprised to find out we can use Asterisk as it is without any modifications! Asterisk is written as a B2B UA (Back to Back User Agent) model (not so good for sip servers from performance point of view) but more than fine for us! You can register to your sip service provider as a user agent , answer calls, play messages, collect dtmf, make outgoing call and many other cool stuff! Asterisk real-time even takes this in another level where you can do the all these things dynamically! The best point is, you can write your logic in any language (in our case java) with AGI and AMI which is just TCP message based programming ! Wait, if you are using java, life is even better for you! Asterisk-java is a wonderful abstraction written on AGI/AMI so you don’t even need to bother about TCP programming. Hats off to open source community, It would take us a Year to accomplish what we have accomplished in 3 months with asterisk.

The free book “Asterisk: The Future of Telephony” is a great resource but I am surprised to see how little information is there on net about using Asterisk as a Sip User Agent emulator! Are we the only one who is using asterisk this way !!!???