~phablet-team/telephony-service/trunk

« back to all changes in this revision

Viewing changes to handler/tests/mock/connection.cpp

Add the infrastructure to write tests for the handler, and write tests for the most important bits of it.

Approved by Tiago Salem Herrmann, PS Jenkins bot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright (C) 2013 Canonical, Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it under
 
5
 * the terms of the GNU General Public License version 3, as published by
 
6
 * the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
 
9
 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
 
10
 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
11
 * General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
 
17
 *          Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
 
18
 */
 
19
 
 
20
#include <QDebug>
 
21
 
 
22
#include <TelepathyQt/Constants>
 
23
#include <TelepathyQt/BaseChannel>
 
24
#include <TelepathyQt/DBusObject>
 
25
 
 
26
// telepathy-mock
 
27
#include "connection.h"
 
28
#include "phoneutils.h"
 
29
#include "protocol.h"
 
30
 
 
31
#include "mockconnectiondbus.h"
 
32
 
 
33
MockConnection::MockConnection(const QDBusConnection &dbusConnection,
 
34
                            const QString &cmName,
 
35
                            const QString &protocolName,
 
36
                            const QVariantMap &parameters) :
 
37
    Tp::BaseConnection(dbusConnection, cmName, protocolName, parameters),
 
38
    mHandleCount(0)
 
39
{
 
40
    setSelfHandle(newHandle("<SelfHandle>"));
 
41
 
 
42
    setConnectCallback(Tp::memFun(this,&MockConnection::connect));
 
43
    setInspectHandlesCallback(Tp::memFun(this,&MockConnection::inspectHandles));
 
44
    setRequestHandlesCallback(Tp::memFun(this,&MockConnection::requestHandles));
 
45
    setCreateChannelCallback(Tp::memFun(this,&MockConnection::createChannel));
 
46
 
 
47
    // initialise requests interface (Connection.Interface.Requests)
 
48
    requestsIface = Tp::BaseConnectionRequestsInterface::create(this);
 
49
 
 
50
    // set requestable text channel properties
 
51
    Tp::RequestableChannelClass text;
 
52
    text.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_TEXT;
 
53
    text.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"]  = Tp::HandleTypeContact;
 
54
    text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle");
 
55
    text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID");
 
56
 
 
57
    // set requestable call channel properties
 
58
    Tp::RequestableChannelClass call;
 
59
    call.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_CALL;
 
60
    call.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"]  = Tp::HandleTypeContact;
 
61
    call.fixedProperties[TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudio"]  = true;
 
62
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle");
 
63
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID");
 
64
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudio");
 
65
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialVideo");
 
66
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudioName");
 
67
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialVideoName");
 
68
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialTransport");
 
69
    call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".HardwareStreaming");
 
70
 
 
71
    requestsIface->requestableChannelClasses << text << call;
 
72
 
 
73
    plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(requestsIface));
 
74
 
 
75
    // init presence interface
 
76
    simplePresenceIface = Tp::BaseConnectionSimplePresenceInterface::create();
 
77
    simplePresenceIface->setSetPresenceCallback(Tp::memFun(this,&MockConnection::setPresence));
 
78
    plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(simplePresenceIface));
 
79
 
 
80
    // Set Presence
 
81
    Tp::SimpleStatusSpec presenceOnline;
 
82
    presenceOnline.type = Tp::ConnectionPresenceTypeAvailable;
 
83
    presenceOnline.maySetOnSelf = true;
 
84
    presenceOnline.canHaveMessage = false;
 
85
 
 
86
    Tp::SimpleStatusSpec presenceOffline;
 
87
    presenceOffline.type = Tp::ConnectionPresenceTypeOffline;
 
88
    presenceOffline.maySetOnSelf = false;
 
89
    presenceOffline.canHaveMessage = false;
 
90
 
 
91
    Tp::SimpleStatusSpecMap statuses;
 
92
    statuses.insert(QLatin1String("available"), presenceOnline);
 
