1 #include <tests/lib/test.h>
2
3 #include <tests/lib/glib-helpers/test-conn-helper.h>
4
5 #include <tests/lib/glib/call/conn.h>
6
7 #define TP_QT_ENABLE_LOWLEVEL_API
8
9 #include <TelepathyQt/Connection>
10 #include <TelepathyQt/ConnectionLowlevel>
11 #include <TelepathyQt/ContactManager>
12 #include <TelepathyQt/PendingChannel>
13 #include <TelepathyQt/PendingContacts>
14 #include <TelepathyQt/PendingReady>
15 #include <TelepathyQt/CallChannel>
16
17 #include <telepathy-glib/debug.h>
18
19 using namespace Tp;
20
21 class TestCallChannel : public Test
22 {
23 Q_OBJECT
24
25 public:
TestCallChannel(QObject * parent=0)26 TestCallChannel(QObject *parent = 0)
27 : Test(parent), mConn(0)
28 { }
29
30 protected Q_SLOTS:
31 void expectRequestContentFinished(Tp::PendingOperation *op);
32 void expectSuccessfulRequestReceiving(Tp::PendingOperation *op);
33
34 void onContentRemoved(const Tp::CallContentPtr &content,
35 const Tp::CallStateReason &reason);
36 void onCallStateChanged(Tp::CallState newState);
37 void onCallFlagsChanged(Tp::CallFlags newFlags);
38 void onRemoteMemberFlagsChanged(
39 const QHash<Tp::ContactPtr, Tp::CallMemberFlags> &remoteMemberFlags,
40 const Tp::CallStateReason &reason);
41 void onRemoteMembersRemoved(const Tp::Contacts &remoteMembers,
42 const Tp::CallStateReason &reason);
43 void onLocalSendingStateChanged(Tp::SendingState localSendingState,
44 const Tp::CallStateReason &reason);
45 void onRemoteSendingStateChanged(
46 const QHash<Tp::ContactPtr, Tp::SendingState> &remoteSendingStates,
47 const Tp::CallStateReason &reason);
48
49 void onLocalHoldStateChanged(Tp::LocalHoldState state, Tp::LocalHoldStateReason reason);
50
51 void onNewChannels(const Tp::ChannelDetailsList &details);
52
53 private Q_SLOTS:
54 void initTestCase();
55 void init();
56
57 void testOutgoingCall();
58 void testIncomingCall();
59 void testHold();
60 void testHangup();
61 void testCallMembers();
62 void testDTMF();
63 void testFeatureCore();
64
65 void cleanup();
66 void cleanupTestCase();
67
68 private:
69 TestConnHelper *mConn;
70
71 CallChannelPtr mChan;
72 CallContentPtr mRequestContentReturn;
73 CallContentPtr mContentRemoved;
74 CallStateReason mCallStateReason;
75 CallState mCallState;
76 CallFlags mCallFlags;
77 QHash<ContactPtr, CallMemberFlags> mRemoteMemberFlags;
78 Contacts mRemoteMembersRemoved;
79 SendingState mLSSCReturn;
80 QQueue<uint> mLocalHoldStates;
81 QQueue<uint> mLocalHoldStateReasons;
82
83 // Remote sending state changed state-machine
84 enum {
85 RSSCStateInitial,
86 RSSCStatePendingSend,
87 RSSCStateSending,
88 RSSCStateDone
89 } mRSSCState;
90 int mSuccessfulRequestReceivings;
91 };
92
expectRequestContentFinished(Tp::PendingOperation * op)93 void TestCallChannel::expectRequestContentFinished(Tp::PendingOperation *op)
94 {
95 if (!op->isFinished()) {
96 qWarning() << "unfinished";
97 mLoop->exit(1);
98 return;
99 }
100
101 if (op->isError()) {
102 qWarning().nospace() << op->errorName()
103 << ": " << op->errorMessage();
104 mLoop->exit(2);
105 return;
106 }
107
108 if (!op->isValid()) {
109 qWarning() << "inconsistent results";
110 mLoop->exit(3);
111 return;
112 }
113
114 PendingCallContent *pmc = qobject_cast<PendingCallContent*>(op);
115 mRequestContentReturn = pmc->content();
116 mLoop->exit(0);
117 }
118
onLocalSendingStateChanged(Tp::SendingState state,const Tp::CallStateReason & reason)119 void TestCallChannel::onLocalSendingStateChanged(Tp::SendingState state,
120 const Tp::CallStateReason &reason)
121 {
122 qDebug() << "local sending state changed";
123 mLSSCReturn = state;
124 mCallStateReason = reason;
125 mLoop->exit(0);
126 }
127
expectSuccessfulRequestReceiving(Tp::PendingOperation * op)128 void TestCallChannel::expectSuccessfulRequestReceiving(Tp::PendingOperation *op)
129 {
130 if (!op->isFinished()) {
131 qWarning() << "unfinished";
132 mLoop->exit(1);
133 return;
134 }
135
136 if (op->isError()) {
137 qWarning().nospace() << op->errorName()
138 << ": " << op->errorMessage();
139 mLoop->exit(2);
140 return;
141 }
142
143 if (!op->isValid()) {
144 qWarning() << "inconsistent results";
145 mLoop->exit(3);
146 return;
147 }
148
149 if (++mSuccessfulRequestReceivings == 2 && mRSSCState == RSSCStateDone) {
150 mLoop->exit(0);
151 }
152 }
153
onContentRemoved(const CallContentPtr & content,const Tp::CallStateReason & reason)154 void TestCallChannel::onContentRemoved(const CallContentPtr &content,
155 const Tp::CallStateReason &reason)
156 {
157 mContentRemoved = content;
158 mCallStateReason = reason;
159 mLoop->exit(0);
160 }
161
onCallStateChanged(CallState newState)162 void TestCallChannel::onCallStateChanged(CallState newState)
163 {
164 mCallState = newState;
165 mLoop->exit(0);
166 }
167
onCallFlagsChanged(CallFlags newFlags)168 void TestCallChannel::onCallFlagsChanged(CallFlags newFlags)
169 {
170 mCallFlags = newFlags;
171 }
172
onRemoteMemberFlagsChanged(const QHash<ContactPtr,CallMemberFlags> & remoteMemberFlags,const CallStateReason & reason)173 void TestCallChannel::onRemoteMemberFlagsChanged(
174 const QHash<ContactPtr, CallMemberFlags> &remoteMemberFlags,
175 const CallStateReason &reason)
176 {
177 mRemoteMemberFlags = remoteMemberFlags;
178 mLoop->exit(0);
179 }
180
onRemoteMembersRemoved(const Tp::Contacts & remoteMembers,const Tp::CallStateReason & reason)181 void TestCallChannel::onRemoteMembersRemoved(const Tp::Contacts &remoteMembers,
182 const Tp::CallStateReason &reason)
183 {
184 mRemoteMembersRemoved = remoteMembers;
185 }
186
onRemoteSendingStateChanged(const QHash<Tp::ContactPtr,SendingState> & states,const Tp::CallStateReason & reason)187 void TestCallChannel::onRemoteSendingStateChanged(
188 const QHash<Tp::ContactPtr, SendingState> &states,
189 const Tp::CallStateReason &reason)
190 {
191 // There should be no further events
192 QVERIFY(mRSSCState != RSSCStateDone);
193
194 QCOMPARE(states.size(), 1);
195 Tp::ContactPtr otherContact = states.keys().first();
196
197 CallContentPtr content = mChan->contentsForType(Tp::MediaStreamTypeVideo).first();
198 QVERIFY(content);
199
200 CallStreamPtr stream = content->streams().first();
201 QVERIFY(stream);
202
203 if (mRSSCState == RSSCStateInitial) {
204 QCOMPARE(states[otherContact], SendingStatePendingSend);
205 mRSSCState = RSSCStatePendingSend;
206 } else if (mRSSCState == RSSCStatePendingSend) {
207 QCOMPARE(states[otherContact], SendingStateSending);
208 mRSSCState = RSSCStateSending;
209
210 QVERIFY(connect(stream->requestReceiving(otherContact, false),
211 SIGNAL(finished(Tp::PendingOperation*)),
212 SLOT(expectSuccessfulRequestReceiving(Tp::PendingOperation*))));
213 } else if (mRSSCState == RSSCStateSending) {
214 QCOMPARE(states[otherContact], SendingStateNone);
215 mRSSCState = RSSCStateDone;
216
217 if (mSuccessfulRequestReceivings == 2) {
218 mLoop->exit(0);
219 }
220 }
221
222 qDebug() << "remote sending state changed to" << states[otherContact];
223 }
224
onLocalHoldStateChanged(Tp::LocalHoldState localHoldState,Tp::LocalHoldStateReason localHoldStateReason)225 void TestCallChannel::onLocalHoldStateChanged(Tp::LocalHoldState localHoldState,
226 Tp::LocalHoldStateReason localHoldStateReason)
227 {
228 mLocalHoldStates.append(localHoldState);
229 mLocalHoldStateReasons.append(localHoldStateReason);
230 mLoop->exit(0);
231 }
232
onNewChannels(const Tp::ChannelDetailsList & channels)233 void TestCallChannel::onNewChannels(const Tp::ChannelDetailsList &channels)
234 {
235 qDebug() << "new channels";
236 Q_FOREACH (const Tp::ChannelDetails &details, channels) {
237 QString channelType = details.properties.value(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType")).toString();
238 bool requested = details.properties.value(TP_QT_IFACE_CHANNEL + QLatin1String(".Requested")).toBool();
239 qDebug() << " channelType:" << channelType;
240 qDebug() << " requested :" << requested;
241
242 if (channelType == TP_QT_IFACE_CHANNEL_TYPE_CALL && !requested) {
243 mChan = CallChannel::create(mConn->client(), details.channel.path(), details.properties);
244 mLoop->exit(0);
245 }
246 }
247 }
248
initTestCase()249 void TestCallChannel::initTestCase()
250 {
251 initTestCaseImpl();
252
253 g_type_init();
254 g_set_prgname("call-channel");
255 tp_debug_set_flags("all");
256 dbus_g_bus_get(DBUS_BUS_STARTER, 0);
257
258 mConn = new TestConnHelper(this,
259 EXAMPLE_TYPE_CALL_CONNECTION,
260 "account", "me@example.com",
261 "protocol", "example",
262 "simulation-delay", 1,
263 NULL);
264 QCOMPARE(mConn->connect(Connection::FeatureSelfContact), true);
265 }
266
init()267 void TestCallChannel::init()
268 {
269 initImpl();
270
271 mRequestContentReturn.reset();
272 mContentRemoved.reset();
273 mCallStateReason = CallStateReason();
274 mCallState = CallStateUnknown;
275 mCallFlags = (CallFlags) 0;
276 mRemoteMemberFlags.clear();
277 mRemoteMembersRemoved.clear();
278 mLSSCReturn = (Tp::SendingState) -1;
279 mLocalHoldStates.clear();
280 mLocalHoldStateReasons.clear();
281 }
282
testOutgoingCall()283 void TestCallChannel::testOutgoingCall()
284 {
285 qDebug() << "requesting contact for alice";
286
287 QList<ContactPtr> contacts = mConn->contacts(QStringList() << QLatin1String("alice"));
288 QCOMPARE(contacts.size(), 1);
289
290 ContactPtr otherContact = contacts.at(0);
291 QVERIFY(otherContact);
292
293 qDebug() << "creating the channel";
294
295 QVariantMap request;
296 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
297 TP_QT_IFACE_CHANNEL_TYPE_CALL);
298 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
299 (uint) Tp::HandleTypeContact);
300 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
301 otherContact->handle()[0]);
302 request.insert(TP_QT_IFACE_CHANNEL_TYPE_CALL + QLatin1String(".InitialAudio"),
303 true);
304 mChan = CallChannelPtr::qObjectCast(mConn->createChannel(request));
305 QVERIFY(mChan);
306
307 qDebug() << "making the channel ready";
308
309 Features features;
310 features << CallChannel::FeatureCallState
311 << CallChannel::FeatureContents;
312
313 QVERIFY(connect(mChan->becomeReady(features),
314 SIGNAL(finished(Tp::PendingOperation*)),
315 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
316 QCOMPARE(mLoop->exec(), 0);
317 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureCallState));
318 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureContents));
319
320 QCOMPARE(mChan->callState(), CallStatePendingInitiator);
321
322 QVERIFY(connect(mChan->accept(),
323 SIGNAL(finished(Tp::PendingOperation*)),
324 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
325 QCOMPARE(mLoop->exec(), 0);
326 QCOMPARE(mChan->callState(), CallStateInitialised);
327 QCOMPARE(mChan->callStateReason().reason, (uint) CallStateChangeReasonUserRequested);
328
329 QVERIFY(connect(mChan.data(),
330 SIGNAL(callStateChanged(Tp::CallState)),
331 SLOT(onCallStateChanged(Tp::CallState))));
332 QCOMPARE(mLoop->exec(), 0);
333 QCOMPARE(mCallState, CallStateAccepted);
334 QCOMPARE(mChan->callState(), CallStateAccepted);
335
336 QCOMPARE(mChan->contents().size(), 1);
337
338 Tp::CallContentPtr content;
339 content = mChan->contents().first();
340 QVERIFY(content);
341 QCOMPARE(content->name(), QString::fromLatin1("audio"));
342 QCOMPARE(content->type(), Tp::MediaStreamTypeAudio);
343 QCOMPARE(content->disposition(), Tp::CallContentDispositionInitial);
344 QVERIFY(mChan->contentByName(QLatin1String("audio")));
345
346 /*
347 QCOMPARE(mChan->groupContacts().size(), 2);
348 QCOMPARE(mChan->groupLocalPendingContacts().size(), 0);
349 QCOMPARE(mChan->groupRemotePendingContacts().size(), 0);
350 QVERIFY(mChan->groupContacts().contains(mConn->client()->selfContact()));
351 QVERIFY(mChan->groupContacts().contains(otherContact));
352 */
353
354 qDebug() << "calling requestContent with a bad type";
355 // RequestContent with bad type must fail
356 QVERIFY(connect(mChan->requestContent(QLatin1String("content1"), (Tp::MediaStreamType) -1,
357 Tp::MediaStreamDirectionNone),
358 SIGNAL(finished(Tp::PendingOperation*)),
359 SLOT(expectRequestContentFinished(Tp::PendingOperation*))));
360 QCOMPARE(mLoop->exec(), 2);
361 QVERIFY(!mRequestContentReturn);
362
363 qDebug() << "calling requestContent with Audio";
364 QCOMPARE(mChan->contentsForType(Tp::MediaStreamTypeAudio).size(), 1);
365
366 mRequestContentReturn.reset();
367 QVERIFY(connect(mChan->requestContent(QLatin1String("content1"), Tp::MediaStreamTypeAudio,
368 Tp::MediaStreamDirectionBidirectional),
369 SIGNAL(finished(Tp::PendingOperation*)),
370 SLOT(expectRequestContentFinished(Tp::PendingOperation*))));
371 QCOMPARE(mLoop->exec(), 0);
372 QVERIFY(!mRequestContentReturn.isNull());
373 QCOMPARE(mRequestContentReturn->name(), QString(QLatin1String("content1")));
374 QCOMPARE(mRequestContentReturn->type(), Tp::MediaStreamTypeAudio);
375 QCOMPARE(mRequestContentReturn->disposition(), Tp::CallContentDispositionNone);
376
377 QCOMPARE(mChan->contentsForType(Tp::MediaStreamTypeAudio).size(), 2);
378
379 qDebug() << "calling requestContent with Video";
380 mRequestContentReturn.reset();
381 QVERIFY(connect(mChan->requestContent(QLatin1String("content2"), Tp::MediaStreamTypeVideo,
382 Tp::MediaStreamDirectionBidirectional),
383 SIGNAL(finished(Tp::PendingOperation*)),
384 SLOT(expectRequestContentFinished(Tp::PendingOperation*))));
385 QCOMPARE(mLoop->exec(), 0);
386 QVERIFY(!mRequestContentReturn.isNull());
387 QCOMPARE(mRequestContentReturn->name(), QString(QLatin1String("content2")));
388 QCOMPARE(mRequestContentReturn->type(), Tp::MediaStreamTypeVideo);
389
390 // test content removal
391 QCOMPARE(mChan->contentsForType(Tp::MediaStreamTypeAudio).size(), 2);
392
393 content = mChan->contentsForType(Tp::MediaStreamTypeAudio).first();
394 QVERIFY(content);
395
396 QVERIFY(connect(mChan.data(),
397 SIGNAL(contentRemoved(Tp::CallContentPtr,Tp::CallStateReason)),
398 SLOT(onContentRemoved(Tp::CallContentPtr,Tp::CallStateReason))));
399 QVERIFY(connect(content->remove(),
400 SIGNAL(finished(Tp::PendingOperation*)),
401 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
402 while (mContentRemoved.isNull()) {
403 QCOMPARE(mLoop->exec(), 0);
404 }
405 QCOMPARE(mChan->contentsForType(Tp::MediaStreamTypeAudio).size(), 1);
406 QCOMPARE(mContentRemoved, content);
407 QCOMPARE(mCallStateReason.reason, (uint) Tp::CallStateChangeReasonUserRequested);
408
409 // test content sending changed
410 content = mChan->contentsForType(Tp::MediaStreamTypeVideo).first();
411 Tp::CallStreamPtr stream = content->streams().first();
412 QVERIFY(content);
413
414 QVERIFY(connect(stream.data(),
415 SIGNAL(localSendingStateChanged(Tp::SendingState,Tp::CallStateReason)),
416 SLOT(onLocalSendingStateChanged(Tp::SendingState,Tp::CallStateReason))));
417
418 qDebug() << "stopping sending";
419
420 QCOMPARE(stream->localSendingState(), Tp::SendingStateSending);
421 QVERIFY(stream->remoteMembers().contains(otherContact));
422
423 QVERIFY(connect(stream->requestSending(false),
424 SIGNAL(finished(Tp::PendingOperation*)),
425 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
426 QCOMPARE(mLoop->exec(), 0);
427
428 qDebug() << "stopping receiving";
429 QVERIFY(connect(stream->requestReceiving(otherContact, false),
430 SIGNAL(finished(Tp::PendingOperation*)),
431 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
432 QCOMPARE(mLoop->exec(), 0);
433
434 qDebug() << "waiting until we're not sending and not receiving";
435
436 while (stream->localSendingState() != Tp::SendingStateNone
437 || stream->remoteSendingState(otherContact) != Tp::SendingStateNone) {
438 qDebug() << "re-entering mainloop to wait for local and remote SSC -> None";
439 // wait local and remote sending state change
440 QCOMPARE(mLoop->exec(), 0);
441 }
442 QCOMPARE(mLSSCReturn, Tp::SendingStateNone);
443 QCOMPARE(stream->localSendingState(), Tp::SendingStateNone);
444 QCOMPARE(stream->remoteSendingState(otherContact), Tp::SendingStateNone);
445
446 qDebug() << "re-enabling sending";
447
448 mLSSCReturn = (Tp::SendingState) -1;
449
450 QVERIFY(connect(stream->requestSending(true),
451 SIGNAL(finished(Tp::PendingOperation*)),
452 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
453 QCOMPARE(mLoop->exec(), 0);
454 while (mLSSCReturn == (Tp::SendingState) -1) {
455 qDebug() << "re-entering mainloop to wait for SSC -> Sending";
456 // wait sending state change
457 QCOMPARE(mLoop->exec(), 0);
458 }
459 QCOMPARE(mLSSCReturn, Tp::SendingStateSending);
460
461 qDebug() << "flushing D-Bus events";
462 processDBusQueue(mChan.data());
463
464 qDebug() << "enabling receiving";
465
466 mRSSCState = RSSCStateInitial;
467 mSuccessfulRequestReceivings = 0;
468
469 QVERIFY(connect(stream.data(),
470 SIGNAL(remoteSendingStateChanged(QHash<Tp::ContactPtr,Tp::SendingState>,Tp::CallStateReason)),
471 SLOT(onRemoteSendingStateChanged(QHash<Tp::ContactPtr,Tp::SendingState>,Tp::CallStateReason))));
472
473 // test content receiving changed
474 QVERIFY(connect(stream->requestReceiving(otherContact, true),
475 SIGNAL(finished(Tp::PendingOperation*)),
476 SLOT(expectSuccessfulRequestReceiving(Tp::PendingOperation*))));
477 QCOMPARE(mLoop->exec(), 0);
478 QCOMPARE(static_cast<uint>(mRSSCState), static_cast<uint>(RSSCStateDone));
479 }
480
testIncomingCall()481 void TestCallChannel::testIncomingCall()
482 {
483 mConn->client()->lowlevel()->setSelfPresence(QLatin1String("away"), QLatin1String("preparing for a test"));
484
485 Tp::Client::ConnectionInterfaceRequestsInterface *connRequestsInterface =
486 mConn->client()->optionalInterface<Tp::Client::ConnectionInterfaceRequestsInterface>();
487 QVERIFY(connect(connRequestsInterface,
488 SIGNAL(NewChannels(const Tp::ChannelDetailsList&)),
489 SLOT(onNewChannels(const Tp::ChannelDetailsList&))));
490
491 mConn->client()->lowlevel()->setSelfPresence(QLatin1String("available"), QLatin1String("call me?"));
492 QCOMPARE(mLoop->exec(), 0);
493
494 QVERIFY(mChan);
495 QCOMPARE(mChan->contents().size(), 0);
496
497 Features features;
498 features << CallChannel::FeatureCallState
499 << CallChannel::FeatureContents;
500
501 QVERIFY(connect(mChan->becomeReady(features),
502 SIGNAL(finished(Tp::PendingOperation*)),
503 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
504 QCOMPARE(mLoop->exec(), 0);
505 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureCallState));
506 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureContents));
507
508 Tp::ContactPtr otherContact = mChan->initiatorContact();
509 QVERIFY(otherContact);
510
511 QCOMPARE(mChan->callState(), CallStateInitialised);
512
513 QVERIFY(connect(mChan.data(),
514 SIGNAL(callFlagsChanged(Tp::CallFlags)),
515 SLOT(onCallFlagsChanged(Tp::CallFlags))));
516
517 QVERIFY(connect(mChan->setRinging(),
518 SIGNAL(finished(Tp::PendingOperation*)),
519 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
520 QCOMPARE(mLoop->exec(), 0);
521 QVERIFY(mCallFlags.testFlag(CallFlagLocallyRinging));
522 QVERIFY(mChan->callFlags().testFlag(CallFlagLocallyRinging));
523
524 QVERIFY(connect(mChan->setQueued(),
525 SIGNAL(finished(Tp::PendingOperation*)),
526 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
527 QCOMPARE(mLoop->exec(), 0);
528 QVERIFY(mCallFlags.testFlag(CallFlagLocallyQueued));
529 QVERIFY(mChan->callFlags().testFlag(CallFlagLocallyQueued));
530
531 QVERIFY(connect(mChan->accept(),
532 SIGNAL(finished(Tp::PendingOperation*)),
533 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
534 QCOMPARE(mLoop->exec(), 0);
535 QCOMPARE(mChan->callState(), CallStateAccepted);
536 QVERIFY(!mCallFlags.testFlag(CallFlagLocallyQueued));
537 QVERIFY(!mCallFlags.testFlag(CallFlagLocallyRinging));
538 QVERIFY(!mChan->callFlags().testFlag(CallFlagLocallyRinging));
539 QVERIFY(!mChan->callFlags().testFlag(CallFlagLocallyQueued));
540
541 /*
542 QCOMPARE(mChan->groupContacts().size(), 2);
543 QCOMPARE(mChan->groupLocalPendingContacts().size(), 0);
544 QCOMPARE(mChan->groupRemotePendingContacts().size(), 0);
545 QVERIFY(mChan->groupContacts().contains(mConn->selfContact()));
546 */
547
548 QCOMPARE(mChan->contents().size(), 1);
549 Tp::CallContentPtr content = mChan->contents().first();
550 QCOMPARE(content->channel(), mChan);
551 QCOMPARE(content->type(), Tp::MediaStreamTypeAudio);
552
553 qDebug() << "requesting a video stream";
554
555 // Request video stream
556 QVERIFY(connect(mChan->requestContent(QLatin1String("video_content"), Tp::MediaStreamTypeVideo,
557 Tp::MediaStreamDirectionBidirectional),
558 SIGNAL(finished(Tp::PendingOperation*)),
559 SLOT(expectRequestContentFinished(Tp::PendingOperation*))));
560 QCOMPARE(mLoop->exec(), 0);
561 content = mRequestContentReturn;
562 QCOMPARE(content->type(), Tp::MediaStreamTypeVideo);
563
564 QCOMPARE(mChan->contents().size(), 2);
565 QVERIFY(mChan->contents().contains(content));
566
567 QCOMPARE(mChan->contentsForType(Tp::MediaStreamTypeAudio).size(), 1);
568 QCOMPARE(mChan->contentsForType(Tp::MediaStreamTypeVideo).size(), 1);
569
570 // test content removal
571 content = mChan->contentsForType(Tp::MediaStreamTypeAudio).first();
572 QVERIFY(content);
573
574 qDebug() << "removing the audio content";
575
576 // call does not have the concept of removing streams, it will remove the content the stream
577 // belongs
578 QVERIFY(connect(mChan.data(),
579 SIGNAL(contentRemoved(Tp::CallContentPtr,Tp::CallStateReason)),
580 SLOT(onContentRemoved(Tp::CallContentPtr,Tp::CallStateReason))));
581
582 QVERIFY(connect(content->remove(),
583 SIGNAL(finished(Tp::PendingOperation*)),
584 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
585 while (mContentRemoved.isNull()) {
586 QCOMPARE(mLoop->exec(), 0);
587 }
588 QCOMPARE(mContentRemoved, content);
589 }
590
testHold()591 void TestCallChannel::testHold()
592 {
593 QList<ContactPtr> contacts = mConn->contacts(QStringList() << QLatin1String("bob"));
594 QCOMPARE(contacts.size(), 1);
595
596 ContactPtr otherContact = contacts.at(0);
597 QVERIFY(otherContact);
598
599 QVariantMap request;
600 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
601 TP_QT_IFACE_CHANNEL_TYPE_CALL);
602 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
603 (uint) Tp::HandleTypeContact);
604 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
605 otherContact->handle()[0]);
606 request.insert(TP_QT_IFACE_CHANNEL_TYPE_CALL + QLatin1String(".InitialAudio"),
607 true);
608 mChan = CallChannelPtr::qObjectCast(mConn->createChannel(request));
609 QVERIFY(mChan);
610
611 QVERIFY(connect(mChan->becomeReady(Tp::CallChannel::FeatureLocalHoldState),
612 SIGNAL(finished(Tp::PendingOperation*)),
613 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
614 QCOMPARE(mLoop->exec(), 0);
615 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureLocalHoldState));
616
617 QCOMPARE(static_cast<uint>(mChan->localHoldState()), static_cast<uint>(Tp::LocalHoldStateUnheld));
618 QCOMPARE(static_cast<uint>(mChan->localHoldStateReason()), static_cast<uint>(Tp::LocalHoldStateReasonNone));
619
620 QVERIFY(connect(mChan.data(),
621 SIGNAL(localHoldStateChanged(Tp::LocalHoldState, Tp::LocalHoldStateReason)),
622 SLOT(onLocalHoldStateChanged(Tp::LocalHoldState, Tp::LocalHoldStateReason))));
623 // Request hold
624 QVERIFY(connect(mChan->requestHold(true),
625 SIGNAL(finished(Tp::PendingOperation*)),
626 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
627 QCOMPARE(mLoop->exec(), 0);
628 while (mLocalHoldStates.size() != 2) {
629 QCOMPARE(mLoop->exec(), 0);
630 }
631 QCOMPARE(mLocalHoldStates.first(), static_cast<uint>(Tp::LocalHoldStatePendingHold));
632 QCOMPARE(mLocalHoldStateReasons.first(), static_cast<uint>(Tp::LocalHoldStateReasonRequested));
633 QCOMPARE(mLocalHoldStates.last(), static_cast<uint>(Tp::LocalHoldStateHeld));
634 QCOMPARE(mLocalHoldStateReasons.last(), static_cast<uint>(Tp::LocalHoldStateReasonRequested));
635 QCOMPARE(static_cast<uint>(mChan->localHoldState()), static_cast<uint>(Tp::LocalHoldStateHeld));
636 QCOMPARE(static_cast<uint>(mChan->localHoldStateReason()), static_cast<uint>(Tp::LocalHoldStateReasonRequested));
637
638 mLocalHoldStates.clear();
639 mLocalHoldStateReasons.clear();
640
641 // Request unhold
642 QVERIFY(connect(mChan->requestHold(false),
643 SIGNAL(finished(Tp::PendingOperation*)),
644 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
645 QCOMPARE(mLoop->exec(), 0);
646 while (mLocalHoldStates.size() != 2) {
647 QCOMPARE(mLoop->exec(), 0);
648 }
649 QCOMPARE(mLocalHoldStates.first(), static_cast<uint>(Tp::LocalHoldStatePendingUnhold));
650 QCOMPARE(mLocalHoldStateReasons.first(), static_cast<uint>(Tp::LocalHoldStateReasonRequested));
651 QCOMPARE(mLocalHoldStates.last(), static_cast<uint>(Tp::LocalHoldStateUnheld));
652 QCOMPARE(mLocalHoldStateReasons.last(), static_cast<uint>(Tp::LocalHoldStateReasonRequested));
653 QCOMPARE(static_cast<uint>(mChan->localHoldState()), static_cast<uint>(Tp::LocalHoldStateUnheld));
654 QCOMPARE(static_cast<uint>(mChan->localHoldStateReason()), static_cast<uint>(Tp::LocalHoldStateReasonRequested));
655 }
656
testHangup()657 void TestCallChannel::testHangup()
658 {
659 qDebug() << "requesting contact for alice";
660
661 QList<ContactPtr> contacts = mConn->contacts(QStringList() << QLatin1String("alice"));
662 QCOMPARE(contacts.size(), 1);
663
664 ContactPtr otherContact = contacts.at(0);
665 QVERIFY(otherContact);
666
667 qDebug() << "creating the channel";
668
669 QVariantMap request;
670 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
671 TP_QT_IFACE_CHANNEL_TYPE_CALL);
672 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
673 (uint) Tp::HandleTypeContact);
674 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
675 otherContact->handle()[0]);
676 request.insert(TP_QT_IFACE_CHANNEL_TYPE_CALL + QLatin1String(".InitialVideo"),
677 true);
678 mChan = CallChannelPtr::qObjectCast(mConn->createChannel(request));
679 QVERIFY(mChan);
680
681 qDebug() << "making the channel ready";
682
683 QVERIFY(connect(mChan->becomeReady(CallChannel::FeatureCallState),
684 SIGNAL(finished(Tp::PendingOperation*)),
685 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
686 QCOMPARE(mLoop->exec(), 0);
687 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureCallState));
688
689 QCOMPARE(mChan->callState(), CallStatePendingInitiator);
690
691 QVERIFY(connect(mChan->hangup(),
692 SIGNAL(finished(Tp::PendingOperation*)),
693 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
694 QCOMPARE(mLoop->exec(), 0);
695 QCOMPARE(mChan->callState(), CallStateEnded);
696 QCOMPARE(mChan->callStateReason().reason, (uint) CallStateChangeReasonUserRequested);
697 }
698
testCallMembers()699 void TestCallChannel::testCallMembers()
700 {
701 qDebug() << "requesting contact for john";
702
703 QList<ContactPtr> contacts = mConn->contacts(QStringList() << QLatin1String("john"));
704 QCOMPARE(contacts.size(), 1);
705
706 ContactPtr otherContact = contacts.at(0);
707 QVERIFY(otherContact);
708
709 qDebug() << "creating the channel";
710
711 QVariantMap request;
712 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
713 TP_QT_IFACE_CHANNEL_TYPE_CALL);
714 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
715 (uint) Tp::HandleTypeContact);
716 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
717 otherContact->handle()[0]);
718 request.insert(TP_QT_IFACE_CHANNEL_TYPE_CALL + QLatin1String(".InitialVideo"),
719 true);
720 mChan = CallChannelPtr::qObjectCast(mConn->createChannel(request));
721 QVERIFY(mChan);
722
723 qDebug() << "making the channel ready";
724
725 Features features;
726 features << CallChannel::FeatureCallState
727 << CallChannel::FeatureCallMembers
728 << CallChannel::FeatureContents;
729
730 QVERIFY(connect(mChan->becomeReady(features),
731 SIGNAL(finished(Tp::PendingOperation*)),
732 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
733 QCOMPARE(mLoop->exec(), 0);
734 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureCallMembers));
735 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureContents));
736
737 qDebug() << "accepting the call";
738
739 QCOMPARE(mChan->callState(), CallStatePendingInitiator);
740 QCOMPARE(mChan->remoteMembers().size(), 1);
741
742 QVERIFY(connect(mChan->accept(),
743 SIGNAL(finished(Tp::PendingOperation*)),
744 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
745 QCOMPARE(mLoop->exec(), 0);
746 QCOMPARE(mChan->callState(), CallStateInitialised);
747 QCOMPARE(mChan->callStateReason().reason, (uint) CallStateChangeReasonUserRequested);
748
749 qDebug() << "ringing on the remote side";
750
751 QVERIFY(connect(mChan.data(),
752 SIGNAL(remoteMemberFlagsChanged(QHash<Tp::ContactPtr,Tp::CallMemberFlags>,Tp::CallStateReason)),
753 SLOT(onRemoteMemberFlagsChanged(QHash<Tp::ContactPtr,Tp::CallMemberFlags>,Tp::CallStateReason))));
754 QCOMPARE(mLoop->exec(), 0);
755
756 QCOMPARE(mChan->callState(), CallStateInitialised);
757 QCOMPARE(mRemoteMemberFlags.size(), 1);
758 QCOMPARE(mChan->remoteMembers().size(), 1);
759 QVERIFY(mRemoteMemberFlags.constBegin().value().testFlag(CallMemberFlagRinging));
760 QVERIFY(mChan->remoteMemberFlags(otherContact).testFlag(CallMemberFlagRinging));
761
762 QVERIFY(disconnect(mChan.data(),
763 SIGNAL(remoteMemberFlagsChanged(QHash<Tp::ContactPtr,Tp::CallMemberFlags>,Tp::CallStateReason)),
764 this,
765 SLOT(onRemoteMemberFlagsChanged(QHash<Tp::ContactPtr,Tp::CallMemberFlags>,Tp::CallStateReason))));
766
767 qDebug() << "remote contact answers";
768
769 QVERIFY(connect(mChan.data(),
770 SIGNAL(callStateChanged(Tp::CallState)),
771 SLOT(onCallStateChanged(Tp::CallState))));
772 QCOMPARE(mLoop->exec(), 0);
773
774 QCOMPARE(mCallState, CallStateAccepted);
775 QCOMPARE(mChan->callState(), CallStateAccepted);
776
777 QVERIFY(disconnect(mChan.data(),
778 SIGNAL(callStateChanged(Tp::CallState)),
779 this,
780 SLOT(onCallStateChanged(Tp::CallState))));
781
782 qDebug() << "testing members";
783
784 QCOMPARE(mChan->contents().size(), 1);
785
786 CallContentPtr content = mChan->contents().at(0);
787 QCOMPARE(content->streams().size(), 1);
788
789 QCOMPARE(mChan->remoteMembers().size(), 1);
790 QCOMPARE(content->streams().at(0)->remoteMembers().size(), 1);
791
792 ContactPtr contact1 = *mChan->remoteMembers().constBegin();
793 ContactPtr contact2 = *content->streams().at(0)->remoteMembers().constBegin();
794
795 QCOMPARE(contact1->id(), QString::fromLatin1("john"));
796 QCOMPARE(contact2->id(), QString::fromLatin1("john"));
797
798 qDebug() << "hanging up";
799
800 QVERIFY(connect(mChan.data(),
801 SIGNAL(remoteMembersRemoved(Tp::Contacts,Tp::CallStateReason)),
802 SLOT(onRemoteMembersRemoved(Tp::Contacts,Tp::CallStateReason))));
803
804 QVERIFY(connect(mChan->hangup(),
805 SIGNAL(finished(Tp::PendingOperation*)),
806 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
807 QCOMPARE(mLoop->exec(), 0);
808 QCOMPARE(mChan->callState(), CallStateEnded);
809 QCOMPARE(mChan->callStateReason().reason, (uint) CallStateChangeReasonUserRequested);
810
811 QCOMPARE(mRemoteMembersRemoved.size(), 1);
812 QCOMPARE((*mRemoteMembersRemoved.constBegin())->id(), QString::fromLatin1("john"));
813 QCOMPARE(mChan->remoteMembers().size(), 0);
814 QCOMPARE(mChan->contents().size(), 0);
815 }
816
testDTMF()817 void TestCallChannel::testDTMF()
818 {
819 mConn->client()->lowlevel()->setSelfPresence(QLatin1String("away"), QLatin1String("preparing for a test"));
820
821 Tp::Client::ConnectionInterfaceRequestsInterface *connRequestsInterface =
822 mConn->client()->optionalInterface<Tp::Client::ConnectionInterfaceRequestsInterface>();
823 QVERIFY(connect(connRequestsInterface,
824 SIGNAL(NewChannels(const Tp::ChannelDetailsList&)),
825 SLOT(onNewChannels(const Tp::ChannelDetailsList&))));
826
827 mConn->client()->lowlevel()->setSelfPresence(QLatin1String("available"), QLatin1String("call me?"));
828 QCOMPARE(mLoop->exec(), 0);
829 QVERIFY(mChan);
830
831 qDebug() << "making the channel ready";
832
833 QVERIFY(connect(mChan->becomeReady(CallChannel::FeatureContents),
834 SIGNAL(finished(Tp::PendingOperation*)),
835 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
836 QCOMPARE(mLoop->exec(), 0);
837 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureContents));
838
839 QVERIFY(connect(mChan->accept(),
840 SIGNAL(finished(Tp::PendingOperation*)),
841 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
842 QCOMPARE(mLoop->exec(), 0);
843
844 QCOMPARE(mChan->contents().size(), 1);
845 Tp::CallContentPtr content = mChan->contents().first();
846 QCOMPARE(content->channel(), mChan);
847 QCOMPARE(content->type(), Tp::MediaStreamTypeAudio);
848
849 /* TODO enable when/if the example call CM actually supports DTMF
850 QVERIFY(content->supportsDTMF());
851 QVERIFY(connect(content->startDTMFTone(DTMFEventDigit0),
852 SIGNAL(finished(Tp::PendingOperation*)),
853 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
854 QCOMPARE(mLoop->exec(), 0);
855
856 QVERIFY(connect(content->stopDTMFTone(),
857 SIGNAL(finished(Tp::PendingOperation*)),
858 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
859 QCOMPARE(mLoop->exec(), 0);
860
861 QVERIFY(connect(content->startDTMFTone((DTMFEvent) 1234),
862 SIGNAL(finished(Tp::PendingOperation*)),
863 SLOT(expectFailure(Tp::PendingOperation*))));
864 QCOMPARE(mLoop->exec(), 0);
865 QCOMPARE(mLastError, QString(TP_QT_ERROR_INVALID_ARGUMENT));
866 */
867
868 qDebug() << "requesting video content";
869
870 QVERIFY(connect(mChan->requestContent(QLatin1String("video_content"),
871 Tp::MediaStreamTypeVideo,
872 Tp::MediaStreamDirectionBidirectional),
873 SIGNAL(finished(Tp::PendingOperation*)),
874 SLOT(expectRequestContentFinished(Tp::PendingOperation*))));
875 QCOMPARE(mLoop->exec(), 0);
876 content = mRequestContentReturn;
877 QCOMPARE(content->type(), Tp::MediaStreamTypeVideo);
878
879 QVERIFY(!content->supportsDTMF());
880 QVERIFY(connect(content->startDTMFTone(DTMFEventDigit0),
881 SIGNAL(finished(Tp::PendingOperation*)),
882 SLOT(expectFailure(Tp::PendingOperation*))));
883 QCOMPARE(mLoop->exec(), 0);
884 QCOMPARE(mLastError, QString(TP_QT_ERROR_NOT_IMPLEMENTED));
885
886 QVERIFY(connect(content->stopDTMFTone(),
887 SIGNAL(finished(Tp::PendingOperation*)),
888 SLOT(expectFailure(Tp::PendingOperation*))));
889 QCOMPARE(mLoop->exec(), 0);
890 QCOMPARE(mLastError, QString(TP_QT_ERROR_NOT_IMPLEMENTED));
891 }
892
testFeatureCore()893 void TestCallChannel::testFeatureCore()
894 {
895 qDebug() << "requesting contact for alice";
896
897 QList<ContactPtr> contacts = mConn->contacts(QStringList() << QLatin1String("alice"));
898 QCOMPARE(contacts.size(), 1);
899
900 ContactPtr otherContact = contacts.at(0);
901 QVERIFY(otherContact);
902
903 qDebug() << "creating the channel";
904
905 QVariantMap request;
906 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
907 TP_QT_IFACE_CHANNEL_TYPE_CALL);
908 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
909 (uint) Tp::HandleTypeContact);
910 request.insert(TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
911 otherContact->handle()[0]);
912 request.insert(TP_QT_IFACE_CHANNEL_TYPE_CALL + QLatin1String(".InitialAudio"),
913 true);
914 mChan = CallChannelPtr::qObjectCast(mConn->createChannel(request));
915 QVERIFY(mChan);
916
917 QVERIFY(connect(mChan->becomeReady(),
918 SIGNAL(finished(Tp::PendingOperation*)),
919 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
920 QCOMPARE(mLoop->exec(), 0);
921 QVERIFY(mChan->isReady(Tp::CallChannel::FeatureCore));
922
923 QVERIFY(mChan->hasInitialAudio());
924 QCOMPARE(mChan->initialAudioName(), QString::fromLatin1("audio"));
925 QVERIFY(!mChan->hasInitialVideo());
926 QCOMPARE(mChan->initialVideoName(), QString::fromLatin1("video"));
927 QVERIFY(mChan->hasMutableContents());
928 QVERIFY(mChan->handlerStreamingRequired());
929
930 qDebug() << "creating second CallChannel object";
931
932 //this object is not passed immutable properties on
933 //the constructor, so it will have to introspect them.
934 CallChannelPtr chan2 = CallChannel::create(mConn->client(), mChan->objectPath(), QVariantMap());
935
936 QVERIFY(connect(chan2->becomeReady(),
937 SIGNAL(finished(Tp::PendingOperation*)),
938 SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
939 QCOMPARE(mLoop->exec(), 0);
940 QVERIFY(chan2->isReady(Tp::CallChannel::FeatureCore));
941
942 QVERIFY(chan2->hasInitialAudio());
943 QCOMPARE(chan2->initialAudioName(), QString::fromLatin1("audio"));
944 QVERIFY(!chan2->hasInitialVideo());
945 QCOMPARE(chan2->initialVideoName(), QString::fromLatin1("video"));
946 QVERIFY(chan2->hasMutableContents());
947 QVERIFY(chan2->handlerStreamingRequired());
948 }
949
cleanup()950 void TestCallChannel::cleanup()
951 {
952 mChan.reset();
953 cleanupImpl();
954 }
955
cleanupTestCase()956 void TestCallChannel::cleanupTestCase()
957 {
958 QCOMPARE(mConn->disconnect(), true);
959 delete mConn;
960
961 cleanupTestCaseImpl();
962 }
963
964 QTEST_MAIN(TestCallChannel)
965 #include "_gen/call-channel.cpp.moc.hpp"
966