webrtcsdp_unittest.cc revision 5e760e7b94af0ecf3abbb793a793c2c551badece
1/*
2 * libjingle
3 * Copyright 2011, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <set>
29#include <string>
30#include <vector>
31
32#include "talk/app/webrtc/jsepsessiondescription.h"
33#include "talk/app/webrtc/webrtcsdp.h"
34#include "talk/base/gunit.h"
35#include "talk/base/logging.h"
36#include "talk/base/messagedigest.h"
37#include "talk/base/scoped_ptr.h"
38#include "talk/base/sslfingerprint.h"
39#include "talk/base/stringencode.h"
40#include "talk/base/stringutils.h"
41#include "talk/media/base/constants.h"
42#include "talk/p2p/base/constants.h"
43#include "talk/session/media/mediasession.h"
44
45using cricket::AudioCodec;
46using cricket::AudioContentDescription;
47using cricket::Candidate;
48using cricket::ContentInfo;
49using cricket::CryptoParams;
50using cricket::ContentGroup;
51using cricket::DataCodec;
52using cricket::DataContentDescription;
53using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
54using cricket::ICE_CANDIDATE_COMPONENT_RTP;
55using cricket::kFecSsrcGroupSemantics;
56using cricket::LOCAL_PORT_TYPE;
57using cricket::NS_JINGLE_DRAFT_SCTP;
58using cricket::NS_JINGLE_ICE_UDP;
59using cricket::NS_JINGLE_RTP;
60using cricket::RtpHeaderExtension;
61using cricket::RELAY_PORT_TYPE;
62using cricket::SessionDescription;
63using cricket::StreamParams;
64using cricket::STUN_PORT_TYPE;
65using cricket::TransportDescription;
66using cricket::TransportInfo;
67using cricket::VideoCodec;
68using cricket::VideoContentDescription;
69using webrtc::IceCandidateCollection;
70using webrtc::IceCandidateInterface;
71using webrtc::JsepIceCandidate;
72using webrtc::JsepSessionDescription;
73using webrtc::SdpParseError;
74using webrtc::SessionDescriptionInterface;
75
76typedef std::vector<AudioCodec> AudioCodecs;
77typedef std::vector<Candidate> Candidates;
78
79static const uint32 kDefaultSctpPort = 5000;
80static const char kSessionTime[] = "t=0 0\r\n";
81static const uint32 kCandidatePriority = 2130706432U;  // pref = 1.0
82static const char kCandidateUfragVoice[] = "ufrag_voice";
83static const char kCandidatePwdVoice[] = "pwd_voice";
84static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n";
85static const char kCandidateUfragVideo[] = "ufrag_video";
86static const char kCandidatePwdVideo[] = "pwd_video";
87static const char kCandidateUfragData[] = "ufrag_data";
88static const char kCandidatePwdData[] = "pwd_data";
89static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n";
90static const uint32 kCandidateGeneration = 2;
91static const char kCandidateFoundation1[] = "a0+B/1";
92static const char kCandidateFoundation2[] = "a0+B/2";
93static const char kCandidateFoundation3[] = "a0+B/3";
94static const char kCandidateFoundation4[] = "a0+B/4";
95static const char kAttributeCryptoVoice[] =
96    "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
97    "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
98    "dummy_session_params\r\n";
99static const char kAttributeCryptoVideo[] =
100    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
101    "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n";
102static const char kFingerprint[] = "a=fingerprint:sha-1 "
103    "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n";
104static const int kExtmapId = 1;
105static const char kExtmapUri[] = "http://example.com/082005/ext.htm#ttime";
106static const char kExtmap[] =
107    "a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n";
108static const char kExtmapWithDirectionAndAttribute[] =
109    "a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n";
110
111static const uint8 kIdentityDigest[] = {0x4A, 0xAD, 0xB9, 0xB1,
112                                        0x3F, 0x82, 0x18, 0x3B,
113                                        0x54, 0x02, 0x12, 0xDF,
114                                        0x3E, 0x5D, 0x49, 0x6B,
115                                        0x19, 0xE5, 0x7C, 0xAB};
116
117struct CodecParams {
118  int max_ptime;
119  int ptime;
120  int min_ptime;
121  int sprop_stereo;
122  int stereo;
123  int useinband;
124  int maxaveragebitrate;
125};
126
127// Reference sdp string
128static const char kSdpFullString[] =
129    "v=0\r\n"
130    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
131    "s=-\r\n"
132    "t=0 0\r\n"
133    "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
134    "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
135    "c=IN IP4 74.125.127.126\r\n"
136    "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
137    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
138    "generation 2\r\n"
139    "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
140    "generation 2\r\n"
141    "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
142    "generation 2\r\n"
143    "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
144    "generation 2\r\n"
145    "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
146    "raddr 192.168.1.5 rport 2346 "
147    "generation 2\r\n"
148    "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
149    "raddr 192.168.1.5 rport 2348 "
150    "generation 2\r\n"
151    "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
152    "a=mid:audio_content_name\r\n"
153    "a=sendrecv\r\n"
154    "a=rtcp-mux\r\n"
155    "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
156    "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
157    "dummy_session_params\r\n"
158    "a=rtpmap:111 opus/48000/2\r\n"
159    "a=rtpmap:103 ISAC/16000\r\n"
160    "a=rtpmap:104 CELT/32000/2\r\n"
161    "a=ssrc:1 cname:stream_1_cname\r\n"
162    "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
163    "a=ssrc:1 mslabel:local_stream_1\r\n"
164    "a=ssrc:1 label:audio_track_id_1\r\n"
165    "a=ssrc:4 cname:stream_2_cname\r\n"
166    "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
167    "a=ssrc:4 mslabel:local_stream_2\r\n"
168    "a=ssrc:4 label:audio_track_id_2\r\n"
169    "m=video 3457 RTP/SAVPF 120\r\n"
170    "c=IN IP4 74.125.224.39\r\n"
171    "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
172    "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
173    "generation 2\r\n"
174    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
175    "generation 2\r\n"
176    "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
177    "generation 2\r\n"
178    "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
179    "generation 2\r\n"
180    "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
181    "generation 2\r\n"
182    "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
183    "generation 2\r\n"
184    "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
185    "a=mid:video_content_name\r\n"
186    "a=sendrecv\r\n"
187    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
188    "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
189    "a=rtpmap:120 VP8/90000\r\n"
190    "a=ssrc:2 cname:stream_1_cname\r\n"
191    "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
192    "a=ssrc:2 mslabel:local_stream_1\r\n"
193    "a=ssrc:2 label:video_track_id_1\r\n"
194    "a=ssrc:3 cname:stream_1_cname\r\n"
195    "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n"
196    "a=ssrc:3 mslabel:local_stream_1\r\n"
197    "a=ssrc:3 label:video_track_id_2\r\n"
198    "a=ssrc-group:FEC 5 6\r\n"
199    "a=ssrc:5 cname:stream_2_cname\r\n"
200    "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n"
201    "a=ssrc:5 mslabel:local_stream_2\r\n"
202    "a=ssrc:5 label:video_track_id_3\r\n"
203    "a=ssrc:6 cname:stream_2_cname\r\n"
204    "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
205    "a=ssrc:6 mslabel:local_stream_2\r\n"
206    "a=ssrc:6 label:video_track_id_3\r\n";
207
208// SDP reference string without the candidates.
209static const char kSdpString[] =
210    "v=0\r\n"
211    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
212    "s=-\r\n"
213    "t=0 0\r\n"
214    "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
215    "m=audio 1 RTP/SAVPF 111 103 104\r\n"
216    "c=IN IP4 0.0.0.0\r\n"
217    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
218    "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
219    "a=mid:audio_content_name\r\n"
220    "a=sendrecv\r\n"
221    "a=rtcp-mux\r\n"
222    "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
223    "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
224    "dummy_session_params\r\n"
225    "a=rtpmap:111 opus/48000/2\r\n"
226    "a=rtpmap:103 ISAC/16000\r\n"
227    "a=rtpmap:104 CELT/32000/2\r\n"
228    "a=ssrc:1 cname:stream_1_cname\r\n"
229    "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
230    "a=ssrc:1 mslabel:local_stream_1\r\n"
231    "a=ssrc:1 label:audio_track_id_1\r\n"
232    "a=ssrc:4 cname:stream_2_cname\r\n"
233    "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
234    "a=ssrc:4 mslabel:local_stream_2\r\n"
235    "a=ssrc:4 label:audio_track_id_2\r\n"
236    "m=video 1 RTP/SAVPF 120\r\n"
237    "c=IN IP4 0.0.0.0\r\n"
238    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
239    "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
240    "a=mid:video_content_name\r\n"
241    "a=sendrecv\r\n"
242    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
243    "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
244    "a=rtpmap:120 VP8/90000\r\n"
245    "a=ssrc:2 cname:stream_1_cname\r\n"
246    "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
247    "a=ssrc:2 mslabel:local_stream_1\r\n"
248    "a=ssrc:2 label:video_track_id_1\r\n"
249    "a=ssrc:3 cname:stream_1_cname\r\n"
250    "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n"
251    "a=ssrc:3 mslabel:local_stream_1\r\n"
252    "a=ssrc:3 label:video_track_id_2\r\n"
253    "a=ssrc-group:FEC 5 6\r\n"
254    "a=ssrc:5 cname:stream_2_cname\r\n"
255    "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n"
256    "a=ssrc:5 mslabel:local_stream_2\r\n"
257    "a=ssrc:5 label:video_track_id_3\r\n"
258    "a=ssrc:6 cname:stream_2_cname\r\n"
259    "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
260    "a=ssrc:6 mslabel:local_stream_2\r\n"
261    "a=ssrc:6 label:video_track_id_3\r\n";
262
263static const char kSdpRtpDataChannelString[] =
264    "m=application 1 RTP/SAVPF 101\r\n"
265    "c=IN IP4 0.0.0.0\r\n"
266    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
267    "a=ice-ufrag:ufrag_data\r\n"
268    "a=ice-pwd:pwd_data\r\n"
269    "a=mid:data_content_name\r\n"
270    "a=sendrecv\r\n"
271    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
272    "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5\r\n"
273    "a=rtpmap:101 google-data/90000\r\n"
274    "a=ssrc:10 cname:data_channel_cname\r\n"
275    "a=ssrc:10 msid:data_channel data_channeld0\r\n"
276    "a=ssrc:10 mslabel:data_channel\r\n"
277    "a=ssrc:10 label:data_channeld0\r\n";
278
279static const char kSdpSctpDataChannelString[] =
280    "m=application 1 DTLS/SCTP 5000\r\n"
281    "c=IN IP4 0.0.0.0\r\n"
282    "a=ice-ufrag:ufrag_data\r\n"
283    "a=ice-pwd:pwd_data\r\n"
284    "a=mid:data_content_name\r\n"
285    "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
286
287static const char kSdpSctpDataChannelWithCandidatesString[] =
288    "m=application 2345 DTLS/SCTP 5000\r\n"
289    "c=IN IP4 74.125.127.126\r\n"
290    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
291    "generation 2\r\n"
292    "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
293    "generation 2\r\n"
294    "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
295    "raddr 192.168.1.5 rport 2346 "
296    "generation 2\r\n"
297    "a=ice-ufrag:ufrag_data\r\n"
298    "a=ice-pwd:pwd_data\r\n"
299    "a=mid:data_content_name\r\n"
300    "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
301
302    static const char kSdpConferenceString[] =
303    "v=0\r\n"
304    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
305    "s=-\r\n"
306    "t=0 0\r\n"
307    "a=msid-semantic: WMS\r\n"
308    "m=audio 1 RTP/SAVPF 111 103 104\r\n"
309    "c=IN IP4 0.0.0.0\r\n"
310    "a=x-google-flag:conference\r\n"
311    "m=video 1 RTP/SAVPF 120\r\n"
312    "c=IN IP4 0.0.0.0\r\n"
313    "a=x-google-flag:conference\r\n";
314
315
316// One candidate reference string as per W3c spec.
317// candidate:<blah> not a=candidate:<blah>CRLF
318static const char kRawCandidate[] =
319    "candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host generation 2";
320// One candidate reference string.
321static const char kSdpOneCandidate[] =
322    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
323    "generation 2\r\n";
324
325// One candidate reference string.
326static const char kSdpOneCandidateOldFormat[] =
327    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host network_name"
328    " eth0 username user_rtp password password_rtp generation 2\r\n";
329
330// Session id and version
331static const char kSessionId[] = "18446744069414584320";
332static const char kSessionVersion[] = "18446462598732840960";
333
334// Ice options
335static const char kIceOption1[] = "iceoption1";
336static const char kIceOption2[] = "iceoption2";
337static const char kIceOption3[] = "iceoption3";
338
339// Content name
340static const char kAudioContentName[] = "audio_content_name";
341static const char kVideoContentName[] = "video_content_name";
342static const char kDataContentName[] = "data_content_name";
343
344// MediaStream 1
345static const char kStreamLabel1[] = "local_stream_1";
346static const char kStream1Cname[] = "stream_1_cname";
347static const char kAudioTrackId1[] = "audio_track_id_1";
348static const uint32 kAudioTrack1Ssrc = 1;
349static const char kVideoTrackId1[] = "video_track_id_1";
350static const uint32 kVideoTrack1Ssrc = 2;
351static const char kVideoTrackId2[] = "video_track_id_2";
352static const uint32 kVideoTrack2Ssrc = 3;
353
354// MediaStream 2
355static const char kStreamLabel2[] = "local_stream_2";
356static const char kStream2Cname[] = "stream_2_cname";
357static const char kAudioTrackId2[] = "audio_track_id_2";
358static const uint32 kAudioTrack2Ssrc = 4;
359static const char kVideoTrackId3[] = "video_track_id_3";
360static const uint32 kVideoTrack3Ssrc = 5;
361static const uint32 kVideoTrack4Ssrc = 6;
362
363// DataChannel
364static const char kDataChannelLabel[] = "data_channel";
365static const char kDataChannelMsid[] = "data_channeld0";
366static const char kDataChannelCname[] = "data_channel_cname";
367static const uint32 kDataChannelSsrc = 10;
368
369// Candidate
370static const char kDummyMid[] = "dummy_mid";
371static const int kDummyIndex = 123;
372
373// Misc
374static const char kDummyString[] = "dummy";
375
376// Helper functions
377
378static bool SdpDeserialize(const std::string& message,
379                           JsepSessionDescription* jdesc) {
380  return webrtc::SdpDeserialize(message, jdesc, NULL);
381}
382
383static bool SdpDeserializeCandidate(const std::string& message,
384                                    JsepIceCandidate* candidate) {
385  return webrtc::SdpDeserializeCandidate(message, candidate, NULL);
386}
387
388// Add some extra |newlines| to the |message| after |line|.
389static void InjectAfter(const std::string& line,
390                        const std::string& newlines,
391                        std::string* message) {
392  const std::string tmp = line + newlines;
393  talk_base::replace_substrs(line.c_str(), line.length(),
394                             tmp.c_str(), tmp.length(), message);
395}
396
397static void Replace(const std::string& line,
398                    const std::string& newlines,
399                    std::string* message) {
400  talk_base::replace_substrs(line.c_str(), line.length(),
401                             newlines.c_str(), newlines.length(), message);
402}
403
404// Expect fail to parase |bad_sdp| and expect |bad_part| be part of the error
405// message.
406static void ExpectParseFailure(const std::string& bad_sdp,
407                               const std::string& bad_part) {
408  JsepSessionDescription desc(kDummyString);
409  SdpParseError error;
410  bool ret = webrtc::SdpDeserialize(bad_sdp, &desc, &error);
411  EXPECT_FALSE(ret);
412  EXPECT_NE(std::string::npos, error.line.find(bad_part.c_str()));
413}
414
415// Expect fail to parse kSdpFullString if replace |good_part| with |bad_part|.
416static void ExpectParseFailure(const char* good_part, const char* bad_part) {
417  std::string bad_sdp = kSdpFullString;
418  Replace(good_part, bad_part, &bad_sdp);
419  ExpectParseFailure(bad_sdp, bad_part);
420}
421
422// Expect fail to parse kSdpFullString if add |newlines| after |injectpoint|.
423static void ExpectParseFailureWithNewLines(const std::string& injectpoint,
424                                           const std::string& newlines,
425                                           const std::string& bad_part) {
426  std::string bad_sdp = kSdpFullString;
427  InjectAfter(injectpoint, newlines, &bad_sdp);
428  ExpectParseFailure(bad_sdp, bad_part);
429}
430
431static void ReplaceDirection(cricket::MediaContentDirection direction,
432                             std::string* message) {
433  std::string new_direction;
434  switch (direction) {
435    case cricket::MD_INACTIVE:
436      new_direction = "a=inactive";
437      break;
438    case cricket::MD_SENDONLY:
439      new_direction = "a=sendonly";
440      break;
441    case cricket::MD_RECVONLY:
442      new_direction = "a=recvonly";
443      break;
444    case cricket::MD_SENDRECV:
445    default:
446      new_direction = "a=sendrecv";
447      break;
448  }
449  Replace("a=sendrecv", new_direction, message);
450}
451
452static void ReplaceRejected(bool audio_rejected, bool video_rejected,
453                            std::string* message) {
454  if (audio_rejected) {
455    Replace("m=audio 2345", "m=audio 0", message);
456  }
457  if (video_rejected) {
458    Replace("m=video 3457", "m=video 0", message);
459  }
460}
461
462// WebRtcSdpTest
463
464class WebRtcSdpTest : public testing::Test {
465 public:
466  WebRtcSdpTest()
467     : jdesc_(kDummyString) {
468    // AudioContentDescription
469    audio_desc_ = CreateAudioContentDescription();
470    AudioCodec opus(111, "opus", 48000, 0, 2, 3);
471    audio_desc_->AddCodec(opus);
472    audio_desc_->AddCodec(AudioCodec(103, "ISAC", 16000, 32000, 1, 2));
473    audio_desc_->AddCodec(AudioCodec(104, "CELT", 32000, 0, 2, 1));
474    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
475
476    // VideoContentDescription
477    talk_base::scoped_ptr<VideoContentDescription> video(
478        new VideoContentDescription());
479    video_desc_ = video.get();
480    StreamParams video_stream1;
481    video_stream1.id = kVideoTrackId1;
482    video_stream1.cname = kStream1Cname;
483    video_stream1.sync_label = kStreamLabel1;
484    video_stream1.ssrcs.push_back(kVideoTrack1Ssrc);
485    video->AddStream(video_stream1);
486    StreamParams video_stream2;
487    video_stream2.id = kVideoTrackId2;
488    video_stream2.cname = kStream1Cname;
489    video_stream2.sync_label = kStreamLabel1;
490    video_stream2.ssrcs.push_back(kVideoTrack2Ssrc);
491    video->AddStream(video_stream2);
492    StreamParams video_stream3;
493    video_stream3.id = kVideoTrackId3;
494    video_stream3.cname = kStream2Cname;
495    video_stream3.sync_label = kStreamLabel2;
496    video_stream3.ssrcs.push_back(kVideoTrack3Ssrc);
497    video_stream3.ssrcs.push_back(kVideoTrack4Ssrc);
498    cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream3.ssrcs);
499    video_stream3.ssrc_groups.push_back(ssrc_group);
500    video->AddStream(video_stream3);
501    video->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_80",
502        "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
503    video->set_protocol(cricket::kMediaProtocolSavpf);
504    video->AddCodec(VideoCodec(
505        120,
506        JsepSessionDescription::kDefaultVideoCodecName,
507        JsepSessionDescription::kMaxVideoCodecWidth,
508        JsepSessionDescription::kMaxVideoCodecHeight,
509        JsepSessionDescription::kDefaultVideoCodecFramerate,
510        JsepSessionDescription::kDefaultVideoCodecPreference));
511
512    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP,
513                     video.release());
514
515    // TransportInfo
516    EXPECT_TRUE(desc_.AddTransportInfo(
517        TransportInfo(kAudioContentName,
518                      TransportDescription(NS_JINGLE_ICE_UDP,
519                                           kCandidateUfragVoice,
520                                           kCandidatePwdVoice))));
521    EXPECT_TRUE(desc_.AddTransportInfo(
522        TransportInfo(kVideoContentName,
523                      TransportDescription(NS_JINGLE_ICE_UDP,
524                                           kCandidateUfragVideo,
525                                           kCandidatePwdVideo))));
526
527    // v4 host
528    int port = 1234;
529    talk_base::SocketAddress address("192.168.1.5", port++);
530    Candidate candidate1(
531        "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
532        "", "", LOCAL_PORT_TYPE,
533        "", kCandidateGeneration, kCandidateFoundation1);
534    address.SetPort(port++);
535    Candidate candidate2(
536        "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority,
537        "", "", LOCAL_PORT_TYPE,
538        "", kCandidateGeneration, kCandidateFoundation1);
539    address.SetPort(port++);
540    Candidate candidate3(
541        "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority,
542        "", "", LOCAL_PORT_TYPE,
543        "", kCandidateGeneration, kCandidateFoundation1);
544    address.SetPort(port++);
545    Candidate candidate4(
546        "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
547        "", "", LOCAL_PORT_TYPE,
548        "", kCandidateGeneration, kCandidateFoundation1);
549
550    // v6 host
551    talk_base::SocketAddress v6_address("::1", port++);
552    cricket::Candidate candidate5(
553        "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
554        "udp", v6_address, kCandidatePriority,
555        "", "", cricket::LOCAL_PORT_TYPE,
556        "", kCandidateGeneration, kCandidateFoundation2);
557    v6_address.SetPort(port++);
558    cricket::Candidate candidate6(
559        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
560        "udp", v6_address, kCandidatePriority,
561        "", "", cricket::LOCAL_PORT_TYPE,
562        "", kCandidateGeneration, kCandidateFoundation2);
563    v6_address.SetPort(port++);
564    cricket::Candidate candidate7(
565        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
566        "udp", v6_address, kCandidatePriority,
567        "", "", cricket::LOCAL_PORT_TYPE,
568        "", kCandidateGeneration, kCandidateFoundation2);
569    v6_address.SetPort(port++);
570    cricket::Candidate candidate8(
571        "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
572        "udp", v6_address, kCandidatePriority,
573        "", "", cricket::LOCAL_PORT_TYPE,
574        "", kCandidateGeneration, kCandidateFoundation2);
575
576    // stun
577    int port_stun = 2345;
578    talk_base::SocketAddress address_stun("74.125.127.126", port_stun++);
579    talk_base::SocketAddress rel_address_stun("192.168.1.5", port_stun++);
580    cricket::Candidate candidate9
581        ("", cricket::ICE_CANDIDATE_COMPONENT_RTP,
582         "udp", address_stun, kCandidatePriority,
583         "", "", STUN_PORT_TYPE,
584         "", kCandidateGeneration, kCandidateFoundation3);
585    candidate9.set_related_address(rel_address_stun);
586
587    address_stun.SetPort(port_stun++);
588    rel_address_stun.SetPort(port_stun++);
589    cricket::Candidate candidate10(
590        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
591        "udp", address_stun, kCandidatePriority,
592        "", "", STUN_PORT_TYPE,
593        "", kCandidateGeneration, kCandidateFoundation3);
594    candidate10.set_related_address(rel_address_stun);
595
596    // relay
597    int port_relay = 3456;
598    talk_base::SocketAddress address_relay("74.125.224.39", port_relay++);
599    cricket::Candidate candidate11(
600        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
601        "udp", address_relay, kCandidatePriority,
602        "", "",
603        cricket::RELAY_PORT_TYPE, "",
604        kCandidateGeneration, kCandidateFoundation4);
605    address_relay.SetPort(port_relay++);
606    cricket::Candidate candidate12(
607        "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
608        "udp", address_relay, kCandidatePriority,
609        "", "",
610        RELAY_PORT_TYPE, "",
611        kCandidateGeneration, kCandidateFoundation4);
612
613    // voice
614    candidates_.push_back(candidate1);
615    candidates_.push_back(candidate2);
616    candidates_.push_back(candidate5);
617    candidates_.push_back(candidate6);
618    candidates_.push_back(candidate9);
619    candidates_.push_back(candidate10);
620
621    // video
622    candidates_.push_back(candidate3);
623    candidates_.push_back(candidate4);
624    candidates_.push_back(candidate7);
625    candidates_.push_back(candidate8);
626    candidates_.push_back(candidate11);
627    candidates_.push_back(candidate12);
628
629    jcandidate_.reset(new JsepIceCandidate(std::string("audio_content_name"),
630                                           0, candidate1));
631
632    // Set up JsepSessionDescription.
633    jdesc_.Initialize(desc_.Copy(), kSessionId, kSessionVersion);
634    std::string mline_id;
635    int mline_index = 0;
636    for (size_t i = 0; i< candidates_.size(); ++i) {
637      // In this test, the audio m line index will be 0, and the video m line
638      // will be 1.
639      bool is_video = (i > 5);
640      mline_id = is_video ? "video_content_name" : "audio_content_name";
641      mline_index = is_video ? 1 : 0;
642      JsepIceCandidate jice(mline_id,
643                            mline_index,
644                            candidates_.at(i));
645      jdesc_.AddCandidate(&jice);
646    }
647  }
648
649  AudioContentDescription* CreateAudioContentDescription() {
650    AudioContentDescription* audio = new AudioContentDescription();
651    audio->set_rtcp_mux(true);
652    StreamParams audio_stream1;
653    audio_stream1.id = kAudioTrackId1;
654    audio_stream1.cname = kStream1Cname;
655    audio_stream1.sync_label = kStreamLabel1;
656    audio_stream1.ssrcs.push_back(kAudioTrack1Ssrc);
657    audio->AddStream(audio_stream1);
658    StreamParams audio_stream2;
659    audio_stream2.id = kAudioTrackId2;
660    audio_stream2.cname = kStream2Cname;
661    audio_stream2.sync_label = kStreamLabel2;
662    audio_stream2.ssrcs.push_back(kAudioTrack2Ssrc);
663    audio->AddStream(audio_stream2);
664    audio->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_32",
665        "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32",
666        "dummy_session_params"));
667    audio->set_protocol(cricket::kMediaProtocolSavpf);
668    return audio;
669  }
670
671  template <class MCD>
672  void CompareMediaContentDescription(const MCD* cd1,
673                                      const MCD* cd2) {
674    // type
675    EXPECT_EQ(cd1->type(), cd1->type());
676
677    // content direction
678    EXPECT_EQ(cd1->direction(), cd2->direction());
679
680    // rtcp_mux
681    EXPECT_EQ(cd1->rtcp_mux(), cd2->rtcp_mux());
682
683    // cryptos
684    EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size());
685    if (cd1->cryptos().size() != cd2->cryptos().size()) {
686      ADD_FAILURE();
687      return;
688    }
689    for (size_t i = 0; i< cd1->cryptos().size(); ++i) {
690      const CryptoParams c1 = cd1->cryptos().at(i);
691      const CryptoParams c2 = cd2->cryptos().at(i);
692      EXPECT_TRUE(c1.Matches(c2));
693      EXPECT_EQ(c1.key_params, c2.key_params);
694      EXPECT_EQ(c1.session_params, c2.session_params);
695    }
696    // protocol
697    EXPECT_EQ(cd1->protocol(), cd2->protocol());
698
699    // codecs
700    EXPECT_EQ(cd1->codecs(), cd2->codecs());
701
702    // bandwidth
703    EXPECT_EQ(cd1->bandwidth(), cd2->bandwidth());
704
705    // streams
706    EXPECT_EQ(cd1->streams(), cd2->streams());
707
708    // extmap
709    ASSERT_EQ(cd1->rtp_header_extensions().size(),
710              cd2->rtp_header_extensions().size());
711    for (size_t i = 0; i< cd1->rtp_header_extensions().size(); ++i) {
712      const RtpHeaderExtension ext1 = cd1->rtp_header_extensions().at(i);
713      const RtpHeaderExtension ext2 = cd2->rtp_header_extensions().at(i);
714      EXPECT_EQ(ext1.uri, ext2.uri);
715      EXPECT_EQ(ext1.id, ext2.id);
716    }
717
718    // buffered mode latency
719    EXPECT_EQ(cd1->buffered_mode_latency(), cd2->buffered_mode_latency());
720  }
721
722
723  void CompareSessionDescription(const SessionDescription& desc1,
724                                 const SessionDescription& desc2) {
725    // Compare content descriptions.
726    if (desc1.contents().size() != desc2.contents().size()) {
727      ADD_FAILURE();
728      return;
729    }
730    for (size_t i = 0 ; i < desc1.contents().size(); ++i) {
731      const cricket::ContentInfo& c1 = desc1.contents().at(i);
732      const cricket::ContentInfo& c2 = desc2.contents().at(i);
733      // content name
734      EXPECT_EQ(c1.name, c2.name);
735      // content type
736      // Note, ASSERT will return from the function, but will not stop the test.
737      ASSERT_EQ(c1.type, c2.type);
738
739      ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2));
740      if (IsAudioContent(&c1)) {
741        const AudioContentDescription* acd1 =
742            static_cast<const AudioContentDescription*>(c1.description);
743        const AudioContentDescription* acd2 =
744            static_cast<const AudioContentDescription*>(c2.description);
745        CompareMediaContentDescription<AudioContentDescription>(acd1, acd2);
746      }
747
748      ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2));
749      if (IsVideoContent(&c1)) {
750        const VideoContentDescription* vcd1 =
751            static_cast<const VideoContentDescription*>(c1.description);
752        const VideoContentDescription* vcd2 =
753            static_cast<const VideoContentDescription*>(c2.description);
754        CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2);
755      }
756
757      ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
758      if (IsDataContent(&c1)) {
759        const DataContentDescription* dcd1 =
760            static_cast<const DataContentDescription*>(c1.description);
761        const DataContentDescription* dcd2 =
762            static_cast<const DataContentDescription*>(c2.description);
763        CompareMediaContentDescription<DataContentDescription>(dcd1, dcd2);
764      }
765    }
766
767    // group
768    const cricket::ContentGroups groups1 = desc1.groups();
769    const cricket::ContentGroups groups2 = desc2.groups();
770    EXPECT_EQ(groups1.size(), groups1.size());
771    if (groups1.size() != groups2.size()) {
772      ADD_FAILURE();
773      return;
774    }
775    for (size_t i = 0; i < groups1.size(); ++i) {
776      const cricket::ContentGroup group1 = groups1.at(i);
777      const cricket::ContentGroup group2 = groups2.at(i);
778      EXPECT_EQ(group1.semantics(), group2.semantics());
779      const cricket::ContentNames names1 = group1.content_names();
780      const cricket::ContentNames names2 = group2.content_names();
781      EXPECT_EQ(names1.size(), names2.size());
782      if (names1.size() != names2.size()) {
783        ADD_FAILURE();
784        return;
785      }
786      cricket::ContentNames::const_iterator iter1 = names1.begin();
787      cricket::ContentNames::const_iterator iter2 = names2.begin();
788      while (iter1 != names1.end()) {
789        EXPECT_EQ(*iter1++, *iter2++);
790      }
791    }
792
793    // transport info
794    const cricket::TransportInfos transports1 = desc1.transport_infos();
795    const cricket::TransportInfos transports2 = desc2.transport_infos();
796    EXPECT_EQ(transports1.size(), transports2.size());
797    if (transports1.size() != transports2.size()) {
798      ADD_FAILURE();
799      return;
800    }
801    for (size_t i = 0; i < transports1.size(); ++i) {
802      const cricket::TransportInfo transport1 = transports1.at(i);
803      const cricket::TransportInfo transport2 = transports2.at(i);
804      EXPECT_EQ(transport1.content_name, transport2.content_name);
805      EXPECT_EQ(transport1.description.transport_type,
806                transport2.description.transport_type);
807      EXPECT_EQ(transport1.description.ice_ufrag,
808                transport2.description.ice_ufrag);
809      EXPECT_EQ(transport1.description.ice_pwd,
810                transport2.description.ice_pwd);
811      if (transport1.description.identity_fingerprint) {
812        EXPECT_EQ(*transport1.description.identity_fingerprint,
813                  *transport2.description.identity_fingerprint);
814      } else {
815        EXPECT_EQ(transport1.description.identity_fingerprint.get(),
816                  transport2.description.identity_fingerprint.get());
817      }
818      EXPECT_EQ(transport1.description.transport_options,
819                transport2.description.transport_options);
820      EXPECT_TRUE(CompareCandidates(transport1.description.candidates,
821                                    transport2.description.candidates));
822    }
823  }
824
825  bool CompareCandidates(const Candidates& cs1, const Candidates& cs2) {
826    EXPECT_EQ(cs1.size(), cs2.size());
827    if (cs1.size() != cs2.size())
828      return false;
829    for (size_t i = 0; i< cs1.size(); ++i) {
830      const Candidate c1 = cs1.at(i);
831      const Candidate c2 = cs2.at(i);
832      EXPECT_TRUE(c1.IsEquivalent(c2));
833    }
834    return true;
835  }
836
837  bool CompareSessionDescription(
838      const JsepSessionDescription& desc1,
839      const JsepSessionDescription& desc2) {
840    EXPECT_EQ(desc1.session_id(), desc2.session_id());
841    EXPECT_EQ(desc1.session_version(), desc2.session_version());
842    CompareSessionDescription(*desc1.description(), *desc2.description());
843    if (desc1.number_of_mediasections() != desc2.number_of_mediasections())
844      return false;
845    for (size_t i = 0; i < desc1.number_of_mediasections(); ++i) {
846      const IceCandidateCollection* cc1 = desc1.candidates(i);
847      const IceCandidateCollection* cc2 = desc2.candidates(i);
848      if (cc1->count() != cc2->count())
849        return false;
850      for (size_t j = 0; j < cc1->count(); ++j) {
851        const IceCandidateInterface* c1 = cc1->at(j);
852        const IceCandidateInterface* c2 = cc2->at(j);
853        EXPECT_EQ(c1->sdp_mid(), c2->sdp_mid());
854        EXPECT_EQ(c1->sdp_mline_index(), c2->sdp_mline_index());
855        EXPECT_TRUE(c1->candidate().IsEquivalent(c2->candidate()));
856      }
857    }
858    return true;
859  }
860
861  // Disable the ice-ufrag and ice-pwd in given |sdp| message by replacing
862  // them with invalid keywords so that the parser will just ignore them.
863  bool RemoveCandidateUfragPwd(std::string* sdp) {
864    const char ice_ufrag[] = "a=ice-ufrag";
865    const char ice_ufragx[] = "a=xice-ufrag";
866    const char ice_pwd[] = "a=ice-pwd";
867    const char ice_pwdx[] = "a=xice-pwd";
868    talk_base::replace_substrs(ice_ufrag, strlen(ice_ufrag),
869        ice_ufragx, strlen(ice_ufragx), sdp);
870    talk_base::replace_substrs(ice_pwd, strlen(ice_pwd),
871        ice_pwdx, strlen(ice_pwdx), sdp);
872    return true;
873  }
874
875  // Update the candidates in |jdesc| to use the given |ufrag| and |pwd|.
876  bool UpdateCandidateUfragPwd(JsepSessionDescription* jdesc, int mline_index,
877      const std::string& ufrag, const std::string& pwd) {
878    std::string content_name;
879    if (mline_index == 0) {
880      content_name = kAudioContentName;
881    } else if (mline_index == 1) {
882      content_name = kVideoContentName;
883    } else {
884      ASSERT(false);
885    }
886    TransportInfo transport_info(
887        content_name, TransportDescription(NS_JINGLE_ICE_UDP,
888                                           ufrag, pwd));
889    SessionDescription* desc =
890        const_cast<SessionDescription*>(jdesc->description());
891    desc->RemoveTransportInfoByName(content_name);
892    EXPECT_TRUE(desc->AddTransportInfo(transport_info));
893    for (size_t i = 0; i < jdesc_.number_of_mediasections(); ++i) {
894      const IceCandidateCollection* cc = jdesc_.candidates(i);
895      for (size_t j = 0; j < cc->count(); ++j) {
896        if (cc->at(j)->sdp_mline_index() == mline_index) {
897          const_cast<Candidate&>(cc->at(j)->candidate()).set_username(
898              ufrag);
899          const_cast<Candidate&>(cc->at(j)->candidate()).set_password(
900              pwd);
901        }
902      }
903    }
904    return true;
905  }
906
907  void AddIceOptions(const std::string& content_name,
908                     const std::vector<std::string>& transport_options) {
909    ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
910    cricket::TransportInfo transport_info =
911        *(desc_.GetTransportInfoByName(content_name));
912    desc_.RemoveTransportInfoByName(content_name);
913    transport_info.description.transport_options = transport_options;
914    desc_.AddTransportInfo(transport_info);
915  }
916
917  void AddFingerprint() {
918    desc_.RemoveTransportInfoByName(kAudioContentName);
919    desc_.RemoveTransportInfoByName(kVideoContentName);
920    talk_base::SSLFingerprint fingerprint(talk_base::DIGEST_SHA_1,
921                                          kIdentityDigest,
922                                          sizeof(kIdentityDigest));
923    EXPECT_TRUE(desc_.AddTransportInfo(
924        TransportInfo(kAudioContentName,
925                      TransportDescription(NS_JINGLE_ICE_UDP,
926                                           std::vector<std::string>(),
927                                           kCandidateUfragVoice,
928                                           kCandidatePwdVoice,
929                                           cricket::ICEMODE_FULL,
930                                           cricket::CONNECTIONROLE_NONE,
931                                           &fingerprint, Candidates()))));
932    EXPECT_TRUE(desc_.AddTransportInfo(
933        TransportInfo(kVideoContentName,
934                      TransportDescription(NS_JINGLE_ICE_UDP,
935                                           std::vector<std::string>(),
936                                           kCandidateUfragVideo,
937                                           kCandidatePwdVideo,
938                                           cricket::ICEMODE_FULL,
939                                           cricket::CONNECTIONROLE_NONE,
940                                           &fingerprint, Candidates()))));
941  }
942
943  void AddExtmap() {
944    audio_desc_ = static_cast<AudioContentDescription*>(
945        audio_desc_->Copy());
946    video_desc_ = static_cast<VideoContentDescription*>(
947        video_desc_->Copy());
948    audio_desc_->AddRtpHeaderExtension(
949        RtpHeaderExtension(kExtmapUri, kExtmapId));
950    video_desc_->AddRtpHeaderExtension(
951        RtpHeaderExtension(kExtmapUri, kExtmapId));
952    desc_.RemoveContentByName(kAudioContentName);
953    desc_.RemoveContentByName(kVideoContentName);
954    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
955    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_);
956  }
957
958  void RemoveCryptos() {
959    audio_desc_->set_cryptos(std::vector<CryptoParams>());
960    video_desc_->set_cryptos(std::vector<CryptoParams>());
961  }
962
963  bool TestSerializeDirection(cricket::MediaContentDirection direction) {
964    audio_desc_->set_direction(direction);
965    video_desc_->set_direction(direction);
966    std::string new_sdp = kSdpFullString;
967    ReplaceDirection(direction, &new_sdp);
968
969    if (!jdesc_.Initialize(desc_.Copy(),
970                           jdesc_.session_id(),
971                           jdesc_.session_version())) {
972      return false;
973    }
974    std::string message = webrtc::SdpSerialize(jdesc_);
975    EXPECT_EQ(new_sdp, message);
976    return true;
977  }
978
979  bool TestSerializeRejected(bool audio_rejected, bool video_rejected) {
980    audio_desc_ = static_cast<AudioContentDescription*>(
981        audio_desc_->Copy());
982    video_desc_ = static_cast<VideoContentDescription*>(
983        video_desc_->Copy());
984    desc_.RemoveContentByName(kAudioContentName);
985    desc_.RemoveContentByName(kVideoContentName);
986    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
987                     audio_desc_);
988    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected,
989                     video_desc_);
990    std::string new_sdp = kSdpFullString;
991    ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
992
993    if (!jdesc_.Initialize(desc_.Copy(),
994                           jdesc_.session_id(),
995                           jdesc_.session_version())) {
996      return false;
997    }
998    std::string message = webrtc::SdpSerialize(jdesc_);
999    EXPECT_EQ(new_sdp, message);
1000    return true;
1001  }
1002
1003  void AddSctpDataChannel() {
1004    talk_base::scoped_ptr<DataContentDescription> data(
1005        new DataContentDescription());
1006    data_desc_ = data.get();
1007    data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp);
1008    DataCodec codec(cricket::kGoogleSctpDataCodecId,
1009                    cricket::kGoogleSctpDataCodecName, 0);
1010    codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort);
1011    data_desc_->AddCodec(codec);
1012    desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release());
1013    EXPECT_TRUE(desc_.AddTransportInfo(
1014           TransportInfo(kDataContentName,
1015                         TransportDescription(NS_JINGLE_ICE_UDP,
1016                                              kCandidateUfragData,
1017                                              kCandidatePwdData))));
1018  }
1019
1020  void AddRtpDataChannel() {
1021    talk_base::scoped_ptr<DataContentDescription> data(
1022        new DataContentDescription());
1023    data_desc_ = data.get();
1024
1025    data_desc_->AddCodec(DataCodec(101, "google-data", 1));
1026    StreamParams data_stream;
1027    data_stream.id = kDataChannelMsid;
1028    data_stream.cname = kDataChannelCname;
1029    data_stream.sync_label = kDataChannelLabel;
1030    data_stream.ssrcs.push_back(kDataChannelSsrc);
1031    data_desc_->AddStream(data_stream);
1032    data_desc_->AddCrypto(CryptoParams(
1033        1, "AES_CM_128_HMAC_SHA1_80",
1034        "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", ""));
1035    data_desc_->set_protocol(cricket::kMediaProtocolSavpf);
1036    desc_.AddContent(kDataContentName, NS_JINGLE_RTP, data.release());
1037    EXPECT_TRUE(desc_.AddTransportInfo(
1038           TransportInfo(kDataContentName,
1039                         TransportDescription(NS_JINGLE_ICE_UDP,
1040                                              kCandidateUfragData,
1041                                              kCandidatePwdData))));
1042  }
1043
1044  bool TestDeserializeDirection(cricket::MediaContentDirection direction) {
1045    std::string new_sdp = kSdpFullString;
1046    ReplaceDirection(direction, &new_sdp);
1047    JsepSessionDescription new_jdesc(kDummyString);
1048
1049    EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1050
1051    audio_desc_->set_direction(direction);
1052    video_desc_->set_direction(direction);
1053    if (!jdesc_.Initialize(desc_.Copy(),
1054                           jdesc_.session_id(),
1055                           jdesc_.session_version())) {
1056      return false;
1057    }
1058    EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
1059    return true;
1060  }
1061
1062  bool TestDeserializeRejected(bool audio_rejected, bool video_rejected) {
1063    std::string new_sdp = kSdpFullString;
1064    ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1065    JsepSessionDescription new_jdesc(JsepSessionDescription::kOffer);
1066
1067    EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1068    audio_desc_ = static_cast<AudioContentDescription*>(
1069        audio_desc_->Copy());
1070    video_desc_ = static_cast<VideoContentDescription*>(
1071        video_desc_->Copy());
1072    desc_.RemoveContentByName(kAudioContentName);
1073    desc_.RemoveContentByName(kVideoContentName);
1074    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
1075                     audio_desc_);
1076    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected,
1077                     video_desc_);
1078    if (!jdesc_.Initialize(desc_.Copy(),
1079                           jdesc_.session_id(),
1080                           jdesc_.session_version())) {
1081      return false;
1082    }
1083    EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
1084    return true;
1085  }
1086
1087  void TestDeserializeExtmap(bool session_level, bool media_level) {
1088    AddExtmap();
1089    JsepSessionDescription new_jdesc("dummy");
1090    ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
1091                                     jdesc_.session_id(),
1092                                     jdesc_.session_version()));
1093    JsepSessionDescription jdesc_with_extmap("dummy");
1094    std::string sdp_with_extmap = kSdpString;
1095    if (session_level) {
1096      InjectAfter(kSessionTime, kExtmapWithDirectionAndAttribute,
1097                  &sdp_with_extmap);
1098    }
1099    if (media_level) {
1100      InjectAfter(kAttributeIcePwdVoice, kExtmapWithDirectionAndAttribute,
1101                  &sdp_with_extmap);
1102      InjectAfter(kAttributeIcePwdVideo, kExtmapWithDirectionAndAttribute,
1103                  &sdp_with_extmap);
1104    }
1105    // The extmap can't be present at the same time in both session level and
1106    // media level.
1107    if (session_level && media_level) {
1108      SdpParseError error;
1109      EXPECT_FALSE(webrtc::SdpDeserialize(sdp_with_extmap,
1110                   &jdesc_with_extmap, &error));
1111      EXPECT_NE(std::string::npos, error.description.find("a=extmap"));
1112    } else {
1113      EXPECT_TRUE(SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap));
1114      EXPECT_TRUE(CompareSessionDescription(jdesc_with_extmap, new_jdesc));
1115    }
1116  }
1117
1118  void VerifyCodecParameter(const cricket::CodecParameterMap& params,
1119      const std::string& name, int expected_value) {
1120    cricket::CodecParameterMap::const_iterator found = params.find(name);
1121    ASSERT_TRUE(found != params.end());
1122    EXPECT_EQ(found->second, talk_base::ToString<int>(expected_value));
1123  }
1124
1125  void TestDeserializeCodecParams(const CodecParams& params,
1126                                  JsepSessionDescription* jdesc_output) {
1127    std::string sdp =
1128        "v=0\r\n"
1129        "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1130        "s=-\r\n"
1131        "t=0 0\r\n"
1132        // Include semantics for WebRTC Media Streams since it is supported by
1133        // this parser, and will be added to the SDP when serializing a session
1134        // description.
1135        "a=msid-semantic: WMS\r\n"
1136        // Pl type 111 preferred.
1137        "m=audio 1 RTP/SAVPF 111 104 103 102\r\n"
1138        // Pltype 111 listed before 103 and 104 in the map.
1139        "a=rtpmap:111 opus/48000/2\r\n"
1140        // Pltype 103 listed before 104.
1141        "a=rtpmap:103 ISAC/16000\r\n"
1142        "a=rtpmap:104 CELT/32000/2\r\n"
1143        "a=rtpmap:102 ISAC/32000/1\r\n"
1144        "a=fmtp:111 0-15,66,70\r\n"
1145        "a=fmtp:111 ";
1146    std::ostringstream os;
1147    os << "minptime=" << params.min_ptime
1148       << "; stereo=" << params.stereo
1149       << "; sprop-stereo=" << params.sprop_stereo
1150       << "; useinbandfec=" << params.useinband
1151       << " maxaveragebitrate=" << params.maxaveragebitrate << "\r\n"
1152       << "a=ptime:" << params.ptime << "\r\n"
1153       << "a=maxptime:" << params.max_ptime << "\r\n";
1154    sdp += os.str();
1155
1156    // Deserialize
1157    SdpParseError error;
1158    EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
1159
1160    const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description());
1161    ASSERT_TRUE(ac != NULL);
1162    const AudioContentDescription* acd =
1163        static_cast<const AudioContentDescription*>(ac->description);
1164    ASSERT_FALSE(acd->codecs().empty());
1165    cricket::AudioCodec opus = acd->codecs()[0];
1166    EXPECT_EQ("opus", opus.name);
1167    EXPECT_EQ(111, opus.id);
1168    VerifyCodecParameter(opus.params, "minptime", params.min_ptime);
1169    VerifyCodecParameter(opus.params, "stereo", params.stereo);
1170    VerifyCodecParameter(opus.params, "sprop-stereo", params.sprop_stereo);
1171    VerifyCodecParameter(opus.params, "useinbandfec", params.useinband);
1172    VerifyCodecParameter(opus.params, "maxaveragebitrate",
1173                         params.maxaveragebitrate);
1174    for (size_t i = 0; i < acd->codecs().size(); ++i) {
1175      cricket::AudioCodec codec = acd->codecs()[i];
1176      VerifyCodecParameter(codec.params, "ptime", params.ptime);
1177      VerifyCodecParameter(codec.params, "maxptime", params.max_ptime);
1178      if (codec.name == "ISAC") {
1179        if (codec.clockrate == 16000) {
1180          EXPECT_EQ(32000, codec.bitrate);
1181        } else {
1182          EXPECT_EQ(56000, codec.bitrate);
1183        }
1184      }
1185    }
1186  }
1187
1188  void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output,
1189                             bool use_wildcard) {
1190    std::string sdp =
1191        "v=0\r\n"
1192        "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1193        "s=-\r\n"
1194        "t=0 0\r\n"
1195        // Include semantics for WebRTC Media Streams since it is supported by
1196        // this parser, and will be added to the SDP when serializing a session
1197        // description.
1198        "a=msid-semantic: WMS\r\n"
1199        "m=audio 1 RTP/SAVPF 111\r\n"
1200        "a=rtpmap:111 opus/48000/2\r\n"
1201        "a=rtcp-fb:111 nack\r\n"
1202        "m=video 3457 RTP/SAVPF 101\r\n"
1203        "a=rtpmap:101 VP8/90000\r\n"
1204        "a=rtcp-fb:101 nack\r\n"
1205        "a=rtcp-fb:101 nack pli\r\n"
1206        "a=rtcp-fb:101 goog-remb\r\n"
1207        "a=rtcp-fb:101 ccm fir\r\n";
1208    std::ostringstream os;
1209    os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") <<  " ccm fir\r\n";
1210    sdp += os.str();
1211    // Deserialize
1212    SdpParseError error;
1213    EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
1214    const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description());
1215    ASSERT_TRUE(ac != NULL);
1216    const AudioContentDescription* acd =
1217        static_cast<const AudioContentDescription*>(ac->description);
1218    ASSERT_FALSE(acd->codecs().empty());
1219    cricket::AudioCodec opus = acd->codecs()[0];
1220    EXPECT_EQ(111, opus.id);
1221    EXPECT_TRUE(opus.HasFeedbackParam(
1222        cricket::FeedbackParam(cricket::kRtcpFbParamNack,
1223                               cricket::kParamValueEmpty)));
1224
1225    const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description());
1226    ASSERT_TRUE(vc != NULL);
1227    const VideoContentDescription* vcd =
1228        static_cast<const VideoContentDescription*>(vc->description);
1229    ASSERT_FALSE(vcd->codecs().empty());
1230    cricket::VideoCodec vp8 = vcd->codecs()[0];
1231    EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName,
1232                 vp8.name.c_str());
1233    EXPECT_EQ(101, vp8.id);
1234    EXPECT_TRUE(vp8.HasFeedbackParam(
1235        cricket::FeedbackParam(cricket::kRtcpFbParamNack,
1236                               cricket::kParamValueEmpty)));
1237    EXPECT_TRUE(vp8.HasFeedbackParam(
1238        cricket::FeedbackParam(cricket::kRtcpFbParamNack,
1239                               cricket::kRtcpFbNackParamPli)));
1240    EXPECT_TRUE(vp8.HasFeedbackParam(
1241        cricket::FeedbackParam(cricket::kRtcpFbParamRemb,
1242                               cricket::kParamValueEmpty)));
1243    EXPECT_TRUE(vp8.HasFeedbackParam(
1244        cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
1245                               cricket::kRtcpFbCcmParamFir)));
1246  }
1247
1248  // Two SDP messages can mean the same thing but be different strings, e.g.
1249  // some of the lines can be serialized in different order.
1250  // However, a deserialized description can be compared field by field and has
1251  // no order. If deserializer has already been tested, serializing then
1252  // deserializing and comparing JsepSessionDescription will test
1253  // the serializer sufficiently.
1254  void TestSerialize(const JsepSessionDescription& jdesc) {
1255    std::string message = webrtc::SdpSerialize(jdesc);
1256    JsepSessionDescription jdesc_output_des(kDummyString);
1257    SdpParseError error;
1258    EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error));
1259    EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des));
1260  }
1261
1262 protected:
1263  SessionDescription desc_;
1264  AudioContentDescription* audio_desc_;
1265  VideoContentDescription* video_desc_;
1266  DataContentDescription* data_desc_;
1267  Candidates candidates_;
1268  talk_base::scoped_ptr<IceCandidateInterface> jcandidate_;
1269  JsepSessionDescription jdesc_;
1270};
1271
1272void TestMismatch(const std::string& string1, const std::string& string2) {
1273  int position = 0;
1274  for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) {
1275    if (string1.c_str()[i] != string2.c_str()[i]) {
1276      position = static_cast<int>(i);
1277      break;
1278    }
1279  }
1280  EXPECT_EQ(0, position) << "Strings mismatch at the " << position
1281                         << " character\n"
1282                         << " 1: " << string1.substr(position, 20) << "\n"
1283                         << " 2: " << string2.substr(position, 20) << "\n";
1284}
1285
1286std::string GetLine(const std::string& message,
1287                    const std::string& session_description_name) {
1288  size_t start = message.find(session_description_name);
1289  if (std::string::npos == start) {
1290    return "";
1291  }
1292  size_t stop = message.find("\r\n", start);
1293  if (std::string::npos == stop) {
1294    return "";
1295  }
1296  if (stop <= start) {
1297    return "";
1298  }
1299  return message.substr(start, stop - start);
1300}
1301
1302TEST_F(WebRtcSdpTest, SerializeSessionDescription) {
1303  // SessionDescription with desc and candidates.
1304  std::string message = webrtc::SdpSerialize(jdesc_);
1305  TestMismatch(std::string(kSdpFullString), message);
1306}
1307
1308TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
1309  JsepSessionDescription jdesc_empty(kDummyString);
1310  EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty));
1311}
1312
1313// This tests serialization of SDP with a=crypto and a=fingerprint, as would be
1314// the case in a DTLS offer.
1315TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
1316  AddFingerprint();
1317  JsepSessionDescription jdesc_with_fingerprint(kDummyString);
1318  ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
1319                                                kSessionId, kSessionVersion));
1320  std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
1321
1322  std::string sdp_with_fingerprint = kSdpString;
1323  InjectAfter(kAttributeIcePwdVoice,
1324              kFingerprint, &sdp_with_fingerprint);
1325  InjectAfter(kAttributeIcePwdVideo,
1326              kFingerprint, &sdp_with_fingerprint);
1327
1328  EXPECT_EQ(sdp_with_fingerprint, message);
1329}
1330
1331// This tests serialization of SDP with a=fingerprint with no a=crypto, as would
1332// be the case in a DTLS answer.
1333TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
1334  AddFingerprint();
1335  RemoveCryptos();
1336  JsepSessionDescription jdesc_with_fingerprint(kDummyString);
1337  ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
1338                                                kSessionId, kSessionVersion));
1339  std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
1340
1341  std::string sdp_with_fingerprint = kSdpString;
1342  Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint);
1343  Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint);
1344  InjectAfter(kAttributeIcePwdVoice,
1345              kFingerprint, &sdp_with_fingerprint);
1346  InjectAfter(kAttributeIcePwdVideo,
1347              kFingerprint, &sdp_with_fingerprint);
1348
1349  EXPECT_EQ(sdp_with_fingerprint, message);
1350}
1351
1352TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) {
1353  // JsepSessionDescription with desc but without candidates.
1354  JsepSessionDescription jdesc_no_candidates(kDummyString);
1355  ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(),
1356                                             kSessionId, kSessionVersion));
1357  std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
1358  EXPECT_EQ(std::string(kSdpString), message);
1359}
1360
1361TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) {
1362  ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
1363  group.AddContentName(kAudioContentName);
1364  group.AddContentName(kVideoContentName);
1365  desc_.AddGroup(group);
1366  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1367                                jdesc_.session_id(),
1368                                jdesc_.session_version()));
1369  std::string message = webrtc::SdpSerialize(jdesc_);
1370  std::string sdp_with_bundle = kSdpFullString;
1371  InjectAfter(kSessionTime,
1372              "a=group:BUNDLE audio_content_name video_content_name\r\n",
1373              &sdp_with_bundle);
1374  EXPECT_EQ(sdp_with_bundle, message);
1375}
1376
1377TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) {
1378  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1379      GetFirstVideoContent(&desc_)->description);
1380  vcd->set_bandwidth(100 * 1000);
1381  AudioContentDescription* acd = static_cast<AudioContentDescription*>(
1382      GetFirstAudioContent(&desc_)->description);
1383  acd->set_bandwidth(50 * 1000);
1384  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1385                                jdesc_.session_id(),
1386                                jdesc_.session_version()));
1387  std::string message = webrtc::SdpSerialize(jdesc_);
1388  std::string sdp_with_bandwidth = kSdpFullString;
1389  InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n",
1390              "b=AS:100\r\n",
1391              &sdp_with_bandwidth);
1392  InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n",
1393              "b=AS:50\r\n",
1394              &sdp_with_bandwidth);
1395  EXPECT_EQ(sdp_with_bandwidth, message);
1396}
1397
1398TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) {
1399  std::vector<std::string> transport_options;
1400  transport_options.push_back(kIceOption1);
1401  transport_options.push_back(kIceOption3);
1402  AddIceOptions(kAudioContentName, transport_options);
1403  transport_options.clear();
1404  transport_options.push_back(kIceOption2);
1405  transport_options.push_back(kIceOption3);
1406  AddIceOptions(kVideoContentName, transport_options);
1407  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1408                                jdesc_.session_id(),
1409                                jdesc_.session_version()));
1410  std::string message = webrtc::SdpSerialize(jdesc_);
1411  std::string sdp_with_ice_options = kSdpFullString;
1412  InjectAfter(kAttributeIcePwdVoice,
1413              "a=ice-options:iceoption1 iceoption3\r\n",
1414              &sdp_with_ice_options);
1415  InjectAfter(kAttributeIcePwdVideo,
1416              "a=ice-options:iceoption2 iceoption3\r\n",
1417              &sdp_with_ice_options);
1418  EXPECT_EQ(sdp_with_ice_options, message);
1419}
1420
1421TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) {
1422  EXPECT_TRUE(TestSerializeDirection(cricket::MD_RECVONLY));
1423}
1424
1425TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) {
1426  EXPECT_TRUE(TestSerializeDirection(cricket::MD_SENDONLY));
1427}
1428
1429TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) {
1430  EXPECT_TRUE(TestSerializeDirection(cricket::MD_INACTIVE));
1431}
1432
1433TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) {
1434  EXPECT_TRUE(TestSerializeRejected(true, false));
1435}
1436
1437TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) {
1438  EXPECT_TRUE(TestSerializeRejected(false, true));
1439}
1440
1441TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) {
1442  EXPECT_TRUE(TestSerializeRejected(true, true));
1443}
1444
1445TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) {
1446  AddRtpDataChannel();
1447  JsepSessionDescription jsep_desc(kDummyString);
1448
1449  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1450  std::string message = webrtc::SdpSerialize(jsep_desc);
1451
1452  std::string expected_sdp = kSdpString;
1453  expected_sdp.append(kSdpRtpDataChannelString);
1454  EXPECT_EQ(expected_sdp, message);
1455}
1456
1457TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) {
1458  AddSctpDataChannel();
1459  JsepSessionDescription jsep_desc(kDummyString);
1460
1461  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1462  std::string message = webrtc::SdpSerialize(jsep_desc);
1463
1464  std::string expected_sdp = kSdpString;
1465  expected_sdp.append(kSdpSctpDataChannelString);
1466  EXPECT_EQ(message, expected_sdp);
1467}
1468
1469TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
1470  AddRtpDataChannel();
1471  data_desc_->set_bandwidth(100*1000);
1472  JsepSessionDescription jsep_desc(kDummyString);
1473
1474  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1475  std::string message = webrtc::SdpSerialize(jsep_desc);
1476
1477  std::string expected_sdp = kSdpString;
1478  expected_sdp.append(kSdpRtpDataChannelString);
1479  // We want to test that serializing data content ignores bandwidth
1480  // settings (it should always be the default).  Thus, we don't do
1481  // the following:
1482  // TODO(pthatcher): We need to temporarily allow the SDP to control
1483  // this for backwards-compatibility.  Once we don't need that any
1484  // more, remove this.
1485  InjectAfter("a=mid:data_content_name\r\na=sendrecv\r\n",
1486              "b=AS:100\r\n",
1487              &expected_sdp);
1488  EXPECT_EQ(expected_sdp, message);
1489}
1490
1491TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
1492  AddExtmap();
1493  JsepSessionDescription desc_with_extmap("dummy");
1494  ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(),
1495                                          kSessionId, kSessionVersion));
1496  std::string message = webrtc::SdpSerialize(desc_with_extmap);
1497
1498  std::string sdp_with_extmap = kSdpString;
1499  InjectAfter("a=mid:audio_content_name\r\n",
1500              kExtmap, &sdp_with_extmap);
1501  InjectAfter("a=mid:video_content_name\r\n",
1502              kExtmap, &sdp_with_extmap);
1503
1504  EXPECT_EQ(sdp_with_extmap, message);
1505}
1506
1507TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBufferLatency) {
1508  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1509      GetFirstVideoContent(&desc_)->description);
1510  vcd->set_buffered_mode_latency(128);
1511
1512  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1513                                jdesc_.session_id(),
1514                                jdesc_.session_version()));
1515  std::string message = webrtc::SdpSerialize(jdesc_);
1516  std::string sdp_with_buffer_latency = kSdpFullString;
1517  InjectAfter("a=rtpmap:120 VP8/90000\r\n",
1518              "a=x-google-buffer-latency:128\r\n",
1519              &sdp_with_buffer_latency);
1520  EXPECT_EQ(sdp_with_buffer_latency, message);
1521}
1522
1523TEST_F(WebRtcSdpTest, SerializeCandidates) {
1524  std::string message = webrtc::SdpSerializeCandidate(*jcandidate_);
1525  EXPECT_EQ(std::string(kSdpOneCandidate), message);
1526}
1527
1528TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
1529  JsepSessionDescription jdesc(kDummyString);
1530  // Deserialize
1531  EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc));
1532  // Verify
1533  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
1534}
1535
1536TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) {
1537  JsepSessionDescription jdesc(kDummyString);
1538  const char kSdpWithoutMline[] =
1539    "v=0\r\n"
1540    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1541    "s=-\r\n"
1542    "t=0 0\r\n"
1543    "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n";
1544  // Deserialize
1545  EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc));
1546  EXPECT_EQ(0u, jdesc.description()->contents().size());
1547}
1548
1549TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
1550  JsepSessionDescription jdesc(kDummyString);
1551  std::string sdp_without_carriage_return = kSdpFullString;
1552  Replace("\r\n", "\n", &sdp_without_carriage_return);
1553  // Deserialize
1554  EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc));
1555  // Verify
1556  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
1557}
1558
1559TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) {
1560  // SessionDescription with desc but without candidates.
1561  JsepSessionDescription jdesc_no_candidates(kDummyString);
1562  ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(),
1563                                             kSessionId, kSessionVersion));
1564  JsepSessionDescription new_jdesc(kDummyString);
1565  EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc));
1566  EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
1567}
1568
1569TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) {
1570  static const char kSdpNoRtpmapString[] =
1571      "v=0\r\n"
1572      "o=- 11 22 IN IP4 127.0.0.1\r\n"
1573      "s=-\r\n"
1574      "t=0 0\r\n"
1575      "m=audio 49232 RTP/AVP 0 18 103\r\n"
1576      // Codec that doesn't appear in the m= line will be ignored.
1577      "a=rtpmap:104 CELT/32000/2\r\n"
1578      // The rtpmap line for static payload codec is optional.
1579      "a=rtpmap:18 G729/16000\r\n"
1580      "a=rtpmap:103 ISAC/16000\r\n";
1581
1582  JsepSessionDescription jdesc(kDummyString);
1583  EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
1584  cricket::AudioContentDescription* audio =
1585    static_cast<AudioContentDescription*>(
1586        jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
1587  AudioCodecs ref_codecs;
1588  // The codecs in the AudioContentDescription will be sorted by preference.
1589  ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1, 3));
1590  ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1, 2));
1591  ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 32000, 1, 1));
1592  EXPECT_EQ(ref_codecs, audio->codecs());
1593}
1594
1595TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmapButWithFmtp) {
1596  static const char kSdpNoRtpmapString[] =
1597      "v=0\r\n"
1598      "o=- 11 22 IN IP4 127.0.0.1\r\n"
1599      "s=-\r\n"
1600      "t=0 0\r\n"
1601      "m=audio 49232 RTP/AVP 18 103\r\n"
1602      "a=fmtp:18 annexb=yes\r\n"
1603      "a=rtpmap:103 ISAC/16000\r\n";
1604
1605  JsepSessionDescription jdesc(kDummyString);
1606  EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
1607  cricket::AudioContentDescription* audio =
1608    static_cast<AudioContentDescription*>(
1609        jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
1610
1611  cricket::AudioCodec g729 = audio->codecs()[0];
1612  EXPECT_EQ("G729", g729.name);
1613  EXPECT_EQ(8000, g729.clockrate);
1614  EXPECT_EQ(18, g729.id);
1615  cricket::CodecParameterMap::iterator found =
1616      g729.params.find("annexb");
1617  ASSERT_TRUE(found != g729.params.end());
1618  EXPECT_EQ(found->second, "yes");
1619
1620  cricket::AudioCodec isac = audio->codecs()[1];
1621  EXPECT_EQ("ISAC", isac.name);
1622  EXPECT_EQ(103, isac.id);
1623  EXPECT_EQ(16000, isac.clockrate);
1624}
1625
1626// Ensure that we can deserialize SDP with a=fingerprint properly.
1627TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) {
1628  // Add a DTLS a=fingerprint attribute to our session description.
1629  AddFingerprint();
1630  JsepSessionDescription new_jdesc(kDummyString);
1631  ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
1632                                   jdesc_.session_id(),
1633                                   jdesc_.session_version()));
1634
1635  JsepSessionDescription jdesc_with_fingerprint(kDummyString);
1636  std::string sdp_with_fingerprint = kSdpString;
1637  InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
1638  InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
1639  EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint));
1640  EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc));
1641}
1642
1643TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) {
1644  JsepSessionDescription jdesc_with_bundle(kDummyString);
1645  std::string sdp_with_bundle = kSdpFullString;
1646  InjectAfter(kSessionTime,
1647              "a=group:BUNDLE audio_content_name video_content_name\r\n",
1648              &sdp_with_bundle);
1649  EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle));
1650  ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
1651  group.AddContentName(kAudioContentName);
1652  group.AddContentName(kVideoContentName);
1653  desc_.AddGroup(group);
1654  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1655                                jdesc_.session_id(),
1656                                jdesc_.session_version()));
1657  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle));
1658}
1659
1660TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) {
1661  JsepSessionDescription jdesc_with_bandwidth(kDummyString);
1662  std::string sdp_with_bandwidth = kSdpFullString;
1663  InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n",
1664              "b=AS:100\r\n",
1665              &sdp_with_bandwidth);
1666  InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n",
1667              "b=AS:50\r\n",
1668              &sdp_with_bandwidth);
1669  EXPECT_TRUE(
1670      SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
1671  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1672      GetFirstVideoContent(&desc_)->description);
1673  vcd->set_bandwidth(100 * 1000);
1674  AudioContentDescription* acd = static_cast<AudioContentDescription*>(
1675      GetFirstAudioContent(&desc_)->description);
1676  acd->set_bandwidth(50 * 1000);
1677  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1678                                jdesc_.session_id(),
1679                                jdesc_.session_version()));
1680  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
1681}
1682
1683TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) {
1684  JsepSessionDescription jdesc_with_ice_options(kDummyString);
1685  std::string sdp_with_ice_options = kSdpFullString;
1686  InjectAfter(kSessionTime,
1687              "a=ice-options:iceoption3\r\n",
1688              &sdp_with_ice_options);
1689  InjectAfter(kAttributeIcePwdVoice,
1690              "a=ice-options:iceoption1\r\n",
1691              &sdp_with_ice_options);
1692  InjectAfter(kAttributeIcePwdVideo,
1693              "a=ice-options:iceoption2\r\n",
1694              &sdp_with_ice_options);
1695  EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options));
1696  std::vector<std::string> transport_options;
1697  transport_options.push_back(kIceOption3);
1698  transport_options.push_back(kIceOption1);
1699  AddIceOptions(kAudioContentName, transport_options);
1700  transport_options.clear();
1701  transport_options.push_back(kIceOption3);
1702  transport_options.push_back(kIceOption2);
1703  AddIceOptions(kVideoContentName, transport_options);
1704  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1705                                jdesc_.session_id(),
1706                                jdesc_.session_version()));
1707  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options));
1708}
1709
1710TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) {
1711  // Remove the original ice-ufrag and ice-pwd
1712  JsepSessionDescription jdesc_with_ufrag_pwd(kDummyString);
1713  std::string sdp_with_ufrag_pwd = kSdpFullString;
1714  EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd));
1715  // Add session level ufrag and pwd
1716  InjectAfter(kSessionTime,
1717      "a=ice-pwd:session+level+icepwd\r\n"
1718      "a=ice-ufrag:session+level+iceufrag\r\n",
1719      &sdp_with_ufrag_pwd);
1720  // Add media level ufrag and pwd for audio
1721  InjectAfter("a=mid:audio_content_name\r\n",
1722      "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n",
1723      &sdp_with_ufrag_pwd);
1724  // Update the candidate ufrag and pwd to the expected ones.
1725  EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0,
1726      "media+level+iceufrag", "media+level+icepwd"));
1727  EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1,
1728      "session+level+iceufrag", "session+level+icepwd"));
1729  EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd));
1730  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd));
1731}
1732
1733TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBufferLatency) {
1734  JsepSessionDescription jdesc_with_buffer_latency(kDummyString);
1735  std::string sdp_with_buffer_latency = kSdpFullString;
1736  InjectAfter("a=rtpmap:120 VP8/90000\r\n",
1737              "a=x-google-buffer-latency:128\r\n",
1738              &sdp_with_buffer_latency);
1739
1740  EXPECT_TRUE(
1741      SdpDeserialize(sdp_with_buffer_latency, &jdesc_with_buffer_latency));
1742  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1743      GetFirstVideoContent(&desc_)->description);
1744  vcd->set_buffered_mode_latency(128);
1745  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1746                                jdesc_.session_id(),
1747                                jdesc_.session_version()));
1748  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_buffer_latency));
1749}
1750
1751TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) {
1752  EXPECT_TRUE(TestDeserializeDirection(cricket::MD_RECVONLY));
1753}
1754
1755TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) {
1756  EXPECT_TRUE(TestDeserializeDirection(cricket::MD_SENDONLY));
1757}
1758
1759TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) {
1760  EXPECT_TRUE(TestDeserializeDirection(cricket::MD_INACTIVE));
1761}
1762
1763TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) {
1764  EXPECT_TRUE(TestDeserializeRejected(true, false));
1765}
1766
1767TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) {
1768  EXPECT_TRUE(TestDeserializeRejected(false, true));
1769}
1770
1771TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) {
1772  EXPECT_TRUE(TestDeserializeRejected(true, true));
1773}
1774
1775// Tests that we can still handle the sdp uses mslabel and label instead of
1776// msid for backward compatibility.
1777TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) {
1778  JsepSessionDescription jdesc(kDummyString);
1779  std::string sdp_without_msid = kSdpFullString;
1780  Replace("msid", "xmsid", &sdp_without_msid);
1781  // Deserialize
1782  EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc));
1783  // Verify
1784  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
1785}
1786
1787TEST_F(WebRtcSdpTest, DeserializeCandidate) {
1788  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1789
1790  std::string sdp = kSdpOneCandidate;
1791  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1792  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1793  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1794  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
1795
1796  // Candidate line without generation extension.
1797  sdp = kSdpOneCandidate;
1798  Replace(" generation 2", "", &sdp);
1799  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1800  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1801  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1802  Candidate expected = jcandidate_->candidate();
1803  expected.set_generation(0);
1804  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
1805
1806  // Multiple candidate lines.
1807  // Only the first line will be deserialized. The rest will be ignored.
1808  sdp = kSdpOneCandidate;
1809  sdp.append("a=candidate:1 2 tcp 1234 192.168.1.100 5678 typ host\r\n");
1810  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1811  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1812  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1813  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
1814}
1815
1816// This test verifies the deserialization of candidate-attribute
1817// as per RFC 5245. Candiate-attribute will be of the format
1818// candidate:<blah>. This format will be used when candidates
1819// are trickled.
1820TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) {
1821  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1822
1823  std::string candidate_attribute = kRawCandidate;
1824  EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1825  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1826  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1827  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
1828  EXPECT_EQ(2u, jcandidate.candidate().generation());
1829
1830  // Candidate line without generation extension.
1831  candidate_attribute = kRawCandidate;
1832  Replace(" generation 2", "", &candidate_attribute);
1833  EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1834  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1835  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1836  Candidate expected = jcandidate_->candidate();
1837  expected.set_generation(0);
1838  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
1839
1840  // Candidate line without candidate:
1841  candidate_attribute = kRawCandidate;
1842  Replace("candidate:", "", &candidate_attribute);
1843  EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1844
1845  // Concatenating additional candidate. Expecting deserialization to fail.
1846  candidate_attribute = kRawCandidate;
1847  candidate_attribute.append("candidate:1 2 udp 1234 192.168.1.1 typ host");
1848  EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1849}
1850
1851TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) {
1852  AddRtpDataChannel();
1853  JsepSessionDescription jdesc(kDummyString);
1854  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1855
1856  std::string sdp_with_data = kSdpString;
1857  sdp_with_data.append(kSdpRtpDataChannelString);
1858  JsepSessionDescription jdesc_output(kDummyString);
1859
1860  // Deserialize
1861  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
1862  // Verify
1863  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
1864}
1865
1866TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) {
1867  AddSctpDataChannel();
1868  JsepSessionDescription jdesc(kDummyString);
1869  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1870
1871  std::string sdp_with_data = kSdpString;
1872  sdp_with_data.append(kSdpSctpDataChannelString);
1873  JsepSessionDescription jdesc_output(kDummyString);
1874
1875  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
1876  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
1877}
1878
1879// For crbug/344475.
1880TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) {
1881  std::string sdp_with_data = kSdpString;
1882  sdp_with_data.append(kSdpSctpDataChannelString);
1883  // Remove the "\n" at the end.
1884  sdp_with_data = sdp_with_data.substr(0, sdp_with_data.size() - 1);
1885  JsepSessionDescription jdesc_output(kDummyString);
1886
1887  EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
1888  // No crash is a pass.
1889}
1890
1891TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) {
1892  AddSctpDataChannel();
1893  const uint16 kUnusualSctpPort = 9556;
1894  char default_portstr[16];
1895  char unusual_portstr[16];
1896  talk_base::sprintfn(default_portstr, sizeof(default_portstr), "%d",
1897                      kDefaultSctpPort);
1898  talk_base::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d",
1899                      kUnusualSctpPort);
1900
1901  JsepSessionDescription jdesc(kDummyString);
1902  // take our pre-built session description and change the SCTP port.
1903  cricket::SessionDescription* mutant = desc_.Copy();
1904  DataContentDescription* dcdesc = static_cast<DataContentDescription*>(
1905      mutant->GetContentDescriptionByName(kDataContentName));
1906  std::vector<cricket::DataCodec> codecs(dcdesc->codecs());
1907  EXPECT_EQ(codecs.size(), 1UL);
1908  EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId);
1909  codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort);
1910
1911  // note: mutant's owned by jdesc now.
1912  ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion));
1913  mutant = NULL;
1914
1915  std::string sdp_with_data = kSdpString;
1916  sdp_with_data.append(kSdpSctpDataChannelString);
1917  talk_base::replace_substrs(default_portstr, strlen(default_portstr),
1918                             unusual_portstr, strlen(unusual_portstr),
1919                             &sdp_with_data);
1920  JsepSessionDescription jdesc_output(kDummyString);
1921
1922  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
1923  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
1924}
1925
1926TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {
1927  AddRtpDataChannel();
1928  JsepSessionDescription jdesc(kDummyString);
1929  // We want to test that deserializing data content ignores bandwidth
1930  // settings (it should always be the default).  Thus, we don't do
1931  // the following:
1932  // TODO(pthatcher): We need to temporarily allow the SDP to control
1933  // this for backwards-compatibility.  Once we don't need that any
1934  // more, remove this.
1935  DataContentDescription* dcd = static_cast<DataContentDescription*>(
1936     GetFirstDataContent(&desc_)->description);
1937  dcd->set_bandwidth(100 * 1000);
1938  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1939
1940  std::string sdp_with_bandwidth = kSdpString;
1941  sdp_with_bandwidth.append(kSdpRtpDataChannelString);
1942  InjectAfter("a=mid:data_content_name\r\n",
1943              "b=AS:100\r\n",
1944              &sdp_with_bandwidth);
1945  JsepSessionDescription jdesc_with_bandwidth(kDummyString);
1946
1947  EXPECT_TRUE(
1948      SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
1949  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth));
1950}
1951
1952TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) {
1953  TestDeserializeExtmap(true, false);
1954}
1955
1956TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithMediaLevelExtmap) {
1957  TestDeserializeExtmap(false, true);
1958}
1959
1960TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) {
1961  TestDeserializeExtmap(true, true);
1962}
1963
1964TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) {
1965  JsepSessionDescription jdesc(kDummyString);
1966  std::string sdp = kSdpFullString;
1967  sdp = sdp.substr(0, sdp.size() - 2);  // Remove \r\n at the end.
1968  // Deserialize
1969  SdpParseError error;
1970  EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error));
1971  const std::string lastline = "a=ssrc:6 label:video_track_id_3";
1972  EXPECT_EQ(lastline, error.line);
1973  EXPECT_EQ("Invalid SDP line.", error.description);
1974}
1975
1976TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) {
1977  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1978  std::string new_sdp = kSdpOneCandidate;
1979  Replace("udp", "unsupported_transport", &new_sdp);
1980  EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate));
1981  new_sdp = kSdpOneCandidate;
1982  Replace("udp", "uDP", &new_sdp);
1983  EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate));
1984  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1985  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1986  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
1987}
1988
1989TEST_F(WebRtcSdpTest, DeserializeCandidateOldFormat) {
1990  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1991  EXPECT_TRUE(SdpDeserializeCandidate(kSdpOneCandidateOldFormat,&jcandidate));
1992  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1993  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1994  Candidate ref_candidate = jcandidate_->candidate();
1995  ref_candidate.set_username("user_rtp");
1996  ref_candidate.set_password("password_rtp");
1997  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate));
1998}
1999
2000TEST_F(WebRtcSdpTest, DeserializeSdpWithConferenceFlag) {
2001  JsepSessionDescription jdesc(kDummyString);
2002
2003  // Deserialize
2004  EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc));
2005
2006  // Verify
2007  cricket::AudioContentDescription* audio =
2008    static_cast<AudioContentDescription*>(
2009      jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
2010  EXPECT_TRUE(audio->conference_mode());
2011
2012  cricket::VideoContentDescription* video =
2013    static_cast<VideoContentDescription*>(
2014      jdesc.description()->GetContentDescriptionByName(cricket::CN_VIDEO));
2015  EXPECT_TRUE(video->conference_mode());
2016}
2017
2018TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
2019  const char kSdpDestroyer[] = "!@#$%^&";
2020  const char kSdpInvalidLine1[] = " =candidate";
2021  const char kSdpInvalidLine2[] = "a+candidate";
2022  const char kSdpInvalidLine3[] = "a= candidate";
2023  // Broken fingerprint.
2024  const char kSdpInvalidLine4[] = "a=fingerprint:sha-1 "
2025      "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
2026  // Extra field.
2027  const char kSdpInvalidLine5[] = "a=fingerprint:sha-1 "
2028      "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX";
2029  // Missing space.
2030  const char kSdpInvalidLine6[] = "a=fingerprint:sha-1"
2031      "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
2032  // MD5 is not allowed in fingerprints.
2033  const char kSdpInvalidLine7[] = "a=fingerprint:md5 "
2034      "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B";
2035
2036  // Broken session description
2037  ExpectParseFailure("v=", kSdpDestroyer);
2038  ExpectParseFailure("o=", kSdpDestroyer);
2039  ExpectParseFailure("s=-", kSdpDestroyer);
2040  // Broken time description
2041  ExpectParseFailure("t=", kSdpDestroyer);
2042
2043  // Broken media description
2044  ExpectParseFailure("m=audio", "c=IN IP4 74.125.224.39");
2045  ExpectParseFailure("m=video", kSdpDestroyer);
2046
2047  // Invalid lines
2048  ExpectParseFailure("a=candidate", kSdpInvalidLine1);
2049  ExpectParseFailure("a=candidate", kSdpInvalidLine2);
2050  ExpectParseFailure("a=candidate", kSdpInvalidLine3);
2051
2052  // Bogus fingerprint replacing a=sendrev. We selected this attribute
2053  // because it's orthogonal to what we are replacing and hence
2054  // safe.
2055  ExpectParseFailure("a=sendrecv", kSdpInvalidLine4);
2056  ExpectParseFailure("a=sendrecv", kSdpInvalidLine5);
2057  ExpectParseFailure("a=sendrecv", kSdpInvalidLine6);
2058  ExpectParseFailure("a=sendrecv", kSdpInvalidLine7);
2059}
2060
2061TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) {
2062  // ssrc
2063  ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue");
2064  ExpectParseFailure("a=ssrc-group:FEC 5 6", "a=ssrc-group:FEC badvalue 6");
2065  // crypto
2066  ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue ");
2067  // rtpmap
2068  ExpectParseFailure("a=rtpmap:111 ", "a=rtpmap:badvalue ");
2069  ExpectParseFailure("opus/48000/2", "opus/badvalue/2");
2070  ExpectParseFailure("opus/48000/2", "opus/48000/badvalue");
2071  // candidate
2072  ExpectParseFailure("1 udp 2130706432", "badvalue udp 2130706432");
2073  ExpectParseFailure("1 udp 2130706432", "1 udp badvalue");
2074  ExpectParseFailure("192.168.1.5 1234", "192.168.1.5 badvalue");
2075  ExpectParseFailure("rport 2346", "rport badvalue");
2076  ExpectParseFailure("rport 2346 generation 2",
2077                     "rport 2346 generation badvalue");
2078  // m line
2079  ExpectParseFailure("m=audio 2345 RTP/SAVPF 111 103 104",
2080                     "m=audio 2345 RTP/SAVPF 111 badvalue 104");
2081
2082  // bandwidth
2083  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2084                                 "b=AS:badvalue\r\n",
2085                                 "b=AS:badvalue");
2086  // rtcp-fb
2087  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2088                                 "a=rtcp-fb:badvalue nack\r\n",
2089                                 "a=rtcp-fb:badvalue nack");
2090  // extmap
2091  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2092                                 "a=extmap:badvalue http://example.com\r\n",
2093                                 "a=extmap:badvalue http://example.com");
2094  // x-google-buffer-latency
2095  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2096                                 "a=x-google-buffer-latency:badvalue\r\n",
2097                                 "a=x-google-buffer-latency:badvalue");
2098}
2099
2100TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) {
2101  JsepSessionDescription jdesc_output(kDummyString);
2102
2103  const char kSdpWithReorderedPlTypesString[] =
2104      "v=0\r\n"
2105      "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2106      "s=-\r\n"
2107      "t=0 0\r\n"
2108      "m=audio 1 RTP/SAVPF 104 103\r\n"  // Pl type 104 preferred.
2109      "a=rtpmap:111 opus/48000/2\r\n"  // Pltype 111 listed before 103 and 104
2110                                       // in the map.
2111      "a=rtpmap:103 ISAC/16000\r\n"  // Pltype 103 listed before 104 in the map.
2112      "a=rtpmap:104 CELT/32000/2\r\n";
2113
2114  // Deserialize
2115  EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output));
2116
2117  const ContentInfo* ac = GetFirstAudioContent(jdesc_output.description());
2118  ASSERT_TRUE(ac != NULL);
2119  const AudioContentDescription* acd =
2120      static_cast<const AudioContentDescription*>(ac->description);
2121  ASSERT_FALSE(acd->codecs().empty());
2122  EXPECT_EQ("CELT", acd->codecs()[0].name);
2123  EXPECT_EQ(104, acd->codecs()[0].id);
2124}
2125
2126TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) {
2127  JsepSessionDescription jdesc_output(kDummyString);
2128  CodecParams params;
2129  params.max_ptime = 40;
2130  params.ptime = 30;
2131  params.min_ptime = 10;
2132  params.sprop_stereo = 1;
2133  params.stereo = 1;
2134  params.useinband = 1;
2135  params.maxaveragebitrate = 128000;
2136  TestDeserializeCodecParams(params, &jdesc_output);
2137  TestSerialize(jdesc_output);
2138}
2139
2140TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) {
2141  const bool kUseWildcard = false;
2142  JsepSessionDescription jdesc_output(kDummyString);
2143  TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
2144  TestSerialize(jdesc_output);
2145}
2146
2147TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) {
2148  const bool kUseWildcard = true;
2149  JsepSessionDescription jdesc_output(kDummyString);
2150  TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
2151  TestSerialize(jdesc_output);
2152}
2153
2154TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) {
2155  JsepSessionDescription jdesc_output(kDummyString);
2156
2157  const char kSdpWithFmtpString[] =
2158      "v=0\r\n"
2159      "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2160      "s=-\r\n"
2161      "t=0 0\r\n"
2162      "m=video 3457 RTP/SAVPF 120\r\n"
2163      "a=rtpmap:120 VP8/90000\r\n"
2164      "a=fmtp:120 x-google-min-bitrate=10; x-google-max-quantization=40\r\n";
2165
2166  // Deserialize
2167  SdpParseError error;
2168  EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output,
2169                                     &error));
2170
2171  const ContentInfo* vc = GetFirstVideoContent(jdesc_output.description());
2172  ASSERT_TRUE(vc != NULL);
2173  const VideoContentDescription* vcd =
2174      static_cast<const VideoContentDescription*>(vc->description);
2175  ASSERT_FALSE(vcd->codecs().empty());
2176  cricket::VideoCodec vp8 = vcd->codecs()[0];
2177  EXPECT_EQ("VP8", vp8.name);
2178  EXPECT_EQ(120, vp8.id);
2179  cricket::CodecParameterMap::iterator found =
2180      vp8.params.find("x-google-min-bitrate");
2181  ASSERT_TRUE(found != vp8.params.end());
2182  EXPECT_EQ(found->second, "10");
2183  found = vp8.params.find("x-google-max-quantization");
2184  ASSERT_TRUE(found != vp8.params.end());
2185  EXPECT_EQ(found->second, "40");
2186}
2187
2188TEST_F(WebRtcSdpTest, SerializeVideoFmtp) {
2189  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
2190      GetFirstVideoContent(&desc_)->description);
2191
2192  cricket::VideoCodecs codecs = vcd->codecs();
2193  codecs[0].params["x-google-min-bitrate"] = "10";
2194  vcd->set_codecs(codecs);
2195
2196  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
2197                                jdesc_.session_id(),
2198                                jdesc_.session_version()));
2199  std::string message = webrtc::SdpSerialize(jdesc_);
2200  std::string sdp_with_fmtp = kSdpFullString;
2201  InjectAfter("a=rtpmap:120 VP8/90000\r\n",
2202              "a=fmtp:120 x-google-min-bitrate=10\r\n",
2203              &sdp_with_fmtp);
2204  EXPECT_EQ(sdp_with_fmtp, message);
2205}
2206
2207TEST_F(WebRtcSdpTest, DeserializeSdpWithIceLite) {
2208  JsepSessionDescription jdesc_with_icelite(kDummyString);
2209  std::string sdp_with_icelite = kSdpFullString;
2210  EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
2211  cricket::SessionDescription* desc = jdesc_with_icelite.description();
2212  const cricket::TransportInfo* tinfo1 =
2213      desc->GetTransportInfoByName("audio_content_name");
2214  EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode);
2215  const cricket::TransportInfo* tinfo2 =
2216      desc->GetTransportInfoByName("video_content_name");
2217  EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode);
2218  InjectAfter(kSessionTime,
2219              "a=ice-lite\r\n",
2220              &sdp_with_icelite);
2221  EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
2222  desc = jdesc_with_icelite.description();
2223  const cricket::TransportInfo* atinfo =
2224      desc->GetTransportInfoByName("audio_content_name");
2225  EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode);
2226  const cricket::TransportInfo* vtinfo =
2227        desc->GetTransportInfoByName("video_content_name");
2228  EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode);
2229}
2230
2231// Verifies that the candidates in the input SDP are parsed and serialized
2232// correctly in the output SDP.
2233TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) {
2234  std::string sdp_with_data = kSdpString;
2235  sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString);
2236  JsepSessionDescription jdesc_output(kDummyString);
2237
2238  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2239  EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output));
2240}
2241
2242TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) {
2243  AddFingerprint();
2244  TransportInfo audio_transport_info =
2245      *(desc_.GetTransportInfoByName(kAudioContentName));
2246  EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
2247            audio_transport_info.description.connection_role);
2248  audio_transport_info.description.connection_role =
2249        cricket::CONNECTIONROLE_ACTIVE;
2250
2251  TransportInfo video_transport_info =
2252      *(desc_.GetTransportInfoByName(kVideoContentName));
2253  EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
2254            video_transport_info.description.connection_role);
2255  video_transport_info.description.connection_role =
2256        cricket::CONNECTIONROLE_ACTIVE;
2257
2258  desc_.RemoveTransportInfoByName(kAudioContentName);
2259  desc_.RemoveTransportInfoByName(kVideoContentName);
2260
2261  desc_.AddTransportInfo(audio_transport_info);
2262  desc_.AddTransportInfo(video_transport_info);
2263
2264  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
2265                                jdesc_.session_id(),
2266                                jdesc_.session_version()));
2267  std::string message = webrtc::SdpSerialize(jdesc_);
2268  std::string sdp_with_dtlssetup = kSdpFullString;
2269
2270  // Fingerprint attribute is necessary to add DTLS setup attribute.
2271  InjectAfter(kAttributeIcePwdVoice,
2272              kFingerprint, &sdp_with_dtlssetup);
2273  InjectAfter(kAttributeIcePwdVideo,
2274              kFingerprint, &sdp_with_dtlssetup);
2275  // Now adding |setup| attribute.
2276  InjectAfter(kFingerprint,
2277              "a=setup:active\r\n", &sdp_with_dtlssetup);
2278  EXPECT_EQ(sdp_with_dtlssetup, message);
2279}
2280
2281TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
2282  JsepSessionDescription jdesc_with_dtlssetup(kDummyString);
2283  std::string sdp_with_dtlssetup = kSdpFullString;
2284  InjectAfter(kSessionTime,
2285              "a=setup:actpass\r\n",
2286              &sdp_with_dtlssetup);
2287  EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup));
2288  cricket::SessionDescription* desc = jdesc_with_dtlssetup.description();
2289  const cricket::TransportInfo* atinfo =
2290      desc->GetTransportInfoByName("audio_content_name");
2291  EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
2292            atinfo->description.connection_role);
2293  const cricket::TransportInfo* vtinfo =
2294        desc->GetTransportInfoByName("video_content_name");
2295  EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
2296            vtinfo->description.connection_role);
2297}
2298