93
    statuses.insert(QLatin1String("offline"), presenceOffline);
 
94
 
 
95
    simplePresenceIface->setStatuses(statuses);
 
96
    mSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
 
97
    mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
 
98
 
 
99
    contactsIface = Tp::BaseConnectionContactsInterface::create();
 
100
    contactsIface->setGetContactAttributesCallback(Tp::memFun(this,&MockConnection::getContactAttributes));
 
101
    contactsIface->setContactAttributeInterfaces(QStringList()
 
102
                                                 << TP_QT_IFACE_CONNECTION
 
103
                                                 << TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE);
 
104
    plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(contactsIface));
 
105
 
 
106
    mDBus = new MockConnectionDBus(this);
 
107
 
 
108
    setOnline(true);
 
109
}
 
110
 
 
111
void MockConnection::addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath)
 
112
{
 
113
    qDebug() << "addMMSToService " << path << properties << servicePath;
 
114
#if 0
 
115
    FIXME: re-implement
 
116
    MMSDMessage *msg = new MMSDMessage(path, properties);
 
117
    QObject::connect(msg, SIGNAL(propertyChanged(QString,QVariant)), SLOT(onMMSPropertyChanged(QString,QVariant)));
 
118
    mServiceMMSList[servicePath].append(msg);
 
119
    if (properties["Status"] ==  "received") {
 
120
        const QString normalizedNumber = PhoneUtils::normalizePhoneNumber(properties["Sender"].toString());
 
121
        // check if there is an open channel for this number and use it
 
122
        Q_FOREACH(const QString &phoneNumber, mTextChannels.keys()) {
 
123
            if (PhoneUtils::comparePhoneNumbers(normalizedNumber, phoneNumber)) {
 
124
                qDebug() << "existing channel" << mTextChannels[phoneNumber];
 
125
                mTextChannels[phoneNumber]->mmsReceived(path, properties);
 
126
                return;
 
127
            }
 
128
        }
 
129
 
 
130
        Tp::DBusError error;
 
131
        bool yours;
 
132
        qDebug() << "new handle" << normalizedNumber;
 
133
        uint handle = newHandle(normalizedNumber);
 
134
        ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, &error);
 
135
        if(error.isValid()) {
 
136
            qCritical() << "Error creating channel for incoming message " << error.name() << error.message();
 
137
            return;
 
138
        }
 
139
        mTextChannels[normalizedNumber]->mmsReceived(path, properties);
 
140
    }
 
141
#endif
 
142
}
 
143
 
 
144
MockConnection::~MockConnection()
 
145
{
 
146
}
 
147
 
 
148
uint MockConnection::setPresence(const QString& status, const QString& statusMessage, Tp::DBusError *error)
 
149
{
 
150
    qDebug() << "setPresence" << status;
 
151
    if (status == "available") {
 
152
        mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeAvailable;
 
153
    }
 
154
    return selfHandle();
 
155
}
 
156
 
 
157
Tp::ContactAttributesMap MockConnection::getContactAttributes(const Tp::UIntList &handles, const QStringList &ifaces, Tp::DBusError *error)
 
158
{
 
159
    qDebug() << "getContactAttributes" << handles << ifaces;
 
160
    Tp::ContactAttributesMap attributesMap;
 
161
    QVariantMap attributes;
 
162
    Q_FOREACH(uint handle, handles) {
 
163
        attributes[TP_QT_IFACE_CONNECTION+"/contact-id"] = inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error).at(0);
 
164
        if (ifaces.contains(TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) {
 
165
            attributes[TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE+"/presence"] = QVariant::fromValue(mSelfPresence);
 
166
        }
 
167
        attributesMap[handle] = attributes;
 
168
    }
 
169
    return attributesMap;
 
170
}
 
171
 
 
172
void MockConnection::setOnline(bool online)
 
