diff options
Diffstat (limited to 'talk/session/media/mediasessionclient_unittest.cc')
-rw-r--r-- | talk/session/media/mediasessionclient_unittest.cc | 3324 |
1 files changed, 3324 insertions, 0 deletions
diff --git a/talk/session/media/mediasessionclient_unittest.cc b/talk/session/media/mediasessionclient_unittest.cc new file mode 100644 index 0000000000..eb052ef86a --- /dev/null +++ b/talk/session/media/mediasessionclient_unittest.cc @@ -0,0 +1,3324 @@ +/* + * libjingle + * Copyright 2004 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string> +#include <vector> + +#include "talk/media/base/fakemediaengine.h" +#include "talk/media/base/testutils.h" +#include "talk/media/devices/fakedevicemanager.h" +#include "webrtc/p2p/base/constants.h" +#include "webrtc/p2p/client/basicportallocator.h" +#include "talk/session/media/mediasessionclient.h" +#include "webrtc/libjingle/xmllite/xmlbuilder.h" +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmllite/xmlprinter.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/ssladapter.h" + +using cricket::AudioCodec; +using cricket::AudioContentDescription; +using cricket::Codec; +using cricket::DataCodec; +using cricket::DataContentDescription; +using cricket::FeedbackParam; +using cricket::FeedbackParams; +using cricket::VideoCodec; +using cricket::VideoContentDescription; + +// The codecs that our FakeMediaEngine will support. Order is important, since +// the tests check that our messages have codecs in the correct order. +static const cricket::AudioCodec kAudioCodecs[] = { + cricket::AudioCodec(103, "ISAC", 16000, -1, 1, 18), + cricket::AudioCodec(104, "ISAC", 32000, -1, 1, 17), + cricket::AudioCodec(119, "ISACLC", 16000, 40000, 1, 16), + cricket::AudioCodec(99, "speex", 16000, 22000, 1, 15), + cricket::AudioCodec(97, "IPCMWB", 16000, 80000, 1, 14), + cricket::AudioCodec(9, "G722", 8000, 64000, 1, 13), + cricket::AudioCodec(102, "iLBC", 8000, 13300, 1, 12), + cricket::AudioCodec(98, "speex", 8000, 11000, 1, 11), + cricket::AudioCodec(3, "GSM", 8000, 13000, 1, 10), + cricket::AudioCodec(100, "EG711U", 8000, 64000, 1, 9), + cricket::AudioCodec(101, "EG711A", 8000, 64000, 1, 8), + cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 7), + cricket::AudioCodec(8, "PCMA", 8000, 64000, 1, 6), + cricket::AudioCodec(126, "CN", 32000, 0, 1, 5), + cricket::AudioCodec(105, "CN", 16000, 0, 1, 4), + cricket::AudioCodec(13, "CN", 8000, 0, 1, 3), + cricket::AudioCodec(117, "red", 8000, 0, 1, 2), + cricket::AudioCodec(106, "telephone-event", 8000, 0, 1, 1) +}; + +// The codecs that our FakeMediaEngine will support with a different order of +// supported codecs. +static const cricket::AudioCodec kAudioCodecsDifferentPreference[] = { + cricket::AudioCodec(104, "ISAC", 32000, -1, 1, 17), + cricket::AudioCodec(97, "IPCMWB", 16000, 80000, 1, 14), + cricket::AudioCodec(9, "G722", 8000, 64000, 1, 13), + cricket::AudioCodec(119, "ISACLC", 16000, 40000, 1, 16), + cricket::AudioCodec(103, "ISAC", 16000, -1, 1, 18), + cricket::AudioCodec(99, "speex", 16000, 22000, 1, 15), + cricket::AudioCodec(100, "EG711U", 8000, 64000, 1, 9), + cricket::AudioCodec(101, "EG711A", 8000, 64000, 1, 8), + cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 7), + cricket::AudioCodec(8, "PCMA", 8000, 64000, 1, 6), + cricket::AudioCodec(102, "iLBC", 8000, 13300, 1, 12), + cricket::AudioCodec(3, "GSM", 8000, 13000, 1, 10), + cricket::AudioCodec(98, "speex", 8000, 11000, 1, 11), + cricket::AudioCodec(126, "CN", 32000, 0, 1, 5), + cricket::AudioCodec(105, "CN", 16000, 0, 1, 4), + cricket::AudioCodec(13, "CN", 8000, 0, 1, 3), + cricket::AudioCodec(117, "red", 8000, 0, 1, 2), + cricket::AudioCodec(106, "telephone-event", 8000, 0, 1, 1) +}; + +static const cricket::VideoCodec kVideoCodecs[] = { + cricket::VideoCodec(96, "H264-SVC", 320, 200, 30, 1) +}; + +static const cricket::DataCodec kDataCodecs[] = { + cricket::DataCodec(127, "google-data", 0) +}; + +const std::string kGingleCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1'> " \ + " <usage/> " \ + " <rtp:crypto tag='145' crypto-suite='AES_CM_128_HMAC_SHA1_32'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='AES_CM_128_HMAC_SHA1_80'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + +// Jingle offer does not have any <usage> element. +const std::string kJingleCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1'> " \ + " <rtp:crypto tag='145' crypto-suite='AES_CM_128_HMAC_SHA1_32'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='AES_CM_128_HMAC_SHA1_80'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + + +const std::string kGingleRequiredCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1' required='true'> "\ + " <usage/> " \ + " <rtp:crypto tag='145' crypto-suite='AES_CM_128_HMAC_SHA1_32'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='AES_CM_128_HMAC_SHA1_80'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + +const std::string kJingleRequiredCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1' required='true'> "\ + " <rtp:crypto tag='145' crypto-suite='AES_CM_128_HMAC_SHA1_32'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='AES_CM_128_HMAC_SHA1_80'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + + +const std::string kGingleUnsupportedCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1'> " \ + " <usage/> " \ + " <rtp:crypto tag='145' crypto-suite='NOT_SUPPORTED_1'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='NOT_SUPPORTED_2'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + +const std::string kJingleUnsupportedCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1'> " \ + " <rtp:crypto tag='145' crypto-suite='NOT_SUPPORTED_1'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='NOT_SUPPORTED_2'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + + +// With unsupported but with required="true" +const std::string kGingleRequiredUnsupportedCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1' required='true'>" \ + " <usage/> " \ + " <rtp:crypto tag='145' crypto-suite='NOT_SUPPORTED_1'" \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/>" \ + " <rtp:crypto tag='51' crypto-suite='NOT_SUPPORTED_2'" \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + +const std::string kJingleRequiredUnsupportedCryptoOffer = \ + "<rtp:encryption xmlns:rtp='urn:xmpp:jingle:apps:rtp:1' required='true'>" \ + " <rtp:crypto tag='145' crypto-suite='NOT_SUPPORTED_1' " \ + " key-params='inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9'/> " \ + " <rtp:crypto tag='51' crypto-suite='NOT_SUPPORTED_2' " \ + " key-params='inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy'/>" \ + "</rtp:encryption> "; + +const std::string kGingleInitiate( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' clockrate='16000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='104' name='ISAC' clockrate='32000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='119' name='ISACLC' clockrate='16000' bitrate='40000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='99' name='speex' clockrate='16000' bitrate='22000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='97' name='IPCMWB' clockrate='16000' bitrate='80000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='9' name='G722' clockrate='8000' bitrate='64000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='102' name='iLBC' clockrate='8000' bitrate='13300' />" \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='98' name='speex' clockrate='8000' bitrate='11000' />" \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='3' name='GSM' clockrate='8000' bitrate='13000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='100' name='EG711U' clockrate='8000' bitrate='64000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='101' name='EG711A' clockrate='8000' bitrate='64000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='0' name='PCMU' clockrate='8000' bitrate='64000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='8' name='PCMA' clockrate='8000' bitrate='64000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='126' name='CN' clockrate='32000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='105' name='CN' clockrate='16000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='13' name='CN' clockrate='8000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='117' name='red' clockrate='8000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='106' name='telephone-event' clockrate='8000' /> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiate( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " <payload-type id='104' name='ISAC' clockrate='32000'/> " \ + " <payload-type " \ + " id='119' name='ISACLC' clockrate='16000'> " \ + " <parameter name='bitrate' value='40000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='99' name='speex' clockrate='16000'> " \ + " <parameter name='bitrate' value='22000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='97' name='IPCMWB' clockrate='16000'> " \ + " <parameter name='bitrate' value='80000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='9' name='G722' clockrate='8000'> " \ + " <parameter name='bitrate' value='64000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='102' name='iLBC' clockrate='8000'> " \ + " <parameter name='bitrate' value='13300'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='98' name='speex' clockrate='8000'> " \ + " <parameter name='bitrate' value='11000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='3' name='GSM' clockrate='8000'> " \ + " <parameter name='bitrate' value='13000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='100' name='EG711U' clockrate='8000'> " \ + " <parameter name='bitrate' value='64000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='101' name='EG711A' clockrate='8000'> " \ + " <parameter name='bitrate' value='64000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='0' name='PCMU' clockrate='8000'> " \ + " <parameter name='bitrate' value='64000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='8' name='PCMA' clockrate='8000'> " \ + " <parameter name='bitrate' value='64000'/> " \ + " </payload-type> " \ + " <payload-type " \ + " id='126' name='CN' clockrate='32000' /> " \ + " <payload-type " \ + " id='105' name='CN' clockrate='16000' /> " \ + " <payload-type " \ + " id='13' name='CN' clockrate='8000' /> " \ + " <payload-type " \ + " id='117' name='red' clockrate='8000' /> " \ + " <payload-type " \ + " id='106' name='telephone-event' clockrate='8000' /> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleInitiateWithRtcpFb( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'> " \ + " <rtcp-fb type='nack'/> " \ + " </payload-type> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \ + " <rtcp-fb type='nack'/> " \ + " <payload-type id='99' name='H264-SVC'> " \ + " <rtcp-fb type='ccm' subtype='fir'/> " \ + " <parameter name='height' value='200'/> " \ + " <parameter name='width' value='320'/> " \ + " <parameter name='framerate' value='30'/> " \ + " </payload-type> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test data'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='data'> " \ + " <rtcp-fb type='nack'/> " \ + " <payload-type id='127' name='google-data'> " \ + " </payload-type> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kGingleVideoInitiate( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/video'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' clockrate='16000' /> " \ + " <payload-type xmlns='http://www.google.com/session/video' " \ + " id='99' name='H264-SVC' framerate='30' " \ + " height='200' width='320'/> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleVideoInitiate( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \ + " <payload-type id='99' name='H264-SVC'> " \ + " <parameter name='height' value='200'/> " \ + " <parameter name='width' value='320'/> " \ + " <parameter name='framerate' value='30'/> " \ + " </payload-type> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleVideoInitiateWithRtpData( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \ + " <payload-type id='99' name='H264-SVC'> " \ + " <parameter name='height' value='200'/> " \ + " <parameter name='width' value='320'/> " \ + " <parameter name='framerate' value='30'/> " \ + " </payload-type> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test data'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='data'> " \ + " <payload-type id='127' name='google-data'/> " \ + " <rtcp-mux/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleVideoInitiateWithSctpData( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \ + " <payload-type id='99' name='H264-SVC'> " \ + " <parameter name='height' value='200'/> " \ + " <parameter name='width' value='320'/> " \ + " <parameter name='framerate' value='30'/> " \ + " </payload-type> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test data'> " \ + " <description xmlns='google:jingle:sctp' media='data'> " \ + " <stream sid='1'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleVideoInitiateWithBandwidth( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \ + " <payload-type id='99' name='H264-SVC'> " \ + " <parameter name='height' value='200'/> " \ + " <parameter name='width' value='320'/> " \ + " <parameter name='framerate' value='30'/> " \ + " </payload-type> " \ + " <bandwidth type='AS'>42</bandwidth> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleVideoInitiateWithRtcpMux( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " <content name='test video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='video'> " \ + " <payload-type id='99' name='H264-SVC'> " \ + " <parameter name='height' value='200'/> " \ + " <parameter name='width' value='320'/> " \ + " <parameter name='framerate' value='30'/> " \ + " </payload-type> " \ + " <rtcp-mux/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// Initiate string with a combination of supported and unsupported codecs +// Should accept the supported ones +const std::string kGingleInitiateSomeUnsupported( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' clockrate='16000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='97' name='ASDFDS' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='102' name='1010' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='107' name='DFAS' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='100' name='EG711U' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='101' name='EG711A' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='0' name='PCMU' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='110' name=':)' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='13' name='CN' /> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateSomeUnsupported( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'> " \ + " <payload-type " \ + " id='103' name='ISAC' clockrate='16000' /> " \ + " <payload-type " \ + " id='97' name='ASDFDS' /> " \ + " <payload-type " \ + " id='102' name='1010' /> " \ + " <payload-type " \ + " id='107' name='DFAS' /> " \ + " <payload-type " \ + " id='100' name='EG711U' /> " \ + " <payload-type " \ + " id='101' name='EG711A' /> " \ + " <payload-type " \ + " id='0' name='PCMU' /> " \ + " <payload-type " \ + " id='110' name=':)' /> " \ + " <payload-type " \ + " id='13' name='CN' /> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kGingleVideoInitiateWithBandwidth( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/video'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' clockrate='16000' /> " \ + " <payload-type xmlns='http://www.google.com/session/video' " \ + " id='99' name='H264-SVC' framerate='30' " \ + " height='200' width='320'/> " \ + " <bandwidth type='AS'>42</bandwidth> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +// Initiate string without any supported codecs. Should send a reject. +const std::string kGingleInitiateNoSupportedAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='123' name='Supercodec6000' /> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateNoSupportedAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type " \ + " id='123' name='Supercodec6000' /> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// Initiate string without any codecs. Assumes ancient version of Cricket +// and tries a session with ISAC and PCMU +const std::string kGingleInitiateNoAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateNoAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// The codecs are supported, but not at the given clockrates. Should send +// a reject. +const std::string kGingleInitiateWrongClockrates( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' clockrate='8000'/> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='97' name='IPCMWB' clockrate='1337'/> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='102' name='iLBC' clockrate='1982' /> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateWrongClockrates( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type " \ + " id='103' name='ISAC' clockrate='8000'/> " \ + " <payload-type " \ + " id='97' name='IPCMWB' clockrate='1337'/> " \ + " <payload-type " \ + " id='102' name='iLBC' clockrate='1982' /> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// The codecs are supported, but not with the given number of channels. +// Should send a reject. +const std::string kGingleInitiateWrongChannels( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' channels='2'/> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='97' name='IPCMWB' channels='3'/> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateWrongChannels( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type " \ + " id='103' name='ISAC' channels='2'/> " \ + " <payload-type " \ + " id='97' name='IPCMWB' channels='3'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// Initiate with a dynamic codec not using webrtc default payload id. Should +// accept with provided payload id. +const std::string kGingleInitiateDynamicAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='123' name='speex' clockrate='16000'/> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateDynamicAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type " \ + " id='123' name='speex' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// Initiate string with nothing but static codec id's. Should accept. +const std::string kGingleInitiateStaticAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='3' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='0' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='8' /> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateStaticAudioCodecs( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type id='3' /> " \ + " <payload-type id='0' /> " \ + " <payload-type id='8' /> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// Initiate with payload type-less codecs. Should reject. +const std::string kGingleInitiateNoPayloadTypes( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateNoPayloadTypes( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate'> " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type name='ISAC' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +// Initiate with unnamed dynamic codces. Should reject. +const std::string kGingleInitiateDynamicWithoutNames( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <session xmlns='http://www.google.com/session' type='initiate'" \ + " id='abcdef' initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/phone'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='100' clockrate='16000'/> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleInitiateDynamicWithoutNames( + "<iq xmlns='jabber:client' from='me@domain.com/resource' " \ + " to='user@domain.com/resource' type='set' id='123'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-initiate'> " \ + " sid='abcdef' initiator='me@domain.com/resource'> " \ + " <content name='test audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>" \ + " <payload-type id='100' clockrate='16000'/> " \ + " </description> " \ + " <transport xmlns=\"http://www.google.com/transport/p2p\"/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const uint32 kAudioSsrc = 4294967295U; +const uint32 kVideoSsrc = 87654321; +const uint32 kDataSsrc = 1010101; +// Note that this message does not specify a session ID. It must be populated +// before use. +const std::string kGingleAcceptWithSsrcs( + "<iq xmlns='jabber:client' from='me@mydomain.com' " \ + " to='user@domain.com/resource' type='set' id='150'> " \ + " <session xmlns='http://www.google.com/session' type='accept' " \ + " initiator='me@domain.com/resource'> " \ + " <description xmlns='http://www.google.com/session/video'> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='103' name='ISAC' clockrate='16000' /> " \ + " <payload-type xmlns='http://www.google.com/session/phone' " \ + " id='104' name='ISAC' clockrate='32000' /> " \ + " <src-id xmlns='http://www.google.com/session/phone'> " \ + " 4294967295</src-id> " \ + " <src-id>87654321</src-id> " \ + " </description> " \ + " </session> " \ + "</iq> "); + +const std::string kJingleAcceptWithSsrcs( + "<iq xmlns='jabber:client' from='me@mydomain.com' " \ + " to='user@domain.com/resource' type='set' id='150'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-accept' " \ + " initiator='me@domain.com/resource'> " \ + " <content name='audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='audio' ssrc='4294967295'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " <payload-type id='104' name='ISAC' clockrate='32000'/> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " <content name='video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='video' ssrc='87654321'> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleAcceptWithRtpDataSsrcs( + "<iq xmlns='jabber:client' from='me@mydomain.com' " \ + " to='user@domain.com/resource' type='set' id='150'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-accept' " \ + " initiator='me@domain.com/resource'> " \ + " <content name='audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='audio' ssrc='4294967295'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " <payload-type id='104' name='ISAC' clockrate='32000'/> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " <content name='video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='video' ssrc='87654321'> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " <content name='data'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='data' ssrc='1010101'> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +const std::string kJingleAcceptWithSctpData( + "<iq xmlns='jabber:client' from='me@mydomain.com' " \ + " to='user@domain.com/resource' type='set' id='150'> " \ + " <jingle xmlns='urn:xmpp:jingle:1' action='session-accept' " \ + " initiator='me@domain.com/resource'> " \ + " <content name='audio'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='audio' ssrc='4294967295'> " \ + " <payload-type id='103' name='ISAC' clockrate='16000'/> " \ + " <payload-type id='104' name='ISAC' clockrate='32000'/> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " <content name='video'> " \ + " <description xmlns='urn:xmpp:jingle:apps:rtp:1' " \ + " media='video' ssrc='87654321'> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " <content name='data'> " \ + " <description xmlns='google:jingle:sctp'> " \ + " <stream sid='1'/> " \ + " </description> " \ + " <transport xmlns='http://www.google.com/transport/p2p'/> " \ + " </content> " \ + " </jingle> " \ + "</iq> "); + +std::string JingleView(const std::string& ssrc, + const std::string& width, + const std::string& height, + const std::string& framerate) { + // We have some slightly weird whitespace formatting to make the + // actual XML generated match the expected XML here. + return \ + "<cli:iq" + " to='me@mydomain.com'" + " type='set'" + " xmlns:cli='jabber:client'>" + "<jingle" + " xmlns='urn:xmpp:jingle:1'" + " action='session-info'" + " sid=''>" + "<view xmlns='google:jingle'" + " name='video'" + " type='static'" + " ssrc='" + ssrc + "'>" + "<params" + " width='" + width + "'" + " height='" + height + "'" + " framerate='" + framerate + "'" + " preference='0'/>" + "</view>" + "</jingle>" + "</cli:iq>"; +} + +std::string JingleStreamAdd(const std::string& content_name, + const std::string& nick, + const std::string& name, + const std::string& ssrc) { + return \ + "<iq" + " xmlns='jabber:client'" + " from='me@mydomain.com'" + " to='user@domain.com/resource'" + " type='set'" + " id='150'>" + " <jingle" + " xmlns='urn:xmpp:jingle:1'" + " action='description-info'>" + " <content" + " xmlns='urn:xmpp:jingle:1'" + " name='" + content_name + "'>" + " <description" + " xmlns='urn:xmpp:jingle:apps:rtp:1'" + " media='" + content_name + "'>" + " <streams" + " xmlns='google:jingle'>" + " <stream" + " nick='" + nick + "'" + " name='" + name + "'>" + " <ssrc>" + ssrc + "</ssrc>" + " </stream>" + " </streams>" + " </description>" + " </content>" + " </jingle>" + "</iq>"; +} + +std::string JingleOutboundStreamRemove(const std::string& sid, + const std::string& content_name, + const std::string& name) { + return \ + "<cli:iq" + " to='me@mydomain.com'" + " type='set'" + " xmlns:cli='jabber:client'>" + "<jingle" + " xmlns='urn:xmpp:jingle:1'" + " action='description-info'" + " sid='" + sid + "'>" + "<content" + " name='" + content_name + "'" + " creator='initiator'>" + "<description" + " xmlns='urn:xmpp:jingle:apps:rtp:1'" + " media='" + content_name + "'>" + "<streams" + " xmlns='google:jingle'>" + "<stream" + " name='" + name + "'>" + "</stream>" + "</streams>" + "</description>" + "</content>" + "</jingle>" + "</cli:iq>"; +} + +std::string JingleOutboundStreamAdd(const std::string& sid, + const std::string& content_name, + const std::string& name, + const std::string& ssrc) { + return \ + "<cli:iq" + " to='me@mydomain.com'" + " type='set'" + " xmlns:cli='jabber:client'>" + "<jingle" + " xmlns='urn:xmpp:jingle:1'" + " action='description-info'" + " sid='" + sid + "'>" + "<content" + " name='" + content_name + "'" + " creator='initiator'>" + "<description" + " xmlns='urn:xmpp:jingle:apps:rtp:1'" + " media='" + content_name + "'>" + "<streams" + " xmlns='google:jingle'>" + "<stream" + " name='" + name + "'>" + "<ssrc>" + ssrc + "</ssrc>" + "</stream>" + "</streams>" + "</description>" + "</content>" + "</jingle>" + "</cli:iq>"; +} + +std::string JingleStreamAddWithoutSsrc(const std::string& content_name, + const std::string& nick, + const std::string& name) { + return \ + "<iq" + " xmlns='jabber:client'" + " from='me@mydomain.com'" + " to='user@domain.com/resource'" + " type='set'" + " id='150'>" + " <jingle" + " xmlns='urn:xmpp:jingle:1'" + " action='description-info'>" + " <content" + " xmlns='urn:xmpp:jingle:1'" + " name='" + content_name + "'>" + " <description" + " xmlns='urn:xmpp:jingle:apps:rtp:1'" + " media='" + content_name + "'>" + " <streams" + " xmlns='google:jingle'>" + " <stream" + " nick='" + nick + "'" + " name='" + name + "'>" + " </stream>" + " </streams>" + " </description>" + " </content>" + " </jingle>" + "</iq>"; +} + +std::string JingleStreamRemove(const std::string& content_name, + const std::string& nick, + const std::string& name) { + return \ + "<iq" + " xmlns='jabber:client'" + " from='me@mydomain.com'" + " to='user@domain.com/resource'" + " type='set'" + " id='150'>" + " <jingle" + " xmlns='urn:xmpp:jingle:1'" + " action='description-info'>" + " <content" + " xmlns='urn:xmpp:jingle:1'" + " name='" + content_name + "'>" + " <description" + " xmlns='urn:xmpp:jingle:apps:rtp:1'" + " media='" + content_name + "'>" + " <streams" + " xmlns='google:jingle'>" + " <stream" + " nick='" + nick + "'" + " name='" + name + "'/>" + " </streams>" + " </description>" + " </content>" + " </jingle>" + "</iq>"; +} + +// Convenience function to get CallOptions that have audio enabled, +// but not video or data. +static cricket::CallOptions AudioCallOptions() { + cricket::CallOptions options; + options.recv_audio = true; + options.recv_video = false; + options.data_channel_type = cricket::DCT_NONE; + return options; +} + +// Convenience function to get CallOptions that have audio and video +// enabled, but not data. +static cricket::CallOptions VideoCallOptions() { + cricket::CallOptions options; + options.recv_audio = true; + options.recv_video = true; + options.data_channel_type = cricket::DCT_NONE; + return options; +} + +static buzz::XmlElement* CopyElement(const buzz::XmlElement* elem) { + return new buzz::XmlElement(*elem); +} + +static std::string AddEncryption(std::string stanza, std::string encryption) { + std::string::size_type pos = stanza.find("</description>"); + while (pos != std::string::npos) { + stanza = stanza.insert(pos, encryption); + pos = stanza.find("</description>", pos + encryption.length() + 1); + } + return stanza; +} + +static int IntFromJingleCodecParameter(const buzz::XmlElement* parameter, + const std::string& expected_name) { + if (parameter) { + const std::string& actual_name = + parameter->Attr(cricket::QN_PAYLOADTYPE_PARAMETER_NAME); + + EXPECT_EQ(expected_name, actual_name) + << "wrong parameter name. Expected '" + << expected_name << "'. Actually '" + << actual_name << "'."; + + return atoi(parameter->Attr( + cricket::QN_PAYLOADTYPE_PARAMETER_VALUE).c_str()); + } + return 0; +} + +template <class CodecClass, class DescriptionClass> +static void VerifyCodecFbParams(const FeedbackParams& expected, + const DescriptionClass* desc) { + if (!expected.params().empty()) { + ASSERT_TRUE(desc != NULL); + const std::vector<CodecClass> codecs = desc->codecs(); + for (size_t i = 0; i < codecs.size(); ++i) { + EXPECT_EQ(expected, codecs[i].feedback_params); + } + } +} + +// Parses and extracts payload and codec info from test XML. Since +// that XML will be in various contents (Gingle and Jingle), we need an +// abstract parser with one concrete implementation per XML content. +class MediaSessionTestParser { + public: + virtual buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) = 0; + virtual buzz::XmlElement* ContentFromAction(buzz::XmlElement* action) = 0; + virtual buzz::XmlElement* NextContent(buzz::XmlElement* content) = 0; + virtual buzz::XmlElement* PayloadTypeFromContent( + buzz::XmlElement* content) = 0; + virtual buzz::XmlElement* NextFromPayloadType( + buzz::XmlElement* payload_type) = 0; + virtual cricket::AudioCodec AudioCodecFromPayloadType( + const buzz::XmlElement* payload_type) = 0; + virtual cricket::VideoCodec VideoCodecFromPayloadType( + const buzz::XmlElement* payload_type) = 0; + virtual cricket::DataCodec DataCodecFromPayloadType( + const buzz::XmlElement* payload_type) = 0; + virtual buzz::XmlElement* EncryptionFromContent( + buzz::XmlElement* content) = 0; + virtual buzz::XmlElement* NextFromEncryption( + buzz::XmlElement* encryption) = 0; + virtual const buzz::XmlElement* BandwidthFromContent( + buzz::XmlElement* content) = 0; + virtual const buzz::XmlElement* RtcpMuxFromContent( + buzz::XmlElement* content) = 0; + virtual bool ActionIsTerminate(const buzz::XmlElement* action) = 0; + virtual ~MediaSessionTestParser() {} +}; + +class JingleSessionTestParser : public MediaSessionTestParser { + public: + JingleSessionTestParser() {} + + ~JingleSessionTestParser() { + } + + buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) { + return stanza->FirstNamed(cricket::QN_JINGLE); + } + + buzz::XmlElement* ContentFromAction(buzz::XmlElement* action) { + // We need to be able to use multiple contents, but the action + // gets deleted before we can call NextContent, so we need to + // stash away a copy. + action_.reset(CopyElement(action)); + return action_->FirstNamed(cricket::QN_JINGLE_CONTENT); + } + + buzz::XmlElement* NextContent(buzz::XmlElement* content) { + // For some reason, content->NextNamed(cricket::QN_JINGLE_CONTENT) + // doesn't work. + return action_->FirstNamed(cricket::QN_JINGLE_CONTENT) + ->NextNamed(cricket::QN_JINGLE_CONTENT); + } + + buzz::XmlElement* PayloadTypeFromContent(buzz::XmlElement* content) { + buzz::XmlElement* content_desc = + content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); + if (!content_desc) + return NULL; + + return content_desc->FirstNamed(cricket::QN_JINGLE_RTP_PAYLOADTYPE); + } + + buzz::XmlElement* NextFromPayloadType(buzz::XmlElement* payload_type) { + return payload_type->NextNamed(cricket::QN_JINGLE_RTP_PAYLOADTYPE); + } + + void ParsePayloadTypeFeedbackParameters(const buzz::XmlElement* element, + FeedbackParams* params) { + const buzz::XmlElement* param = + element->FirstNamed(cricket::QN_JINGLE_RTCP_FB); + for (; param != NULL; + param = param->NextNamed(cricket::QN_JINGLE_RTCP_FB)) { + std::string type = param->Attr(cricket::QN_TYPE); + std::string subtype = param->Attr(cricket::QN_SUBTYPE); + if (!type.empty()) { + params->Add(FeedbackParam(type, subtype)); + } + } + } + + cricket::AudioCodec AudioCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + int id = 0; + if (payload_type->HasAttr(cricket::QN_ID)) + id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); + + std::string name; + if (payload_type->HasAttr(cricket::QN_NAME)) + name = payload_type->Attr(cricket::QN_NAME); + + int clockrate = 0; + if (payload_type->HasAttr(cricket::QN_CLOCKRATE)) + clockrate = atoi(payload_type->Attr(cricket::QN_CLOCKRATE).c_str()); + + int bitrate = IntFromJingleCodecParameter( + payload_type->FirstNamed(cricket::QN_PARAMETER), "bitrate"); + + int channels = 1; + if (payload_type->HasAttr(cricket::QN_CHANNELS)) + channels = atoi(payload_type->Attr( + cricket::QN_CHANNELS).c_str()); + + AudioCodec codec = AudioCodec(id, name, clockrate, bitrate, channels, 0); + ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params); + return codec; + } + + cricket::VideoCodec VideoCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + int id = 0; + if (payload_type->HasAttr(cricket::QN_ID)) + id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); + + std::string name; + if (payload_type->HasAttr(cricket::QN_NAME)) + name = payload_type->Attr(cricket::QN_NAME); + + int width = 0; + int height = 0; + int framerate = 0; + const buzz::XmlElement* param = + payload_type->FirstNamed(cricket::QN_PARAMETER); + if (param) { + width = IntFromJingleCodecParameter(param, "width"); + param = param->NextNamed(cricket::QN_PARAMETER); + if (param) { + height = IntFromJingleCodecParameter(param, "height"); + param = param->NextNamed(cricket::QN_PARAMETER); + if (param) { + framerate = IntFromJingleCodecParameter(param, "framerate"); + } + } + } + VideoCodec codec = VideoCodec(id, name, width, height, framerate, 0); + ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params); + return codec; + } + + cricket::DataCodec DataCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + int id = 0; + if (payload_type->HasAttr(cricket::QN_ID)) + id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); + + std::string name; + if (payload_type->HasAttr(cricket::QN_NAME)) + name = payload_type->Attr(cricket::QN_NAME); + + DataCodec codec = DataCodec(id, name, 0); + ParsePayloadTypeFeedbackParameters(payload_type, &codec.feedback_params); + return codec; + } + + bool ActionIsTerminate(const buzz::XmlElement* action) { + return (action->HasAttr(cricket::QN_ACTION) && + action->Attr(cricket::QN_ACTION) == "session-terminate"); + } + + buzz::XmlElement* EncryptionFromContent(buzz::XmlElement* content) { + buzz::XmlElement* content_desc = + content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); + if (!content_desc) + return NULL; + + return content_desc->FirstNamed(cricket::QN_ENCRYPTION); + } + + buzz::XmlElement* NextFromEncryption(buzz::XmlElement* encryption) { + return encryption->NextNamed(cricket::QN_ENCRYPTION); + } + + const buzz::XmlElement* BandwidthFromContent(buzz::XmlElement* content) { + buzz::XmlElement* content_desc = + content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); + if (!content_desc) + return NULL; + + return content_desc->FirstNamed(cricket::QN_JINGLE_RTP_BANDWIDTH); + } + + const buzz::XmlElement* RtcpMuxFromContent(buzz::XmlElement* content) { + return content->FirstNamed(cricket::QN_JINGLE_RTCP_MUX); + } + + private: + rtc::scoped_ptr<buzz::XmlElement> action_; +}; + +class GingleSessionTestParser : public MediaSessionTestParser { + public: + GingleSessionTestParser() : found_content_count_(0) {} + + buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) { + return stanza->FirstNamed(cricket::QN_GINGLE_SESSION); + } + + buzz::XmlElement* ContentFromAction(buzz::XmlElement* session) { + buzz::XmlElement* content = + session->FirstNamed(cricket::QN_GINGLE_AUDIO_CONTENT); + if (content == NULL) + content = session->FirstNamed(cricket::QN_GINGLE_VIDEO_CONTENT); + return content; + } + + // Assumes contents are in order of audio, and then video. + buzz::XmlElement* NextContent(buzz::XmlElement* content) { + found_content_count_++; + return content; + } + + buzz::XmlElement* PayloadTypeFromContent(buzz::XmlElement* content) { + if (found_content_count_ > 0) { + return content->FirstNamed(cricket::QN_GINGLE_VIDEO_PAYLOADTYPE); + } else { + return content->FirstNamed(cricket::QN_GINGLE_AUDIO_PAYLOADTYPE); + } + } + + buzz::XmlElement* NextFromPayloadType(buzz::XmlElement* payload_type) { + if (found_content_count_ > 0) { + return payload_type->NextNamed(cricket::QN_GINGLE_VIDEO_PAYLOADTYPE); + } else { + return payload_type->NextNamed(cricket::QN_GINGLE_AUDIO_PAYLOADTYPE); + } + } + + cricket::AudioCodec AudioCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + int id = 0; + if (payload_type->HasAttr(cricket::QN_ID)) + id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); + + std::string name; + if (payload_type->HasAttr(cricket::QN_NAME)) + name = payload_type->Attr(cricket::QN_NAME); + + int clockrate = 0; + if (payload_type->HasAttr(cricket::QN_CLOCKRATE)) + clockrate = atoi(payload_type->Attr(cricket::QN_CLOCKRATE).c_str()); + + int bitrate = 0; + if (payload_type->HasAttr(cricket::QN_BITRATE)) + bitrate = atoi(payload_type->Attr(cricket::QN_BITRATE).c_str()); + + int channels = 1; + if (payload_type->HasAttr(cricket::QN_CHANNELS)) + channels = atoi(payload_type->Attr(cricket::QN_CHANNELS).c_str()); + + return cricket::AudioCodec(id, name, clockrate, bitrate, channels, 0); + } + + cricket::VideoCodec VideoCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + int id = 0; + if (payload_type->HasAttr(cricket::QN_ID)) + id = atoi(payload_type->Attr(cricket::QN_ID).c_str()); + + std::string name; + if (payload_type->HasAttr(cricket::QN_NAME)) + name = payload_type->Attr(cricket::QN_NAME); + + int width = 0; + if (payload_type->HasAttr(cricket::QN_WIDTH)) + width = atoi(payload_type->Attr(cricket::QN_WIDTH).c_str()); + + int height = 0; + if (payload_type->HasAttr(cricket::QN_HEIGHT)) + height = atoi(payload_type->Attr(cricket::QN_HEIGHT).c_str()); + + int framerate = 1; + if (payload_type->HasAttr(cricket::QN_FRAMERATE)) + framerate = atoi(payload_type->Attr(cricket::QN_FRAMERATE).c_str()); + + return cricket::VideoCodec(id, name, width, height, framerate, 0); + } + + cricket::DataCodec DataCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + // Gingle can't do data codecs. + return cricket::DataCodec(0, "", 0); + } + + buzz::XmlElement* EncryptionFromContent( + buzz::XmlElement* content) { + return content->FirstNamed(cricket::QN_ENCRYPTION); + } + + buzz::XmlElement* NextFromEncryption(buzz::XmlElement* encryption) { + return encryption->NextNamed(cricket::QN_ENCRYPTION); + } + + const buzz::XmlElement* BandwidthFromContent(buzz::XmlElement* content) { + return content->FirstNamed(cricket::QN_GINGLE_VIDEO_BANDWIDTH); + } + + const buzz::XmlElement* RtcpMuxFromContent(buzz::XmlElement* content) { + return NULL; + } + + bool ActionIsTerminate(const buzz::XmlElement* session) { + return (session->HasAttr(buzz::QN_TYPE) && + session->Attr(buzz::QN_TYPE) == "terminate"); + } + + int found_content_count_; +}; + +class MediaSessionClientTest : public sigslot::has_slots<> { + public: + explicit MediaSessionClientTest(MediaSessionTestParser* parser, + cricket::SignalingProtocol initial_protocol) { + nm_ = new rtc::BasicNetworkManager(); + pa_ = new cricket::BasicPortAllocator(nm_); + sm_ = new cricket::SessionManager(pa_, NULL); + fme_ = new cricket::FakeMediaEngine(); + fdme_ = new cricket::FakeDataEngine(); + + FeedbackParams params_nack_fir; + params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamCcm, + cricket::kRtcpFbCcmParamFir)); + params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamNack)); + FeedbackParams params_nack; + params_nack.Add(FeedbackParam(cricket::kRtcpFbParamNack)); + + std::vector<cricket::AudioCodec> + audio_codecs(kAudioCodecs, kAudioCodecs + ARRAY_SIZE(kAudioCodecs)); + SetCodecFeedbackParams(&audio_codecs, params_nack); + fme_->SetAudioCodecs(audio_codecs); + std::vector<cricket::VideoCodec> + video_codecs(kVideoCodecs, kVideoCodecs + ARRAY_SIZE(kVideoCodecs)); + SetCodecFeedbackParams(&video_codecs, params_nack_fir); + fme_->SetVideoCodecs(video_codecs); + std::vector<cricket::DataCodec> + data_codecs(kDataCodecs, kDataCodecs + ARRAY_SIZE(kDataCodecs)); + SetCodecFeedbackParams(&data_codecs, params_nack); + fdme_->SetDataCodecs(data_codecs); + + client_ = new cricket::MediaSessionClient( + buzz::Jid("user@domain.com/resource"), sm_, + fme_, fdme_, new cricket::FakeDeviceManager()); + client_->session_manager()->SignalOutgoingMessage.connect( + this, &MediaSessionClientTest::OnSendStanza); + client_->session_manager()->SignalSessionCreate.connect( + this, &MediaSessionClientTest::OnSessionCreate); + client_->SignalCallCreate.connect( + this, &MediaSessionClientTest::OnCallCreate); + client_->SignalCallDestroy.connect( + this, &MediaSessionClientTest::OnCallDestroy); + + call_ = NULL; + parser_ = parser; + initial_protocol_ = initial_protocol; + expect_incoming_crypto_ = false; + expect_outgoing_crypto_ = false; + expected_video_bandwidth_ = cricket::kAutoBandwidth; + expected_video_rtcp_mux_ = false; + } + + ~MediaSessionClientTest() { + delete client_; + delete sm_; + delete pa_; + delete nm_; + delete parser_; + ClearStanzas(); + } + + buzz::XmlElement* ActionFromStanza(buzz::XmlElement* stanza) { + return parser_->ActionFromStanza(stanza); + } + + buzz::XmlElement* ContentFromAction(buzz::XmlElement* action) { + return parser_->ContentFromAction(action); + } + + buzz::XmlElement* PayloadTypeFromContent(buzz::XmlElement* payload) { + return parser_->PayloadTypeFromContent(payload); + } + + buzz::XmlElement* NextFromPayloadType(buzz::XmlElement* payload_type) { + return parser_->NextFromPayloadType(payload_type); + } + + buzz::XmlElement* EncryptionFromContent(buzz::XmlElement* content) { + return parser_->EncryptionFromContent(content); + } + + buzz::XmlElement* NextFromEncryption(buzz::XmlElement* encryption) { + return parser_->NextFromEncryption(encryption); + } + + cricket::AudioCodec AudioCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + return parser_->AudioCodecFromPayloadType(payload_type); + } + + cricket::VideoCodec VideoCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + return parser_->VideoCodecFromPayloadType(payload_type); + } + + cricket::DataCodec DataCodecFromPayloadType( + const buzz::XmlElement* payload_type) { + return parser_->DataCodecFromPayloadType(payload_type); + } + + const AudioContentDescription* GetFirstAudioContentDescription( + const cricket::SessionDescription* sdesc) { + const cricket::ContentInfo* content = + cricket::GetFirstAudioContent(sdesc); + if (content == NULL) + return NULL; + return static_cast<const AudioContentDescription*>(content->description); + } + + const cricket::VideoContentDescription* GetFirstVideoContentDescription( + const cricket::SessionDescription* sdesc) { + const cricket::ContentInfo* content = + cricket::GetFirstVideoContent(sdesc); + if (content == NULL) + return NULL; + return static_cast<const cricket::VideoContentDescription*>( + content->description); + } + + void CheckCryptoFromGoodIncomingInitiate(const cricket::Session* session) { + ASSERT_TRUE(session != NULL); + const AudioContentDescription* content = + GetFirstAudioContentDescription(session->remote_description()); + ASSERT_TRUE(content != NULL); + ASSERT_EQ(2U, content->cryptos().size()); + ASSERT_EQ(145, content->cryptos()[0].tag); + ASSERT_EQ("AES_CM_128_HMAC_SHA1_32", content->cryptos()[0].cipher_suite); + ASSERT_EQ("inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9", + content->cryptos()[0].key_params); + ASSERT_EQ(51, content->cryptos()[1].tag); + ASSERT_EQ("AES_CM_128_HMAC_SHA1_80", content->cryptos()[1].cipher_suite); + ASSERT_EQ("inline:J4lfdUL8W1F7TNJKcbuygaQuA429SJy2e9JctPUy", + content->cryptos()[1].key_params); + } + + void CheckCryptoForGoodOutgoingAccept(const cricket::Session* session) { + const AudioContentDescription* content = + GetFirstAudioContentDescription(session->local_description()); + ASSERT_EQ(1U, content->cryptos().size()); + ASSERT_EQ(145, content->cryptos()[0].tag); + ASSERT_EQ("AES_CM_128_HMAC_SHA1_32", content->cryptos()[0].cipher_suite); + ASSERT_EQ(47U, content->cryptos()[0].key_params.size()); + } + + void CheckBadCryptoFromIncomingInitiate(const cricket::Session* session) { + const AudioContentDescription* content = + GetFirstAudioContentDescription(session->remote_description()); + ASSERT_EQ(1U, content->cryptos().size()); + ASSERT_EQ(145, content->cryptos()[0].tag); + ASSERT_EQ("NOT_SUPPORTED", content->cryptos()[0].cipher_suite); + ASSERT_EQ("inline:hsWuSQJxx7przmb8HM+ZkeNcG3HezSNID7LmfDa9", + content->cryptos()[0].key_params); + } + + void CheckNoCryptoForOutgoingAccept(const cricket::Session* session) { + const AudioContentDescription* content = + GetFirstAudioContentDescription(session->local_description()); + ASSERT_TRUE(content->cryptos().empty()); + } + + void CheckRtcpFb(const cricket::SessionDescription* sdesc) { + VerifyCodecFbParams<AudioCodec>(expected_audio_fb_params_, + GetFirstAudioContentDescription(sdesc)); + + VerifyCodecFbParams<VideoCodec>(expected_video_fb_params_, + GetFirstVideoContentDescription(sdesc)); + + VerifyCodecFbParams<DataCodec>(expected_data_fb_params_, + GetFirstDataContentDescription(sdesc)); + } + + void CheckVideoBandwidth(int expected_bandwidth, + const cricket::SessionDescription* sdesc) { + const cricket::VideoContentDescription* video = + GetFirstVideoContentDescription(sdesc); + if (video != NULL) { + ASSERT_EQ(expected_bandwidth, video->bandwidth()); + } + } + + void CheckVideoRtcpMux(bool expected_video_rtcp_mux, + const cricket::SessionDescription* sdesc) { + const cricket::VideoContentDescription* video = + GetFirstVideoContentDescription(sdesc); + if (video != NULL) { + ASSERT_EQ(expected_video_rtcp_mux, video->rtcp_mux()); + } + } + + virtual void CheckRtpDataContent(buzz::XmlElement* content) { + if (initial_protocol_) { + // Gingle can not write out data content. + return; + } + + buzz::XmlElement* e = PayloadTypeFromContent(content); + ASSERT_TRUE(e != NULL); + cricket::DataCodec codec = DataCodecFromPayloadType(e); + EXPECT_EQ(127, codec.id); + EXPECT_EQ("google-data", codec.name); + EXPECT_EQ(expected_data_fb_params_, codec.feedback_params); + + CheckDataRtcpMux(true, call_->sessions()[0]->local_description()); + CheckDataRtcpMux(true, call_->sessions()[0]->remote_description()); + if (expect_outgoing_crypto_) { + content = parser_->NextContent(content); + buzz::XmlElement* encryption = EncryptionFromContent(content); + ASSERT_TRUE(encryption != NULL); + // TODO(pthatcher): Check encryption parameters? + } + } + + virtual void CheckSctpDataContent(buzz::XmlElement* content) { + if (initial_protocol_) { + // Gingle can not write out data content. + return; + } + + buzz::XmlElement* payload_type = PayloadTypeFromContent(content); + ASSERT_TRUE(payload_type == NULL); + buzz::XmlElement* encryption = EncryptionFromContent(content); + ASSERT_TRUE(encryption == NULL); + // TODO(pthatcher): Check for <streams>. + } + + void CheckDataRtcpMux(bool expected_data_rtcp_mux, + const cricket::SessionDescription* sdesc) { + const cricket::DataContentDescription* data = + GetFirstDataContentDescription(sdesc); + if (data != NULL) { + ASSERT_EQ(expected_data_rtcp_mux, data->rtcp_mux()); + } + } + + void CheckAudioSsrcForIncomingAccept(const cricket::Session* session) { + const AudioContentDescription* audio = + GetFirstAudioContentDescription(session->remote_description()); + ASSERT_TRUE(audio != NULL); + ASSERT_EQ(kAudioSsrc, audio->first_ssrc()); + } + + void CheckVideoSsrcForIncomingAccept(const cricket::Session* session) { + const cricket::VideoContentDescription* video = + GetFirstVideoContentDescription(session->remote_description()); + ASSERT_TRUE(video != NULL); + ASSERT_EQ(kVideoSsrc, video->first_ssrc()); + } + + void CheckDataSsrcForIncomingAccept(const cricket::Session* session) { + const cricket::DataContentDescription* data = + GetFirstDataContentDescription(session->remote_description()); + ASSERT_TRUE(data != NULL); + ASSERT_EQ(kDataSsrc, data->first_ssrc()); + } + + void TestGoodIncomingInitiate(const std::string& initiate_string, + const cricket::CallOptions& options, + buzz::XmlElement** element) { + *element = NULL; + + rtc::scoped_ptr<buzz::XmlElement> el( + buzz::XmlElement::ForStr(initiate_string)); + client_->session_manager()->OnIncomingMessage(el.get()); + ASSERT_TRUE(call_ != NULL); + ASSERT_TRUE(call_->sessions()[0] != NULL); + ASSERT_EQ(cricket::Session::STATE_RECEIVEDINITIATE, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); + ClearStanzas(); + CheckVideoBandwidth(expected_video_bandwidth_, + call_->sessions()[0]->remote_description()); + CheckVideoRtcpMux(expected_video_rtcp_mux_, + call_->sessions()[0]->remote_description()); + CheckRtcpFb(call_->sessions()[0]->remote_description()); + if (expect_incoming_crypto_) { + CheckCryptoFromGoodIncomingInitiate(call_->sessions()[0]); + } + + // TODO(pthatcher): Add tests for sending <bandwidth> in accept. + call_->AcceptSession(call_->sessions()[0], options); + ASSERT_EQ(cricket::Session::STATE_SENTACCEPT, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); + + buzz::XmlElement* e = ActionFromStanza(stanzas_[0]); + ASSERT_TRUE(e != NULL); + ASSERT_TRUE(ContentFromAction(e) != NULL); + *element = CopyElement(ContentFromAction(e)); + ASSERT_TRUE(*element != NULL); + ClearStanzas(); + if (expect_outgoing_crypto_) { + CheckCryptoForGoodOutgoingAccept(call_->sessions()[0]); + } + + if (options.data_channel_type == cricket::DCT_RTP) { + CheckDataRtcpMux(true, call_->sessions()[0]->local_description()); + CheckDataRtcpMux(true, call_->sessions()[0]->remote_description()); + // TODO(pthatcher): Check rtcpmux and crypto? + } + + call_->Terminate(); + ASSERT_EQ(cricket::Session::STATE_SENTTERMINATE, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); + e = ActionFromStanza(stanzas_[0]); + ASSERT_TRUE(e != NULL); + ASSERT_TRUE(parser_->ActionIsTerminate(e)); + ClearStanzas(); + } + + void TestRejectOffer(const std::string &initiate_string, + const cricket::CallOptions& options, + buzz::XmlElement** element) { + *element = NULL; + + rtc::scoped_ptr<buzz::XmlElement> el( + buzz::XmlElement::ForStr(initiate_string)); + client_->session_manager()->OnIncomingMessage(el.get()); + ASSERT_TRUE(call_ != NULL); + ASSERT_TRUE(call_->sessions()[0] != NULL); + ASSERT_EQ(cricket::Session::STATE_RECEIVEDINITIATE, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); + ClearStanzas(); + + call_->AcceptSession(call_->sessions()[0], options); + ASSERT_EQ(cricket::Session::STATE_SENTACCEPT, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); + + buzz::XmlElement* e = ActionFromStanza(stanzas_[0]); + ASSERT_TRUE(e != NULL); + ASSERT_TRUE(ContentFromAction(e) != NULL); + *element = CopyElement(ContentFromAction(e)); + ASSERT_TRUE(*element != NULL); + ClearStanzas(); + + buzz::XmlElement* content = *element; + // The NextContent method actually returns the second content. So we + // can't handle the case when audio, video and data are all enabled. But + // since we are testing rejection, it won't be the case. + if (options.has_audio()) { + ASSERT_TRUE(content != NULL); + ASSERT_EQ("test audio", content->Attr(buzz::QName("", "name"))); + content = parser_->NextContent(content); + } + + if (options.has_video()) { + ASSERT_TRUE(content != NULL); + ASSERT_EQ("test video", content->Attr(buzz::QName("", "name"))); + content = parser_->NextContent(content); + } + + if (options.has_data()) { + ASSERT_TRUE(content != NULL); + ASSERT_EQ("test data", content->Attr(buzz::QName("", "name"))); + content = parser_->NextContent(content); + } + + call_->Terminate(); + ASSERT_EQ(cricket::Session::STATE_SENTTERMINATE, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); + e = ActionFromStanza(stanzas_[0]); + ASSERT_TRUE(e != NULL); + ASSERT_TRUE(parser_->ActionIsTerminate(e)); + ClearStanzas(); + } + + void TestBadIncomingInitiate(const std::string& initiate_string) { + rtc::scoped_ptr<buzz::XmlElement> el( + buzz::XmlElement::ForStr(initiate_string)); + client_->session_manager()->OnIncomingMessage(el.get()); + ASSERT_TRUE(call_ != NULL); + ASSERT_TRUE(call_->sessions()[0] != NULL); + ASSERT_EQ(cricket::Session::STATE_SENTREJECT, + call_->sessions()[0]->state()); + ASSERT_EQ(2U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[1]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[1]->Attr(buzz::QN_TYPE)); + ClearStanzas(); + } + + void VerifyAudioCodec(const AudioCodec& codec, int id, + const std::string& name, int clockrate, + int bitrate, int channels) { + ASSERT_EQ(id, codec.id); + ASSERT_EQ(name, codec.name); + ASSERT_EQ(clockrate, codec.clockrate); + ASSERT_EQ(bitrate, codec.bitrate); + ASSERT_EQ(channels, codec.channels); + ASSERT_EQ(expected_audio_fb_params_, codec.feedback_params); + } + + void TestGoodOutgoingInitiate(const cricket::CallOptions& options) { + if (initial_protocol_ == cricket::PROTOCOL_JINGLE) { + // rtcp fb is only implemented for jingle. + ExpectRtcpFb(); + } + + client_->CreateCall(); + ASSERT_TRUE(call_ != NULL); + call_->InitiateSession(buzz::Jid("me@mydomain.com"), + buzz::Jid("me@mydomain.com"), options); + ASSERT_TRUE(call_->sessions()[0] != NULL); + ASSERT_EQ(cricket::Session::STATE_SENTINITIATE, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); + buzz::XmlElement* action = ActionFromStanza(stanzas_[0]); + ASSERT_TRUE(action != NULL); + buzz::XmlElement* content = ContentFromAction(action); + ASSERT_TRUE(content != NULL); + + buzz::XmlElement* e = PayloadTypeFromContent(content); + ASSERT_TRUE(e != NULL); + cricket::AudioCodec codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 103, "ISAC", 16000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 104, "ISAC", 32000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 119, "ISACLC", 16000, 40000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 99, "speex", 16000, 22000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 97, "IPCMWB", 16000, 80000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 9, "G722", 8000, 64000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 102, "iLBC", 8000, 13300, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 98, "speex", 8000, 11000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 3, "GSM", 8000, 13000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 100, "EG711U", 8000, 64000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 101, "EG711A", 8000, 64000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 0, "PCMU", 8000, 64000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 8, "PCMA", 8000, 64000, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 126, "CN", 32000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 105, "CN", 16000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 13, "CN", 8000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 117, "red", 8000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + VerifyAudioCodec(codec, 106, "telephone-event", 8000, 0, 1); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e == NULL); + + if (expect_outgoing_crypto_) { + buzz::XmlElement* encryption = EncryptionFromContent(content); + ASSERT_TRUE(encryption != NULL); + + if (client_->secure() == cricket::SEC_REQUIRED) { + ASSERT_TRUE(cricket::GetXmlAttr( + encryption, cricket::QN_ENCRYPTION_REQUIRED, false)); + } + + if (content->Name().Namespace() == cricket::NS_GINGLE_AUDIO) { + e = encryption->FirstNamed(cricket::QN_GINGLE_AUDIO_CRYPTO_USAGE); + ASSERT_TRUE(e != NULL); + ASSERT_TRUE( + e->NextNamed(cricket::QN_GINGLE_AUDIO_CRYPTO_USAGE) == NULL); + ASSERT_TRUE( + e->FirstNamed(cricket::QN_GINGLE_VIDEO_CRYPTO_USAGE) == NULL); + } + + e = encryption->FirstNamed(cricket::QN_CRYPTO); + ASSERT_TRUE(e != NULL); + ASSERT_EQ("0", e->Attr(cricket::QN_CRYPTO_TAG)); + ASSERT_EQ("AES_CM_128_HMAC_SHA1_32", e->Attr(cricket::QN_CRYPTO_SUITE)); + std::string key_0 = e->Attr(cricket::QN_CRYPTO_KEY_PARAMS); + ASSERT_EQ(47U, key_0.length()); + ASSERT_EQ("inline:", key_0.substr(0, 7)); + + e = e->NextNamed(cricket::QN_CRYPTO); + ASSERT_TRUE(e != NULL); + ASSERT_EQ("1", e->Attr(cricket::QN_CRYPTO_TAG)); + ASSERT_EQ("AES_CM_128_HMAC_SHA1_80", e->Attr(cricket::QN_CRYPTO_SUITE)); + std::string key_1 = e->Attr(cricket::QN_CRYPTO_KEY_PARAMS); + ASSERT_EQ(47U, key_1.length()); + ASSERT_EQ("inline:", key_1.substr(0, 7)); + ASSERT_NE(key_0, key_1); + + encryption = NextFromEncryption(encryption); + ASSERT_TRUE(encryption == NULL); + } + + if (options.has_video()) { + CheckVideoBandwidth(options.video_bandwidth, + call_->sessions()[0]->local_description()); + CheckVideoRtcpMux(expected_video_rtcp_mux_, + call_->sessions()[0]->remote_description()); + content = parser_->NextContent(content); + const buzz::XmlElement* bandwidth = + parser_->BandwidthFromContent(content); + if (options.video_bandwidth == cricket::kAutoBandwidth) { + ASSERT_TRUE(bandwidth == NULL); + } else { + ASSERT_TRUE(bandwidth != NULL); + ASSERT_EQ("AS", bandwidth->Attr(buzz::QName("", "type"))); + ASSERT_EQ(rtc::ToString(options.video_bandwidth / 1000), + bandwidth->BodyText()); + } + + buzz::XmlElement* e = PayloadTypeFromContent(content); + ASSERT_TRUE(e != NULL); + VideoCodec codec = VideoCodecFromPayloadType(e); + VideoCodec expected_codec = kVideoCodecs[0]; + expected_codec.preference = codec.preference; + expected_codec.feedback_params = expected_video_fb_params_; + EXPECT_EQ(expected_codec, codec); + } + + if (options.data_channel_type == cricket::DCT_RTP) { + content = parser_->NextContent(content); + CheckRtpDataContent(content); + } + + if (options.data_channel_type == cricket::DCT_SCTP) { + content = parser_->NextContent(content); + CheckSctpDataContent(content); + } + + ClearStanzas(); + } + + void TestHasAllSupportedAudioCodecs(buzz::XmlElement* e) { + ASSERT_TRUE(e != NULL); + + e = PayloadTypeFromContent(e); + ASSERT_TRUE(e != NULL); + cricket::AudioCodec codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(103, codec.id); + ASSERT_EQ("ISAC", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(104, codec.id); + ASSERT_EQ("ISAC", codec.name); + ASSERT_EQ(32000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(119, codec.id); + ASSERT_EQ("ISACLC", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(40000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(99, codec.id); + ASSERT_EQ("speex", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(22000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(97, codec.id); + ASSERT_EQ("IPCMWB", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(80000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(9, codec.id); + ASSERT_EQ("G722", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(64000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(102, codec.id); + ASSERT_EQ("iLBC", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(13300, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(98, codec.id); + ASSERT_EQ("speex", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(11000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(3, codec.id); + ASSERT_EQ("GSM", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(13000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(100, codec.id); + ASSERT_EQ("EG711U", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(64000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(101, codec.id); + ASSERT_EQ("EG711A", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(64000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(0, codec.id); + ASSERT_EQ("PCMU", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(64000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(8, codec.id); + ASSERT_EQ("PCMA", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(64000, codec.bitrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(126, codec.id); + ASSERT_EQ("CN", codec.name); + ASSERT_EQ(32000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(105, codec.id); + ASSERT_EQ("CN", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(13, codec.id); + ASSERT_EQ("CN", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(117, codec.id); + ASSERT_EQ("red", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(106, codec.id); + ASSERT_EQ("telephone-event", codec.name); + ASSERT_EQ(8000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e == NULL); + } + + void TestCodecsOfVideoInitiate(buzz::XmlElement* content) { + ASSERT_TRUE(content != NULL); + buzz::XmlElement* payload_type = PayloadTypeFromContent(content); + ASSERT_TRUE(payload_type != NULL); + cricket::AudioCodec codec = AudioCodecFromPayloadType(payload_type); + ASSERT_EQ(103, codec.id); + ASSERT_EQ("ISAC", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + content = parser_->NextContent(content); + ASSERT_TRUE(content != NULL); + payload_type = PayloadTypeFromContent(content); + ASSERT_TRUE(payload_type != NULL); + cricket::VideoCodec vcodec = + parser_->VideoCodecFromPayloadType(payload_type); + ASSERT_EQ(99, vcodec.id); + ASSERT_EQ("H264-SVC", vcodec.name); + ASSERT_EQ(320, vcodec.width); + ASSERT_EQ(200, vcodec.height); + ASSERT_EQ(30, vcodec.framerate); + } + + void TestHasAudioCodecsFromInitiateSomeUnsupported(buzz::XmlElement* e) { + ASSERT_TRUE(e != NULL); + e = PayloadTypeFromContent(e); + ASSERT_TRUE(e != NULL); + + cricket::AudioCodec codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(103, codec.id); + ASSERT_EQ("ISAC", codec.name); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(100, codec.id); + ASSERT_EQ("EG711U", codec.name); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(101, codec.id); + ASSERT_EQ("EG711A", codec.name); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(0, codec.id); + ASSERT_EQ("PCMU", codec.name); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(13, codec.id); + ASSERT_EQ("CN", codec.name); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e == NULL); + } + + void TestHasAudioCodecsFromInitiateDynamicAudioCodecs( + buzz::XmlElement* e) { + ASSERT_TRUE(e != NULL); + e = PayloadTypeFromContent(e); + ASSERT_TRUE(e != NULL); + + cricket::AudioCodec codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(123, codec.id); + ASSERT_EQ(16000, codec.clockrate); + ASSERT_EQ(1, codec.channels); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e == NULL); + } + + void TestHasDefaultAudioCodecs(buzz::XmlElement* e) { + ASSERT_TRUE(e != NULL); + e = PayloadTypeFromContent(e); + ASSERT_TRUE(e != NULL); + + cricket::AudioCodec codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(103, codec.id); + ASSERT_EQ("ISAC", codec.name); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(0, codec.id); + ASSERT_EQ("PCMU", codec.name); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e == NULL); + } + + void TestHasAudioCodecsFromInitiateStaticAudioCodecs( + buzz::XmlElement* e) { + ASSERT_TRUE(e != NULL); + e = PayloadTypeFromContent(e); + ASSERT_TRUE(e != NULL); + + cricket::AudioCodec codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(3, codec.id); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(0, codec.id); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e != NULL); + codec = AudioCodecFromPayloadType(e); + ASSERT_EQ(8, codec.id); + + e = NextFromPayloadType(e); + ASSERT_TRUE(e == NULL); + } + + void TestGingleInitiateWithUnsupportedCrypto( + const std::string &initiate_string, + buzz::XmlElement** element) { + *element = NULL; + + rtc::scoped_ptr<buzz::XmlElement> el( + buzz::XmlElement::ForStr(initiate_string)); + client_->session_manager()->OnIncomingMessage(el.get()); + + ASSERT_EQ(cricket::Session::STATE_RECEIVEDINITIATE, + call_->sessions()[0]->state()); + ClearStanzas(); + CheckBadCryptoFromIncomingInitiate(call_->sessions()[0]); + + call_->AcceptSession(call_->sessions()[0], cricket::CallOptions()); + ClearStanzas(); + CheckNoCryptoForOutgoingAccept(call_->sessions()[0]); + + call_->Terminate(); + ASSERT_EQ(cricket::Session::STATE_SENTTERMINATE, + call_->sessions()[0]->state()); + ClearStanzas(); + } + + void TestIncomingAcceptWithSsrcs( + const std::string& accept_string, + cricket::CallOptions& options) { + client_->CreateCall(); + ASSERT_TRUE(call_ != NULL); + + call_->InitiateSession(buzz::Jid("me@mydomain.com"), + buzz::Jid("me@mydomain.com"), options); + ASSERT_TRUE(call_->sessions()[0] != NULL); + ASSERT_EQ(cricket::Session::STATE_SENTINITIATE, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_SET), stanzas_[0]->Attr(buzz::QN_TYPE)); + buzz::XmlElement* action = ActionFromStanza(stanzas_[0]); + ASSERT_TRUE(action != NULL); + buzz::XmlElement* content = ContentFromAction(action); + ASSERT_TRUE(content != NULL); + if (initial_protocol_ == cricket::PROTOCOL_JINGLE) { + buzz::XmlElement* content_desc = + content->FirstNamed(cricket::QN_JINGLE_RTP_CONTENT); + ASSERT_TRUE(content_desc != NULL); + ASSERT_EQ("", content_desc->Attr(cricket::QN_SSRC)); + } + ClearStanzas(); + + // We need to insert the session ID into the session accept message. + rtc::scoped_ptr<buzz::XmlElement> el( + buzz::XmlElement::ForStr(accept_string)); + const std::string sid = call_->sessions()[0]->id(); + if (initial_protocol_ == cricket::PROTOCOL_JINGLE) { + buzz::XmlElement* jingle = el->FirstNamed(cricket::QN_JINGLE); + jingle->SetAttr(cricket::QN_SID, sid); + } else { + buzz::XmlElement* session = el->FirstNamed(cricket::QN_GINGLE_SESSION); + session->SetAttr(cricket::QN_ID, sid); + } + + client_->session_manager()->OnIncomingMessage(el.get()); + + ASSERT_EQ(cricket::Session::STATE_RECEIVEDACCEPT, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_TRUE(buzz::QN_IQ == stanzas_[0]->Name()); + ASSERT_TRUE(stanzas_[0]->HasAttr(buzz::QN_TYPE)); + ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); + ClearStanzas(); + + CheckAudioSsrcForIncomingAccept(call_->sessions()[0]); + CheckVideoSsrcForIncomingAccept(call_->sessions()[0]); + if (options.data_channel_type == cricket::DCT_RTP) { + CheckDataSsrcForIncomingAccept(call_->sessions()[0]); + } + // TODO(pthatcher): Check kDataSid if DCT_SCTP. + // const uint32 kDataSid = 0; + } + + size_t ClearStanzas() { + size_t size = stanzas_.size(); + for (size_t i = 0; i < size; i++) { + delete stanzas_[i]; + } + stanzas_.clear(); + return size; + } + + buzz::XmlElement* SetJingleSid(buzz::XmlElement* stanza) { + buzz::XmlElement* jingle = + stanza->FirstNamed(cricket::QN_JINGLE); + jingle->SetAttr(cricket::QN_SID, call_->sessions()[0]->id()); + return stanza; + } + + void TestSendVideoStreamUpdate() { + cricket::CallOptions options = VideoCallOptions(); + options.is_muc = true; + + client_->CreateCall(); + call_->InitiateSession(buzz::Jid("me@mydomain.com"), + buzz::Jid("me@mydomain.com"), options); + ClearStanzas(); + + cricket::StreamParams stream; + stream.id = "test-stream"; + stream.ssrcs.push_back(1001); + rtc::scoped_ptr<buzz::XmlElement> expected_stream_add( + buzz::XmlElement::ForStr( + JingleOutboundStreamAdd( + call_->sessions()[0]->id(), + "video", stream.id, "1001"))); + rtc::scoped_ptr<buzz::XmlElement> expected_stream_remove( + buzz::XmlElement::ForStr( + JingleOutboundStreamRemove( + call_->sessions()[0]->id(), + "video", stream.id))); + + call_->SendVideoStreamUpdate(call_->sessions()[0], + call_->CreateVideoStreamUpdate(stream)); + ASSERT_EQ(1U, stanzas_.size()); + EXPECT_EQ(expected_stream_add->Str(), stanzas_[0]->Str()); + ClearStanzas(); + + stream.ssrcs.clear(); + call_->SendVideoStreamUpdate(call_->sessions()[0], + call_->CreateVideoStreamUpdate(stream)); + ASSERT_EQ(1U, stanzas_.size()); + EXPECT_EQ(expected_stream_remove->Str(), stanzas_[0]->Str()); + ClearStanzas(); + } + + void TestStreamsUpdateAndViewRequests() { + cricket::CallOptions options = VideoCallOptions(); + options.is_muc = true; + + client_->CreateCall(); + call_->InitiateSession(buzz::Jid("me@mydomain.com"), + buzz::Jid("me@mydomain.com"), options); + ASSERT_EQ(1U, ClearStanzas()); + ASSERT_EQ(0U, last_streams_added_.audio().size()); + ASSERT_EQ(0U, last_streams_added_.video().size()); + ASSERT_EQ(0U, last_streams_removed_.audio().size()); + ASSERT_EQ(0U, last_streams_removed_.video().size()); + + rtc::scoped_ptr<buzz::XmlElement> accept_stanza( + buzz::XmlElement::ForStr(kJingleAcceptWithSsrcs)); + SetJingleSid(accept_stanza.get()); + client_->session_manager()->OnIncomingMessage(accept_stanza.get()); + ASSERT_EQ(cricket::Session::STATE_RECEIVEDACCEPT, + call_->sessions()[0]->state()); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_EQ(std::string(buzz::STR_RESULT), stanzas_[0]->Attr(buzz::QN_TYPE)); + ClearStanzas(); + // Need to clear the added streams, because they are populated when + // receiving an accept message now. + last_streams_added_.mutable_video()->clear(); + last_streams_added_.mutable_audio()->clear(); + + call_->sessions()[0]->SetState(cricket::Session::STATE_INPROGRESS); + + rtc::scoped_ptr<buzz::XmlElement> streams_stanza( + buzz::XmlElement::ForStr( + JingleStreamAdd("video", "Bob", "video1", "ABC"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + // First one is ignored because of bad syntax. + ASSERT_EQ(1U, stanzas_.size()); + // TODO(pthatcher): Figure out how to make this an ERROR rather than RESULT. + ASSERT_EQ(std::string(buzz::STR_ERROR), stanzas_[0]->Attr(buzz::QN_TYPE)); + ClearStanzas(); + ASSERT_EQ(0U, last_streams_added_.audio().size()); + ASSERT_EQ(0U, last_streams_added_.video().size()); + ASSERT_EQ(0U, last_streams_removed_.audio().size()); + ASSERT_EQ(0U, last_streams_removed_.video().size()); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAdd("audio", "Bob", "audio1", "1234"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_added_.audio().size()); + ASSERT_EQ("Bob", last_streams_added_.audio()[0].groupid); + ASSERT_EQ(1U, last_streams_added_.audio()[0].ssrcs.size()); + ASSERT_EQ(1234U, last_streams_added_.audio()[0].first_ssrc()); + + // Ignores adds without ssrcs. + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAddWithoutSsrc("audio", "Bob", "audioX"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_added_.audio().size()); + ASSERT_EQ(1234U, last_streams_added_.audio()[0].first_ssrc()); + + // Ignores stream updates with unknown content names. (Don't terminate). + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAddWithoutSsrc("foo", "Bob", "foo"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAdd("audio", "Joe", "audio1", "2468"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_added_.audio().size()); + ASSERT_EQ("Joe", last_streams_added_.audio()[0].groupid); + ASSERT_EQ(1U, last_streams_added_.audio()[0].ssrcs.size()); + ASSERT_EQ(2468U, last_streams_added_.audio()[0].first_ssrc()); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAdd("video", "Bob", "video1", "5678"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_added_.video().size()); + ASSERT_EQ("Bob", last_streams_added_.video()[0].groupid); + ASSERT_EQ(1U, last_streams_added_.video()[0].ssrcs.size()); + ASSERT_EQ(5678U, last_streams_added_.video()[0].first_ssrc()); + + // We're testing that a "duplicate" is effectively ignored. + last_streams_added_.mutable_video()->clear(); + last_streams_removed_.mutable_video()->clear(); + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAdd("video", "Bob", "video1", "5678"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(0U, last_streams_added_.video().size()); + ASSERT_EQ(0U, last_streams_removed_.video().size()); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamAdd("video", "Bob", "video2", "5679"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_added_.video().size()); + ASSERT_EQ("Bob", last_streams_added_.video()[0].groupid); + ASSERT_EQ(1U, last_streams_added_.video()[0].ssrcs.size()); + ASSERT_EQ(5679U, last_streams_added_.video()[0].first_ssrc()); + + cricket::FakeVoiceMediaChannel* voice_channel = fme_->GetVoiceChannel(0); + ASSERT_TRUE(voice_channel != NULL); + ASSERT_TRUE(voice_channel->HasRecvStream(1234U)); + ASSERT_TRUE(voice_channel->HasRecvStream(2468U)); + cricket::FakeVideoMediaChannel* video_channel = fme_->GetVideoChannel(0); + ASSERT_TRUE(video_channel != NULL); + ASSERT_TRUE(video_channel->HasRecvStream(5678U)); + ClearStanzas(); + + cricket::ViewRequest viewRequest; + cricket::StaticVideoView staticVideoView( + cricket::StreamSelector(5678U), 640, 480, 30); + viewRequest.static_video_views.push_back(staticVideoView); + rtc::scoped_ptr<buzz::XmlElement> expected_view_elem( + buzz::XmlElement::ForStr(JingleView("5678", "640", "480", "30"))); + SetJingleSid(expected_view_elem.get()); + + ASSERT_TRUE( + call_->SendViewRequest(call_->sessions()[0], viewRequest)); + ASSERT_EQ(1U, stanzas_.size()); + ASSERT_EQ(expected_view_elem->Str(), stanzas_[0]->Str()); + ClearStanzas(); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamRemove("audio", "Bob", "audio1"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_removed_.audio().size()); + ASSERT_EQ(1U, last_streams_removed_.audio()[0].ssrcs.size()); + EXPECT_EQ(1234U, last_streams_removed_.audio()[0].first_ssrc()); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamRemove("video", "Bob", "video1"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_removed_.video().size()); + ASSERT_EQ(1U, last_streams_removed_.video()[0].ssrcs.size()); + EXPECT_EQ(5678U, last_streams_removed_.video()[0].first_ssrc()); + + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamRemove("video", "Bob", "video2"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(1U, last_streams_removed_.video().size()); + ASSERT_EQ(1U, last_streams_removed_.video()[0].ssrcs.size()); + EXPECT_EQ(5679U, last_streams_removed_.video()[0].first_ssrc()); + + // Duplicate removal: should be ignored. + last_streams_removed_.mutable_audio()->clear(); + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamRemove("audio", "Bob", "audio1"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(0U, last_streams_removed_.audio().size()); + + // Duplicate removal: should be ignored. + last_streams_removed_.mutable_video()->clear(); + streams_stanza.reset(buzz::XmlElement::ForStr( + JingleStreamRemove("video", "Bob", "video1"))); + SetJingleSid(streams_stanza.get()); + client_->session_manager()->OnIncomingMessage(streams_stanza.get()); + ASSERT_EQ(0U, last_streams_removed_.video().size()); + + voice_channel = fme_->GetVoiceChannel(0); + ASSERT_TRUE(voice_channel != NULL); + ASSERT_FALSE(voice_channel->HasRecvStream(1234U)); + ASSERT_TRUE(voice_channel->HasRecvStream(2468U)); + video_channel = fme_->GetVideoChannel(0); + ASSERT_TRUE(video_channel != NULL); + ASSERT_FALSE(video_channel->HasRecvStream(5678U)); + + // Fails because ssrc is now invalid. + ASSERT_FALSE( + call_->SendViewRequest(call_->sessions()[0], viewRequest)); + + ClearStanzas(); + } + + void MakeSignalingSecure(cricket::SecurePolicy secure) { + client_->set_secure(secure); + } + + void ExpectCrypto(cricket::SecurePolicy secure) { + MakeSignalingSecure(secure); + expect_incoming_crypto_ = true; +#ifdef HAVE_SRTP + expect_outgoing_crypto_ = true; +#endif + } + + void ExpectVideoBandwidth(int bandwidth) { + expected_video_bandwidth_ = bandwidth; + } + + void ExpectVideoRtcpMux(bool rtcp_mux) { + expected_video_rtcp_mux_ = rtcp_mux; + } + + template <class C> + void SetCodecFeedbackParams(std::vector<C>* codecs, + const FeedbackParams& fb_params) { + for (size_t i = 0; i < codecs->size(); ++i) { + codecs->at(i).feedback_params = fb_params; + } + } + + void ExpectRtcpFb() { + FeedbackParams params_nack_fir; + params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamCcm, + cricket::kRtcpFbCcmParamFir)); + params_nack_fir.Add(FeedbackParam(cricket::kRtcpFbParamNack)); + + FeedbackParams params_nack; + params_nack.Add(FeedbackParam(cricket::kRtcpFbParamNack)); + + expected_audio_fb_params_ = params_nack; + expected_video_fb_params_ = params_nack_fir; + expected_data_fb_params_ = params_nack; + } + + cricket::FakeMediaEngine* fme() { return fme_; } + + private: + void OnSendStanza(cricket::SessionManager* manager, + const buzz::XmlElement* stanza) { + LOG(LS_INFO) << stanza->Str(); + stanzas_.push_back(new buzz::XmlElement(*stanza)); + } + + void OnSessionCreate(cricket::Session* session, bool initiate) { + session->set_current_protocol(initial_protocol_); + } + + void OnCallCreate(cricket::Call *call) { + call_ = call; + call->SignalMediaStreamsUpdate.connect( + this, &MediaSessionClientTest::OnMediaStreamsUpdate); + } + + void OnCallDestroy(cricket::Call *call) { + call_ = NULL; + } + + void OnMediaStreamsUpdate(cricket::Call *call, + cricket::Session *session, + const cricket::MediaStreams& added, + const cricket::MediaStreams& removed) { + last_streams_added_.CopyFrom(added); + last_streams_removed_.CopyFrom(removed); + } + + rtc::NetworkManager* nm_; + cricket::PortAllocator* pa_; + cricket::SessionManager* sm_; + cricket::FakeMediaEngine* fme_; + cricket::FakeDataEngine* fdme_; + cricket::MediaSessionClient* client_; + + cricket::Call* call_; + std::vector<buzz::XmlElement* > stanzas_; + MediaSessionTestParser* parser_; + cricket::SignalingProtocol initial_protocol_; + bool expect_incoming_crypto_; + bool expect_outgoing_crypto_; + int expected_video_bandwidth_; + bool expected_video_rtcp_mux_; + FeedbackParams expected_audio_fb_params_; + FeedbackParams expected_video_fb_params_; + FeedbackParams expected_data_fb_params_; + cricket::MediaStreams last_streams_added_; + cricket::MediaStreams last_streams_removed_; +}; + +MediaSessionClientTest* GingleTest() { + return new MediaSessionClientTest(new GingleSessionTestParser(), + cricket::PROTOCOL_GINGLE); +} + +MediaSessionClientTest* JingleTest() { + return new MediaSessionClientTest(new JingleSessionTestParser(), + cricket::PROTOCOL_JINGLE); +} + +class MediaSessionTest : public ::testing::Test {}; + +TEST_F(MediaSessionTest, JingleGoodInitiateWithRtcpFb) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + + cricket::CallOptions options = VideoCallOptions(); + options.data_channel_type = cricket::DCT_SCTP; + test->ExpectRtcpFb(); + test->TestGoodIncomingInitiate( + kJingleInitiateWithRtcpFb, options, elem.use()); +} + +TEST_F(MediaSessionTest, JingleGoodVideoInitiate) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kJingleVideoInitiate, VideoCallOptions(), elem.use()); + test->TestCodecsOfVideoInitiate(elem.get()); +} + +TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithBandwidth) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->ExpectVideoBandwidth(42000); + test->TestGoodIncomingInitiate( + kJingleVideoInitiateWithBandwidth, VideoCallOptions(), elem.use()); +} + +TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithRtcpMux) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->ExpectVideoRtcpMux(true); + test->TestGoodIncomingInitiate( + kJingleVideoInitiateWithRtcpMux, VideoCallOptions(), elem.use()); +} + +TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithRtpData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + cricket::CallOptions options = VideoCallOptions(); + options.data_channel_type = cricket::DCT_RTP; + test->TestGoodIncomingInitiate( + AddEncryption(kJingleVideoInitiateWithRtpData, kJingleCryptoOffer), + options, + elem.use()); +} + +TEST_F(MediaSessionTest, JingleGoodVideoInitiateWithSctpData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + cricket::CallOptions options = VideoCallOptions(); + options.data_channel_type = cricket::DCT_SCTP; + test->TestGoodIncomingInitiate(kJingleVideoInitiateWithSctpData, + options, + elem.use()); +} + +TEST_F(MediaSessionTest, JingleRejectAudio) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + cricket::CallOptions options = VideoCallOptions(); + options.recv_audio = false; + options.data_channel_type = cricket::DCT_RTP; + test->TestRejectOffer(kJingleVideoInitiateWithRtpData, options, elem.use()); +} + +TEST_F(MediaSessionTest, JingleRejectVideo) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + cricket::CallOptions options = AudioCallOptions(); + options.data_channel_type = cricket::DCT_RTP; + test->TestRejectOffer(kJingleVideoInitiateWithRtpData, options, elem.use()); +} + +TEST_F(MediaSessionTest, JingleRejectData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestRejectOffer( + kJingleVideoInitiateWithRtpData, VideoCallOptions(), elem.use()); +} + +TEST_F(MediaSessionTest, JingleRejectVideoAndData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestRejectOffer( + kJingleVideoInitiateWithRtpData, AudioCallOptions(), elem.use()); +} + +TEST_F(MediaSessionTest, JingleGoodInitiateAllSupportedAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kJingleInitiate, AudioCallOptions(), elem.use()); + test->TestHasAllSupportedAudioCodecs(elem.get()); +} + +// Changes the codecs that our FakeMediaEngine will support with a different +// preference order than the incoming offer. +// Verifies the answer accepts the preference order of the remote peer. +TEST_F(MediaSessionTest, JingleGoodInitiateDifferentPreferenceAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->fme()->SetAudioCodecs(MAKE_VECTOR(kAudioCodecsDifferentPreference)); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kJingleInitiate, AudioCallOptions(), elem.use()); + test->TestHasAllSupportedAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, JingleGoodInitiateSomeUnsupportedAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kJingleInitiateSomeUnsupported, AudioCallOptions(), elem.use()); + test->TestHasAudioCodecsFromInitiateSomeUnsupported(elem.get()); +} + +TEST_F(MediaSessionTest, JingleGoodInitiateDynamicAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kJingleInitiateDynamicAudioCodecs, AudioCallOptions(), elem.use()); + test->TestHasAudioCodecsFromInitiateDynamicAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, JingleGoodInitiateStaticAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kJingleInitiateStaticAudioCodecs, AudioCallOptions(), elem.use()); + test->TestHasAudioCodecsFromInitiateStaticAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, JingleBadInitiateNoAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(kJingleInitiateNoAudioCodecs); +} + +TEST_F(MediaSessionTest, JingleBadInitiateNoSupportedAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(kJingleInitiateNoSupportedAudioCodecs); +} + +TEST_F(MediaSessionTest, JingleBadInitiateWrongClockrates) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(kJingleInitiateWrongClockrates); +} + +TEST_F(MediaSessionTest, JingleBadInitiateWrongChannels) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(kJingleInitiateWrongChannels); +} + +TEST_F(MediaSessionTest, JingleBadInitiateNoPayloadTypes) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(kJingleInitiateNoPayloadTypes); +} + +TEST_F(MediaSessionTest, JingleBadInitiateDynamicWithoutNames) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(kJingleInitiateDynamicWithoutNames); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiate) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestGoodOutgoingInitiate(AudioCallOptions()); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithBandwidth) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.video_bandwidth = 42000; + test->TestGoodOutgoingInitiate(options); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithRtcpMux) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.rtcp_mux_enabled = true; + test->TestGoodOutgoingInitiate(options); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithRtpData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options; + options.data_channel_type = cricket::DCT_RTP; + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodOutgoingInitiate(options); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithSctpData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options; + options.data_channel_type = cricket::DCT_SCTP; + test->TestGoodOutgoingInitiate(options); +} + +// Crypto related tests. + +// Offer has crypto but the session is not secured, just ignore it. +TEST_F(MediaSessionTest, JingleInitiateWithCryptoIsIgnoredWhenNotSecured) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + AddEncryption(kJingleVideoInitiate, kJingleCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has crypto required but the session is not secure, fail. +TEST_F(MediaSessionTest, JingleInitiateWithCryptoRequiredWhenNotSecured) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate(AddEncryption(kJingleVideoInitiate, + kJingleRequiredCryptoOffer)); +} + +// Offer has no crypto but the session is secure required, fail. +TEST_F(MediaSessionTest, JingleInitiateWithNoCryptoFailsWhenSecureRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->ExpectCrypto(cricket::SEC_REQUIRED); + test->TestBadIncomingInitiate(kJingleInitiate); +} + +// Offer has crypto and session is secure, expect crypto in the answer. +TEST_F(MediaSessionTest, JingleInitiateWithCryptoWhenSecureEnabled) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodIncomingInitiate( + AddEncryption(kJingleVideoInitiate, kJingleCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has crypto and session is secure required, expect crypto in +// the answer. +TEST_F(MediaSessionTest, JingleInitiateWithCryptoWhenSecureRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->ExpectCrypto(cricket::SEC_REQUIRED); + test->TestGoodIncomingInitiate( + AddEncryption(kJingleVideoInitiate, kJingleCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has unsupported crypto and session is secure, no crypto in +// the answer. +TEST_F(MediaSessionTest, JingleInitiateWithUnsupportedCrypto) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->MakeSignalingSecure(cricket::SEC_ENABLED); + test->TestGoodIncomingInitiate( + AddEncryption(kJingleInitiate, kJingleUnsupportedCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has unsupported REQUIRED crypto and session is not secure, fail. +TEST_F(MediaSessionTest, JingleInitiateWithRequiredUnsupportedCrypto) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestBadIncomingInitiate( + AddEncryption(kJingleInitiate, kJingleRequiredUnsupportedCryptoOffer)); +} + +// Offer has unsupported REQUIRED crypto and session is secure, fail. +TEST_F(MediaSessionTest, + JingleInitiateWithRequiredUnsupportedCryptoWhenSecure) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->MakeSignalingSecure(cricket::SEC_ENABLED); + test->TestBadIncomingInitiate( + AddEncryption(kJingleInitiate, kJingleRequiredUnsupportedCryptoOffer)); +} + +// Offer has unsupported REQUIRED crypto and session is required secure, fail. +TEST_F(MediaSessionTest, + JingleInitiateWithRequiredUnsupportedCryptoWhenSecureRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->MakeSignalingSecure(cricket::SEC_REQUIRED); + test->TestBadIncomingInitiate( + AddEncryption(kJingleInitiate, kJingleRequiredUnsupportedCryptoOffer)); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithCrypto) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodOutgoingInitiate(AudioCallOptions()); +} + +TEST_F(MediaSessionTest, JingleGoodOutgoingInitiateWithCryptoRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->ExpectCrypto(cricket::SEC_REQUIRED); + test->TestGoodOutgoingInitiate(AudioCallOptions()); +} + +TEST_F(MediaSessionTest, JingleIncomingAcceptWithSsrcs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.is_muc = true; + test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithSsrcs, options); +} + +TEST_F(MediaSessionTest, JingleIncomingAcceptWithRtpDataSsrcs) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.is_muc = true; + options.data_channel_type = cricket::DCT_RTP; + test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithRtpDataSsrcs, options); +} + +TEST_F(MediaSessionTest, JingleIncomingAcceptWithSctpData) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.is_muc = true; + options.data_channel_type = cricket::DCT_SCTP; + test->TestIncomingAcceptWithSsrcs(kJingleAcceptWithSctpData, options); +} + +TEST_F(MediaSessionTest, JingleStreamsUpdateAndView) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestStreamsUpdateAndViewRequests(); +} + +TEST_F(MediaSessionTest, JingleSendVideoStreamUpdate) { + rtc::scoped_ptr<MediaSessionClientTest> test(JingleTest()); + test->TestSendVideoStreamUpdate(); +} + +// Gingle tests + +TEST_F(MediaSessionTest, GingleGoodVideoInitiate) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + kGingleVideoInitiate, VideoCallOptions(), elem.use()); + test->TestCodecsOfVideoInitiate(elem.get()); +} + +TEST_F(MediaSessionTest, GingleGoodVideoInitiateWithBandwidth) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectVideoBandwidth(42000); + test->TestGoodIncomingInitiate( + kGingleVideoInitiateWithBandwidth, VideoCallOptions(), elem.use()); +} + +TEST_F(MediaSessionTest, GingleGoodInitiateAllSupportedAudioCodecs) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + kGingleInitiate, AudioCallOptions(), elem.use()); + test->TestHasAllSupportedAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, GingleGoodInitiateAllSupportedAudioCodecsWithCrypto) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleCryptoOffer), + AudioCallOptions(), + elem.use()); + test->TestHasAllSupportedAudioCodecs(elem.get()); +} + +// Changes the codecs that our FakeMediaEngine will support with a different +// preference order than the incoming offer. +// Verifies the answer accepts the preference order of the remote peer. +TEST_F(MediaSessionTest, GingleGoodInitiateDifferentPreferenceAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->fme()->SetAudioCodecs(MAKE_VECTOR(kAudioCodecsDifferentPreference)); + rtc::scoped_ptr<buzz::XmlElement> elem; + test->TestGoodIncomingInitiate( + kGingleInitiate, AudioCallOptions(), elem.use()); + test->TestHasAllSupportedAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, GingleGoodInitiateSomeUnsupportedAudioCodecs) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + kGingleInitiateSomeUnsupported, AudioCallOptions(), elem.use()); + test->TestHasAudioCodecsFromInitiateSomeUnsupported(elem.get()); +} + +TEST_F(MediaSessionTest, GingleGoodInitiateDynamicAudioCodecs) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + kGingleInitiateDynamicAudioCodecs, AudioCallOptions(), elem.use()); + test->TestHasAudioCodecsFromInitiateDynamicAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, GingleGoodInitiateStaticAudioCodecs) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + kGingleInitiateStaticAudioCodecs, AudioCallOptions(), elem.use()); + test->TestHasAudioCodecsFromInitiateStaticAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, GingleGoodInitiateNoAudioCodecs) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + kGingleInitiateNoAudioCodecs, AudioCallOptions(), elem.use()); + test->TestHasDefaultAudioCodecs(elem.get()); +} + +TEST_F(MediaSessionTest, GingleBadInitiateNoSupportedAudioCodecs) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate(kGingleInitiateNoSupportedAudioCodecs); +} + +TEST_F(MediaSessionTest, GingleBadInitiateWrongClockrates) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate(kGingleInitiateWrongClockrates); +} + +TEST_F(MediaSessionTest, GingleBadInitiateWrongChannels) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate(kGingleInitiateWrongChannels); +} + +TEST_F(MediaSessionTest, GingleBadInitiateNoPayloadTypes) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate(kGingleInitiateNoPayloadTypes); +} + +TEST_F(MediaSessionTest, GingleBadInitiateDynamicWithoutNames) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate(kGingleInitiateDynamicWithoutNames); +} + +TEST_F(MediaSessionTest, GingleGoodOutgoingInitiate) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodOutgoingInitiate(AudioCallOptions()); +} + +TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithBandwidth) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.video_bandwidth = 42000; + test->TestGoodOutgoingInitiate(options); +} + +// Crypto related tests. + +// Offer has crypto but the session is not secured, just ignore it. +TEST_F(MediaSessionTest, GingleInitiateWithCryptoIsIgnoredWhenNotSecured) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestGoodIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has crypto required but the session is not secure, fail. +TEST_F(MediaSessionTest, GingleInitiateWithCryptoRequiredWhenNotSecured) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate(AddEncryption(kGingleInitiate, + kGingleRequiredCryptoOffer)); +} + +// Offer has no crypto but the session is secure required, fail. +TEST_F(MediaSessionTest, GingleInitiateWithNoCryptoFailsWhenSecureRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectCrypto(cricket::SEC_REQUIRED); + test->TestBadIncomingInitiate(kGingleInitiate); +} + +// Offer has crypto and session is secure, expect crypto in the answer. +TEST_F(MediaSessionTest, GingleInitiateWithCryptoWhenSecureEnabled) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has crypto and session is secure required, expect crypto in +// the answer. +TEST_F(MediaSessionTest, GingleInitiateWithCryptoWhenSecureRequired) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectCrypto(cricket::SEC_REQUIRED); + test->TestGoodIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has unsupported crypto and session is secure, no crypto in +// the answer. +TEST_F(MediaSessionTest, GingleInitiateWithUnsupportedCrypto) { + rtc::scoped_ptr<buzz::XmlElement> elem; + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->MakeSignalingSecure(cricket::SEC_ENABLED); + test->TestGoodIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleUnsupportedCryptoOffer), + VideoCallOptions(), + elem.use()); +} + +// Offer has unsupported REQUIRED crypto and session is not secure, fail. +TEST_F(MediaSessionTest, GingleInitiateWithRequiredUnsupportedCrypto) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->TestBadIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleRequiredUnsupportedCryptoOffer)); +} + +// Offer has unsupported REQUIRED crypto and session is secure, fail. +TEST_F(MediaSessionTest, + GingleInitiateWithRequiredUnsupportedCryptoWhenSecure) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->MakeSignalingSecure(cricket::SEC_ENABLED); + test->TestBadIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleRequiredUnsupportedCryptoOffer)); +} + +// Offer has unsupported REQUIRED crypto and session is required secure, fail. +TEST_F(MediaSessionTest, + GingleInitiateWithRequiredUnsupportedCryptoWhenSecureRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->MakeSignalingSecure(cricket::SEC_REQUIRED); + test->TestBadIncomingInitiate( + AddEncryption(kGingleInitiate, kGingleRequiredUnsupportedCryptoOffer)); +} + +TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithCrypto) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodOutgoingInitiate(AudioCallOptions()); +} + +TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithCryptoRequired) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + test->ExpectCrypto(cricket::SEC_REQUIRED); + test->TestGoodOutgoingInitiate(AudioCallOptions()); +} + +TEST_F(MediaSessionTest, GingleIncomingAcceptWithSsrcs) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + cricket::CallOptions options = VideoCallOptions(); + options.is_muc = true; + test->TestIncomingAcceptWithSsrcs(kGingleAcceptWithSsrcs, options); +} + +TEST_F(MediaSessionTest, GingleGoodOutgoingInitiateWithRtpData) { + rtc::scoped_ptr<MediaSessionClientTest> test(GingleTest()); + cricket::CallOptions options; + options.data_channel_type = cricket::DCT_RTP; + test->ExpectCrypto(cricket::SEC_ENABLED); + test->TestGoodOutgoingInitiate(options); +} |