1""" 2Test StreamError events when new content is rejected in-call. 3""" 4 5import dbus 6 7from gabbletest import make_result_iq, sync_stream, exec_test 8from servicetest import ( 9 make_channel_proxy, unwrap, EventPattern, assertEquals, assertLength) 10from jingletest2 import JingleTest2, JingleProtocol031 11import constants as cs 12 13from twisted.words.xish import xpath 14 15from config import VOIP_ENABLED 16 17if not VOIP_ENABLED: 18 print "NOTE: built with --disable-voip" 19 raise SystemExit(77) 20 21def _content_reject_predicate(event): 22 reason = xpath.queryForNodes("/iq" 23 "/jingle[@action='content-reject']" 24 "/reason/failed-application", 25 event.stanza) 26 27 return bool(reason) 28 29def _start_audio_session(jp, q, bus, conn, stream, incoming): 30 jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'foo@bar.com/Foo') 31 jt.prepare() 32 33 self_handle = conn.GetSelfHandle() 34 remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] 35 36 if incoming: 37 jt.incoming_call() 38 else: 39 ret = conn.Requests.CreateChannel( 40 { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, 41 cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, 42 cs.TARGET_HANDLE: remote_handle, 43 cs.INITIAL_AUDIO: True 44 }) 45 46 nc, e = q.expect_many( 47 EventPattern('dbus-signal', signal='NewChannel', 48 predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), 49 EventPattern('dbus-signal', signal='NewSessionHandler')) 50 51 path = nc.args[0] 52 53 media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') 54 media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') 55 56 # S-E was notified about new session handler, and calls Ready on it 57 session_handler = make_channel_proxy(conn, e.args[0], 58 'Media.SessionHandler') 59 session_handler.Ready() 60 61 nsh_event = q.expect('dbus-signal', signal='NewStreamHandler') 62 63 # S-E gets notified about a newly-created stream 64 stream_handler = make_channel_proxy(conn, nsh_event.args[0], 65 'Media.StreamHandler') 66 67 group_props = media_chan.GetAll( 68 cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE) 69 70 if incoming: 71 assertEquals([remote_handle], group_props['Members']) 72 assertEquals(unwrap(group_props['LocalPendingMembers']), 73 [(self_handle, remote_handle, cs.GC_REASON_INVITED, '')]) 74 else: 75 assertEquals([self_handle], group_props['Members']) 76 77 streams = media_chan.ListStreams( 78 dbus_interface=cs.CHANNEL_TYPE_STREAMED_MEDIA) 79 80 stream_id = streams[0][0] 81 82 stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) 83 stream_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) 84 85 msg = u"None of the codecs are good for us, damn!" 86 87 expected_events = [] 88 89 if incoming: 90 stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) 91 stream_handler.SupportedCodecs(jt.get_audio_codecs_dbus()) 92 93 e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) 94 assertEquals(jt.peer, e.query['initiator']) 95 content = xpath.queryForNodes('/iq/jingle/content', e.stanza)[0] 96 assertEquals('initiator', content['creator']) 97 98 stream.send(make_result_iq(stream, e.stanza)) 99 100 media_chan.AddMembers([self_handle], 'accepted') 101 102 memb, acc, _, _, _ = q.expect_many( 103 EventPattern('dbus-signal', signal='MembersChanged', 104 args=[u'', [self_handle], [], [], [], self_handle, 105 cs.GC_REASON_NONE]), 106 EventPattern('stream-iq', 107 predicate=jp.action_predicate('session-accept')), 108 EventPattern('dbus-signal', signal='SetStreamSending', 109 args=[True]), 110 EventPattern('dbus-signal', signal='SetStreamPlaying', 111 args=[True]), 112 EventPattern('dbus-signal', signal='StreamDirectionChanged', 113 args=[stream_id, 114 cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0])) 115 116 stream.send(make_result_iq(stream, acc.stanza)) 117 118 active_event = jp.rtp_info_event("active") 119 if active_event is not None: 120 q.expect_many(active_event) 121 122 members = media_chan.GetMembers() 123 assert set(members) == set([self_handle, remote_handle]), members 124 else: 125 stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) 126 session_initiate = q.expect( 127 'stream-iq', 128 predicate=jp.action_predicate('session-initiate')) 129 130 q.expect('dbus-signal', signal='MembersChanged', path=path, 131 args=['', [], [], [], [remote_handle], self_handle, 132 cs.GC_REASON_INVITED]) 133 134 jt.parse_session_initiate(session_initiate.query) 135 stream.send(jp.xml(jp.ResultIq('test@localhost', 136 session_initiate.stanza, []))) 137 138 jt.accept() 139 140 q.expect_many( 141 EventPattern('stream-iq', iq_type='result'), 142 # Call accepted 143 EventPattern('dbus-signal', signal='MembersChanged', 144 args=['', [remote_handle], [], [], [], remote_handle, 145 cs.GC_REASON_NONE]), 146 ) 147 return jt, media_iface 148 149def _start_audio_session_outgoing(jp, q, bus, conn, stream): 150 return _start_audio_session(jp, q, bus, conn, stream, False) 151 152def _start_audio_session_incoming(jp, q, bus, conn, stream): 153 return _start_audio_session(jp, q, bus, conn, stream, True) 154 155def _remote_content_add(jp, q, bus, conn, stream, initiate_call_func): 156 jt, chan = initiate_call_func(jp, q, bus, conn, stream) 157 158 video_codecs = [ 159 jp.PayloadType(name, str(rate), str(id), parameters) \ 160 for (name, id, rate, parameters) in jt.video_codecs] 161 162 node = jp.SetIq(jt.peer, jt.jid, [ 163 jp.Jingle(jt.sid, jt.peer, 'content-add', [ 164 jp.Content( 165 'videostream', 'initiator', 'both', 166 jp.Description('video', video_codecs), 167 jp.TransportGoogleP2P()) ]) ]) 168 stream.send(jp.xml(node)) 169 170 _, nsh = q.expect_many( 171 EventPattern('dbus-signal', signal='StreamAdded'), 172 EventPattern('dbus-signal', signal='NewStreamHandler')) 173 174 stream_handler_path, stream_id, media_type, direction = nsh.args 175 176 video_handler = make_channel_proxy(conn, stream_handler_path, 177 'Media.StreamHandler') 178 179 video_handler.NewNativeCandidate("fake", 180 jt.get_remote_transports_dbus()) 181 video_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) 182 183 msg = u"None of the codecs are good for us, damn!" 184 185 video_handler.Error(cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg) 186 187 q.expect_many( 188 EventPattern('dbus-signal', signal='StreamError', 189 args=[stream_id, 190 cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, 191 msg]), 192 EventPattern('stream-iq', predicate=_content_reject_predicate)) 193 194def _local_content_add(jp, q, bus, conn, stream, initiate_call_func): 195 jt, chan = initiate_call_func(jp, q, bus, conn, stream) 196 197 remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] 198 199 chan.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) 200 201 nsh = q.expect('dbus-signal', signal='NewStreamHandler') 202 stream_handler_path, stream_id, media_type, direction = nsh.args 203 video_handler = make_channel_proxy(conn, stream_handler_path, 204 'Media.StreamHandler') 205 206 video_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) 207 video_handler.Ready(jt.get_audio_codecs_dbus()) 208 video_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) 209 210 e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) 211 c = e.query.firstChildElement() 212 stream.send(make_result_iq(stream, e.stanza)) 213 214 node = jp.SetIq(jt.peer, jt.jid, [ 215 jp.Jingle(jt.sid, jt.peer, 'content-reject', [ 216 ('reason', None, {}, [ 217 ('failed-application', None, {}, [])]), 218 jp.Content(c['name'], c['creator'], c['senders']) ]) ]) 219 stream.send(jp.xml(node)) 220 221 q.expect('dbus-signal', signal='StreamError', 222 args=[stream_id, 223 cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, 224 ""]), 225 226def test_remote_content_add_incoming(jp, q, bus, conn, stream): 227 _remote_content_add(jp, q, bus, conn, stream, 228 _start_audio_session_incoming) 229 230def test_remote_content_add_outgoing(jp, q, bus, conn, stream): 231 _remote_content_add(jp, q, bus, conn, stream, 232 _start_audio_session_outgoing) 233 234def test_local_content_add_incoming(jp, q, bus, conn, stream): 235 _local_content_add(jp, q, bus, conn, stream, _start_audio_session_incoming) 236 237def test_local_content_add_outgoing(jp, q, bus, conn, stream): 238 _local_content_add(jp, q, bus, conn, stream, _start_audio_session_outgoing) 239 240if __name__ == '__main__': 241 for f in (test_local_content_add_incoming, 242 test_local_content_add_outgoing, 243 test_remote_content_add_incoming, 244 test_remote_content_add_outgoing): 245 exec_test( 246 lambda q, b, c, s: f(JingleProtocol031(), q, b, c, s)) 247