173
{
 
174
    qDebug() << "setOnline" << online;
 
175
    Tp::SimpleContactPresences presences;
 
176
    if (online) {
 
177
        mSelfPresence.status = "available";
 
178
        mSelfPresence.statusMessage = "";
 
179
        mSelfPresence.type = Tp::ConnectionPresenceTypeAvailable;
 
180
    } else {
 
181
        mSelfPresence.status = "offline";
 
182
        mSelfPresence.statusMessage = "";
 
183
        mSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
 
184
    }
 
185
    presences[selfHandle()] = mSelfPresence;
 
186
    simplePresenceIface->setPresences(presences);
 
187
}
 
188
 
 
189
uint MockConnection::newHandle(const QString &identifier)
 
190
{
 
191
    mHandles[++mHandleCount] = identifier;
 
192
    return mHandleCount;
 
193
}
 
194
 
 
195
QStringList MockConnection::inspectHandles(uint handleType, const Tp::UIntList& handles, Tp::DBusError *error)
 
196
{
 
197
    QStringList identifiers;
 
198
 
 
199
    if( handleType != Tp::HandleTypeContact ) {
 
200
        error->set(TP_QT_ERROR_INVALID_ARGUMENT,"Not supported");
 
201
        return QStringList();
 
202
    }
 
203
 
 
204
    qDebug() << "MockConnection::inspectHandles " << handles;
 
205
    Q_FOREACH( uint handle, handles) {
 
206
        if (mHandles.keys().contains(handle)) {
 
207
            identifiers.append(mHandles.value(handle));
 
208
        } else {
 
209
            error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found");
 
210
            return QStringList();
 
211
        }
 
212
    }
 
213
    qDebug() << "MockConnection::inspectHandles " << identifiers;
 
214
    return identifiers;
 
215
}
 
216
 
 
217
void MockConnection::connect(Tp::DBusError *error) {
 
218
    qDebug() << "MockConnection::connect";
 
219
    setStatus(Tp::ConnectionStatusConnected, Tp::ConnectionStatusReasonRequested);
 
220
}
 
221
 
 
222
Tp::UIntList MockConnection::requestHandles(uint handleType, const QStringList& identifiers, Tp::DBusError* error)
 
223
{
 
224
    qDebug() << "requestHandles";
 
225
    Tp::UIntList handles;
 
226
 
 
227
    if( handleType != Tp::HandleTypeContact ) {
 
228
        error->set(TP_QT_ERROR_INVALID_ARGUMENT, "Not supported");
 
229
        return Tp::UIntList();
 
230
    }
 
231
 
 
232
    Q_FOREACH( const QString& identifier, identifiers) {
 
233
        if (mHandles.values().contains(identifier)) {
 
234
            handles.append(mHandles.key(identifier));
 
235
        } else {
 
236
            handles.append(newHandle(identifier));
 
237
        }
 
238
    }
 
239
    qDebug() << "requestHandles" << handles;
 
240
    return handles;
 
241
}
 
242
 
 
243
Tp::BaseChannelPtr MockConnection::createTextChannel(uint targetHandleType,
 
244
                                               uint targetHandle, Tp::DBusError *error)
 
245
{
 
246
    Q_UNUSED(targetHandleType);
 
247
    Q_UNUSED(error);
 
248
 
 
249
    QString requestedId = mHandles.value(targetHandle);
 
250
 
 
251
    if (mTextChannels.contains(requestedId)) {
 
252
        return mTextChannels[requestedId]->baseChannel();
 
253
    }
 
254
 
 
255
    MockTextChannel *channel = new MockTextChannel(this, requestedId, targetHandle);
 
256
    QObject::connect(channel, SIGNAL(messageRead(QString)), SLOT(onMessageRead(QString)));
 
257
    QObject::connect(channel, SIGNAL(destroyed()), SLOT(onTextChannelClosed()));
 
258
    QObject::connect(channel, SIGNAL(messageSent(QString,QVariantMap)), SIGNAL(messageSent(QString,QVariantMap)));
 
259
    qDebug() << channel;
 
260
    mTextChannels[requestedId] = channel;
 
261
    return channel->baseChannel();
 
262
}
 
263
 
 
264
void MockConnection::onMessageRead(const QString &id)
 
265
{
 
266
    // FIXME: implement
 
267
}
 
268
 
 
269
Tp::BaseChannelPtr MockConnection::createCallChannel(uint targetHandleType,
 
270
                                               uint targetHandle, Tp::DBusError *error)
 
271
{
 
272
    Q_UNUSED(targetHandleType);
 
273
 
 
274
    bool success = true;
 
275
    QString requestedId = mHandles.value(targetHandle);
 
276
 
 
277
    Q_FOREACH(const QString &id, mCallChannels.keys()) {
 
278
        if (id == requestedId) {
 
279
            return mCallChannels[id]->baseChannel();
 
280
        }
 
281
    }
 
282
 
 
283
    QString state = "dialing";
 
284
    if (mInitialCallStatus.contains(requestedId)) {
 
285
        state = mInitialCallStatus.take(requestedId);
 
286
    }
 
287
 
 
288
    mCallChannels[requestedId] = new MockCallChannel(this, requestedId, state, targetHandle);
 
289
    QObject::connect(mCallChannels[requestedId], SIGNAL(destroyed()), SLOT(onCallChannelClosed()));
 
290
    QObject::connect(mCallChannels[requestedId], SIGNAL(callStateChanged(MockCallChannel*,QString)), SLOT(onCallStateChanged(MockCallChannel*,QString)));
 
291
    qDebug() << mCallChannels[requestedId];
 
292
 
 
293
    if (!mIncomingCalls.contains(requestedId)) {
 
294
        Q_EMIT callReceived(requestedId);
 
295
    }
 
296
    return mCallChannels[requestedId]->baseChannel();
 
297
}
 
298
 
 
299
Tp::BaseChannelPtr MockConnection::createChannel(const QString& channelType, uint targetHandleType,
 
300
                                               uint targetHandle, Tp::DBusError *error)
 
301
{
 
302
    qDebug() << "MockConnection::createChannel" << targetHandle;
 
303
    if( (targetHandleType != Tp::HandleTypeContact) || targetHandle == 0 || !mHandles.keys().contains(targetHandle)) {
 
304
        error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found");
 
305
        return Tp::BaseChannelPtr();
 
306
    }
 
307
 
 
308
    if (mSelfPresence.type != Tp::ConnectionPresenceTypeAvailable) {
 
309
        error->set(TP_QT_ERROR_NETWORK_ERROR, "No network available");
 
310
        return Tp::BaseChannelPtr();
 
311
    }
 
312
 
 
313
    if (channelType == TP_QT_IFACE_CHANNEL_TYPE_TEXT) {
 
314
        return createTextChannel(targetHandleType, targetHandle, error);
 
315
    } else if (channelType == TP_QT_IFACE_CHANNEL_TYPE_CALL) {
 
316
        return createCallChannel(targetHandleType, targetHandle, error);
 
317
    } else {
 
318
        error->set(TP_QT_ERROR_NOT_IMPLEMENTED, "Channel type not available");
 
319
    }
 
320
 
 
321
    return Tp::BaseChannelPtr();
 
322
}
 
323
 
 
324
void MockConnection::placeIncomingMessage(const QString &message, const QVariantMap &info)
 
325
{
 
326
    const QString sender = info["Sender"].toString();
 
327
    // check if there is an open channel for this sender and use it
 
328
    Q_FOREACH(const QString &id, mTextChannels.keys()) {
 
329
        if (id == sender) {
 
330
            mTextChannels[id]->messageReceived(message, info);
 
331
            return;
 
332
        }
 
333
    }
 
334
 
 
335
    Tp::DBusError error;
 
336
    bool yours;
 
337
    uint handle = newHandle(sender);
 
338
    ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, &error);
 
339
    if(error.isValid()) {
 
340
        qWarning() << "Error creating channel for incoming message" << error.name() << error.message();
 
341
        return;
 
342
    }
 
343
    mTextChannels[sender]->messageReceived(message, info);
 
344
}
 
345
 
 
346
void MockConnection::onTextChannelClosed()
 
347
{
 
348
    MockTextChannel *channel = static_cast<MockTextChannel*>(sender());
 
349
    if (channel) {
 
350
        QString key = mTextChannels.key(channel);
 
351
        qDebug() << "text channel closed for number " << key;
 
352
        mTextChannels.remove(key);
 
353
    }
 
354
}
 
355
 
 
356
void MockConnection::onCallChannelClosed()
 
357
{
 
358
    qDebug() << "onCallChannelClosed()";
 
359
    MockCallChannel *channel = static_cast<MockCallChannel*>(sender());
 
360
    if (channel) {
 
361
        QString key = mCallChannels.key(channel);
 
362
        qDebug() << "call channel closed for number " << key;
 
363
        mCallChannels.remove(key);
 
364
        mIncomingCalls.removeAll(key);
 
365
        Q_EMIT callEnded(key);
 
366
    }
 
367
}
 
368
 
 
369
void MockConnection::onCallStateChanged(MockCallChannel *channel, const QString &state)
 
370
{
 
371
    const QString key = mCallChannels.key(channel);
 
372
    if (key.isEmpty()) {
 
373
        return;
 
374
    }
 
375
 
 
376
    Q_EMIT callStateChanged(key, channel->objectPath(), state);
 
377
}
 
378
 
 
379
uint MockConnection::ensureHandle(const QString &id)
 
380
{
 
381
    if (mHandles.values().contains(id)) {
 
382
        return mHandles.key(id);
 
383
    }
 
384
    return newHandle(id);
 
385
}
 
386
 
 
387
void MockConnection::placeCall(const QVariantMap &properties)
 
388
{
 
389
    qDebug() << "new call" << properties;
 
390
 
 
391
    bool yours;
 
392
    Tp::DBusError error;
 
393
    QString callerId = properties["Caller"].toString();
 
394
    QString state = properties["State"].toString();
 
395
 
 
396
    if (mCallChannels.contains(callerId)) {
 
397
        return;
 
398
    }
 
399
 
 
400
    uint handle = ensureHandle(callerId);
 
401
    uint initiatorHandle = 0;
 
402
    if (state == "incoming" || state == "waiting") {
 
403
        initiatorHandle = handle;
 
404
    } else {
 
405
        initiatorHandle = selfHandle();
 
406
    }
 
407
 
 
408
    qDebug() << "initiatorHandle " <<initiatorHandle;
 
409
    qDebug() << "handle" << handle;
 
410
 
 
411
    mInitialCallStatus[callerId] = state;
 
412
    mIncomingCalls.append(callerId);
 
413
 
 
414
    Tp::BaseChannelPtr channel  = ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_CALL, Tp::HandleTypeContact, handle, yours, initiatorHandle, false, &error);
 
415
    if (error.isValid() || channel.isNull()) {
 
416
        qWarning() << "error creating the channel " << error.name() << error.message();
 
417
        return;
 
418
    }
 
419
}
 
420
 
 
421
void MockConnection::hangupCall(const QString &callerId)
 
422
{
 
423
    if (!mCallChannels.contains(callerId)) {
 
424
        return;
 
425
    }
 
426
 
 
427
    mCallChannels[callerId]->setCallState("disconnected");
 
428
    mIncomingCalls.removeAll(callerId);
 
429
}
 
430
 
 
431
void MockConnection::setCallState(const QString &phoneNumber, const QString &state)
 
432
{
 
433
    if (!mCallChannels.contains(phoneNumber)) {
 
434
        return;
 
435
    }
 
436
 
 
437
    mCallChannels[phoneNumber]->setCallState(state);
 
438
}