=== modified file '.bzrignore'
--- .bzrignore	2014-01-07 19:53:45 +0000
+++ .bzrignore	2015-03-19 23:42:32 +0000
@@ -32,19 +32,22 @@
 handler/handleradaptor.*
 indicator/*.desktop
 indicator/*.service
+indicator/NotificationsInterface.*
 
 Testing
-Ubuntu/Telephony/tests/*Test
 Ubuntu/Telephony/qmldir
-libtelephonyservice/tests/*Test
 CTestTestfile.cmake
 test_*.xml
+cmake_uninstall.cmake
 
 coverage*
 
 po/*.gmo
 
-handler/tests/HandlerTest
-handler/tests/dbus-test-wrapper.sh
-handler/tests/mock/mockconnectionadaptor.*
-handler/tests/mock/telepathy-mock
+tests/*/*Test
+tests/common/dbus-test-wrapper.sh
+tests/common/mock/mockconnectionadaptor.*
+tests/common/mock/telepathy-mock
+tests/Ubuntu.Telephony/ContactWatcherTest
+tests/libtelephonyservice/GreeterContactsTestExe
+tests/libtelephonyservice/GreeterContactsTestServerExe

=== modified file 'CMakeLists.txt'
--- CMakeLists.txt	2014-08-05 20:04:00 +0000
+++ CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -9,23 +9,6 @@
 include(CheckIncludeFileCXX)
 include(CheckIncludeFile)
 
-include(EnableCoverageReport)
-#####################################################################
-# Enable code coverage calculation with gcov/gcovr/lcov
-# Usage:
-#  * Switch build type to coverage (use ccmake or cmake-gui)
-#  * Invoke make, make test, make coverage
-#  * Find html report in subdir coveragereport
-#  * Find xml report feasible for jenkins in coverage.xml
-#####################################################################
-IF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE])
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs" )
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs" )
-  SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -coverage" )
-  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -coverage" )
-  ENABLE_COVERAGE_REPORT(TARGETS ${PHONE_APP})
-ENDIF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE])
-
 # Instruct CMake to run moc automatically when needed.
 set(CMAKE_AUTOMOC ON)
 
@@ -81,6 +64,14 @@
     ${CMAKE_CURRENT_SOURCE_DIR}
     )
 
+# generate a macro to make it easier to enable coverage support on targets
+function(ENABLE_COVERAGE)
+    get_directory_property(COVERAGE_TARGETS DIRECTORY ${CMAKE_SOURCE_DIR} COVERAGE_TARGETS)
+    list(APPEND COVERAGE_TARGETS ${ARGN})
+    MESSAGE(STATUS "Enabling coverage report for target(s): ${ARGN}")
+    set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY COVERAGE_TARGETS ${COVERAGE_TARGETS})
+endfunction()
+
 enable_testing()
 
 add_definitions(-std=c++11)
@@ -98,3 +89,24 @@
 add_subdirectory(tools)
 add_subdirectory(icons)
 add_subdirectory(po)
+add_subdirectory(tests)
+
+include(EnableCoverageReport)
+#####################################################################
+# Enable code coverage calculation with gcov/gcovr/lcov
+# Usage:
+#  * Switch build type to coverage (use ccmake or cmake-gui)
+#  * Invoke make, make test, make coverage
+#  * Find html report in subdir coveragereport
+#  * Find xml report feasible for jenkins in coverage.xml
+#####################################################################
+IF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE])
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs" )
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs" )
+  SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -coverage" )
+  SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -coverage" )
+  GET_DIRECTORY_PROPERTY(COVERAGE_TARGETS DIRECTORY ${CMAKE_SOURCE_DIR} COVERAGE_TARGETS)
+  ENABLE_COVERAGE_REPORT(TARGETS ${COVERAGE_TARGETS})
+ENDIF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE])
+
+

=== modified file 'Ubuntu/Telephony/CMakeLists.txt'
--- Ubuntu/Telephony/CMakeLists.txt	2014-04-23 20:55:13 +0000
+++ Ubuntu/Telephony/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -19,10 +19,11 @@
     telephonyservice
     )
 
+enable_coverage(telephonyservice-qml)
+
 configure_file(qmldir.in ${CMAKE_CURRENT_BINARY_DIR}/qmldir)
 set(PLUGIN_DIR ${QT_INSTALL_QML}/Ubuntu/Telephony)
 install(TARGETS telephonyservice-qml DESTINATION ${PLUGIN_DIR})
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir DESTINATION ${PLUGIN_DIR})
 
 add_subdirectory(PhoneNumber)
-add_subdirectory(tests)

=== modified file 'Ubuntu/Telephony/components.cpp'
--- Ubuntu/Telephony/components.cpp	2014-08-25 14:49:53 +0000
+++ Ubuntu/Telephony/components.cpp	2015-03-19 23:42:32 +0000
@@ -23,6 +23,7 @@
 #include "components.h"
 #include "telepathyhelper.h"
 #include "callentry.h"
+#include "chatentry.h"
 #include "callmanager.h"
 #include "ussdmanager.h"
 #include "channelobserver.h"
@@ -64,6 +65,8 @@
     // @uri Telephony
     qmlRegisterUncreatableType<TelepathyHelper>(uri, 0, 1, "TelepathyHelper", "This is a singleton helper class");
     qmlRegisterUncreatableType<CallEntry>(uri, 0, 1, "CallEntry", "Objects of this type are created in CallManager and made available to QML for usage");
+    qmlRegisterUncreatableType<ChatEntry>(uri, 0, 1, "ChatEntry", "Objects of this type are created in ChatManager and made available to QML for usage");
+    qmlRegisterUncreatableType<ContactChatState>(uri, 0, 1, "ContactChatState", "Objects of this type are created in ChatEntry and made available to QML");
     qmlRegisterUncreatableType<AudioOutput>(uri, 0, 1, "AudioOutput", "Objects of this type are created in CallEntry and made available to QML for usage");
     qmlRegisterUncreatableType<AccountEntry>(uri, 0, 1, "AccountEntry", "Objects of this type are created in TelepathyHelper and made available to QML");
     qmlRegisterType<ContactWatcher>(uri, 0, 1, "ContactWatcher");

=== modified file 'approver/CMakeLists.txt'
--- approver/CMakeLists.txt	2014-07-06 16:19:49 +0000
+++ approver/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -25,6 +25,8 @@
     telephonyservice
     )
 
+enable_coverage(telephony-service-approver)
+
 configure_file(org.freedesktop.Telepathy.Client.TelephonyServiceApprover.service.in org.freedesktop.Telepathy.Client.TelephonyServiceApprover.service)
 install(TARGETS telephony-service-approver RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Telepathy.Client.TelephonyServiceApprover.service DESTINATION share/dbus-1/services)

=== modified file 'handler/CMakeLists.txt'
--- handler/CMakeLists.txt	2014-08-05 20:04:00 +0000
+++ handler/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -26,6 +26,8 @@
     telephonyservice
     )
 
+enable_coverage(telephony-service-handler)
+
 configure_file(com.canonical.TelephonyServiceHandler.service.in com.canonical.TelephonyServiceHandler.service)
 configure_file(org.freedesktop.Telepathy.Client.TelephonyServiceHandler.service.in org.freedesktop.Telepathy.Client.TelephonyServiceHandler.service)
 
@@ -33,9 +35,3 @@
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Telepathy.Client.TelephonyServiceHandler.service DESTINATION share/dbus-1/services)
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/com.canonical.TelephonyServiceHandler.service DESTINATION share/dbus-1/services)
 install(FILES TelephonyServiceHandler.client DESTINATION share/telepathy/clients)
-
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc*")
-    message(STATUS "Tests disabled on ppc")
-else()
-    add_subdirectory(tests)
-endif()

=== modified file 'handler/Handler.xml'
--- handler/Handler.xml	2015-01-19 21:14:26 +0000
+++ handler/Handler.xml	2015-03-19 23:42:32 +0000
@@ -44,6 +44,21 @@
             <arg name="messageIds" type="as" direction="in"/>
             <arg name="accountId" type="s" direction="in"/>
         </method>
+        <method name="StartChat">
+            <dox:d><![CDATA[
+                Start a chat with the given participants
+            ]]></dox:d>
+            <arg name="accountId" type="s" direction="in"/>
+            <arg name="participants" type="as" direction="in"/>
+        </method>
+        <method name="StartChatRoom">
+            <dox:d><![CDATA[
+                Start a chat room with the given properties
+            ]]></dox:d>
+            <arg name="accountId" type="s" direction="in"/>
+            <arg name="properties" type="a{sv}" direction="in"/>
+            <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
+        </method>
 
         <!-- Call Related -->
         <method name="StartCall">

=== modified file 'handler/handler.cpp'
--- handler/handler.cpp	2015-02-09 20:32:32 +0000
+++ handler/handler.cpp	2015-03-19 23:42:32 +0000
@@ -97,6 +97,7 @@
     specList << TelepathyHelper::audioConferenceSpec();
     specList << Tp::ChannelClassSpec::audioCall();
     specList << Tp::ChannelClassSpec::textChat();
+    specList << Tp::ChannelClassSpec::unnamedTextChat();
 
     return specList;
 }

=== modified file 'handler/handlerdbus.cpp'
--- handler/handlerdbus.cpp	2015-01-19 21:14:26 +0000
+++ handler/handlerdbus.cpp	2015-03-19 23:42:32 +0000
@@ -114,6 +114,16 @@
     TextHandler::instance()->acknowledgeMessages(numbers, messageIds, accountId);
 }
 
+void HandlerDBus::StartChat(const QString &accountId, const QStringList &participants)
+{
+    TextHandler::instance()->startChat(participants, accountId);
+}
+
+void HandlerDBus::StartChatRoom(const QString &accountId, const QVariantMap &properties)
+{
+    TextHandler::instance()->startChatRoom(accountId, properties);
+}
+
 void HandlerDBus::StartCall(const QString &number, const QString &accountId)
 {
     CallHandler::instance()->startCall(number, accountId);

=== modified file 'handler/handlerdbus.h'
--- handler/handlerdbus.h	2015-01-19 21:14:26 +0000
+++ handler/handlerdbus.h	2015-03-19 23:42:32 +0000
@@ -59,6 +59,8 @@
     Q_NOREPLY void SendSilentMessage(const QStringList &number, const QString &message, const QString &accountId);
     Q_NOREPLY void SendMMS(const QStringList &numbers, const AttachmentList &attachments, const QString &accountId);
     Q_NOREPLY void AcknowledgeMessages(const QStringList &numbers, const QStringList &messageIds, const QString &accountId);
+    Q_NOREPLY void StartChat(const QString &accountId, const QStringList &participants);
+    Q_NOREPLY void StartChatRoom(const QString &accountId, const QVariantMap &properties);
 
     // call related
     Q_NOREPLY void StartCall(const QString &number, const QString &accountId);

=== removed file 'handler/tests/telepathyhelper.cpp'
--- handler/tests/telepathyhelper.cpp	2014-01-07 19:53:45 +0000
+++ handler/tests/telepathyhelper.cpp	1970-01-01 00:00:00 +0000
@@ -1,279 +0,0 @@
-/**
- * Copyright (C) 2013 Canonical, Ltd.
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 3, as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Authors: 
- *  Tiago Salem Herrmann <tiago.herrmann@canonical.com>
- *  Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
- */
-
-#include "telepathyhelper.h"
-#include <TelepathyQt/AbstractClient>
-#include <TelepathyQt/AccountSet>
-#include <TelepathyQt/ClientRegistrar>
-#include <TelepathyQt/PendingReady>
-#include <TelepathyQt/PendingAccount>
-
-TelepathyHelper::TelepathyHelper(QObject *parent)
-    : QObject(parent),
-      //mChannelObserver(0),
-      mFirstTime(true),
-      mConnected(false),
-      mHandlerInterface(0)
-{
-    Tp::registerTypes();
-
-    mAccountFeatures << Tp::Account::FeatureCore;
-    mContactFeatures << Tp::Contact::FeatureAlias
-                     << Tp::Contact::FeatureAvatarData
-                     << Tp::Contact::FeatureAvatarToken
-                     << Tp::Contact::FeatureCapabilities
-                     << Tp::Contact::FeatureSimplePresence;
-    mConnectionFeatures << Tp::Connection::FeatureCore
-                        << Tp::Connection::FeatureSelfContact
-                        << Tp::Connection::FeatureSimplePresence;
-
-    Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus());
-    channelFactory->addCommonFeatures(Tp::Channel::FeatureCore);
-
-    mAccountManager = Tp::AccountManager::create(
-            Tp::AccountFactory::create(QDBusConnection::sessionBus(), mAccountFeatures),
-            Tp::ConnectionFactory::create(QDBusConnection::sessionBus(), mConnectionFeatures),
-            channelFactory,
-            Tp::ContactFactory::create(mContactFeatures));
-
-    connect(mAccountManager->becomeReady(Tp::AccountManager::FeatureCore),
-            SIGNAL(finished(Tp::PendingOperation*)),
-            SLOT(onAccountManagerReady(Tp::PendingOperation*)));
-
-    mClientRegistrar = Tp::ClientRegistrar::create(mAccountManager);
-}
-
-TelepathyHelper::~TelepathyHelper()
-{
-}
-
-TelepathyHelper *TelepathyHelper::instance()
-{
-    static TelepathyHelper* helper = new TelepathyHelper();
-    return helper;
-}
-
-QString TelepathyHelper::accountId() const
-{
-    if (mAccount) {
-        return mAccount->uniqueIdentifier();
-    }
-    return QString();
-}
-
-Tp::AccountPtr TelepathyHelper::account() const
-{
-    return mAccount;
-}
-
-/*
-ChannelObserver *TelepathyHelper::channelObserver() const
-{
-    return mChannelObserver;
-}
-*/
-
-bool TelepathyHelper::connected() const
-{
-    return mConnected;
-}
-
-/*
-void TelepathyHelper::registerChannelObserver(const QString &observerName)
-{
-    QString name = observerName;
-
-    if (name.isEmpty()) {
-        name = "TelephonyPluginObserver";
-    }
-
-    if (mChannelObserver) {
-        mChannelObserver->deleteLater();
-    }
-
-    mChannelObserver = new ChannelObserver(this);
-    registerClient(mChannelObserver, name);
-
-    // messages
-    connect(mChannelObserver, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)),
-            ChatManager::instance(), SLOT(onTextChannelAvailable(Tp::TextChannelPtr)));
-
-    // calls
-    connect(mChannelObserver, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)),
-            CallManager::instance(), SLOT(onCallChannelAvailable(Tp::CallChannelPtr)));
-
-    Q_EMIT channelObserverCreated(mChannelObserver);
-}
-
-void TelepathyHelper::unregisterChannelObserver()
-{
-    Tp::AbstractClientPtr clientPtr(mChannelObserver);
-    if (clientPtr) {
-        mClientRegistrar->unregisterClient(clientPtr);
-    }
-    mChannelObserver->deleteLater();
-    mChannelObserver = NULL;
-    Q_EMIT channelObserverUnregistered();
-}
-*/
-
-QStringList TelepathyHelper::supportedProtocols() const
-{
-    QStringList protocols;
-    protocols << "mock";
-    return protocols;
-}
-
-void TelepathyHelper::initializeAccount()
-{
-    // watch for account state and connection changes
-    connect(mAccount.data(),
-            SIGNAL(stateChanged(bool)),
-            SLOT(onAccountStateChanged(bool)));
-    connect(mAccount.data(),
-            SIGNAL(connectionChanged(const Tp::ConnectionPtr&)),
-            SLOT(onAccountConnectionChanged(const Tp::ConnectionPtr&)));
-
-    // and make sure it is enabled and connected
-    if (!mAccount->isEnabled()) {
-        ensureAccountEnabled();
-    } else {
-        ensureAccountConnected();
-    }
-}
-
-void TelepathyHelper::ensureAccountEnabled()
-{
-    mAccount->setConnectsAutomatically(true);
-    connect(mAccount->setEnabled(true),
-            SIGNAL(finished(Tp::PendingOperation*)),
-            SLOT(onAccountEnabled(Tp::PendingOperation*)));
-}
-
-void TelepathyHelper::ensureAccountConnected()
-{
-    // if the account is not connected, request it to connect
-    if (!mAccount->connection() || mAccount->connectionStatus() != Tp::ConnectionStatusConnected) {
-        Tp::Presence presence(Tp::ConnectionPresenceTypeAvailable, "available", "online");
-        mAccount->setRequestedPresence(presence);
-    } else {
-        watchSelfContactPresence();
-    }
-
-    if (mFirstTime) {
-        Q_EMIT accountReady();
-        mFirstTime = false;
-    }
-}
-
-void TelepathyHelper::watchSelfContactPresence()
-{
-    if (mAccount.isNull() || mAccount->connection().isNull()) {
-        return;
-    }
-
-    connect(mAccount->connection()->selfContact().data(),
-            SIGNAL(presenceChanged(Tp::Presence)),
-            SLOT(onPresenceChanged(Tp::Presence)));
-    onPresenceChanged(mAccount->connection()->selfContact()->presence());
-}
-
-void TelepathyHelper::registerClient(Tp::AbstractClient *client, QString name)
-{
-    Tp::AbstractClientPtr clientPtr(client);
-    bool succeeded = mClientRegistrar->registerClient(clientPtr, name);
-    if (!succeeded) {
-        name.append("%1");
-        int count = 0;
-        // limit the number of registered clients to 20, that should be a safe margin
-        while (!succeeded && count < 20) {
-            succeeded = mClientRegistrar->registerClient(clientPtr, name.arg(++count));
-            if (succeeded) {
-                name = name.arg(count);
-            }
-        }
-    }
-
-    if (succeeded) {
-        QObject *object = dynamic_cast<QObject*>(client);
-        if (object) {
-            object->setProperty("clientName", TP_QT_IFACE_CLIENT + "." + name );
-        }
-    }
-}
-
-void TelepathyHelper::onAccountManagerReady(Tp::PendingOperation *op)
-{
-    Q_UNUSED(op)
-
-    Tp::AccountSetPtr accountSet;
-    // try to find an account of the one of supported protocols
-    Q_FOREACH(const QString &protocol, supportedProtocols()) {
-        accountSet = mAccountManager->accountsByProtocol(protocol);
-        if (accountSet->accounts().count() > 0) {
-            break;
-        }
-    }
-
-    if (accountSet->accounts().count() == 0) {
-        qCritical() << "No compatible telepathy account found!";
-        return;
-    }
-
-    mAccount = accountSet->accounts()[0];
-
-    // in case we have more than one account, the first one to show on the list is going to be used
-    if (accountSet->accounts().count() > 1) {
-        qWarning() << "There are more than just one account of type" << mAccount->protocolName();
-    }
-
-    Q_EMIT accountIdChanged();
-
-    initializeAccount();
-}
-
-void TelepathyHelper::onAccountEnabled(Tp::PendingOperation *op)
-{
-    // we might need to do more stuff once the account is enabled, but making sure it is connected is a good start
-    ensureAccountConnected();
-}
-
-void TelepathyHelper::onAccountStateChanged(bool enabled)
-{
-    if (!enabled) {
-        ensureAccountEnabled();
-    }
-}
-
-void TelepathyHelper::onAccountConnectionChanged(const Tp::ConnectionPtr &connection)
-{
-    if (connection.isNull()) {
-        ensureAccountConnected();
-    } else {
-        watchSelfContactPresence();
-    }
-    Q_EMIT connectionChanged();
-}
-
-void TelepathyHelper::onPresenceChanged(const Tp::Presence &presence)
-{
-    mConnected = (presence.type() == Tp::ConnectionPresenceTypeAvailable);
-    Q_EMIT connectedChanged();
-}

=== removed file 'handler/tests/telepathyhelper.h'
--- handler/tests/telepathyhelper.h	2014-06-27 17:45:58 +0000
+++ handler/tests/telepathyhelper.h	1970-01-01 00:00:00 +0000
@@ -1,95 +0,0 @@
-/**
- * Copyright (C) 2013 Canonical, Ltd.
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 3, as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Authors: 
- *  Tiago Salem Herrmann <tiago.herrmann@canonical.com>
- *  Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
- */
-
-#ifndef TELEPATHYHELPER_H
-#define TELEPATHYHELPER_H
-
-#include <QObject>
-#include <TelepathyQt/AccountManager>
-#include <TelepathyQt/Contact>
-#include <TelepathyQt/Connection>
-#include <TelepathyQt/ConnectionManager>
-#include <TelepathyQt/Types>
-//#include "channelobserver.h"
-
-#define CANONICAL_TELEPHONY_VOICEMAIL_IFACE "com.canonical.Telephony.Voicemail"
-#define CANONICAL_TELEPHONY_SPEAKER_IFACE "com.canonical.Telephony.Speaker"
-#define CANONICAL_TELEPHONY_EMERGENCYMODE_IFACE "com.canonical.Telephony.EmergencyMode"
-
-class TelepathyHelper : public QObject
-{
-    Q_OBJECT
-    Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
-    Q_PROPERTY(QString accountId READ accountId NOTIFY accountIdChanged)
-
-public:
-    ~TelepathyHelper();
-
-    static TelepathyHelper *instance();
-    Tp::AccountPtr account() const;
-    //ChannelObserver *channelObserver() const;
-
-    bool connected() const;
-    QString accountId() const;
-
-    void registerClient(Tp::AbstractClient *client, QString name);
-
-Q_SIGNALS:
-    //void channelObserverCreated(ChannelObserver *observer);
-    //void channelObserverUnregistered();
-    void accountReady();
-    void connectionChanged();
-    void connectedChanged();
-    void accountIdChanged();
-
-public Q_SLOTS:
-    //Q_INVOKABLE void registerChannelObserver(const QString &observerName = QString::null);
-    //Q_INVOKABLE void unregisterChannelObserver();
-
-protected:
-    QStringList supportedProtocols() const;
-    void initializeAccount();
-    void ensureAccountEnabled();
-    void ensureAccountConnected();
-    void watchSelfContactPresence();
-
-private Q_SLOTS:
-    void onAccountManagerReady(Tp::PendingOperation *op);
-    void onAccountEnabled(Tp::PendingOperation *op);
-    void onAccountStateChanged(bool enabled);
-    void onAccountConnectionChanged(const Tp::ConnectionPtr &connection);
-    void onPresenceChanged(const Tp::Presence &presence);
-
-private:
-    explicit TelepathyHelper(QObject *parent = 0);
-    Tp::AccountManagerPtr mAccountManager;
-    Tp::Features mAccountManagerFeatures;
-    Tp::Features mAccountFeatures;
-    Tp::Features mContactFeatures;
-    Tp::Features mConnectionFeatures;
-    Tp::ClientRegistrarPtr mClientRegistrar;
-    Tp::AccountPtr mAccount;
-    //ChannelObserver *mChannelObserver;
-    bool mFirstTime;
-    bool mConnected;
-    QDBusInterface *mHandlerInterface;
-};
-
-#endif // TELEPATHYHELPER_H

=== modified file 'handler/texthandler.cpp'
--- handler/texthandler.cpp	2015-03-19 23:42:32 +0000
+++ handler/texthandler.cpp	2015-03-19 23:42:32 +0000
@@ -118,6 +118,12 @@
             SLOT(onContactsAvailable(Tp::PendingOperation*)));
 }
 
+void TextHandler::startChatRoom(const QString &accountId, const QVariantMap &properties)
+{
+    Q_UNUSED(accountId)
+    Q_UNUSED(properties)
+}
+
 void TextHandler::startChat(const Tp::AccountPtr &account, const Tp::Contacts &contacts)
 {
     if (contacts.size() == 1) {

=== modified file 'handler/texthandler.h'
--- handler/texthandler.h	2015-03-19 23:42:32 +0000
+++ handler/texthandler.h	2015-03-19 23:42:32 +0000
@@ -34,6 +34,7 @@
 public:
     static TextHandler *instance();
     void startChat(const QStringList &recipients, const QString &accountId);
+    void startChatRoom(const QString &accountId, const QVariantMap &properties);
     void startChat(const Tp::AccountPtr &account, const Tp::Contacts &contacts);
 
 public Q_SLOTS:

=== modified file 'indicator/CMakeLists.txt'
--- indicator/CMakeLists.txt	2014-04-07 19:21:14 +0000
+++ indicator/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -47,6 +47,8 @@
     telephonyservice
     )
 
+enable_coverage(telephony-service-indicator)
+
 configure_file(org.freedesktop.Telepathy.Client.TelephonyServiceIndicator.service.in org.freedesktop.Telepathy.Client.TelephonyServiceIndicator.service)
 install(TARGETS telephony-service-indicator RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Telepathy.Client.TelephonyServiceIndicator.service DESTINATION share/dbus-1/services)

=== modified file 'libtelephonyservice/CMakeLists.txt'
--- libtelephonyservice/CMakeLists.txt	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -8,6 +8,7 @@
     callnotification.cpp
     channelobserver.cpp
     chatmanager.cpp
+    chatentry.cpp
     contactutils.cpp
     greetercontacts.cpp
     ofonoaccountentry.cpp
@@ -38,4 +39,4 @@
 
 qt5_use_modules(telephonyservice Contacts Core DBus Feedback Multimedia Qml Quick)
 
-add_subdirectory(tests)
+enable_coverage(telephonyservice)

=== modified file 'libtelephonyservice/accountentry.cpp'
--- libtelephonyservice/accountentry.cpp	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/accountentry.cpp	2015-03-19 23:42:32 +0000
@@ -41,7 +41,8 @@
 
 bool AccountEntry::active() const
 {
-    return (!mAccount->connection().isNull() &&
+    return (!mAccount.isNull() &&
+            !mAccount->connection().isNull() &&
             !mAccount->connection()->selfContact().isNull() &&
              mAccount->connection()->selfContact()->presence().type() != Tp::ConnectionPresenceTypeOffline);
 }
@@ -70,10 +71,7 @@
         return QString::null;
     }
     Tp::Presence presence = mAccount->connection()->selfContact()->presence();
-    if (presence.type() == Tp::ConnectionPresenceTypeAvailable) {
-        return presence.statusMessage();
-    }
-    return QString::null;
+    return presence.statusMessage();
 }
 
 QString AccountEntry::selfContactId() const
@@ -147,6 +145,10 @@
             SIGNAL(connectionChanged(Tp::ConnectionPtr)),
             SLOT(onConnectionChanged()));
 
+    connect(this,
+            SIGNAL(connectedChanged()),
+            SIGNAL(activeChanged()));
+
     // and make sure it is enabled and connected
     if (!mAccount->isEnabled()) {
         QTimer::singleShot(0, this, SLOT(ensureEnabled()));
@@ -203,15 +205,12 @@
     Q_EMIT statusMessageChanged();
     Q_EMIT connectedChanged();
     Q_EMIT selfContactIdChanged();
-    Q_EMIT activeChanged();
 }
 
 void AccountEntry::onConnectionChanged()
 {
     if (!mAccount->connection()) {
-
-
-        // and ensure the account gets connected
+        // ensure the account gets connected
         ensureConnected();
     } else {
         mConnectionInfo.busName = mAccount->connection()->busName();
@@ -226,5 +225,4 @@
 
     Q_EMIT connectedChanged();
     Q_EMIT selfContactIdChanged();
-    Q_EMIT activeChanged();
 }

=== modified file 'libtelephonyservice/accountentryfactory.cpp'
--- libtelephonyservice/accountentryfactory.cpp	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/accountentryfactory.cpp	2015-03-19 23:42:32 +0000
@@ -25,7 +25,7 @@
 
 AccountEntry *AccountEntryFactory::createEntry(const Tp::AccountPtr &account, QObject *parent)
 {
-    QString protocol = account->protocolName();
+    QString protocol = account.isNull() ? "" : account->protocolName();
 
     // FIXME: check what other accounts need extra properties/methods
     if (protocol == "ofono") {
@@ -34,7 +34,3 @@
 
     return new AccountEntry(account, parent);
 }
-
-AccountEntryFactory::AccountEntryFactory()
-{
-}

=== modified file 'libtelephonyservice/accountentryfactory.h'
--- libtelephonyservice/accountentryfactory.h	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/accountentryfactory.h	2015-03-19 23:42:32 +0000
@@ -31,8 +31,6 @@
 public:
     static AccountEntry *createEntry(const Tp::AccountPtr &account, QObject *parent = 0);
 
-private:
-    explicit AccountEntryFactory();
 };
 
 #endif // ACCOUNTENTRYFACTORY_H

=== modified file 'libtelephonyservice/channelobserver.cpp'
--- libtelephonyservice/channelobserver.cpp	2015-02-09 20:32:32 +0000
+++ libtelephonyservice/channelobserver.cpp	2015-03-19 23:42:32 +0000
@@ -38,6 +38,7 @@
     specList << TelepathyHelper::audioConferenceSpec();
     specList << Tp::ChannelClassSpec::audioCall();
     specList << Tp::ChannelClassSpec::textChat();
+    specList << Tp::ChannelClassSpec::unnamedTextChat();
 
     return specList;
 }

=== added file 'libtelephonyservice/chatentry.cpp'
--- libtelephonyservice/chatentry.cpp	1970-01-01 00:00:00 +0000
+++ libtelephonyservice/chatentry.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * Authors:
+ *  Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "telepathyhelper.h"
+#include "accountentry.h"
+#include "chatentry.h"
+
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/Connection>
+
+Q_DECLARE_METATYPE(ContactChatStates)
+
+ChatEntry::ChatEntry(const Tp::TextChannelPtr &channel, QObject *parent) :
+    QObject(parent),
+    mChannel(channel)
+{
+    qRegisterMetaType<ContactChatStates>();
+    mAccount = TelepathyHelper::instance()->accountForConnection(mChannel->connection());
+    Q_FOREACH (Tp::ContactPtr contact, mChannel->groupContacts(false)) {
+        ContactChatState *state = new ContactChatState(contact->id(), mChannel->chatState(contact));
+        mChatStates[contact->id()] = state;
+    }
+
+    connect(channel.data(), SIGNAL(chatStateChanged(const Tp::ContactPtr &, Tp::ChannelChatState)),
+                            this, SLOT(onChatStateChanged(const Tp::ContactPtr &,Tp::ChannelChatState)));
+    connect(channel.data(), SIGNAL(groupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &,
+            const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)), this, SIGNAL(participantsChanged()));
+}
+
+ChatEntry::~ChatEntry()
+{
+    QMapIterator<QString, ContactChatState*> it(mChatStates);
+    while (it.hasNext()) {
+        it.next();
+        delete it.value();
+    }
+}
+
+void ChatEntry::onChatStateChanged(const Tp::ContactPtr &contact, Tp::ChannelChatState state)
+{
+    if (mChatStates.contains(contact->id())) {
+        mChatStates[contact->id()]->setState(state);
+        return;
+    }
+    ContactChatState *newState = new ContactChatState(contact->id(), state);
+    mChatStates[contact->id()] = newState;
+    Q_EMIT chatStatesChanged();
+}
+
+ChatEntry::ChatType ChatEntry::chatType()
+{
+    return (ChatType)mChannel->targetHandleType();
+}
+
+Tp::TextChannelPtr ChatEntry::channel()
+{
+    return mChannel;
+}
+
+QStringList ChatEntry::participants()
+{
+    QStringList participantList;
+    Q_FOREACH (Tp::ContactPtr contact, mChannel->groupContacts(false)) {
+        participantList << contact->id();
+    }
+    return participantList;
+}
+
+AccountEntry *ChatEntry::account()
+{
+    return mAccount;
+}
+
+QQmlListProperty<ContactChatState> ChatEntry::chatStates()
+{
+    return QQmlListProperty<ContactChatState>(this, 0, chatStatesCount, chatStatesAt);
+}
+
+int ChatEntry::chatStatesCount(QQmlListProperty<ContactChatState> *p)
+{
+    ChatEntry *entry = qobject_cast<ChatEntry*>(p->object);
+    if (!entry) {
+        return 0;
+    }
+    return entry->mChatStates.count();
+}
+
+ContactChatState *ChatEntry::chatStatesAt(QQmlListProperty<ContactChatState> *p, int index)
+{
+    ChatEntry *entry = qobject_cast<ChatEntry*>(p->object);
+    if (!entry) {
+        return 0;
+    }
+    return entry->mChatStates.values()[index];
+}

=== added file 'libtelephonyservice/chatentry.h'
--- libtelephonyservice/chatentry.h	1970-01-01 00:00:00 +0000
+++ libtelephonyservice/chatentry.h	2015-03-19 23:42:32 +0000
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * Authors:
+ *  Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CHATENTRY_H
+#define CHATENTRY_H
+
+#include <QObject>
+#include <TelepathyQt/TextChannel>
+
+class AccountEntry;
+
+class ContactChatState : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QString contactId READ contactId CONSTANT)
+    Q_PROPERTY(int state READ state WRITE setState NOTIFY stateChanged)
+public:
+    ContactChatState(const QString &contactId, int state) : mContactId(contactId), mState(state) {}
+    QString contactId() { return mContactId; }
+    int state() { return mState; }
+    void setState(int state) {
+        mState = state;
+        Q_EMIT stateChanged();
+    }
+Q_SIGNALS:
+    void stateChanged();
+private:
+    QString mContactId;
+    int mState;
+};
+
+typedef QList<ContactChatState* > ContactChatStates;
+
+class ChatEntry : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(AccountEntry* account READ account CONSTANT)
+    Q_PROPERTY(ChatType chatType READ chatType CONSTANT)
+    Q_PROPERTY(QStringList participants READ participants NOTIFY participantsChanged)
+    Q_PROPERTY(QQmlListProperty<ContactChatState> chatStates
+               READ chatStates
+               NOTIFY chatStatesChanged)
+
+    Q_ENUMS(ChatType)
+public:
+    enum ChatType {
+        ChatTypeNone    = Tp::HandleTypeNone,
+        ChatTypeContact = Tp::HandleTypeContact,
+        ChatTypeRoom    = Tp::HandleTypeRoom
+    };
+
+    explicit ChatEntry(const Tp::TextChannelPtr &channel, QObject *parent = 0);
+    ~ChatEntry();
+    Tp::TextChannelPtr channel();
+    AccountEntry *account();
+    QQmlListProperty<ContactChatState> chatStates();
+    QStringList participants();
+    ChatType chatType();
+    static int chatStatesCount(QQmlListProperty<ContactChatState> *p);
+    static ContactChatState *chatStatesAt(QQmlListProperty<ContactChatState> *p, int index);
+
+private Q_SLOTS:
+    void onChatStateChanged(const Tp::ContactPtr &contact, Tp::ChannelChatState state);
+
+Q_SIGNALS:
+    void chatStatesChanged();
+    void participantsChanged();
+
+private:
+    AccountEntry *mAccount;
+    Tp::TextChannelPtr mChannel;
+    QMap<QString, ContactChatState*> mChatStates;
+};
+
+#endif // CHATENTRY_H

=== modified file 'libtelephonyservice/chatmanager.cpp'
--- libtelephonyservice/chatmanager.cpp	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/chatmanager.cpp	2015-03-19 23:42:32 +0000
@@ -26,6 +26,7 @@
 #include "dbustypes.h"
 #include "accountentry.h"
 
+#include <TelepathyQt/Contact>
 #include <TelepathyQt/ContactManager>
 #include <TelepathyQt/PendingContacts>
 #include <QDBusArgument>
@@ -45,18 +46,40 @@
     argument.endStructure();
     return argument;
 }
+
 ChatManager::ChatManager(QObject *parent)
-: QObject(parent)
+: QObject(parent),
+  mReady(false)
 {
     qDBusRegisterMetaType<AttachmentList>();
     qDBusRegisterMetaType<AttachmentStruct>();
     // wait one second for other acknowledge calls before acknowledging messages to avoid many round trips
     mMessagesAckTimer.setInterval(1000);
     mMessagesAckTimer.setSingleShot(true);
+    connect(TelepathyHelper::instance(), SIGNAL(channelObserverUnregistered()), SLOT(onChannelObserverUnregistered()));
+    connect(TelepathyHelper::instance(), SIGNAL(setupReady()), SLOT(onTelepathyReady()));
     connect(&mMessagesAckTimer, SIGNAL(timeout()), SLOT(onAckTimerTriggered()));
     connect(TelepathyHelper::instance(), SIGNAL(connectedChanged()), SLOT(onConnectedChanged()));
 }
 
+void ChatManager::onTelepathyReady()
+{
+    mReady = true;
+    Q_FOREACH(const Tp::TextChannelPtr &channel, mPendingChannels) {
+        onTextChannelAvailable(channel);
+    }
+    mPendingChannels.clear();
+}
+
+void ChatManager::onChannelObserverUnregistered()
+{
+    Q_FOREACH(ChatEntry *entry, mChatEntries) {
+        // for some reason deleteLater is not working
+        delete entry;
+    }
+    mChatEntries.clear();
+}
+
 void ChatManager::onConnectedChanged()
 {
     if (TelepathyHelper::instance()->connected()) {
@@ -114,6 +137,7 @@
 
 void ChatManager::sendMessage(const QStringList &recipients, const QString &message, const QString &accountId)
 {
+    // FIXME: this probably should be handle internally by telepathy-ofono
     if (recipients.size() > 1 && TelepathyHelper::instance()->mmsGroupChat()) {
         sendMMS(recipients, message, QVariant(), accountId);
         return;
@@ -138,7 +162,12 @@
 
 void ChatManager::onTextChannelAvailable(Tp::TextChannelPtr channel)
 {
-    mChannels.append(channel);
+    if (!mReady) {
+        mPendingChannels.append(channel);
+        return;
+    }
+    ChatEntry *chatEntry = new ChatEntry(channel, this);
+    mChatEntries.append(chatEntry);
 
     connect(channel.data(),
             SIGNAL(messageReceived(Tp::ReceivedMessage)),
@@ -146,10 +175,38 @@
     connect(channel.data(),
             SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)),
             SLOT(onMessageSent(Tp::Message,Tp::MessageSendingFlags,QString)));
+     connect(channel.data(),
+            SIGNAL(invalidated(Tp::DBusProxy*,const QString&, const QString&)),
+            SLOT(onChannelInvalidated()));
 
     Q_FOREACH(const Tp::ReceivedMessage &message, channel->messageQueue()) {
         onMessageReceived(message);
     }
+
+    Q_EMIT chatsChanged();
+    Q_EMIT chatEntryCreated(chatEntry->account()->accountId(), chatEntry->participants(), chatEntry);
+}
+
+void ChatManager::onChannelInvalidated()
+{
+    Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()));
+    ChatEntry *chatEntry = chatEntryForChannel(channel);
+    if (chatEntry) {
+        mChatEntries.removeAll(chatEntry);
+        // for some reason deleteLater is not working
+        delete chatEntry;
+        Q_EMIT chatsChanged();
+    }
+}
+
+ChatEntry *ChatManager::chatEntryForChannel(const Tp::TextChannelPtr &channel)
+{
+    Q_FOREACH (ChatEntry *chatEntry, mChatEntries) {
+        if (channel == chatEntry->channel()) {
+            return chatEntry;
+        }
+    }
+    return NULL;
 }
 
 void ChatManager::onMessageReceived(const Tp::ReceivedMessage &message)
@@ -173,9 +230,12 @@
         return;
     }
 
-    if (!channel->targetContact().isNull()) {
-        Q_EMIT messageSent(channel->targetContact()->id(), sentMessage.text());
+    QStringList recipients;
+    Q_FOREACH(const Tp::ContactPtr &contact, channel->groupContacts(false)) {
+        recipients << contact->id();
     }
+
+    Q_EMIT messageSent(recipients, sentMessage.text());
 }
 
 void ChatManager::acknowledgeMessage(const QStringList &recipients, const QString &messageId, const QString &accountId)
@@ -217,3 +277,63 @@
 
     mMessagesToAck.clear();
 }
+
+QList<ChatEntry*> ChatManager::chatEntries() const
+{
+    return mChatEntries;
+}
+
+ChatEntry *ChatManager::chatEntryForParticipants(const QString &accountId, const QStringList &participants, bool create)
+{
+    if (participants.count() == 0 || accountId.isEmpty()) {
+        return NULL;
+    }
+
+    Q_FOREACH (ChatEntry *chatEntry, mChatEntries) {
+        int participantCount = 0;
+        Tp::Contacts contacts = chatEntry->channel()->groupContacts(false);
+        if (participants.count() != contacts.count()) {
+            continue;
+        }
+        // iterate over participants
+        Q_FOREACH (const Tp::ContactPtr &contact, contacts) {
+            if (participants.contains(contact->id())) {
+                participantCount++;
+            } else {
+                break;
+            }
+        }
+        if (participantCount == participants.count()) {
+            return chatEntry;
+        }
+    }
+
+    if (create) {
+        QDBusInterface *phoneAppHandler = TelepathyHelper::instance()->handlerInterface();
+        phoneAppHandler->call("StartChat", accountId, participants);
+    }
+    return NULL;
+}
+
+ChatEntry *ChatManager::chatEntryForChatRoom(const QString &accountId, const QVariantMap &properties, bool create)
+{
+    Q_UNUSED(accountId)
+    Q_UNUSED(properties)
+    Q_UNUSED(create)
+}
+
+QQmlListProperty<ChatEntry> ChatManager::chats()
+{
+    return QQmlListProperty<ChatEntry>(this, 0, chatsCount, chatAt);
+}
+
+int ChatManager::chatsCount(QQmlListProperty<ChatEntry> *p)
+{
+    return ChatManager::instance()->chatEntries().count();
+}
+
+ChatEntry *ChatManager::chatAt(QQmlListProperty<ChatEntry> *p, int index)
+{
+    return ChatManager::instance()->chatEntries()[index];
+}
+

=== modified file 'libtelephonyservice/chatmanager.h'
--- libtelephonyservice/chatmanager.h	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/chatmanager.h	2015-03-19 23:42:32 +0000
@@ -24,40 +24,62 @@
 
 #include <QtCore/QObject>
 #include <QtCore/QMap>
+#include <QQmlListProperty>
 #include <TelepathyQt/TextChannel>
 #include <TelepathyQt/ReceivedMessage>
 #include "dbustypes.h"
+#include "chatentry.h"
 
 class ChatManager : public QObject
 {
     Q_OBJECT
+    Q_PROPERTY(QQmlListProperty<ChatEntry> chats
+                   READ chats
+                   NOTIFY chatsChanged)
 public:
     static ChatManager *instance();
 
     Q_INVOKABLE void sendMessage(const QStringList &recipients, const QString &message, const QString &accountId = QString::null);
     Q_INVOKABLE void sendMMS(const QStringList &recipients, const QString &message, const QVariant &attachments, const QString &accountId = QString:: null);
+    Q_INVOKABLE ChatEntry *chatEntryForParticipants(const QString &accountId, const QStringList &participants, bool create = false);
+    Q_INVOKABLE ChatEntry *chatEntryForChatRoom(const QString &accountId, const QVariantMap &properties, bool create);
+
+    QQmlListProperty<ChatEntry> chats();
+    static int chatsCount(QQmlListProperty<ChatEntry> *p);
+    static ChatEntry* chatAt(QQmlListProperty<ChatEntry> *p, int index);
 
 Q_SIGNALS:
-    void messageReceived(const QString &recipient, const QString &message, const QDateTime &timestamp, const QString &messageId, bool unread);
-    void messageSent(const QString &recipient, const QString &message);
+    void messageReceived(const QString &sender, const QString &message, const QDateTime &timestamp, const QString &messageId, bool unread);
+    void messageSent(const QStringList &recipients, const QString &message);
+    void chatsChanged();
+    void chatEntryCreated(QString accountId, QStringList participants, ChatEntry *chatEntry);
 
 public Q_SLOTS:
     void onTextChannelAvailable(Tp::TextChannelPtr channel);
+    void onChannelInvalidated();
     void onConnectedChanged();
     void onMessageReceived(const Tp::ReceivedMessage &message);
     void onMessageSent(const Tp::Message &sentMessage, const Tp::MessageSendingFlags flags, const QString &message);
 
     void acknowledgeMessage(const QStringList &recipients, const QString &messageId, const QString &accountId = QString::null);
 
+private Q_SLOTS:
+    void onChannelObserverUnregistered();
+    void onTelepathyReady();
+
 protected Q_SLOTS:
     void onAckTimerTriggered();
 
 private:
     explicit ChatManager(QObject *parent = 0);
+    ChatEntry *chatEntryForChannel(const Tp::TextChannelPtr &channel);
+    QList<ChatEntry*> chatEntries() const;
 
-    QList<Tp::TextChannelPtr> mChannels;
+    mutable QList<ChatEntry*> mChatEntries;
     QMap<QString, QMap<QStringList,QStringList> > mMessagesToAck;
+    QList<Tp::TextChannelPtr> mPendingChannels;
     QTimer mMessagesAckTimer;
+    bool mReady;
 };
 
 #endif // CHATMANAGER_H

=== modified file 'libtelephonyservice/ofonoaccountentry.cpp'
--- libtelephonyservice/ofonoaccountentry.cpp	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/ofonoaccountentry.cpp	2015-03-19 23:42:32 +0000
@@ -203,6 +203,5 @@
         mSerial = ussdIface.property("Serial").toString();
     }
 
-    Q_EMIT simLockedChanged();
     Q_EMIT serialChanged();
 }

=== modified file 'libtelephonyservice/telepathyhelper.cpp'
--- libtelephonyservice/telepathyhelper.cpp	2015-03-19 23:42:32 +0000
+++ libtelephonyservice/telepathyhelper.cpp	2015-03-19 23:42:32 +0000
@@ -57,7 +57,8 @@
                            "org.freedesktop.URfkill",
                            QDBusConnection::systemBus())
 {
-    mAccountFeatures << Tp::Account::FeatureCore;
+    mAccountFeatures << Tp::Account::FeatureCore
+                     << Tp::Account::FeatureProtocolInfo;
     mContactFeatures << Tp::Contact::FeatureAlias
                      << Tp::Contact::FeatureAvatarData
                      << Tp::Contact::FeatureAvatarToken
@@ -400,7 +401,7 @@
     Q_FOREACH(AccountEntry *account, mAccounts) {
         QString modemObjName = account->account()->parameters().value("modem-objpath").toString();
         if (modemObjName.isEmpty()) {
-            sortedOtherAccounts[account->displayName()] = account;
+            sortedOtherAccounts[account->accountId()] = account;
         } else {
             sortedOfonoAccounts[modemObjName] = account;
         }

=== added directory 'tests'
=== added file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ tests/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -0,0 +1,46 @@
+macro(generate_test TESTNAME USE_DBUS)
+    add_executable(${TESTNAME} ${ARGN} ${TESTNAME}.cpp)
+    qt5_use_modules(${TESTNAME} Core DBus Test Qml)
+    set(TEST_COMMAND )
+    if (${USE_DBUS})
+        set(TEST_COMMAND -p ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -p -xunitxml -p -o -p ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml)
+        add_test(${TESTNAME} ${DBUS_RUNNER} --keep-env
+                                            --task ${CMAKE_BINARY_DIR}/tests/common/dbus-test-wrapper.sh ${TEST_COMMAND})
+    else (${USE_DBUS})
+        add_test(${TESTNAME} ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -xunitxml -o ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml)
+    endif(${USE_DBUS})
+    # force telepathy not to use system approvers when available,
+    # also force usage of memory backend in history-service
+    set(TMPDIR "/tmp/telephony_service_test_home")
+    set(TEST_ENVIRONMENT "HOME=${TMPDIR};"
+                         "HISTORY_SQLITE_DBPATH=:memory:;"
+                         "XDG_CONFIG_HOME=${TMPDIR};
+                          XDG_DATA_HOME=${TMPDIR};
+                          XDG_CACHE_DIR=${TMPDIR};
+                          XDG_CACHE_HOME=${TMPDIR};
+                          XDG_DATA_DIRS=${TMPDIR};
+                          MC_ACCOUNT_DIR=${TMPDIR};
+                          MC_MANAGER_DIR=${TMPDIR}")
+    set_tests_properties(${TESTNAME} PROPERTIES
+                          ENVIRONMENT "${TEST_ENVIRONMENT}"
+                          TIMEOUT 30)
+    target_link_libraries(${TESTNAME}
+                          ${TP_QT5_LIBRARIES}
+                          telephonyservice
+                          mockcontroller
+                         )
+
+    # as the tests link to telephonyservice, we need to enable coverage on them too
+    enable_coverage(${TESTNAME})
+endmacro(generate_test)
+
+add_subdirectory(common)
+
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc*")
+    message(STATUS "Handler tests disabled on ppc")
+else()
+    add_subdirectory(handler)
+endif()
+
+add_subdirectory(libtelephonyservice)
+add_subdirectory(Ubuntu.Telephony)

=== renamed directory 'Ubuntu/Telephony/tests' => 'tests/Ubuntu.Telephony'
=== modified file 'tests/Ubuntu.Telephony/CMakeLists.txt'
--- Ubuntu/Telephony/tests/CMakeLists.txt	2014-07-11 21:30:21 +0000
+++ tests/Ubuntu.Telephony/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -1,6 +1,7 @@
 include_directories(
     ${CMAKE_CURRENT_BINARY_DIR}
     ${CMAKE_SOURCE_DIR}/Ubuntu/Telephony
+    ${CMAKE_SOURCE_DIR}/libtelephonyservice
     )
 
 macro(generate_tests)

=== added directory 'tests/common'
=== added file 'tests/common/CMakeLists.txt'
--- tests/common/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ tests/common/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -0,0 +1,6 @@
+configure_file(dbus-test-wrapper.sh.in ${CMAKE_CURRENT_BINARY_DIR}/dbus-test-wrapper.sh)
+
+add_library(mockcontroller STATIC mockcontroller.cpp mockcontroller.h)
+qt5_use_modules(mockcontroller Core DBus)
+
+add_subdirectory(mock)

=== renamed file 'handler/tests/dbus-test-wrapper.sh.in' => 'tests/common/dbus-test-wrapper.sh.in'
--- handler/tests/dbus-test-wrapper.sh.in	2014-03-27 13:26:31 +0000
+++ tests/common/dbus-test-wrapper.sh.in	2015-03-19 23:42:32 +0000
@@ -13,15 +13,19 @@
 dconf write /org/gnome/empathy/use-conn false
 
 # start the mock connection manager
-@CMAKE_CURRENT_BINARY_DIR@/mock/telepathy-mock &
+@CMAKE_BINARY_DIR@/tests/common/mock/telepathy-mock &
 MOCK_PID=$!
 
 # wait 1 sec for the mock to start up
 sleep 1
 
+# add one generic account
 mc-tool add mock/mock account0
 
-@CMAKE_CURRENT_BINARY_DIR@/../telephony-service-handler &
+# and one phone account
+mc-tool add mock/ofono account0
+
+@CMAKE_BINARY_DIR@/handler/telephony-service-handler &
 
 HANDLER_PID=$!
 

=== renamed directory 'handler/tests/mock' => 'tests/common/mock'
=== modified file 'tests/common/mock/CMakeLists.txt'
--- handler/tests/mock/CMakeLists.txt	2014-03-13 17:11:09 +0000
+++ tests/common/mock/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -4,11 +4,14 @@
     ${Qt5DBus_INCLUDE_DIRS}
     ${CMAKE_CURRENT_BINARY_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_SOURCE_DIR}/libtelephonyservice
     )
 
 find_library(TELEPATHY_QT5_SERVICE_LIBRARIES telepathy-qt5-service)
 
-set(mock_SRCS main.cpp protocol.cpp connection.cpp textchannel.cpp callchannel.cpp conferencecallchannel.cpp mockconnectiondbus.cpp speakeriface.cpp)
+set(mock_SRCS main.cpp protocol.cpp connection.cpp textchannel.cpp callchannel.cpp
+              conferencecallchannel.cpp mockconnectiondbus.cpp speakeriface.cpp
+              emergencymodeiface.cpp ussdiface.cpp voicemailiface.cpp)
 qt5_add_dbus_adaptor(mock_SRCS MockConnection.xml mockconnectiondbus.h MockConnectionDBus)
 
 add_executable(telepathy-mock ${mock_SRCS})

=== modified file 'tests/common/mock/MockConnection.xml'
--- handler/tests/mock/MockConnection.xml	2014-03-20 21:03:26 +0000
+++ tests/common/mock/MockConnection.xml	2015-03-19 23:42:32 +0000
@@ -38,6 +38,55 @@
             <arg name="phoneNumber" type="s" direction="in"/>
             <arg name="status" type="s" direction="in"/>
         </method>
+        <method name="SetOnline">
+            <dox:d><![CDATA[
+                Set the account online/offline
+            ]]></dox:d>
+            <arg name="online" type="b" direction="in"/>
+        </method>
+        <method name="SetPresence">
+            <dox:d><![CDATA[
+                Set the requested presence
+            ]]></dox:d>
+            <arg name="status" type="s" direction="in"/>
+            <arg name="statusMessage" type="s" direction="in"/>
+        </method>
+        <method name="SetVoicemailIndicator">
+            <dox:d><![CDATA[
+                Set the voicemail indicator on/off
+            ]]></dox:d>
+            <arg name="active" type="b" direction="in"/>
+        </method>
+        <method name="SetVoicemailNumber">
+            <dox:d><![CDATA[
+                Set the voicemail number
+            ]]></dox:d>
+            <arg name="number" type="s" direction="in"/>
+        </method>
+        <method name="SetVoicemailCount">
+            <dox:d><![CDATA[
+                Set the voicemail count
+            ]]></dox:d>
+            <arg name="count" type="i" direction="in"/>
+        </method>
+        <method name="SetEmergencyNumbers">
+            <dox:d><![CDATA[
+                Set the emergency numbers
+            ]]></dox:d>
+            <arg name="numbers" type="as" direction="in"/>
+        </method>
+        <method name="Serial">
+            <dox:d><![CDATA[
+                Set the USSD serial
+            ]]></dox:d>
+            <arg name="value" type="s" direction="out"/>
+        </method>
+        <signal name="MessageRead">
+            <dox:d><![CDATA[
+                A message was acknowledged
+            ]]></dox:d>
+            <arg name="messageId" type="s"/>
+        </signal>
         <signal name="MessageSent">
             <dox:d><![CDATA[
                 A message was sent from the client.
@@ -45,6 +94,7 @@
             <arg name="message" type="s"/>
             <arg name="properties" type="a{sv}"/>
             <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
         </signal>
         <signal name="CallReceived">
             <dox:d><![CDATA[

=== modified file 'tests/common/mock/connection.cpp'
--- handler/tests/mock/connection.cpp	2014-04-03 16:38:01 +0000
+++ tests/common/mock/connection.cpp	2015-03-19 23:42:32 +0000
@@ -36,7 +36,7 @@
                             const QString &protocolName,
                             const QVariantMap &parameters) :
     Tp::BaseConnection(dbusConnection, cmName, protocolName, parameters),
-    mHandleCount(0), mConferenceCall(0)
+    mHandleCount(0), mConferenceCall(0), mVoicemailIndicator(false), mVoicemailCount(0)
 {
     setSelfHandle(newHandle("<SelfHandle>"));
 
@@ -54,6 +54,7 @@
     text.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"]  = Tp::HandleTypeContact;
     text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle");
     text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID");
+    text.allowedProperties.append(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles"));
 
     // set requestable call channel properties
     Tp::RequestableChannelClass call;
@@ -77,26 +78,41 @@
     // init presence interface
     simplePresenceIface = Tp::BaseConnectionSimplePresenceInterface::create();
     simplePresenceIface->setSetPresenceCallback(Tp::memFun(this,&MockConnection::setPresence));
+    simplePresenceIface->setMaxmimumStatusMessageLength(255);
     plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(simplePresenceIface));
 
     // Set Presence
     Tp::SimpleStatusSpec presenceOnline;
     presenceOnline.type = Tp::ConnectionPresenceTypeAvailable;
     presenceOnline.maySetOnSelf = true;
-    presenceOnline.canHaveMessage = false;
+    presenceOnline.canHaveMessage = true;
 
     Tp::SimpleStatusSpec presenceOffline;
     presenceOffline.type = Tp::ConnectionPresenceTypeOffline;
-    presenceOffline.maySetOnSelf = false;
-    presenceOffline.canHaveMessage = false;
-
-    Tp::SimpleStatusSpecMap statuses;
-    statuses.insert(QLatin1String("available"), presenceOnline);
-    statuses.insert(QLatin1String("offline"), presenceOffline);
-
-    simplePresenceIface->setStatuses(statuses);
+    presenceOffline.maySetOnSelf = true;
+    presenceOffline.canHaveMessage = true;
+
+    Tp::SimpleStatusSpec presenceAway;
+    presenceAway.type = Tp::ConnectionPresenceTypeAway;
+    presenceAway.maySetOnSelf = true;
+    presenceAway.canHaveMessage = true;
+
+    mStatuses.insert(QLatin1String("available"), presenceOnline);
+    mStatuses.insert(QLatin1String("offline"), presenceOffline);
+    mStatuses.insert(QLatin1String("away"), presenceAway);
+    mStatuses.insert(QLatin1String("simlocked"), presenceAway);
+    mStatuses.insert(QLatin1String("flightmode"), presenceOffline);
+    mStatuses.insert(QLatin1String("nosim"), presenceOffline);
+    mStatuses.insert(QLatin1String("nomodem"), presenceOffline);
+    mStatuses.insert(QLatin1String("registered"), presenceOnline);
+    mStatuses.insert(QLatin1String("roaming"), presenceOnline);
+    mStatuses.insert(QLatin1String("unregistered"), presenceAway);
+    mStatuses.insert(QLatin1String("denied"), presenceAway);
+    mStatuses.insert(QLatin1String("unknown"), presenceAway);
+    mStatuses.insert(QLatin1String("searching"), presenceAway);
+
+    simplePresenceIface->setStatuses(mStatuses);
     mSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
-    mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeOffline;
 
     contactsIface = Tp::BaseConnectionContactsInterface::create();
     contactsIface->setGetContactAttributesCallback(Tp::memFun(this,&MockConnection::getContactAttributes));
@@ -105,6 +121,35 @@
                                                  << TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE);
     plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(contactsIface));
 
+    // init custom emergency mode interface (not provided by telepathy
+    emergencyModeIface = BaseConnectionEmergencyModeInterface::create();
+    emergencyModeIface->setEmergencyNumbersCallback(Tp::memFun(this,&MockConnection::emergencyNumbers));
+    plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(emergencyModeIface));
+    mEmergencyNumbers << "123" << "456" << "789";
+    emergencyModeIface->setEmergencyNumbers(mEmergencyNumbers);
+
+    // init custom voicemail interface (not provided by telepathy)
+    voicemailIface = BaseConnectionVoicemailInterface::create();
+    voicemailIface->setVoicemailCountCallback(Tp::memFun(this,&MockConnection::voicemailCount));
+    voicemailIface->setVoicemailIndicatorCallback(Tp::memFun(this,&MockConnection::voicemailIndicator));
+    voicemailIface->setVoicemailNumberCallback(Tp::memFun(this,&MockConnection::voicemailNumber));
+    voicemailIface->setVoicemailNumber(mVoicemailNumber);
+    plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(voicemailIface));
+    voicemailIface->setVoicemailCount(mVoicemailCount);
+    voicemailIface->setVoicemailIndicator(mVoicemailIndicator);
+    mVoicemailNumber = "555";
+
+    supplementaryServicesIface = BaseConnectionUSSDInterface::create();
+    supplementaryServicesIface->setInitiateCallback(Tp::memFun(this,&MockConnection::USSDInitiate));
+    supplementaryServicesIface->setRespondCallback(Tp::memFun(this,&MockConnection::USSDRespond));
+    supplementaryServicesIface->setCancelCallback(Tp::memFun(this,&MockConnection::USSDCancel));
+
+    static int serial = 0;
+    serial++;
+    supplementaryServicesIface->setSerial(QString("accountserial%1").arg(QString::number(serial)));
+
+    plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(supplementaryServicesIface));
+
     mDBus = new MockConnectionDBus(this);
 
     setOnline(true);
@@ -143,16 +188,49 @@
 #endif
 }
 
+MockTextChannel *MockConnection::textChannelForRecipients(const QStringList &recipients)
+{
+    Q_FOREACH(MockTextChannel *channel, mTextChannels) {
+        QStringList channelRecipients = channel->recipients();
+        if (channelRecipients.length() != recipients.length()) {
+            continue;
+        }
+
+        bool ok = true;
+        Q_FOREACH(const QString &recipient, recipients) {
+            if (!channelRecipients.contains(recipient)) {
+                ok = false;
+                break;
+            }
+        }
+
+        if (ok) {
+            return channel;
+        }
+    }
+    return 0;
+}
+
 MockConnection::~MockConnection()
 {
 }
 
 uint MockConnection::setPresence(const QString& status, const QString& statusMessage, Tp::DBusError *error)
 {
-    qDebug() << "setPresence" << status;
-    if (status == "available") {
-        mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeAvailable;
+    qDebug() << "setPresence" << status << statusMessage;
+    Tp::SimpleContactPresences presences;
+    if (!mStatuses.contains(status) || !mStatuses[status].maySetOnSelf) {
+        error->set(TP_QT_ERROR_INVALID_ARGUMENT, "Status not supported or cannot be set");
+        return selfHandle();
     }
+
+    Tp::SimpleStatusSpec spec = mStatuses[status];
+    mSelfPresence.status = status;
+    mSelfPresence.type = spec.type;
+    mSelfPresence.statusMessage = spec.canHaveMessage ? statusMessage : "";
+
+    presences[selfHandle()] = mSelfPresence;
+    simplePresenceIface->setPresences(presences);
     return selfHandle();
 }
 
@@ -248,29 +326,44 @@
 }
 
 Tp::BaseChannelPtr MockConnection::createTextChannel(uint targetHandleType,
-                                               uint targetHandle, Tp::DBusError *error)
+                                                     uint targetHandle,
+                                                     const QVariantMap &hints,
+                                                     Tp::DBusError *error)
 {
     Q_UNUSED(targetHandleType);
     Q_UNUSED(error);
 
-    QString requestedId = mHandles.value(targetHandle);
-
-    if (mTextChannels.contains(requestedId)) {
-        return mTextChannels[requestedId]->baseChannel();
-    }
-
-    MockTextChannel *channel = new MockTextChannel(this, requestedId, targetHandle);
+    if (hints.contains(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles")) &&
+            targetHandleType == Tp::HandleTypeNone && targetHandle == 0) {
+
+    }
+
+    QStringList recipients;
+    bool flash;
+    if (hints.contains(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles"))) {
+        recipients << inspectHandles(Tp::HandleTypeContact, qdbus_cast<Tp::UIntList>(hints[TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE + QLatin1String(".InitialInviteeHandles")]), error);
+    } else {
+        recipients << mHandles.value(targetHandle);
+    }
+
+    if (hints.contains(TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash"))) {
+        flash = hints[TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash")].toBool();
+    }
+
+    // FIXME: test flash messages
+    MockTextChannel *channel = new MockTextChannel(this, recipients, targetHandle);
     QObject::connect(channel, SIGNAL(messageRead(QString)), SLOT(onMessageRead(QString)));
     QObject::connect(channel, SIGNAL(destroyed()), SLOT(onTextChannelClosed()));
     QObject::connect(channel, SIGNAL(messageSent(QString,QVariantMap)), SIGNAL(messageSent(QString,QVariantMap)));
     qDebug() << channel;
-    mTextChannels[requestedId] = channel;
+    mTextChannels << channel;
     return channel->baseChannel();
 }
 
 void MockConnection::onMessageRead(const QString &id)
 {
-    // FIXME: implement
+    // FIXME: check what else to do
+    Q_EMIT messageRead(id);
 }
 
 void MockConnection::onConferenceCallChannelClosed()
@@ -355,7 +448,7 @@
     }
 
     if (channelType == TP_QT_IFACE_CHANNEL_TYPE_TEXT) {
-        return createTextChannel(targetHandleType, targetHandle, error);
+        return createTextChannel(targetHandleType, targetHandle, hints, error);
     } else if (channelType == TP_QT_IFACE_CHANNEL_TYPE_CALL) {
         return createCallChannel(targetHandleType, targetHandle, hints, error);
     } else {
@@ -367,33 +460,36 @@
 
 void MockConnection::placeIncomingMessage(const QString &message, const QVariantMap &info)
 {
-    const QString sender = info["Sender"].toString();
-    // check if there is an open channel for this sender and use it
-    Q_FOREACH(const QString &id, mTextChannels.keys()) {
-        if (id == sender) {
-            mTextChannels[id]->messageReceived(message, info);
-            return;
-        }
-    }
-
-    Tp::DBusError error;
-    bool yours;
-    uint handle = newHandle(sender);
-    ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, QVariantMap(), &error);
-    if(error.isValid()) {
-        qWarning() << "Error creating channel for incoming message" << error.name() << error.message();
-        return;
-    }
-    mTextChannels[sender]->messageReceived(message, info);
+    QString sender = info["Sender"].toString();
+    QStringList recipients = info["Recipients"].toStringList();
+
+    MockTextChannel *channel = textChannelForRecipients(recipients);
+    if (!channel) {
+        // request the channel
+        Tp::DBusError error;
+        bool yours;
+        uint handle = newHandle(sender);
+        ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, QVariantMap(), &error);
+        if(error.isValid()) {
+            qWarning() << "Error creating channel for incoming message" << error.name() << error.message();
+            return;
+        }
+
+        channel = textChannelForRecipients(recipients);
+        if (!channel) {
+            return;
+        }
+    }
+
+    channel->messageReceived(message, info);
 }
 
 void MockConnection::onTextChannelClosed()
 {
     MockTextChannel *channel = static_cast<MockTextChannel*>(sender());
     if (channel) {
-        QString key = mTextChannels.key(channel);
-        qDebug() << "text channel closed for number " << key;
-        mTextChannels.remove(key);
+        qDebug() << "text channel closed for recipients " << channel->recipients();
+        mTextChannels.removeAll(channel);
     }
 }
 
@@ -469,6 +565,70 @@
     return channel->objectPath();
 }
 
+QStringList MockConnection::emergencyNumbers(Tp::DBusError *error)
+{
+    return mEmergencyNumbers;
+}
+
+void MockConnection::setEmergencyNumbers(const QStringList &emergencyNumbers)
+{
+    mEmergencyNumbers = emergencyNumbers;
+    emergencyModeIface->setEmergencyNumbers(emergencyNumbers);
+}
+
+bool MockConnection::voicemailIndicator(Tp::DBusError *error)
+{
+    return mVoicemailIndicator;
+}
+
+void MockConnection::setVoicemailIndicator(bool visible)
+{
+    mVoicemailIndicator = visible;
+    voicemailIface->setVoicemailIndicator(visible);
+}
+
+QString MockConnection::voicemailNumber(Tp::DBusError *error)
+{
+    return mVoicemailNumber;
+}
+
+void MockConnection::setVoicemailNumber(const QString &number)
+{
+    mVoicemailNumber = number;
+    voicemailIface->setVoicemailNumber(mVoicemailNumber);
+}
+
+uint MockConnection::voicemailCount(Tp::DBusError *error)
+{
+    return mVoicemailCount;
+}
+
+void MockConnection::setVoicemailCount(int count)
+{
+    mVoicemailCount = count;
+    voicemailIface->setVoicemailCount(mVoicemailCount);
+}
+
+void MockConnection::USSDInitiate(const QString &command, Tp::DBusError *error)
+{
+    // FIXME: implement
+}
+
+void MockConnection::USSDRespond(const QString &reply, Tp::DBusError *error)
+{
+    // FIXME: implement
+}
+
+void MockConnection::USSDCancel(Tp::DBusError *error)
+{
+    // FIXME: implement
+}
+
+QString MockConnection::serial()
+{
+    return supplementaryServicesIface->serial();
+}
+
 void MockConnection::hangupCall(const QString &callerId)
 {
     if (!mCallChannels.contains(callerId)) {

=== modified file 'tests/common/mock/connection.h'
--- handler/tests/mock/connection.h	2014-03-20 21:03:26 +0000
+++ tests/common/mock/connection.h	2015-03-19 23:42:32 +0000
@@ -34,6 +34,9 @@
 #include "textchannel.h"
 #include "callchannel.h"
 #include "dbustypes.h"
+#include "emergencymodeiface.h"
+#include "ussdiface.h"
+#include "voicemailiface.h"
 
 class MockTextChannel;
 class MockCallChannel;
@@ -65,9 +68,16 @@
     uint newHandle(const QString &identifier);
     QMap<QString, MockCallChannel*> callChannels();
 
+    // phone custom interfaces
+    BaseConnectionEmergencyModeInterfacePtr emergencyModeIface;
+    BaseConnectionVoicemailInterfacePtr voicemailIface;
+    BaseConnectionUSSDInterfacePtr supplementaryServicesIface;
+
     uint ensureHandle(const QString &id);
     Tp::BaseChannelPtr createTextChannel(uint targetHandleType,
-                                         uint targetHandle, Tp::DBusError *error);
+                                         uint targetHandle,
+                                         const QVariantMap &hints,
+                                         Tp::DBusError *error);
     Tp::BaseChannelPtr createCallChannel(uint targetHandleType,
                                          uint targetHandle,
                                          const QVariantMap &hints,
@@ -76,7 +86,23 @@
     ~MockConnection();
 
     QString placeCall(const QVariantMap &properties);
+
+    QStringList emergencyNumbers(Tp::DBusError *error);
+    void setEmergencyNumbers(const QStringList &emergencyNumbers);
+
+    bool voicemailIndicator(Tp::DBusError *error);
+    void setVoicemailIndicator(bool visible);
+    QString voicemailNumber(Tp::DBusError *error);
+    void setVoicemailNumber(const QString &number);
+    uint voicemailCount(Tp::DBusError *error);
+    void setVoicemailCount(int count);
+    void USSDInitiate(const QString &command, Tp::DBusError *error);
+    void USSDRespond(const QString &reply, Tp::DBusError *error);
+    void USSDCancel(Tp::DBusError *error);
+    QString serial();
+
 Q_SIGNALS:
+    void messageRead(const QString &messageId);
     void messageSent(const QString &message, const QVariantMap &info);
     void callReceived(const QString &callerId);
     void callEnded(const QString &callerId);
@@ -102,20 +128,28 @@
 
 private:
     void addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath);
+
+    MockTextChannel *textChannelForRecipients(const QStringList &recipients);
+
     QMap<uint, QString> mHandles;
+    Tp::SimpleStatusSpecMap mStatuses;
 
-    QMap<QString, MockTextChannel*> mTextChannels;
+    QList<MockTextChannel*> mTextChannels;
     QMap<QString, MockCallChannel*> mCallChannels;
     QMap<QString, QString> mInitialCallStatus;
 
     QStringList mModems;
     uint mHandleCount;
     Tp::SimplePresence mSelfPresence;
-    Tp::SimplePresence mRequestedSelfPresence;
 
     MockConnectionDBus *mDBus;
     QStringList mIncomingCalls;
     MockConferenceCallChannel *mConferenceCall;
+
+    QStringList mEmergencyNumbers;
+    int mVoicemailCount;
+    bool mVoicemailIndicator;
+    QString mVoicemailNumber;
 };
 
 #endif

=== added file 'tests/common/mock/emergencymodeiface.cpp'
--- tests/common/mock/emergencymodeiface.cpp	1970-01-01 00:00:00 +0000
+++ tests/common/mock/emergencymodeiface.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,133 @@
+/**
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
+ */
+
+#include <QDebug>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBusObject>
+
+#include "emergencymodeiface.h"
+
+BaseConnectionEmergencyModeInterface::Adaptee::Adaptee(BaseConnectionEmergencyModeInterface *interface)
+    : QObject(interface),
+      mInterface(interface)
+{
+}
+
+
+struct TP_QT_NO_EXPORT BaseConnectionEmergencyModeInterface::Private {
+    Private(BaseConnectionEmergencyModeInterface *parent)
+        : adaptee(new BaseConnectionEmergencyModeInterface::Adaptee(parent)) {
+    }
+    EmergencyNumbersCallback emergencyNumbersCB;
+    BaseConnectionEmergencyModeInterface::Adaptee *adaptee;
+    QString fakeEmergencyNumber;
+};
+
+BaseConnectionEmergencyModeInterface::Adaptee::~Adaptee()
+{
+}
+
+void BaseConnectionEmergencyModeInterface::Adaptee::emergencyNumbers(const ConnectionInterfaceEmergencyModeAdaptor::EmergencyNumbersContextPtr &context)
+{
+    if (!mInterface->mPriv->emergencyNumbersCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    QStringList numbers = mInterface->mPriv->emergencyNumbersCB(&error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    if (mInterface->mPriv->fakeEmergencyNumber.isEmpty()) {
+        context->setFinished(numbers);
+    } else {
+        context->setFinished(QStringList() << numbers << mInterface->mPriv->fakeEmergencyNumber);
+    }
+}
+
+BaseConnectionEmergencyModeInterface::BaseConnectionEmergencyModeInterface()
+    : AbstractConnectionInterface(TP_QT_IFACE_CONNECTION_EMERGENCYMODE),
+      mPriv(new Private(this))
+{
+}
+
+BaseConnectionEmergencyModeInterface::~BaseConnectionEmergencyModeInterface()
+{
+    delete mPriv;
+}
+
+void BaseConnectionEmergencyModeInterface::setEmergencyNumbersCallback(const EmergencyNumbersCallback &cb)
+{
+    mPriv->emergencyNumbersCB = cb;
+}
+
+void BaseConnectionEmergencyModeInterface::setEmergencyNumbers(const QStringList &numbers)
+{
+    QStringList finalEmergencyList(numbers);
+
+    if (!mPriv->fakeEmergencyNumber.isEmpty()) {
+        finalEmergencyList << mPriv->fakeEmergencyNumber;
+    }
+
+    Q_EMIT mPriv->adaptee->emergencyNumbersChanged(finalEmergencyList);
+}
+
+void BaseConnectionEmergencyModeInterface::setFakeEmergencyNumber(const QString &fakeEmergencyNumber)
+{
+    mPriv->fakeEmergencyNumber = fakeEmergencyNumber;
+}
+
+QVariantMap BaseConnectionEmergencyModeInterface::immutableProperties() const
+{
+    QVariantMap map;
+    return map;
+}
+
+void BaseConnectionEmergencyModeInterface::createAdaptor()
+{
+    (void) new ConnectionInterfaceEmergencyModeAdaptor(dbusObject()->dbusConnection(),
+            mPriv->adaptee, dbusObject());
+}
+
+
+ConnectionInterfaceEmergencyModeAdaptor::ConnectionInterfaceEmergencyModeAdaptor(const QDBusConnection& bus, QObject* adaptee, QObject* parent)
+    : Tp::AbstractAdaptor(bus, adaptee, parent)
+{
+    connect(adaptee, SIGNAL(emergencyNumbersChanged(QStringList)), SIGNAL(EmergencyNumbersChanged(QStringList)));
+}
+
+ConnectionInterfaceEmergencyModeAdaptor::~ConnectionInterfaceEmergencyModeAdaptor()
+{
+}
+
+QStringList ConnectionInterfaceEmergencyModeAdaptor::EmergencyNumbers(const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("emergencyNumbers(ConnectionInterfaceEmergencyModeAdaptor::EmergencyNumbersContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return QStringList();
+    }
+
+    EmergencyNumbersContextPtr ctx = EmergencyNumbersContextPtr(
+            new Tp::MethodInvocationContext< QStringList >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "emergencyNumbers",
+        Q_ARG(ConnectionInterfaceEmergencyModeAdaptor::EmergencyNumbersContextPtr, ctx));
+    return QStringList();
+}

=== added file 'tests/common/mock/emergencymodeiface.h'
--- tests/common/mock/emergencymodeiface.h	1970-01-01 00:00:00 +0000
+++ tests/common/mock/emergencymodeiface.h	2015-03-19 23:42:32 +0000
@@ -0,0 +1,121 @@
+/**
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
+ */
+
+#ifndef OFONOEMERGENCYMODEIFACE_H
+#define OFONOEMERGENCYMODEIFACE_H
+
+// telepathy-qt
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/BaseConnection>
+#include <TelepathyQt/AbstractAdaptor>
+#include <TelepathyQt/DBusError>
+#include <TelepathyQt/Callbacks>
+
+class BaseConnectionEmergencyModeInterface;
+
+typedef Tp::SharedPtr<BaseConnectionEmergencyModeInterface> BaseConnectionEmergencyModeInterfacePtr;
+
+#define TP_QT_IFACE_CONNECTION_EMERGENCYMODE "com.canonical.Telephony.EmergencyMode"
+
+class TP_QT_EXPORT BaseConnectionEmergencyModeInterface : public Tp::AbstractConnectionInterface
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(BaseConnectionEmergencyModeInterface)
+
+public:
+    static BaseConnectionEmergencyModeInterfacePtr create() {
+        return BaseConnectionEmergencyModeInterfacePtr(new BaseConnectionEmergencyModeInterface());
+    }
+    template<typename BaseConnectionEmergencyModeInterfaceSubclass>
+    static Tp::SharedPtr<BaseConnectionEmergencyModeInterfaceSubclass> create() {
+        return Tp::SharedPtr<BaseConnectionEmergencyModeInterfaceSubclass>(
+                   new BaseConnectionEmergencyModeInterfaceSubclass());
+    }
+    QVariantMap immutableProperties() const;
+    virtual ~BaseConnectionEmergencyModeInterface();
+
+    typedef Tp::Callback1<QStringList, Tp::DBusError*> EmergencyNumbersCallback;
+    void setEmergencyNumbersCallback(const EmergencyNumbersCallback &cb);
+    void setFakeEmergencyNumber(const QString &fakeEmergencyNumber);
+
+public Q_SLOTS:
+    void setEmergencyNumbers(const QStringList &numbers);
+
+protected:
+    BaseConnectionEmergencyModeInterface();
+
+private:
+    void createAdaptor();
+
+    class Adaptee;
+    friend class Adaptee;
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+
+class TP_QT_EXPORT ConnectionInterfaceEmergencyModeAdaptor : public Tp::AbstractAdaptor
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", TP_QT_IFACE_CONNECTION_EMERGENCYMODE)
+    Q_CLASSINFO("D-Bus Introspection", ""
+"  <interface name=\"com.canonical.Telephony.EmergencyMode\">\n"
+"    <method name=\"EmergencyNumbers\">\n"
+"      <arg direction=\"out\" type=\"as\" name=\"emergencyNumbers\"/>\n"
+"    </method>\n"
+"    <signal name=\"EmergencyNumbersChanged\">\n"
+"      <arg type=\"as\" name=\"numbers\"/>\n"
+"    </signal>\n"
+"  </interface>\n"
+"")
+
+public:
+    ConnectionInterfaceEmergencyModeAdaptor(const QDBusConnection& dbusConnection, QObject* adaptee, QObject* parent);
+    virtual ~ConnectionInterfaceEmergencyModeAdaptor();
+
+    typedef Tp::MethodInvocationContextPtr< QStringList > EmergencyNumbersContextPtr;
+
+public Q_SLOTS: // METHODS
+    QStringList EmergencyNumbers(const QDBusMessage& dbusMessage);
+
+Q_SIGNALS: // SIGNALS
+    void EmergencyNumbersChanged(const QStringList &numbers);
+};
+
+
+class TP_QT_NO_EXPORT BaseConnectionEmergencyModeInterface::Adaptee : public QObject
+{
+    Q_OBJECT
+
+public:
+    Adaptee(BaseConnectionEmergencyModeInterface *interface);
+    ~Adaptee();
+
+private Q_SLOTS:
+    void emergencyNumbers(const ConnectionInterfaceEmergencyModeAdaptor::EmergencyNumbersContextPtr &context);
+
+Q_SIGNALS:
+    void emergencyNumbersChanged(const QStringList &numbers);
+
+public:
+    BaseConnectionEmergencyModeInterface *mInterface;
+};
+
+#endif

=== modified file 'tests/common/mock/main.cpp'
--- handler/tests/mock/main.cpp	2014-01-07 19:53:45 +0000
+++ tests/common/mock/main.cpp	2015-03-19 23:42:32 +0000
@@ -31,10 +31,17 @@
     Tp::enableDebug(true);
     Tp::enableWarnings(true);
 
+    // create a standard protocol
     Tp::BaseProtocolPtr proto = Tp::BaseProtocol::create<Protocol>(
             QDBusConnection::sessionBus(), QLatin1String("mock"));
+
+    // create a phone protocol
+    Tp::BaseProtocolPtr phoneProto = Tp::BaseProtocol::create<Protocol>(
+            QDBusConnection::sessionBus(), QLatin1String("ofono"));
+
     Tp::BaseConnectionManagerPtr cm = Tp::BaseConnectionManager::create(
             QDBusConnection::sessionBus(), QLatin1String("mock"));
+    cm->addProtocol(phoneProto);
     cm->addProtocol(proto);
     cm->registerObject();
 

=== modified file 'tests/common/mock/mockconnectiondbus.cpp'
--- handler/tests/mock/mockconnectiondbus.cpp	2014-03-20 21:03:26 +0000
+++ tests/common/mock/mockconnectiondbus.cpp	2015-03-19 23:42:32 +0000
@@ -28,7 +28,9 @@
 MockConnectionDBus::MockConnectionDBus(MockConnection *parent) :
     QObject(parent), mAdaptor(0), mConnection(parent)
 {
-
+    connect(mConnection,
+            SIGNAL(messageRead(QString)),
+            SIGNAL(MessageRead(QString)));
     connect(mConnection,
             SIGNAL(messageSent(QString,QVariantMap)),
             SIGNAL(MessageSent(QString,QVariantMap)));
@@ -65,7 +67,7 @@
         mAdaptor = new MockConnectionAdaptor(this);
     }
 
-    return QDBusConnection::sessionBus().registerObject("/com/canonical/MockConnection", this);
+    return QDBusConnection::sessionBus().registerObject("/com/canonical/MockConnection/" + mConnection->protocolName(), this);
 }
 
 void MockConnectionDBus::PlaceIncomingMessage(const QString &message, const QVariantMap &properties)
@@ -87,3 +89,39 @@
 {
     mConnection->setCallState(phoneNumber, state);
 }
+
+void MockConnectionDBus::SetOnline(bool online)
+{
+    mConnection->setOnline(online);
+}
+
+void MockConnectionDBus::SetPresence(const QString &status, const QString &statusMessage)
+{
+    Tp::DBusError error;
+    mConnection->setPresence(status, statusMessage, &error);
+}
+
+void MockConnectionDBus::SetVoicemailIndicator(bool active)
+{
+    mConnection->setVoicemailIndicator(active);
+}
+
+void MockConnectionDBus::SetVoicemailNumber(const QString &number)
+{
+    mConnection->setVoicemailNumber(number);
+}
+
+void MockConnectionDBus::SetVoicemailCount(int count)
+{
+    mConnection->setVoicemailCount(count);
+}
+
+void MockConnectionDBus::SetEmergencyNumbers(const QStringList &numbers)
+{
+    mConnection->setEmergencyNumbers(numbers);
+}
+
+QString MockConnectionDBus::Serial()
+{
+    return mConnection->serial();
+}

=== modified file 'tests/common/mock/mockconnectiondbus.h'
--- handler/tests/mock/mockconnectiondbus.h	2014-03-20 21:03:26 +0000
+++ tests/common/mock/mockconnectiondbus.h	2015-03-19 23:42:32 +0000
@@ -36,9 +36,23 @@
     QString PlaceCall(const QVariantMap &properties);
     void HangupCall(const QString &callerId);
     void SetCallState(const QString &phoneNumber, const QString &state);
+    void SetOnline(bool online);
+    void SetPresence(const QString &status, const QString &statusMessage);
+
+    // voicemail stuff
+    void SetVoicemailIndicator(bool active);
+    void SetVoicemailNumber(const QString &number);
+    void SetVoicemailCount(int count);
+
+    // emergency numbers stuff
+    void SetEmergencyNumbers(const QStringList &numbers);
+
+    // USSD stuff
+    QString Serial();
 
 Q_SIGNALS:
     // signals that will be relayed into the bus
+    void MessageRead(const QString &messageId);
     void MessageSent(const QString &mesasge, const QVariantMap &properties);
     void CallReceived(const QString &callerId);
     void CallEnded(const QString &callerId);

=== modified file 'tests/common/mock/protocol.cpp'
--- handler/tests/mock/protocol.cpp	2014-01-07 19:53:45 +0000
+++ tests/common/mock/protocol.cpp	2015-03-19 23:42:32 +0000
@@ -30,6 +30,11 @@
                                  Tp::RequestableChannelClassSpec::audioCall());
 
     setCreateConnectionCallback(memFun(this, &Protocol::createConnection));
+
+    Tp::ProtocolParameterList parameters;
+    Tp::ProtocolParameter parameter("modem-objpath", "s", 0);
+    parameters << parameter;
+    setParameters(parameters);
 }
 
 Tp::BaseConnectionPtr Protocol::createConnection(const QVariantMap &parameters, Tp::DBusError *error) {

=== modified file 'tests/common/mock/speakeriface.h'
--- handler/tests/mock/speakeriface.h	2014-03-13 17:11:09 +0000
+++ tests/common/mock/speakeriface.h	2015-03-19 23:42:32 +0000
@@ -1,19 +1,20 @@
 /**
- * Copyright (C) 2013 Canonical, Ltd.
+ * Copyright (C) 2013-2015 Canonical, Ltd.
  *
  * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License version 3, as published by
+ * the terms of the GNU General Public License version 3, as published by
  * the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
  * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
+ * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
  */
 
 #ifndef OFONOSPEAKERIFACE_H

=== modified file 'tests/common/mock/textchannel.cpp'
--- handler/tests/mock/textchannel.cpp	2014-01-07 19:53:45 +0000
+++ tests/common/mock/textchannel.cpp	2015-03-19 23:42:32 +0000
@@ -36,20 +36,24 @@
     return argument;
 }
 
-MockTextChannel::MockTextChannel(MockConnection *conn, QString phoneNumber, uint targetHandle, QObject *parent):
+MockTextChannel::MockTextChannel(MockConnection *conn, QStringList recipients, uint targetHandle, QObject *parent):
     QObject(parent),
     mConnection(conn),
-    mPhoneNumber(phoneNumber),
+    mRecipients(recipients),
     mTargetHandle(targetHandle),
     mMessageCounter(1)
 {
     qDBusRegisterMetaType<AttachmentStruct>();
     qDBusRegisterMetaType<AttachmentList>();
 
+    Tp::HandleType type = recipients.count() > 1 ? Tp::HandleTypeNone : Tp::HandleTypeContact;
+
+
     Tp::BaseChannelPtr baseChannel = Tp::BaseChannel::create(mConnection,
                                                              TP_QT_IFACE_CHANNEL_TYPE_TEXT,
                                                              targetHandle,
-                                                             Tp::HandleTypeContact);
+                                                             type);
+    mBaseChannel = baseChannel;
     Tp::BaseChannelTextTypePtr textType = Tp::BaseChannelTextType::create(baseChannel.data());
     baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(textType));
 
@@ -67,9 +71,15 @@
                                                           deliveryReportingSupport);
 
     mMessagesIface->setSendMessageCallback(Tp::memFun(this,&MockTextChannel::sendMessage));
-
     baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMessagesIface));
-    mBaseChannel = baseChannel;
+
+    // group stuff
+    mGroupIface = Tp::BaseChannelGroupInterface::create(Tp::ChannelGroupFlagCanAdd, conn->selfHandle());
+    mGroupIface->setAddMembersCallback(Tp::memFun(this,&MockTextChannel::onAddMembers));
+    mGroupIface->setRemoveMembersCallback(Tp::memFun(this,&MockTextChannel::onRemoveMembers));
+    baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mGroupIface));
+    addMembers(recipients);
+
     mTextChannel = Tp::BaseChannelTextTypePtr::dynamicCast(mBaseChannel->interface(TP_QT_IFACE_CHANNEL_TYPE_TEXT));
     mTextChannel->setMessageAcknowledgedCallback(Tp::memFun(this,&MockTextChannel::messageAcknowledged));
     QObject::connect(mBaseChannel.data(), SIGNAL(closed()), this, SLOT(deleteLater()));
@@ -101,11 +111,20 @@
     QString messageText = body["content"].variant().toString();
     QVariantMap properties;
     properties["SentTime"] = QDateTime::currentDateTime().toString(Qt::ISODate);
-    properties["Recipients"] = QStringList() << mPhoneNumber;
+    properties["Recipients"] = mRecipients;
     properties["Id"] = id;
 
     Q_EMIT messageSent(messageText, properties);
 
+    QTimer *deliveryReportTimer = new QTimer(this);
+    deliveryReportTimer->setSingleShot(true);
+    deliveryReportTimer->setInterval(100);
+    connect(deliveryReportTimer, &QTimer::timeout, [id, deliveryReportTimer, this] {
+        this->placeDeliveryReport(id, "sent");
+        deliveryReportTimer->deleteLater();
+    });
+    deliveryReportTimer->start();
+
     return id;
 }
 
@@ -125,7 +144,8 @@
     Tp::MessagePartList partList;
     Tp::MessagePart header;
     header["message-sender"] = QDBusVariant(mTargetHandle);
-    header["message-sender-id"] = QDBusVariant(mPhoneNumber);
+    // FIXME: fix it
+    header["message-sender-id"] = QDBusVariant(mRecipients.first());
     header["message-type"] = QDBusVariant(Tp::ChannelTextMessageTypeDeliveryReport);
     header["delivery-status"] = QDBusVariant(delivery_status);
     header["delivery-token"] = QDBusVariant(messageId);
@@ -145,7 +165,7 @@
     header["message-token"] = QDBusVariant(info["SentTime"].toString() +"-" + QString::number(mMessageCounter++));
     header["message-received"] = QDBusVariant(QDateTime::fromString(info["SentTime"].toString(), Qt::ISODate).toTime_t());
     header["message-sender"] = QDBusVariant(mTargetHandle);
-    header["message-sender-id"] = QDBusVariant(mPhoneNumber);
+    header["message-sender-id"] = QDBusVariant(mRecipients.first());
     header["message-type"] = QDBusVariant(Tp::ChannelTextMessageTypeNormal);
     partList << header << body;
 
@@ -198,3 +218,45 @@
 
     mTextChannel->addReceivedMessage(message);
 }
+
+void MockTextChannel::addMembers(QStringList recipients)
+{
+    Tp::UIntList handles;
+    Q_FOREACH(const QString &recipient, recipients) {
+        uint handle = mConnection->ensureHandle(recipient);
+        handles << handle;
+        if (!mRecipients.contains(recipient)) {
+            mRecipients << recipient;
+        }
+        if (!mMembers.contains(handle)) {
+            mMembers << handle;
+        }
+    }
+    mGroupIface->addMembers(handles, recipients);
+}
+
+QStringList MockTextChannel::recipients() const
+{
+    return mRecipients;
+}
+
+Tp::UIntList MockTextChannel::members()
+{
+    return mMembers;
+}
+
+void MockTextChannel::onAddMembers(const Tp::UIntList &handles, const QString &message, Tp::DBusError *error)
+{
+    addMembers(mConnection->inspectHandles(Tp::HandleTypeContact, handles, error));
+}
+
+void MockTextChannel::onRemoveMembers(const Tp::UIntList &handles, const QString &message, Tp::DBusError *error)
+{
+    Q_FOREACH(uint handle, handles) {
+        Q_FOREACH(const QString &recipient, mConnection->inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error)) {
+            mRecipients.removeAll(recipient);
+        }
+        mMembers.removeAll(handle);
+    }
+    mGroupIface->removeMembers(handles);
+}

=== modified file 'tests/common/mock/textchannel.h'
--- handler/tests/mock/textchannel.h	2014-01-07 19:53:45 +0000
+++ tests/common/mock/textchannel.h	2015-03-19 23:42:32 +0000
@@ -35,13 +35,19 @@
 {
     Q_OBJECT
 public:
-    MockTextChannel(MockConnection *conn, QString phoneNumber, uint targetHandle, QObject *parent = 0);
+    MockTextChannel(MockConnection *conn, QStringList recipients, uint targetHandle, QObject *parent = 0);
     QString sendMessage(const Tp::MessagePartList& message, uint flags, Tp::DBusError* error);
     void messageReceived(const QString & message, const QVariantMap &info);
     Tp::BaseChannelPtr baseChannel();
     void messageAcknowledged(const QString &id);
     void mmsReceived(const QString &id, const QVariantMap &properties);
 
+    void addMembers(QStringList recipients);
+    QStringList recipients() const;
+    Tp::UIntList members();
+    void onAddMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error);
+    void onRemoveMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error);
+
 public Q_SLOTS:
     void placeDeliveryReport(const QString &messageId, const QString &status);
 
@@ -52,12 +58,14 @@
 private:
     ~MockTextChannel();
     Tp::BaseChannelPtr mBaseChannel;
-    QString mPhoneNumber;
+    QStringList mRecipients;
     MockConnection *mConnection;
     uint mTargetHandle;
     Tp::BaseChannelMessagesInterfacePtr mMessagesIface;
+    Tp::BaseChannelGroupInterfacePtr mGroupIface;
     Tp::BaseChannelTextTypePtr mTextChannel;
     uint mMessageCounter;
+    Tp::UIntList mMembers;
 };
 
 #endif // MOCKTEXTCHANNEL_H

=== added file 'tests/common/mock/ussdiface.cpp'
--- tests/common/mock/ussdiface.cpp	1970-01-01 00:00:00 +0000
+++ tests/common/mock/ussdiface.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,312 @@
+/**
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
+ */
+
+#include <QDebug>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBusObject>
+
+#include "ussdiface.h"
+
+// Conn.I.USSD
+BaseConnectionUSSDInterface::Adaptee::Adaptee(BaseConnectionUSSDInterface *interface)
+    : QObject(interface),
+      mInterface(interface)
+{
+}
+
+
+struct TP_QT_NO_EXPORT BaseConnectionUSSDInterface::Private {
+    Private(BaseConnectionUSSDInterface *parent)
+        : adaptee(new BaseConnectionUSSDInterface::Adaptee(parent)) {
+    }
+    QString state;
+    QString serial;
+    InitiateCallback initiateCB;
+    RespondCallback respondCB;
+    CancelCallback cancelCB;
+    BaseConnectionUSSDInterface::Adaptee *adaptee;
+};
+
+BaseConnectionUSSDInterface::Adaptee::~Adaptee()
+{
+}
+
+void BaseConnectionUSSDInterface::Adaptee::initiate(const QString &command, const ConnectionInterfaceUSSDAdaptor::InitiateContextPtr &context)
+{
+    if (!mInterface->mPriv->initiateCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    mInterface->mPriv->initiateCB(command, &error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    context->setFinished();
+}
+
+void BaseConnectionUSSDInterface::Adaptee::respond(const QString &reply, const ConnectionInterfaceUSSDAdaptor::RespondContextPtr &context)
+{
+    if (!mInterface->mPriv->respondCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    mInterface->mPriv->respondCB(reply, &error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    context->setFinished();
+}
+
+void BaseConnectionUSSDInterface::Adaptee::cancel(const ConnectionInterfaceUSSDAdaptor::CancelContextPtr &context)
+{
+    if (!mInterface->mPriv->cancelCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    mInterface->mPriv->cancelCB(&error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    context->setFinished();
+}
+
+BaseConnectionUSSDInterface::BaseConnectionUSSDInterface()
+    : AbstractConnectionInterface(TP_QT_IFACE_CONNECTION_USSD),
+      mPriv(new Private(this))
+{
+}
+
+BaseConnectionUSSDInterface::~BaseConnectionUSSDInterface()
+{
+    delete mPriv;
+}
+
+void BaseConnectionUSSDInterface::setInitiateCallback(const InitiateCallback &cb)
+{
+    mPriv->initiateCB = cb;
+}
+
+void BaseConnectionUSSDInterface::setRespondCallback(const RespondCallback &cb)
+{
+    mPriv->respondCB = cb;
+}
+
+void BaseConnectionUSSDInterface::setCancelCallback(const CancelCallback &cb)
+{
+    mPriv->cancelCB = cb;
+}
+
+QString BaseConnectionUSSDInterface::state() const
+{
+    return mPriv->state;
+}
+
+void BaseConnectionUSSDInterface::setSerial(const QString &serial) const
+{
+    mPriv->serial = serial;
+}
+
+
+QString BaseConnectionUSSDInterface::serial() const
+{
+    return mPriv->serial;
+}
+
+void BaseConnectionUSSDInterface::StateChanged(const QString &state)
+{
+    mPriv->state = state;
+    Q_EMIT mPriv->adaptee->stateChanged(state);
+}
+
+void BaseConnectionUSSDInterface::InitiateUSSDComplete(const QString &ussdResp)
+{
+    Q_EMIT mPriv->adaptee->initiateUSSDComplete(ussdResp);
+}
+
+void BaseConnectionUSSDInterface::RespondComplete(bool success, const QString &ussdResp)
+{
+    Q_EMIT mPriv->adaptee->respondComplete(success, ussdResp);
+}
+
+void BaseConnectionUSSDInterface::BarringComplete(const QString &ssOp, const QString &cbService, const QVariantMap &cbMap)
+{
+    Q_EMIT mPriv->adaptee->barringComplete(ssOp, cbService, cbMap);
+}
+
+void BaseConnectionUSSDInterface::ForwardingComplete(const QString &ssOp, const QString &cfService, const QVariantMap &cfMap)
+{
+    Q_EMIT mPriv->adaptee->forwardingComplete(ssOp, cfService, cfMap);
+}
+
+void BaseConnectionUSSDInterface::WaitingComplete(const QString &ssOp, const QVariantMap &cwMap)
+{
+    Q_EMIT mPriv->adaptee->waitingComplete(ssOp, cwMap);
+}
+
+void BaseConnectionUSSDInterface::CallingLinePresentationComplete(const QString &ssOp, const QString &status)
+{
+    Q_EMIT mPriv->adaptee->callingLinePresentationComplete(ssOp, status);
+}
+
+void BaseConnectionUSSDInterface::ConnectedLinePresentationComplete(const QString &ssOp, const QString &status)
+{
+    Q_EMIT mPriv->adaptee->connectedLinePresentationComplete(ssOp, status);
+}
+
+void BaseConnectionUSSDInterface::CallingLineRestrictionComplete(const QString &ssOp, const QString &status)
+{
+    Q_EMIT mPriv->adaptee->callingLineRestrictionComplete(ssOp, status);
+}
+
+void BaseConnectionUSSDInterface::ConnectedLineRestrictionComplete(const QString &ssOp, const QString &status)
+{
+    Q_EMIT mPriv->adaptee->connectedLineRestrictionComplete(ssOp, status);
+}
+
+void BaseConnectionUSSDInterface::InitiateFailed()
+{
+    Q_EMIT mPriv->adaptee->initiateFailed();
+}
+
+void BaseConnectionUSSDInterface::NotificationReceived(const QString &message)
+{
+    Q_EMIT mPriv->adaptee->notificationReceived(message);
+}
+
+void BaseConnectionUSSDInterface::RequestReceived(const QString &message)
+{
+    Q_EMIT mPriv->adaptee->requestReceived(message);
+}
+
+
+QVariantMap BaseConnectionUSSDInterface::immutableProperties() const
+{
+    QVariantMap map;
+    return map;
+}
+
+void BaseConnectionUSSDInterface::createAdaptor()
+{
+    (void) new ConnectionInterfaceUSSDAdaptor(dbusObject()->dbusConnection(),
+            mPriv->adaptee, dbusObject());
+}
+
+
+ConnectionInterfaceUSSDAdaptor::ConnectionInterfaceUSSDAdaptor(const QDBusConnection& bus, QObject* adaptee, QObject* parent)
+    : Tp::AbstractAdaptor(bus, adaptee, parent)
+{
+    connect(adaptee, SIGNAL(notificationReceived(const QString &)), SIGNAL(NotificationReceived(const QString &)));
+    connect(adaptee, SIGNAL(requestReceived(const QString &)), SIGNAL(RequestReceived(const QString &)));
+
+    connect(adaptee, SIGNAL(initiateUSSDComplete(const QString &)), SIGNAL(InitiateUSSDComplete(const QString &)));
+
+    connect(adaptee, SIGNAL(barringComplete(const QString &, const QString &, const QVariantMap &)), 
+        SIGNAL(BarringComplete(const QString &, const QString &, const QVariantMap &)));
+
+    connect(adaptee, SIGNAL(forwardingComplete(const QString &, const QString &, const QVariantMap &)), 
+        SIGNAL(ForwardingComplete(const QString &, const QString &, const QVariantMap &)));
+
+    connect(adaptee, SIGNAL(waitingComplete(const QString &, const QVariantMap &)), 
+        SIGNAL(WaitingComplete(const QString &, const QVariantMap &)));
+
+    connect(adaptee, SIGNAL(callingLinePresentationComplete(const QString &, const QString &)), 
+        SIGNAL(CallingLinePresentationComplete(const QString &, const QString &)));
+
+    connect(adaptee, SIGNAL(connectedLinePresentationComplete(const QString &, const QString &)), 
+        SIGNAL(ConnectedLinePresentationComplete(const QString &, const QString &)));
+
+    connect(adaptee, SIGNAL(callingLineRestrictionComplete(const QString &, const QString &)), 
+        SIGNAL(CallingLineRestrictionComplete(const QString &, const QString &)));
+
+    connect(adaptee, SIGNAL(connectedLineRestrictionComplete(const QString &, const QString &)), 
+        SIGNAL(ConnectedLineRestrictionComplete(const QString &, const QString &)));
+
+    connect(adaptee, SIGNAL(initiateFailed()), SIGNAL(InitiateFailed()));
+
+    connect(adaptee, SIGNAL(stateChanged(const QString&)), SIGNAL(StateChanged(const QString&)));
+
+    connect(adaptee, SIGNAL(respondComplete(bool, const QString &)), SIGNAL(RespondComplete(bool, const QString &)));
+}
+
+ConnectionInterfaceUSSDAdaptor::~ConnectionInterfaceUSSDAdaptor()
+{
+}
+
+void ConnectionInterfaceUSSDAdaptor::Initiate(const QString &command, const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("initiate(const QString &,ConnectionInterfaceUSSDAdaptor::InitiateContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return;
+    }
+
+    InitiateContextPtr ctx = InitiateContextPtr(
+            new Tp::MethodInvocationContext< >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "initiate",
+        Q_ARG(QString, command),
+        Q_ARG(ConnectionInterfaceUSSDAdaptor::InitiateContextPtr, ctx));
+    return;
+}
+
+void ConnectionInterfaceUSSDAdaptor::Respond(const QString &reply, const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("respond(QConnectionInterfaceUSSDAdaptor::RespondContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return;
+    }
+
+    RespondContextPtr ctx = RespondContextPtr(
+            new Tp::MethodInvocationContext< >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "respond",
+        Q_ARG(QString, reply),
+        Q_ARG(ConnectionInterfaceUSSDAdaptor::RespondContextPtr, ctx));
+    return;
+}
+
+void ConnectionInterfaceUSSDAdaptor::Cancel(const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("cancel(ConnectionInterfaceUSSDAdaptor::CancelContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return;
+    }
+
+    CancelContextPtr ctx = CancelContextPtr(
+            new Tp::MethodInvocationContext< >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "cancel",
+        Q_ARG(ConnectionInterfaceUSSDAdaptor::CancelContextPtr, ctx));
+    return;
+}
+
+QString ConnectionInterfaceUSSDAdaptor::Serial() const
+{
+    return qvariant_cast< QString >(adaptee()->property("serial"));
+}
+
+
+QString ConnectionInterfaceUSSDAdaptor::State() const
+{
+    return qvariant_cast< QString >(adaptee()->property("state"));
+}
+

=== added file 'tests/common/mock/ussdiface.h'
--- tests/common/mock/ussdiface.h	1970-01-01 00:00:00 +0000
+++ tests/common/mock/ussdiface.h	2015-03-19 23:42:32 +0000
@@ -0,0 +1,244 @@
+/**
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
+ */
+
+#ifndef OFONOUSSDIFACE_H
+#define OFONOUSSDIFACE_H
+
+// telepathy-qt
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/BaseConnection>
+#include <TelepathyQt/AbstractAdaptor>
+#include <TelepathyQt/DBusError>
+#include <TelepathyQt/Callbacks>
+
+class BaseConnectionUSSDInterface;
+
+typedef Tp::SharedPtr<BaseConnectionUSSDInterface> BaseConnectionUSSDInterfacePtr;
+
+#define TP_QT_IFACE_CONNECTION_USSD "com.canonical.Telephony.USSD"
+
+class TP_QT_EXPORT BaseConnectionUSSDInterface : public Tp::AbstractConnectionInterface
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(BaseConnectionUSSDInterface)
+
+public:
+    static BaseConnectionUSSDInterfacePtr create() {
+        return BaseConnectionUSSDInterfacePtr(new BaseConnectionUSSDInterface());
+    }
+    template<typename BaseConnectionUSSDInterfaceSubclass>
+    static Tp::SharedPtr<BaseConnectionUSSDInterfaceSubclass> create() {
+        return Tp::SharedPtr<BaseConnectionUSSDInterfaceSubclass>(
+                   new BaseConnectionUSSDInterfaceSubclass());
+    }
+    QVariantMap immutableProperties() const;
+    virtual ~BaseConnectionUSSDInterface();
+
+    typedef Tp::Callback2<void, const QString&, Tp::DBusError*> InitiateCallback;
+    void setInitiateCallback(const InitiateCallback &cb);
+
+    typedef Tp::Callback2<void, const QString&, Tp::DBusError*> RespondCallback;
+    void setRespondCallback(const RespondCallback &cb);
+
+    typedef Tp::Callback1<void, Tp::DBusError*> CancelCallback;
+    void setCancelCallback(const CancelCallback &cb);
+
+
+    QString state() const;
+    QString serial() const;
+
+public Q_SLOTS:
+    void NotificationReceived(const QString &message);
+    void RequestReceived(const QString &message);
+
+    void InitiateUSSDComplete(const QString &ussdResp);
+    void RespondComplete(bool success, const QString &ussdResp);
+    void BarringComplete(const QString &ssOp, const QString &cbService, const QVariantMap &cbMap);
+    void ForwardingComplete(const QString &ssOp, const QString &cfService, const QVariantMap &cfMap);
+    void WaitingComplete(const QString &ssOp, const QVariantMap &cwMap);
+    void CallingLinePresentationComplete(const QString &ssOp, const QString &status);
+    void ConnectedLinePresentationComplete(const QString &ssOp, const QString &status);
+    void CallingLineRestrictionComplete(const QString &ssOp, const QString &status);
+    void ConnectedLineRestrictionComplete(const QString &ssOp, const QString &status);
+    void InitiateFailed();
+    void StateChanged(const QString &state);
+
+    void setSerial(const QString& serial) const;
+protected:
+    BaseConnectionUSSDInterface();
+
+private:
+    void createAdaptor();
+
+    class Adaptee;
+    friend class Adaptee;
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+
+class TP_QT_EXPORT ConnectionInterfaceUSSDAdaptor : public Tp::AbstractAdaptor
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", TP_QT_IFACE_CONNECTION_USSD)
+    Q_CLASSINFO("D-Bus Introspection", ""
+"  <interface name=\"com.canonical.Telephony.USSD\">\n"
+"    <property access=\"read\" type=\"s\" name=\"State\"/>\n"
+"    <property access=\"read\" type=\"s\" name=\"Serial\"/>\n"
+"    <method name=\"Initiate\">\n"
+"      <arg direction=\"in\" type=\"s\" name=\"command\"/>\n"
+"    </method>\n"
+"    <method name=\"Respond\">\n"
+"      <arg direction=\"in\" type=\"s\" name=\"reply\"/>\n"
+"    </method>\n"
+"    <method name=\"Cancel\" />\n"
+"    <signal name=\"NotificationReceived\">\n"
+"      <arg type=\"s\" name=\"message\"/>\n"
+"    </signal>\n"
+"    <signal name=\"RequestReceived\">\n"
+"      <arg type=\"s\" name=\"message\"/>\n"
+"    </signal>\n"
+"    <signal name=\"InitiateUSSDComplete\">\n"
+"      <arg type=\"s\" name=\"response\"/>\n"
+"    </signal>\n"
+"    <signal name=\"RespondComplete\">\n"
+"      <arg type=\"b\" name=\"success\"/>\n"
+"      <arg type=\"s\" name=\"response\"/>\n"
+"    </signal>\n"
+"    <signal name=\"BarringComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"s\" name=\"cbService\"/>\n"
+"      <arg type=\"a{sv}\" name=\"cbMap\"/>\n"
+"    </signal>\n"
+"    <signal name=\"ForwardingComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"s\" name=\"cfService\"/>\n"
+"      <arg type=\"a{sv}\" name=\"cfMap\"/>\n"
+"    </signal>\n"
+"    <signal name=\"WaitingComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"a{sv}\" name=\"cwMap\"/>\n"
+"    </signal>\n"
+"    <signal name=\"CallingLinePresentationComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"s\" name=\"status\"/>\n"
+"    </signal>\n"
+"    <signal name=\"ConnectedLinePresentationComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"s\" name=\"status\"/>\n"
+"    </signal>\n"
+"    <signal name=\"CallingLineRestrictionComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"s\" name=\"status\"/>\n"
+"    </signal>\n"
+"    <signal name=\"ConnectedLineRestrictionComplete\">\n"
+"      <arg type=\"s\" name=\"ssOp\"/>\n"
+"      <arg type=\"s\" name=\"status\"/>\n"
+"    </signal>\n"
+"    <signal name=\"InitiateFailed\" />\n"
+"    <signal name=\"StateChanged\">\n"
+"      <arg type=\"s\" name=\"state\"/>\n"
+"    </signal>\n"
+"  </interface>\n"
+"")
+    Q_PROPERTY(QString State READ State)
+    Q_PROPERTY(QString Serial READ Serial)
+
+public:
+    ConnectionInterfaceUSSDAdaptor(const QDBusConnection& dbusConnection, QObject* adaptee, QObject* parent);
+    virtual ~ConnectionInterfaceUSSDAdaptor();
+
+    typedef Tp::MethodInvocationContextPtr< > InitiateContextPtr;
+    typedef Tp::MethodInvocationContextPtr< > RespondContextPtr;
+    typedef Tp::MethodInvocationContextPtr< > CancelContextPtr;
+
+public Q_SLOTS: // METHODS
+    void Initiate(const QString &command, const QDBusMessage& dbusMessage);
+    void Respond(const QString &reply, const QDBusMessage& dbusMessage);
+    void Cancel(const QDBusMessage& dbusMessage);
+
+    QString State() const;
+    QString Serial() const;
+
+Q_SIGNALS: // SIGNALS
+    void NotificationReceived(const QString &message);
+    void RequestReceived(const QString &message);
+
+    void InitiateUSSDComplete(const QString &ussdResp);
+    void RespondComplete(bool success, const QString &ussdResp);
+    void BarringComplete(const QString &ssOp, const QString &cbService, const QVariantMap &cbMap);
+    void ForwardingComplete(const QString &ssOp, const QString &cfService, const QVariantMap &cfMap);
+    void WaitingComplete(const QString &ssOp, const QVariantMap &cwMap);
+    void CallingLinePresentationComplete(const QString &ssOp, const QString &status);
+    void ConnectedLinePresentationComplete(const QString &ssOp, const QString &status);
+    void CallingLineRestrictionComplete(const QString &ssOp, const QString &status);
+    void ConnectedLineRestrictionComplete(const QString &ssOp, const QString &status);
+    void InitiateFailed();
+
+    void StateChanged(const QString &state);
+};
+
+
+class TP_QT_NO_EXPORT BaseConnectionUSSDInterface::Adaptee : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QString state READ state)
+    Q_PROPERTY(QString serial READ serial)
+
+public:
+    Adaptee(BaseConnectionUSSDInterface *interface);
+    ~Adaptee();
+    QString state() const
+    {
+        return mInterface->state();
+    }
+    QString serial() const
+    {
+        return mInterface->serial();
+    }
+
+
+private Q_SLOTS:
+    void initiate(const QString &command, const ConnectionInterfaceUSSDAdaptor::InitiateContextPtr &context);
+    void respond(const QString &reply, const ConnectionInterfaceUSSDAdaptor::RespondContextPtr &context);
+    void cancel(const ConnectionInterfaceUSSDAdaptor::CancelContextPtr &context);
+
+Q_SIGNALS:
+    void notificationReceived(const QString &message);
+    void requestReceived(const QString &message);
+
+    void initiateUSSDComplete(const QString &ussdResp);
+    void barringComplete(const QString &ssOp, const QString &cbService, const QVariantMap &cbMap);
+    void forwardingComplete(const QString &ssOp, const QString &cfService, const QVariantMap &cfMap);
+    void waitingComplete(const QString &ssOp, const QVariantMap &cwMap);
+    void callingLinePresentationComplete(const QString &ssOp, const QString &status);
+    void connectedLinePresentationComplete(const QString &ssOp, const QString &status);
+    void callingLineRestrictionComplete(const QString &ssOp, const QString &status);
+    void connectedLineRestrictionComplete(const QString &ssOp, const QString &status);
+    void initiateFailed();
+    void respondComplete(bool success, const QString &response);
+
+    void stateChanged(const QString &state);
+
+public:
+    BaseConnectionUSSDInterface *mInterface;
+};
+
+#endif

=== added file 'tests/common/mock/voicemailiface.cpp'
--- tests/common/mock/voicemailiface.cpp	1970-01-01 00:00:00 +0000
+++ tests/common/mock/voicemailiface.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,202 @@
+/**
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
+ */
+
+#include <QDebug>
+
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/DBusObject>
+
+#include "voicemailiface.h"
+
+// Conn.I.Voicemail
+BaseConnectionVoicemailInterface::Adaptee::Adaptee(BaseConnectionVoicemailInterface *interface)
+    : QObject(interface),
+      mInterface(interface)
+{
+}
+
+
+struct TP_QT_NO_EXPORT BaseConnectionVoicemailInterface::Private {
+    Private(BaseConnectionVoicemailInterface *parent)
+        : adaptee(new BaseConnectionVoicemailInterface::Adaptee(parent)) {
+    }
+    VoicemailCountCallback voicemailCountCB;
+    VoicemailNumberCallback voicemailNumberCB;
+    VoicemailIndicatorCallback voicemailIndicatorCB;
+    BaseConnectionVoicemailInterface::Adaptee *adaptee;
+};
+
+BaseConnectionVoicemailInterface::Adaptee::~Adaptee()
+{
+}
+
+void BaseConnectionVoicemailInterface::Adaptee::voicemailIndicator(const ConnectionInterfaceVoicemailAdaptor::VoicemailIndicatorContextPtr &context)
+{
+    if (!mInterface->mPriv->voicemailIndicatorCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    bool active = mInterface->mPriv->voicemailIndicatorCB(&error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    context->setFinished(active);
+}
+
+void BaseConnectionVoicemailInterface::Adaptee::voicemailNumber(const ConnectionInterfaceVoicemailAdaptor::VoicemailNumberContextPtr &context)
+{
+    if (!mInterface->mPriv->voicemailNumberCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    QString number = mInterface->mPriv->voicemailNumberCB(&error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    context->setFinished(number);
+}
+
+void BaseConnectionVoicemailInterface::Adaptee::voicemailCount(const ConnectionInterfaceVoicemailAdaptor::VoicemailCountContextPtr &context)
+{
+    if (!mInterface->mPriv->voicemailCountCB.isValid()) {
+        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented"));
+        return;
+    }
+    Tp::DBusError error;
+    uint count = mInterface->mPriv->voicemailCountCB(&error);
+    if (error.isValid()) {
+        context->setFinishedWithError(error.name(), error.message());
+        return;
+    }
+    context->setFinished(count);
+}
+
+
+BaseConnectionVoicemailInterface::BaseConnectionVoicemailInterface()
+    : AbstractConnectionInterface(TP_QT_IFACE_CONNECTION_VOICEMAIL),
+      mPriv(new Private(this))
+{
+}
+
+BaseConnectionVoicemailInterface::~BaseConnectionVoicemailInterface()
+{
+    delete mPriv;
+}
+
+void BaseConnectionVoicemailInterface::setVoicemailIndicatorCallback(const VoicemailIndicatorCallback &cb)
+{
+    mPriv->voicemailIndicatorCB = cb;
+}
+
+void BaseConnectionVoicemailInterface::setVoicemailNumberCallback(const VoicemailNumberCallback &cb)
+{
+    mPriv->voicemailNumberCB = cb;
+}
+
+void BaseConnectionVoicemailInterface::setVoicemailCountCallback(const VoicemailCountCallback &cb)
+{
+    mPriv->voicemailCountCB = cb;
+}
+
+void BaseConnectionVoicemailInterface::setVoicemailCount(int count)
+{
+    Q_EMIT mPriv->adaptee->voicemailCountChanged(uint(count));
+}
+
+void BaseConnectionVoicemailInterface::setVoicemailNumber(const QString &voicemailNumber)
+{
+    Q_EMIT mPriv->adaptee->voicemailNumberChanged(voicemailNumber);
+}
+
+void BaseConnectionVoicemailInterface::setVoicemailIndicator(bool active)
+{
+    Q_EMIT mPriv->adaptee->voicemailIndicatorChanged(active);
+}
+
+QVariantMap BaseConnectionVoicemailInterface::immutableProperties() const
+{
+    QVariantMap map;
+    return map;
+}
+
+void BaseConnectionVoicemailInterface::createAdaptor()
+{
+    (void) new ConnectionInterfaceVoicemailAdaptor(dbusObject()->dbusConnection(),
+            mPriv->adaptee, dbusObject());
+}
+
+
+ConnectionInterfaceVoicemailAdaptor::ConnectionInterfaceVoicemailAdaptor(const QDBusConnection& bus, QObject* adaptee, QObject* parent)
+    : Tp::AbstractAdaptor(bus, adaptee, parent)
+{
+    connect(adaptee, SIGNAL(voicemailCountChanged(uint)), SIGNAL(VoicemailCountChanged(uint)));
+    connect(adaptee, SIGNAL(voicemailIndicatorChanged(bool)), SIGNAL(VoicemailIndicatorChanged(bool)));
+    connect(adaptee, SIGNAL(voicemailNumberChanged(QString)), SIGNAL(VoicemailNumberChanged(QString)));
+}
+
+ConnectionInterfaceVoicemailAdaptor::~ConnectionInterfaceVoicemailAdaptor()
+{
+}
+
+bool ConnectionInterfaceVoicemailAdaptor::VoicemailIndicator(const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("voicemailIndicator(ConnectionInterfaceVoicemailAdaptor::VoicemailIndicatorContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return bool();
+    }
+
+    VoicemailIndicatorContextPtr ctx = VoicemailIndicatorContextPtr(
+            new Tp::MethodInvocationContext< bool >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "voicemailIndicator",
+        Q_ARG(ConnectionInterfaceVoicemailAdaptor::VoicemailIndicatorContextPtr, ctx));
+    return bool();
+}
+
+QString ConnectionInterfaceVoicemailAdaptor::VoicemailNumber(const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("voicemailNumber(ConnectionInterfaceVoicemailAdaptor::VoicemailNumberContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return QString();
+    }
+
+    VoicemailNumberContextPtr ctx = VoicemailNumberContextPtr(
+            new Tp::MethodInvocationContext< QString >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "voicemailNumber",
+        Q_ARG(ConnectionInterfaceVoicemailAdaptor::VoicemailNumberContextPtr, ctx));
+    return QString();
+}
+
+uint ConnectionInterfaceVoicemailAdaptor::VoicemailCount(const QDBusMessage& dbusMessage)
+{
+    if (!adaptee()->metaObject()->indexOfMethod("voicemailCount(ConnectionInterfaceVoicemailAdaptor::VoicemailCountContextPtr)") == -1) {
+        dbusConnection().send(dbusMessage.createErrorReply(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")));
+        return uint();
+    }
+
+    VoicemailCountContextPtr ctx = VoicemailCountContextPtr(
+            new Tp::MethodInvocationContext< uint >(dbusConnection(), dbusMessage));
+    QMetaObject::invokeMethod(adaptee(), "voicemailCount",
+        Q_ARG(ConnectionInterfaceVoicemailAdaptor::VoicemailCountContextPtr, ctx));
+    return uint();
+}
+

=== added file 'tests/common/mock/voicemailiface.h'
--- tests/common/mock/voicemailiface.h	1970-01-01 00:00:00 +0000
+++ tests/common/mock/voicemailiface.h	2015-03-19 23:42:32 +0000
@@ -0,0 +1,150 @@
+/**
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 3, as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
+            Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
+ */
+
+#ifndef OFONOVOICEMAILIFACE_H
+#define OFONOVOICEMAILIFACE_H
+
+// telepathy-qt
+#include <TelepathyQt/Constants>
+#include <TelepathyQt/BaseConnection>
+#include <TelepathyQt/AbstractAdaptor>
+#include <TelepathyQt/DBusError>
+#include <TelepathyQt/Callbacks>
+
+class BaseConnectionVoicemailInterface;
+
+typedef Tp::SharedPtr<BaseConnectionVoicemailInterface> BaseConnectionVoicemailInterfacePtr;
+
+#define TP_QT_IFACE_CONNECTION_VOICEMAIL "com.canonical.Telephony.Voicemail"
+
+class TP_QT_EXPORT BaseConnectionVoicemailInterface : public Tp::AbstractConnectionInterface
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(BaseConnectionVoicemailInterface)
+
+public:
+    static BaseConnectionVoicemailInterfacePtr create() {
+        return BaseConnectionVoicemailInterfacePtr(new BaseConnectionVoicemailInterface());
+    }
+    template<typename BaseConnectionVoicemailInterfaceSubclass>
+    static Tp::SharedPtr<BaseConnectionVoicemailInterfaceSubclass> create() {
+        return Tp::SharedPtr<BaseConnectionVoicemailInterfaceSubclass>(
+                   new BaseConnectionVoicemailInterfaceSubclass());
+    }
+    QVariantMap immutableProperties() const;
+    virtual ~BaseConnectionVoicemailInterface();
+
+    typedef Tp::Callback1<uint, Tp::DBusError*> VoicemailCountCallback;
+    void setVoicemailCountCallback(const VoicemailCountCallback &cb);
+
+    typedef Tp::Callback1<bool, Tp::DBusError*> VoicemailIndicatorCallback;
+    void setVoicemailIndicatorCallback(const VoicemailIndicatorCallback &cb);
+
+    typedef Tp::Callback1<QString, Tp::DBusError*> VoicemailNumberCallback;
+    void setVoicemailNumberCallback(const VoicemailNumberCallback &cb);
+
+public Q_SLOTS:
+    void setVoicemailCount(int count);
+    void setVoicemailIndicator(bool active);
+    void setVoicemailNumber(const QString &voicemailNumber);
+
+protected:
+    BaseConnectionVoicemailInterface();
+
+private:
+    void createAdaptor();
+
+    class Adaptee;
+    friend class Adaptee;
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+
+class TP_QT_EXPORT ConnectionInterfaceVoicemailAdaptor : public Tp::AbstractAdaptor
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", TP_QT_IFACE_CONNECTION_VOICEMAIL)
+    Q_CLASSINFO("D-Bus Introspection", ""
+"  <interface name=\"com.canonical.Telephony.Voicemail\">\n"
+"    <method name=\"VoicemailIndicator\">\n"
+"      <arg direction=\"out\" type=\"b\" name=\"voicemailIndicator\"/>\n"
+"    </method>\n"
+"    <method name=\"VoicemailNumber\">\n"
+"      <arg direction=\"out\" type=\"s\" name=\"voicemailNumber\"/>\n"
+"    </method>\n"
+"    <method name=\"VoicemailCount\">\n"
+"      <arg direction=\"out\" type=\"u\" name=\"voicemailCount\"/>\n"
+"    </method>\n"
+"    <signal name=\"VoicemailCountChanged\">\n"
+"      <arg type=\"u\" name=\"count\"/>\n"
+"    </signal>\n"
+"    <signal name=\"VoicemailIndicatorChanged\">\n"
+"      <arg type=\"b\" name=\"active\"/>\n"
+"    </signal>\n"
+"    <signal name=\"VoicemailNumberChanged\">\n"
+"      <arg type=\"s\" name=\"voicemailNumber\"/>\n"
+"    </signal>\n"
+"  </interface>\n"
+"")
+
+public:
+    ConnectionInterfaceVoicemailAdaptor(const QDBusConnection& dbusConnection, QObject* adaptee, QObject* parent);
+    virtual ~ConnectionInterfaceVoicemailAdaptor();
+
+    typedef Tp::MethodInvocationContextPtr< bool > VoicemailIndicatorContextPtr;
+    typedef Tp::MethodInvocationContextPtr< QString > VoicemailNumberContextPtr;
+    typedef Tp::MethodInvocationContextPtr< uint > VoicemailCountContextPtr;
+
+public Q_SLOTS: // METHODS
+    bool VoicemailIndicator(const QDBusMessage& dbusMessage);
+    QString VoicemailNumber(const QDBusMessage& dbusMessage);
+    uint VoicemailCount(const QDBusMessage& dbusMessage);
+
+Q_SIGNALS: // SIGNALS
+    void VoicemailCountChanged(uint count);
+    void VoicemailIndicatorChanged(bool active);
+    void VoicemailNumberChanged(const QString &voicemailNumber);
+};
+
+
+class TP_QT_NO_EXPORT BaseConnectionVoicemailInterface::Adaptee : public QObject
+{
+    Q_OBJECT
+
+public:
+    Adaptee(BaseConnectionVoicemailInterface *interface);
+    ~Adaptee();
+
+private Q_SLOTS:
+    void voicemailIndicator(const ConnectionInterfaceVoicemailAdaptor::VoicemailIndicatorContextPtr &context);
+    void voicemailNumber(const ConnectionInterfaceVoicemailAdaptor::VoicemailNumberContextPtr &context);
+    void voicemailCount(const ConnectionInterfaceVoicemailAdaptor::VoicemailCountContextPtr &context);
+
+Q_SIGNALS:
+    void voicemailCountChanged(uint count);
+    void voicemailIndicatorChanged(bool active);
+    void voicemailNumberChanged(const QString &voicemailNumber);
+
+public:
+    BaseConnectionVoicemailInterface *mInterface;
+};
+
+#endif

=== renamed file 'handler/tests/mockcontroller.cpp' => 'tests/common/mockcontroller.cpp'
--- handler/tests/mockcontroller.cpp	2014-03-20 21:03:26 +0000
+++ tests/common/mockcontroller.cpp	2015-03-19 23:42:32 +0000
@@ -21,27 +21,30 @@
 #include "mockcontroller.h"
 #include <QDBusReply>
 
-#define MOCK_SERVICE "com.canonical.MockConnection"
-#define MOCK_OBJECT "/com/canonical/MockConnection"
-#define MOCK_INTERFACE "com.canonical.MockConnection"
-
-MockController::MockController(QObject *parent) :
-    QObject(parent),
-    mMockInterface(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE)
-{
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "MessageSent", this, SIGNAL(messageSent(QString, QVariantMap)));
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "CallReceived", this, SIGNAL(callReceived(QString)));
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "CallEnded", this, SIGNAL(callEnded(QString)));
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "CallStateChanged", this, SIGNAL(callStateChanged(QString,QString,QString)));
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "ConferenceCreated", this, SIGNAL(conferenceCreated(QString)));
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "ChannelMerged", this, SIGNAL(channelMerged(QString)));
-    QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "ChannelSplitted", this, SIGNAL(channelSplitted(QString)));
-}
-
-MockController *MockController::instance()
-{
-    static MockController *self = new MockController();
-    return self;
+static const QString mockService("com.canonical.MockConnection");
+static const QString mockObject("/com/canonical/MockConnection/%1");
+static const QString mockInterface("com.canonical.MockConnection");
+
+MockController::MockController(const QString &protocol, QObject *parent) :
+    QObject(parent), mProtocol(protocol), mMockObject(mockObject.arg(protocol)),
+    mMockInterface(mockService, mockObject.arg(protocol), mockInterface)
+{
+    connect(&mMockInterface, SIGNAL(MessageRead(QString)),
+            this, SIGNAL(messageRead(QString)));
+    connect(&mMockInterface, SIGNAL(MessageSent(QString, QVariantMap)),
+            this, SIGNAL(messageSent(QString, QVariantMap)));
+    connect(&mMockInterface, SIGNAL(CallReceived(QString)),
+            this, SIGNAL(callReceived(QString)));
+    connect(&mMockInterface, SIGNAL(CallEnded(QString)),
+            this, SIGNAL(callEnded(QString)));
+    connect(&mMockInterface, SIGNAL(CallStateChanged(QString, QString, QString)),
+            this, SIGNAL(callStateChanged(QString,QString,QString)));
+    connect(&mMockInterface, SIGNAL(ConferenceCreated(QString)),
+            this, SIGNAL(conferenceCreated(QString)));
+    connect(&mMockInterface, SIGNAL(ChannelMerged(QString)),
+            this, SIGNAL(channelMerged(QString)));
+    connect(&mMockInterface, SIGNAL(ChannelSplitted(QString)),
+            this, SIGNAL(channelSplitted(QString)));
 }
 
 void MockController::placeIncomingMessage(const QString &message, const QVariantMap &properties)
@@ -64,3 +67,43 @@
 {
     mMockInterface.call("SetCallState", phoneNumber, state);
 }
+
+void MockController::setOnline(bool online)
+{
+    mMockInterface.call("SetOnline", online);
+}
+
+void MockController::setPresence(const QString &status, const QString &statusMessage)
+{
+    mMockInterface.call("SetPresence", status, statusMessage);
+}
+
+void MockController::setVoicemailNumber(const QString &number)
+{
+    mMockInterface.call("SetVoicemailNumber", number);
+}
+
+void MockController::setVoicemailIndicator(bool active)
+{
+    mMockInterface.call("SetVoicemailIndicator", active);
+}
+
+void MockController::setVoicemailCount(int count)
+{
+    mMockInterface.call("SetVoicemailCount", count);
+}
+
+void MockController::setEmergencyNumbers(const QStringList &numbers)
+{
+    mMockInterface.call("SetEmergencyNumbers", numbers);
+}
+
+QString MockController::serial()
+{
+    QDBusReply<QString> reply = mMockInterface.call("Serial");
+    if (!reply.isValid()) {
+        return QString::null;
+    }
+
+    return reply.value();
+}

=== renamed file 'handler/tests/mockcontroller.h' => 'tests/common/mockcontroller.h'
--- handler/tests/mockcontroller.h	2014-03-20 21:03:26 +0000
+++ tests/common/mockcontroller.h	2015-03-19 23:42:32 +0000
@@ -28,9 +28,10 @@
 {
     Q_OBJECT
 public:
-    static MockController *instance();
+    explicit MockController(const QString &protocol, QObject *parent = 0);
 
 Q_SIGNALS:
+    void messageRead(const QString &messageId);
     void messageSent(const QString &message, const QVariantMap &properties);
     void callReceived(const QString &callerId);
     void callEnded(const QString &callerId);
@@ -44,10 +45,24 @@
     QString placeCall(const QVariantMap &properties);
     void hangupCall(const QString &callerId);
     void setCallState(const QString &phoneNumber, const QString &state);
+    void setOnline(bool online);
+    void setPresence(const QString &status, const QString &statusMessage);
+
+    // voicemail stuff
+    void setVoicemailNumber(const QString &number);
+    void setVoicemailIndicator(bool active);
+    void setVoicemailCount(int count);
+
+    // emergency numbers stuff
+    void setEmergencyNumbers(const QStringList &numbers);
+
+    // USSD stuff
+    QString serial();
 
 private:
-    explicit MockController(QObject *parent = 0);
     QDBusInterface mMockInterface;
+    QString mProtocol;
+    QString mMockObject;
 };
 
 #endif // MOCKCONTROLLER_H

=== renamed directory 'handler/tests' => 'tests/handler'
=== modified file 'tests/handler/CMakeLists.txt'
--- handler/tests/CMakeLists.txt	2014-01-22 12:33:43 +0000
+++ tests/handler/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -1,43 +1,11 @@
 include_directories(
     ${CMAKE_CURRENT_BINARY_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/../common
     ${CMAKE_SOURCE_DIR}
+    ${CMAKE_SOURCE_DIR}/libtelephonyservice
+    ${TP_QT5_INCLUDE_DIRS}
     )
 
-macro(generate_test TESTNAME USE_DBUS)
-    add_executable(${TESTNAME} ${ARGN} ${TESTNAME}.cpp)
-    qt5_use_modules(${TESTNAME} Core DBus Test)
-    set(TEST_COMMAND )
-    if (${USE_DBUS})
-        set(TEST_COMMAND -p ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -p -xunitxml -p -o -p ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml)
-        add_test(${TESTNAME} ${DBUS_RUNNER} --keep-env
-                                            --task ${CMAKE_CURRENT_BINARY_DIR}/dbus-test-wrapper.sh ${TEST_COMMAND})
-    else (${USE_DBUS})
-        add_test(${TESTNAME} ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -xunitxml -o ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml)
-    endif(${USE_DBUS})
-    # force telepathy not to use system approvers when available,
-    # also force usage of memory backend in history-service
-    set(TMPDIR "/tmp/telephony_service_test_home")
-    set(TEST_ENVIRONMENT "HOME=${TMPDIR};"
-                         "HISTORY_SQLITE_DBPATH=:memory:;"
-                         "XDG_CONFIG_HOME=${TMPDIR};
-                          XDG_DATA_HOME=${TMPDIR};
-                          XDG_CACHE_DIR=${TMPDIR};
-                          XDG_CACHE_HOME=${TMPDIR};
-                          XDG_DATA_DIRS=${TMPDIR};
-                          MC_ACCOUNT_DIR=${TMPDIR};
-                          MC_MANAGER_DIR=${TMPDIR}")
-    set_tests_properties(${TESTNAME} PROPERTIES
-                          ENVIRONMENT "${TEST_ENVIRONMENT}"
-                          TIMEOUT 30)
-    target_link_libraries(${TESTNAME}
-                          ${TP_QT5_LIBRARIES}
-                         )
-endmacro(generate_test)
-
-configure_file(dbus-test-wrapper.sh.in ${CMAKE_CURRENT_BINARY_DIR}/dbus-test-wrapper.sh)
-
 if (DBUS_RUNNER)
-    generate_test(HandlerTest True telepathyhelper.cpp handlercontroller.cpp mockcontroller.cpp approver.cpp)
+    generate_test(HandlerTest True handlercontroller.cpp approver.cpp)
 endif(DBUS_RUNNER)
-
-add_subdirectory(mock)

=== modified file 'tests/handler/HandlerTest.cpp'
--- handler/tests/HandlerTest.cpp	2014-08-18 17:37:19 +0000
+++ tests/handler/HandlerTest.cpp	2015-03-19 23:42:32 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Canonical, Ltd.
+ * Copyright (C) 2013-2015 Canonical, Ltd.
  *
  * This file is part of telephony-service.
  *
@@ -21,6 +21,7 @@
 #include "handlercontroller.h"
 #include "mockcontroller.h"
 #include "approver.h"
+#include "accountentry.h"
 #include "telepathyhelper.h"
 
 #define DEFAULT_TIMEOUT 15000
@@ -42,11 +43,15 @@
 private:
     void waitForCallActive(const QString &callerId);
     Approver *mApprover;
+    MockController *mMockController;
+    AccountEntry *mAccount;
 };
 
 void HandlerTest::initTestCase()
 {
-    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(accountReady()));
+    Tp::registerTypes();
+
+    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(setupReady()));
     QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, DEFAULT_TIMEOUT);
     QTRY_VERIFY_WITH_TIMEOUT(TelepathyHelper::instance()->connected(), DEFAULT_TIMEOUT);
 
@@ -58,18 +63,24 @@
 
     // we need to wait in order to give telepathy time to notify about the approver
     QTest::qWait(3000);
+
+    // and create the mock controller
+    mMockController = new MockController("mock", this);
+
+    mAccount = TelepathyHelper::instance()->accountForId("mock/mock/account0");
+    QVERIFY(mAccount);
 }
 
 void HandlerTest::testMakingCalls()
 {
     QString callerId("1234567");
-    QSignalSpy callReceivedSpy(MockController::instance(), SIGNAL(callReceived(QString)));
+    QSignalSpy callReceivedSpy(mMockController, SIGNAL(callReceived(QString)));
     // FIXME: add support for multiple accounts
-    HandlerController::instance()->startCall(callerId, TelepathyHelper::instance()->accountId());
+    HandlerController::instance()->startCall(callerId, mAccount->accountId());
     QTRY_COMPARE(callReceivedSpy.count(), 1);
     QCOMPARE(callReceivedSpy.first().first().toString(), callerId);
 
-    MockController::instance()->hangupCall(callerId);
+    mMockController->hangupCall(callerId);
 }
 
 void HandlerTest::testHangUpCall()
@@ -81,7 +92,7 @@
     properties["State"] = "incoming";
 
     QSignalSpy approverCallSpy(mApprover, SIGNAL(newCall()));
-    QString objectPath = MockController::instance()->placeCall(properties);
+    QString objectPath = mMockController->placeCall(properties);
     QVERIFY(!objectPath.isEmpty());
 
     // wait for the channel to hit the approver
@@ -91,7 +102,7 @@
     waitForCallActive(callerId);
 
     // and finally request the hangup
-    QSignalSpy callEndedSpy(MockController::instance(), SIGNAL(callEnded(QString)));
+    QSignalSpy callEndedSpy(mMockController, SIGNAL(callEnded(QString)));
     HandlerController::instance()->hangUpCall(objectPath);
     QTRY_COMPARE(callEndedSpy.count(), 1);
 }
@@ -105,7 +116,7 @@
     properties["State"] = "incoming";
 
     QSignalSpy approverCallSpy(mApprover, SIGNAL(newCall()));
-    QString objectPath = MockController::instance()->placeCall(properties);
+    QString objectPath = mMockController->placeCall(properties);
     QVERIFY(!objectPath.isEmpty());
 
     // wait for the channel to hit the approver
@@ -114,7 +125,7 @@
 
     waitForCallActive(callerId);
 
-    QSignalSpy callStateSpy(MockController::instance(), SIGNAL(callStateChanged(QString,QString,QString)));
+    QSignalSpy callStateSpy(mMockController, SIGNAL(callStateChanged(QString,QString,QString)));
 
     // set the call on hold
     HandlerController::instance()->setHold(objectPath, true);
@@ -127,7 +138,7 @@
     QTRY_COMPARE(callStateSpy.count(), 1);
     QCOMPARE(callStateSpy.first()[2].toString(), QString("active"));
 
-    MockController::instance()->hangupCall(callerId);
+    mMockController->hangupCall(callerId);
 }
 
 void HandlerTest::testCallProperties()
@@ -140,7 +151,7 @@
 
     QSignalSpy approverCallSpy(mApprover, SIGNAL(newCall()));
     QSignalSpy handlerCallPropertiesSpy(HandlerController::instance(), SIGNAL(callPropertiesChanged(QString,QVariantMap)));
-    MockController::instance()->placeCall(properties);
+    mMockController->placeCall(properties);
 
     // wait for the channel to hit the approver
     QTRY_COMPARE(approverCallSpy.count(), 1);
@@ -196,7 +207,7 @@
     properties["State"] = "incoming";
 
     QSignalSpy approverCallSpy(mApprover, SIGNAL(newCall()));
-    QString call1 = MockController::instance()->placeCall(properties);
+    QString call1 = mMockController->placeCall(properties);
 
     // wait for the channel to hit the approver
     QTRY_COMPARE(approverCallSpy.count(), 1);
@@ -206,7 +217,7 @@
 
     // make a second call
     properties["Caller"] = callerId2;
-    QString call2 = MockController::instance()->placeCall(properties);
+    QString call2 = mMockController->placeCall(properties);
     // wait for the channel to hit the approver
     QTRY_COMPARE(approverCallSpy.count(), 1);
     mApprover->acceptCall();
@@ -214,27 +225,27 @@
     waitForCallActive(callerId2);
 
     // now create the conf call
-    QSignalSpy conferenceCreatedSpy(MockController::instance(), SIGNAL(conferenceCreated(QString)));
+    QSignalSpy conferenceCreatedSpy(mMockController, SIGNAL(conferenceCreated(QString)));
     HandlerController::instance()->createConferenceCall(QStringList() << call1 << call2);
     QTRY_COMPARE(conferenceCreatedSpy.count(), 1);
     QString conferenceObjectPath = conferenceCreatedSpy.first().first().toString();
 
     // now place a third call and try to merge it
     properties["Caller"] = callerId3;
-    QString call3 = MockController::instance()->placeCall(properties);
+    QString call3 = mMockController->placeCall(properties);
     QTRY_COMPARE(approverCallSpy.count(), 1);
     mApprover->acceptCall();
     approverCallSpy.clear();
     waitForCallActive(callerId3);
 
     // merge that call on the conference
-    QSignalSpy channelMergedSpy(MockController::instance(), SIGNAL(channelMerged(QString)));
+    QSignalSpy channelMergedSpy(mMockController, SIGNAL(channelMerged(QString)));
     HandlerController::instance()->mergeCall(conferenceObjectPath, call3);
     QTRY_COMPARE(channelMergedSpy.count(), 1);
     QCOMPARE(channelMergedSpy.first().first().toString(), call3);
 
     // now try to split one of the channels
-    QSignalSpy channelSplittedSpy(MockController::instance(), SIGNAL(channelSplitted(QString)));
+    QSignalSpy channelSplittedSpy(mMockController, SIGNAL(channelSplitted(QString)));
     HandlerController::instance()->splitCall(call2);
     QTRY_COMPARE(channelSplittedSpy.count(), 1);
     QCOMPARE(channelSplittedSpy.first().first().toString(), call2);
@@ -250,9 +261,9 @@
 {
     QString recipient("22222222");
     QString message("Hello, world!");
-    QSignalSpy messageSentSpy(MockController::instance(), SIGNAL(messageSent(QString,QVariantMap)));
+    QSignalSpy messageSentSpy(mMockController, SIGNAL(messageSent(QString,QVariantMap)));
     // FIXME: add support for multiple accounts
-    HandlerController::instance()->sendMessage(recipient, message, TelepathyHelper::instance()->accountId());
+    HandlerController::instance()->sendMessage(recipient, message, mAccount->accountId());
     QTRY_COMPARE(messageSentSpy.count(), 1);
     QString sentMessage = messageSentSpy.first().first().toString();
     QVariantMap messageProperties = messageSentSpy.first().last().value<QVariantMap>();
@@ -284,7 +295,7 @@
 void HandlerTest::waitForCallActive(const QString &callerId)
 {
     // wait until the call state is "accepted"
-    QSignalSpy callStateSpy(MockController::instance(), SIGNAL(callStateChanged(QString,QString,QString)));
+    QSignalSpy callStateSpy(mMockController, SIGNAL(callStateChanged(QString,QString,QString)));
     QString state;
     QString objectPath;
     QString caller;

=== renamed directory 'libtelephonyservice/tests' => 'tests/libtelephonyservice'
=== added file 'tests/libtelephonyservice/AccountEntryFactoryTest.cpp'
--- tests/libtelephonyservice/AccountEntryFactoryTest.cpp	1970-01-01 00:00:00 +0000
+++ tests/libtelephonyservice/AccountEntryFactoryTest.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QtCore/QObject>
+#include <QtTest/QtTest>
+#include "accountentry.h"
+#include "accountentryfactory.h"
+#include "ofonoaccountentry.h"
+#include "telepathyhelper.h"
+
+#define DEFAULT_TIMEOUT 15000
+
+class AccountEntryFactoryTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void testCorrectInstancesCreated();
+    void testNullAccount();
+};
+
+void AccountEntryFactoryTest::initTestCase()
+{
+    Tp::registerTypes();
+
+    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(setupReady()));
+    QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, DEFAULT_TIMEOUT);
+    QTRY_VERIFY_WITH_TIMEOUT(TelepathyHelper::instance()->connected(), DEFAULT_TIMEOUT);
+
+    // we need to wait in order to give telepathy time to notify about the approver
+    QTest::qWait(3000);
+}
+
+void AccountEntryFactoryTest::testCorrectInstancesCreated()
+{
+    AccountEntry *genericAccount = TelepathyHelper::instance()->accountForId("mock/mock/account0");
+    QVERIFY(genericAccount);
+    QCOMPARE(genericAccount->type(), AccountEntry::GenericAccount);
+    QVERIFY(!qobject_cast<OfonoAccountEntry*>(genericAccount));
+
+    AccountEntry *ofonoAccount = TelepathyHelper::instance()->accountForId("mock/ofono/account0");
+    QVERIFY(ofonoAccount);
+    QCOMPARE(ofonoAccount->type(), AccountEntry::PhoneAccount);
+    QVERIFY(qobject_cast<OfonoAccountEntry*>(ofonoAccount));
+}
+
+void AccountEntryFactoryTest::testNullAccount()
+{
+    AccountEntry *nullAccount = AccountEntryFactory::createEntry(Tp::AccountPtr(), this);
+    QVERIFY(nullAccount);
+    QVERIFY(nullAccount->accountId().isNull());
+}
+
+
+QTEST_MAIN(AccountEntryFactoryTest)
+#include "AccountEntryFactoryTest.moc"

=== added file 'tests/libtelephonyservice/AccountEntryTest.cpp'
--- tests/libtelephonyservice/AccountEntryTest.cpp	1970-01-01 00:00:00 +0000
+++ tests/libtelephonyservice/AccountEntryTest.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QtCore/QObject>
+#include <QtTest/QtTest>
+#include "accountentry.h"
+#include "accountentryfactory.h"
+#include "telepathyhelper.h"
+#include "mockcontroller.h"
+
+#define DEFAULT_TIMEOUT 15000
+
+class AccountEntryTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void testAccountId();
+    void testActive();
+    void testDisplayName();
+    void testStatus();
+    void testStatusMessage();
+    void testConnected();
+    void testCompareIds_data();
+    void testCompareIds();
+
+private:
+    AccountEntry *mAccount;
+    Tp::AccountPtr mTpAccount;
+    AccountEntry *mNullAccount;
+    MockController *mMockController;
+};
+
+void AccountEntryTest::initTestCase()
+{
+    Tp::registerTypes();
+
+    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(setupReady()));
+    QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, DEFAULT_TIMEOUT);
+    QTRY_VERIFY_WITH_TIMEOUT(TelepathyHelper::instance()->connected(), DEFAULT_TIMEOUT);
+
+    mAccount = TelepathyHelper::instance()->accountForId("mock/mock/account0");
+    QVERIFY(mAccount);
+
+    mTpAccount = mAccount->account();
+    QVERIFY(!mTpAccount.isNull());
+    QTRY_VERIFY(mTpAccount->isReady(Tp::Account::FeatureCore));
+
+    // wait for the connection to appear
+    QTRY_VERIFY(!mTpAccount->connection().isNull());
+
+    // create a null account
+    mNullAccount = AccountEntryFactory::createEntry(Tp::AccountPtr(), this);
+
+    // and create the mock controller
+    mMockController = new MockController("mock", this);
+}
+
+void AccountEntryTest::testAccountId()
+{
+    QCOMPARE(mAccount->accountId(), mTpAccount->uniqueIdentifier());
+    QVERIFY(mNullAccount->accountId().isNull());
+}
+
+void AccountEntryTest::testActive()
+{
+    QSignalSpy activeChangedSpy(mAccount, SIGNAL(activeChanged()));
+
+    // the mock account is enabled/connected by default, so make sure it is like that
+    QVERIFY(mAccount->active());
+
+    // now set the account offline and see if the active flag changes correctly
+    mMockController->setOnline(false);
+    QTRY_VERIFY(!mAccount->active());
+    QCOMPARE(activeChangedSpy.count(), 1);
+
+    // now re-enable the account and check that the entry is updated
+    activeChangedSpy.clear();
+    mMockController->setOnline(true);
+    QTRY_VERIFY(mAccount->active());
+    QCOMPARE(activeChangedSpy.count(), 1);
+
+    // check that for a null account active is false
+    QVERIFY(!mNullAccount->active());
+}
+
+void AccountEntryTest::testDisplayName()
+{
+    QSignalSpy displayNameChangedSpy(mAccount, SIGNAL(displayNameChanged()));
+
+    // check that the value is correct already
+    QCOMPARE(mAccount->displayName(), mTpAccount->displayName());
+
+    // now try to set the display in the telepathy account directly and see that the entry gets updated
+    QString newDisplayName = "some other display name";
+    mTpAccount->setDisplayName(newDisplayName);
+    QTRY_COMPARE(mAccount->displayName(), newDisplayName);
+    QCOMPARE(displayNameChangedSpy.count(), 1);
+
+    // and try setting the display name in the entry itself
+    displayNameChangedSpy.clear();
+    newDisplayName = "changing again";
+    mAccount->setDisplayName(newDisplayName);
+    QTRY_COMPARE(mAccount->displayName(), newDisplayName);
+    QCOMPARE(displayNameChangedSpy.count(), 1);
+    QCOMPARE(mTpAccount->displayName(), newDisplayName);
+
+    // check that for a null account the displayName is null
+    QVERIFY(mNullAccount->displayName().isNull());
+}
+
+void AccountEntryTest::testStatus()
+{
+    QSignalSpy statusChangedSpy(mAccount, SIGNAL(statusChanged()));
+
+    // check that the value is correct already
+    QCOMPARE(mAccount->status(), mTpAccount->connection()->selfContact()->presence().status());
+
+    // and now set a new value
+    Tp::Presence presence(Tp::ConnectionPresenceTypeAway, "away", "away");
+    mTpAccount->setRequestedPresence(presence);
+
+    QTRY_COMPARE(mAccount->status(), QString("away"));
+    QCOMPARE(statusChangedSpy.count(), 1);
+
+    // check that for a null account the status is null
+    QVERIFY(mNullAccount->status().isNull());
+}
+
+void AccountEntryTest::testStatusMessage()
+{
+    QSignalSpy statusMessageChangedSpy(mAccount, SIGNAL(statusMessageChanged()));
+
+    // check that the value is correct already
+    QTRY_COMPARE(mAccount->statusMessage(), mTpAccount->connection()->selfContact()->presence().statusMessage());
+
+    // and now set a new value
+    QString statusMessage("I am online");
+    Tp::Presence presence(Tp::ConnectionPresenceTypeAvailable, "available", statusMessage);
+    mTpAccount->setRequestedPresence(presence);
+
+    QTRY_COMPARE(mAccount->statusMessage(), statusMessage);
+    QCOMPARE(statusMessageChangedSpy.count(), 1);
+
+    // check that for a null account the displayName is null
+    QVERIFY(mNullAccount->statusMessage().isNull());
+}
+
+void AccountEntryTest::testConnected()
+{
+    QSignalSpy connectedChangedSpy(mAccount, SIGNAL(connectedChanged()));
+
+    // the mock account is enabled/connected by default, so make sure it is like that
+    QVERIFY(mAccount->connected());
+
+    // now set the account offline and see if the active flag changes correctly
+    mMockController->setOnline(false);
+    QTRY_VERIFY(!mAccount->connected());
+    QCOMPARE(connectedChangedSpy.count(), 1);
+
+    // now re-enable the account and check that the entry is updated
+    connectedChangedSpy.clear();
+    mMockController->setOnline(true);
+    QTRY_VERIFY(mAccount->connected());
+    QCOMPARE(connectedChangedSpy.count(), 1);
+
+    // check that for a null account the displayName is null
+    QVERIFY(!mNullAccount->connected());
+}
+
+void AccountEntryTest::testCompareIds_data()
+{
+    QTest::addColumn<QString>("first");
+    QTest::addColumn<QString>("second");
+    QTest::addColumn<bool>("expectedResult");
+
+    QTest::newRow("identical values") << "1234567" << "1234567" << true;
+    QTest::newRow("case difference") << "TestId" << "testid" << false;
+    QTest::newRow("phone prefix") << "1234567" << "1231234567" << false;
+}
+
+void AccountEntryTest::testCompareIds()
+{
+    QFETCH(QString, first);
+    QFETCH(QString, second);
+    QFETCH(bool, expectedResult);
+
+    QCOMPARE(mAccount->compareIds(first, second), expectedResult);
+}
+
+QTEST_MAIN(AccountEntryTest)
+#include "AccountEntryTest.moc"

=== modified file 'tests/libtelephonyservice/CMakeLists.txt'
--- libtelephonyservice/tests/CMakeLists.txt	2014-07-01 16:05:21 +0000
+++ tests/libtelephonyservice/CMakeLists.txt	2015-03-19 23:42:32 +0000
@@ -1,12 +1,15 @@
+set(LIBTELEPHONYSERVICE_DIR ${CMAKE_SOURCE_DIR}/libtelephonyservice)
 include_directories(
     ${CMAKE_CURRENT_BINARY_DIR}
-    ${CMAKE_SOURCE_DIR}/libtelephonyservice
+    ${LIBTELEPHONYSERVICE_DIR}
+    ${TP_QT5_INCLUDE_DIRS}
+    ${CMAKE_SOURCE_DIR}/tests/common
     )
 
 add_executable(GreeterContactsTestServerExe GreeterContactsTestServer.cpp)
 qt5_use_modules(GreeterContactsTestServerExe Core DBus)
 
-add_executable(GreeterContactsTestExe GreeterContactsTest.cpp ../greetercontacts.cpp)
+add_executable(GreeterContactsTestExe GreeterContactsTest.cpp ${LIBTELEPHONYSERVICE_DIR}/greetercontacts.cpp)
 set_target_properties(GreeterContactsTestExe PROPERTIES COMPILE_DEFINITIONS "AS_BUSNAME=sessionBus;CMAKE_SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\"")
 qt5_use_modules(GreeterContactsTestExe Contacts Core DBus Test)
 
@@ -27,18 +30,10 @@
     DEPENDENCIES GreeterContactsTestServerExe GreeterContactsTestExe
     )
 
-macro(generate_tests)
-    foreach(test ${ARGN})
-        add_executable(${test} ${test}.cpp)
-        qt5_use_modules(${test} Contacts Core DBus Qml Test)
-        target_link_libraries(${test}
-            telephonyservice
-            )
-        add_test(${test} ${CMAKE_CURRENT_BINARY_DIR}/${test} -platform offscreen -xunitxml -o ${CMAKE_BINARY_DIR}/test_${test}.xml)
-    endforeach(test)
-endmacro(generate_tests)
-
-generate_tests(
-    ContactUtilsTest
-    PhoneUtilsTest
-    )
+generate_test(ContactUtilsTest False)
+generate_test(PhoneUtilsTest False)
+generate_test(AccountEntryFactoryTest True)
+generate_test(AccountEntryTest True)
+generate_test(ChatManagerTest True)
+generate_test(OfonoAccountEntryTest True)
+generate_test(TelepathyHelperTest True)

=== added file 'tests/libtelephonyservice/ChatManagerTest.cpp'
--- tests/libtelephonyservice/ChatManagerTest.cpp	1970-01-01 00:00:00 +0000
+++ tests/libtelephonyservice/ChatManagerTest.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QtCore/QObject>
+#include <QtTest/QtTest>
+#include "accountentry.h"
+#include "chatmanager.h"
+#include "telepathyhelper.h"
+#include "mockcontroller.h"
+
+#define DEFAULT_TIMEOUT 15000
+
+class ChatManagerTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void testSendMessage_data();
+    void testSendMessage();
+    void testMessageReceived();
+    void testAcknowledgeMessages();
+
+private:
+    MockController *mGenericMockController;
+    MockController *mPhoneMockController;
+};
+
+void ChatManagerTest::initTestCase()
+{
+    Tp::registerTypes();
+
+    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(setupReady()));
+    QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, DEFAULT_TIMEOUT);
+    QTRY_VERIFY_WITH_TIMEOUT(TelepathyHelper::instance()->connected(), DEFAULT_TIMEOUT);
+
+    TelepathyHelper::instance()->registerChannelObserver();
+
+    // just give telepathy some time to register the observer
+    QTest::qWait(1000);
+
+    // and create the mock controller
+    mGenericMockController = new MockController("mock", this);
+    mPhoneMockController = new MockController("ofono", this);
+}
+
+void ChatManagerTest::testSendMessage_data()
+{
+    QTest::addColumn<QStringList>("recipients");
+    QTest::addColumn<QString>("message");
+    QTest::addColumn<QString>("accountId");
+
+    QTest::newRow("message via the generic account") << (QStringList() << "recipient1") << QString("Hello world") << QString("mock/mock/account0");
+    QTest::newRow("message via the phone account") << (QStringList() << "recipient2") << QString("Hello Phone World") << QString("mock/ofono/account0");
+    QTest::newRow("multiple recipients") << (QStringList() << "123" << "456" << "789") << QString("Hi friends!") << QString("mock/mock/account0");
+}
+
+void ChatManagerTest::testSendMessage()
+{
+    QFETCH(QStringList, recipients);
+    QFETCH(QString, message);
+    QFETCH(QString, accountId);
+
+    // just to make it easier, sort the recipients
+    qSort(recipients);
+
+    MockController *controller = (accountId == "mock/mock/account0") ? mGenericMockController : mPhoneMockController;
+    QSignalSpy controllerMessageSentSpy(controller, SIGNAL(messageSent(QString,QVariantMap)));
+    QSignalSpy messageSentSpy(ChatManager::instance(), SIGNAL(messageSent(QStringList,QString)));
+
+    ChatManager::instance()->sendMessage(recipients, message, accountId);
+
+    QTRY_COMPARE(controllerMessageSentSpy.count(), 1);
+    QString messageText = controllerMessageSentSpy.first()[0].toString();
+    QVariantMap messageProperties = controllerMessageSentSpy.first()[1].toMap();
+    QStringList messageRecipients = messageProperties["Recipients"].toStringList();
+    qSort(messageRecipients);
+    QCOMPARE(messageText, message);
+    QCOMPARE(messageRecipients, recipients);
+
+    QTRY_COMPARE(messageSentSpy.count(), 1);
+    messageRecipients = messageSentSpy.first()[0].toStringList();
+    qSort(messageRecipients);
+    messageText = messageSentSpy.first()[1].toString();
+    QCOMPARE(messageText, message);
+    QCOMPARE(messageRecipients, recipients);
+}
+
+void ChatManagerTest::testMessageReceived()
+{
+    QSignalSpy messageReceivedSpy(ChatManager::instance(), SIGNAL(messageReceived(QString,QString,QDateTime,QString,bool)));
+
+    QVariantMap properties;
+    properties["Sender"] = "12345";
+    properties["Recipients"] = (QStringList() << "12345");
+    QString message("Hi there");
+    mGenericMockController->placeIncomingMessage(message, properties);
+
+    QTRY_COMPARE(messageReceivedSpy.count(), 1);
+    QString sender = messageReceivedSpy.first()[0].toString();
+    QString receivedMessage = messageReceivedSpy.first()[1].toString();
+    QCOMPARE(sender, properties["Sender"].toString());
+    QCOMPARE(receivedMessage, message);
+}
+
+void ChatManagerTest::testAcknowledgeMessages()
+{
+    QSignalSpy messageReceivedSpy(ChatManager::instance(), SIGNAL(messageReceived(QString,QString,QDateTime,QString,bool)));
+
+    QVariantMap properties;
+    properties["Sender"] = "12345";
+    properties["Recipients"] = (QStringList() << "12345");
+    QStringList messages;
+    messages << "Hi there" << "How are you" << "Always look on the bright side of life";
+    Q_FOREACH(const QString &message, messages) {
+        mGenericMockController->placeIncomingMessage(message, properties);
+        QTest::qWait(100);
+    }
+    QTRY_COMPARE(messageReceivedSpy.count(), messages.count());
+
+    QStringList messageIds;
+    for (int i = 0; i < messages.count(); ++i) {
+        QString messageId = messageReceivedSpy[i][3].toString();
+        messageIds << messageId;
+    }
+
+    QSignalSpy messageReadSpy(mGenericMockController, SIGNAL(messageRead(QString)));
+    Q_FOREACH(const QString &messageId, messageIds) {
+        ChatManager::instance()->acknowledgeMessage(properties["Recipients"].toStringList(), messageId, "mock/mock/account0");
+    }
+
+    QTRY_COMPARE(messageReadSpy.count(), messageIds.count());
+    QStringList receivedIds;
+    for (int i = 0; i < messageReadSpy.count(); ++i) {
+        receivedIds << messageReadSpy[i][0].toString();
+    }
+
+    qSort(receivedIds);
+    qSort(messageIds);
+    QCOMPARE(receivedIds, messageIds);
+}
+
+QTEST_MAIN(ChatManagerTest)
+#include "ChatManagerTest.moc"

=== added file 'tests/libtelephonyservice/OfonoAccountEntryTest.cpp'
--- tests/libtelephonyservice/OfonoAccountEntryTest.cpp	1970-01-01 00:00:00 +0000
+++ tests/libtelephonyservice/OfonoAccountEntryTest.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QtCore/QObject>
+#include <QtTest/QtTest>
+#include "ofonoaccountentry.h"
+#include "telepathyhelper.h"
+#include "mockcontroller.h"
+
+#define DEFAULT_TIMEOUT 15000
+
+class OfonoAccountEntryTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void testAccountType();
+    void testConnected();
+    void testCompareIds_data();
+    void testCompareIds();
+    void testEmergencyNumbers();
+    void testSerial();
+    void testVoicemailIndicator();
+    void testVoicemailNumber();
+    void testVoicemailCount();
+    void testSimLocked();
+    void testEmergencyCallsAvailable_data();
+    void testEmergencyCallsAvailable();
+    void testNetworkName();
+
+private:
+    OfonoAccountEntry *mAccount;
+    Tp::AccountPtr mTpAccount;
+    MockController *mMockController;
+};
+
+void OfonoAccountEntryTest::initTestCase()
+{
+    Tp::registerTypes();
+
+    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(setupReady()));
+    QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, DEFAULT_TIMEOUT);
+    QTRY_VERIFY_WITH_TIMEOUT(TelepathyHelper::instance()->connected(), DEFAULT_TIMEOUT);
+
+    mAccount = qobject_cast<OfonoAccountEntry*>(TelepathyHelper::instance()->accountForId("mock/ofono/account0"));
+    QVERIFY(mAccount);
+
+    mTpAccount = mAccount->account();
+    QVERIFY(!mTpAccount.isNull());
+    QTRY_VERIFY(mTpAccount->isReady(Tp::Account::FeatureCore));
+
+    // wait for the connection to appear
+    QTRY_VERIFY(!mTpAccount->connection().isNull());
+
+    // create the mock controller
+    mMockController = new MockController("ofono", this);
+}
+
+void OfonoAccountEntryTest::testAccountType()
+{
+    QCOMPARE(mAccount->type(), AccountEntry::PhoneAccount);
+}
+
+void OfonoAccountEntryTest::testConnected()
+{
+    // right now the ofono account connection status behave exactly like the generic class,
+    // but as the code path is different, test it again
+    QSignalSpy connectedChangedSpy(mAccount, SIGNAL(connectedChanged()));
+
+    // the mock account is enabled/connected by default, so make sure it is like that
+    QVERIFY(mAccount->connected());
+
+    // now set the account offline and see if the active flag changes correctly
+    mMockController->setOnline(false);
+    QTRY_VERIFY(!mAccount->connected());
+    QCOMPARE(connectedChangedSpy.count(), 1);
+
+    // now re-enable the account and check that the entry is updated
+    connectedChangedSpy.clear();
+    mMockController->setOnline(true);
+    QTRY_VERIFY(mAccount->connected());
+    QCOMPARE(connectedChangedSpy.count(), 1);
+}
+
+void OfonoAccountEntryTest::testCompareIds_data()
+{
+    QTest::addColumn<QString>("first");
+    QTest::addColumn<QString>("second");
+    QTest::addColumn<bool>("expectedResult");
+
+    QTest::newRow("identical values") << "1234567" << "1234567" << true;
+    QTest::newRow("case difference") << "TestId" << "testid" << false;
+    QTest::newRow("phone prefix") << "1234567" << "1231234567" << true;
+}
+
+void OfonoAccountEntryTest::testCompareIds()
+{
+    QFETCH(QString, first);
+    QFETCH(QString, second);
+    QFETCH(bool, expectedResult);
+
+    QCOMPARE(mAccount->compareIds(first, second), expectedResult);
+}
+
+void OfonoAccountEntryTest::testEmergencyNumbers()
+{
+    QSignalSpy emergencyNumbersChangedSpy(mAccount, SIGNAL(emergencyNumbersChanged()));
+
+    // check that the list is not empty at startup
+    QVERIFY(!mAccount->emergencyNumbers().isEmpty());
+
+    QStringList numbers;
+    numbers << "111" << "190" << "911";
+    qSort(numbers);
+    mMockController->setEmergencyNumbers(numbers);
+    QTRY_COMPARE(emergencyNumbersChangedSpy.count(), 1);
+
+    QStringList emergencyNumbers = mAccount->emergencyNumbers();
+    qSort(emergencyNumbers);
+
+    QCOMPARE(emergencyNumbers, numbers);
+}
+
+void OfonoAccountEntryTest::testSerial()
+{
+    QCOMPARE(mAccount->serial(), mMockController->serial());
+}
+
+void OfonoAccountEntryTest::testVoicemailIndicator()
+{
+    // voicemail indicator is off by default on the mock CM
+    QVERIFY(!mAccount->voicemailIndicator());
+
+    QSignalSpy voiceMailIndicatorSpy(mAccount, SIGNAL(voicemailIndicatorChanged()));
+
+    // set to true
+    mMockController->setVoicemailIndicator(true);
+    QTRY_COMPARE(voiceMailIndicatorSpy.count(), 1);
+    QVERIFY(mAccount->voicemailIndicator());
+
+    // and set back to false
+    voiceMailIndicatorSpy.clear();
+    mMockController->setVoicemailIndicator(false);
+    QTRY_COMPARE(voiceMailIndicatorSpy.count(), 1);
+    QVERIFY(!mAccount->voicemailIndicator());
+}
+
+void OfonoAccountEntryTest::testVoicemailNumber()
+{
+    QSignalSpy voicemailNumberSpy(mAccount, SIGNAL(voicemailNumberChanged()));
+
+    // check that the number is not empty at startup
+    QVERIFY(!mAccount->voicemailNumber().isEmpty());
+
+    // try changing the number
+    QString number("12345");
+    mMockController->setVoicemailNumber(number);
+    QTRY_COMPARE(voicemailNumberSpy.count(), 1);
+    QCOMPARE(mAccount->voicemailNumber(), number);
+}
+
+void OfonoAccountEntryTest::testVoicemailCount()
+{
+    QSignalSpy voicemailCountSpy(mAccount, SIGNAL(voicemailCountChanged()));
+
+    // check that the count is zero at startup
+    QCOMPARE((int)mAccount->voicemailCount(), 0);
+
+    // set it to a bigger value
+    int count = 10;
+    mMockController->setVoicemailCount(count);
+    QTRY_COMPARE(voicemailCountSpy.count(), 1);
+    QCOMPARE((int)mAccount->voicemailCount(), count);
+
+    // and back to zero
+    voicemailCountSpy.clear();
+    mMockController->setVoicemailCount(0);
+    QTRY_COMPARE(voicemailCountSpy.count(), 1);
+    QCOMPARE((int)mAccount->voicemailCount(), 0);
+}
+
+void OfonoAccountEntryTest::testSimLocked()
+{
+    QSignalSpy simLockedSpy(mAccount, SIGNAL(simLockedChanged()));
+
+    // check that it is not locked by default
+    QVERIFY(!mAccount->simLocked());
+
+    // now try to set the status to simlocked
+    mMockController->setPresence("simlocked", "simlocked");
+    QTRY_COMPARE(simLockedSpy.count(), 1);
+    QVERIFY(mAccount->simLocked());
+}
+
+void OfonoAccountEntryTest::testEmergencyCallsAvailable_data()
+{
+    QTest::addColumn<QString>("status");
+    QTest::addColumn<bool>("available");
+
+    QTest::newRow("available") << "available" << true;
+    QTest::newRow("away") << "away" << true;
+    QTest::newRow("simlocked") << "simlocked" << true;
+    QTest::newRow("flightmode") << "flightmode" << false;
+    QTest::newRow("nosim") << "nosim" << true;
+    QTest::newRow("nomodem") << "nomodem" << false;
+    QTest::newRow("registered") << "registered" << true;
+    QTest::newRow("roaming") << "roaming" << true;
+    QTest::newRow("unregistered") << "unregistered" << true;
+    QTest::newRow("denied") << "denied" << true;
+    QTest::newRow("unknown") << "unknown" << true;
+    QTest::newRow("searching") << "searching" << true;
+}
+
+void OfonoAccountEntryTest::testEmergencyCallsAvailable()
+{
+    QFETCH(QString, status);
+    QFETCH(bool, available);
+
+    QSignalSpy statusChangedSpy(mAccount, SIGNAL(statusChanged()));
+    mMockController->setPresence(status, "");
+
+    QTRY_COMPARE(statusChangedSpy.count(), 1);
+    QCOMPARE(mAccount->status(), status);
+    QCOMPARE(mAccount->emergencyCallsAvailable(), available);
+}
+
+void OfonoAccountEntryTest::testNetworkName()
+{
+    QSignalSpy networkNameChangedSpy(mAccount, SIGNAL(statusMessageChanged()));
+
+    // set the value
+    QString statusMessage("SomeNetwork");
+    Tp::Presence presence(Tp::ConnectionPresenceTypeAvailable, "available", statusMessage);
+    mTpAccount->setRequestedPresence(presence);
+
+    QTRY_COMPARE(mAccount->networkName(), statusMessage);
+    QCOMPARE(networkNameChangedSpy.count(), 1);
+}
+
+QTEST_MAIN(OfonoAccountEntryTest)
+#include "OfonoAccountEntryTest.moc"

=== added file 'tests/libtelephonyservice/TelepathyHelperTest.cpp'
--- tests/libtelephonyservice/TelepathyHelperTest.cpp	1970-01-01 00:00:00 +0000
+++ tests/libtelephonyservice/TelepathyHelperTest.cpp	2015-03-19 23:42:32 +0000
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This file is part of telephony-service.
+ *
+ * telephony-service is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * telephony-service is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QtCore/QObject>
+#include <QtTest/QtTest>
+#include <TelepathyQt/PendingAccount>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/Account>
+#include "accountentry.h"
+#include "accountentryfactory.h"
+#include "telepathyhelper.h"
+#include "mockcontroller.h"
+
+#define DEFAULT_TIMEOUT 15000
+
+class TelepathyHelperTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void testConnected();
+    void testAccounts();
+    void testAccountSorting();
+    void testAccountIds();
+    void testActiveAccounts();
+    void testAccountForId();
+    void testAccountForConnection();
+    void testEmergencyCallsAvailable();
+
+    // helper slots
+    void onAccountManagerReady(Tp::PendingOperation *op);
+    Tp::AccountPtr addAccount(const QString &manager,
+                              const QString &protocol,
+                              const QString &displayName,
+                              const QVariantMap &parameters = QVariantMap());
+    bool removeAccount(const Tp::AccountPtr &account);
+
+private:
+    MockController *mGenericController;
+    MockController *mPhoneController;
+    Tp::AccountManagerPtr mAccountManager;
+
+    bool ready;
+};
+
+void TelepathyHelperTest::initTestCase()
+{
+    Tp::registerTypes();
+
+    QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(setupReady()));
+    QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, DEFAULT_TIMEOUT);
+    QTRY_VERIFY_WITH_TIMEOUT(TelepathyHelper::instance()->connected(), DEFAULT_TIMEOUT);
+
+    // create an account manager instance to help testing
+    Tp::Features accountFeatures;
+    accountFeatures << Tp::Account::FeatureCore;
+    Tp::Features contactFeatures;
+    contactFeatures << Tp::Contact::FeatureAlias
+                    << Tp::Contact::FeatureAvatarData
+                    << Tp::Contact::FeatureAvatarToken
+                    << Tp::Contact::FeatureCapabilities
+                    << Tp::Contact::FeatureSimplePresence;
+    Tp::Features connectionFeatures;
+    connectionFeatures << Tp::Connection::FeatureCore
+                       << Tp::Connection::FeatureSelfContact
+                       << Tp::Connection::FeatureSimplePresence;
+
+    Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus());
+    channelFactory->addCommonFeatures(Tp::Channel::FeatureCore);
+
+    mAccountManager = Tp::AccountManager::create(
+            Tp::AccountFactory::create(QDBusConnection::sessionBus(), accountFeatures),
+            Tp::ConnectionFactory::create(QDBusConnection::sessionBus(), connectionFeatures),
+            channelFactory,
+            Tp::ContactFactory::create(contactFeatures));
+
+    ready = false;
+    connect(mAccountManager->becomeReady(Tp::AccountManager::FeatureCore),
+            SIGNAL(finished(Tp::PendingOperation*)),
+            SLOT(onAccountManagerReady(Tp::PendingOperation*)));
+
+    QTRY_VERIFY(ready);
+
+    // and create the mock controller
+    mGenericController = new MockController("mock", this);
+    mPhoneController = new MockController("ofono", this);
+}
+
+void TelepathyHelperTest::testConnected()
+{
+    QSignalSpy connectedChangedSpy(TelepathyHelper::instance(), SIGNAL(connectedChanged()));
+
+    // check that the default status is connected (always true for mock accounts)
+    QVERIFY(TelepathyHelper::instance()->connected());
+
+    // set one of the accounts offline and check that the connected status stays true
+    mGenericController->setOnline(false);
+    QTest::qWait(1000);
+    QCOMPARE(connectedChangedSpy.count(), 0);
+    QVERIFY(TelepathyHelper::instance()->connected());
+
+    // and set the other account as offline too. This time connected needs to change to false
+    mPhoneController->setOnline(false);
+    QTRY_COMPARE(connectedChangedSpy.count(), 1);
+    QVERIFY(!TelepathyHelper::instance()->connected());
+
+    // now set one of the accounts back online
+    connectedChangedSpy.clear();
+    mPhoneController->setOnline(true);
+    QTRY_COMPARE(connectedChangedSpy.count(), 1);
+    QVERIFY(TelepathyHelper::instance()->connected());
+
+    // and the other one just in case
+    connectedChangedSpy.clear();
+    mGenericController->setOnline(true);
+    QTest::qWait(1000);
+    QCOMPARE(connectedChangedSpy.count(), 0);
+    QVERIFY(TelepathyHelper::instance()->connected());
+}
+
+void TelepathyHelperTest::testAccounts()
+{
+    QCOMPARE(TelepathyHelper::instance()->accounts().count(), 2);
+    AccountEntry *first = TelepathyHelper::instance()->accounts()[0];
+    AccountEntry *second = TelepathyHelper::instance()->accounts()[1];
+    QVERIFY(first->accountId() != second->accountId());
+
+    // now check that new accounts are captured
+    QSignalSpy accountsChangedSpy(TelepathyHelper::instance(), SIGNAL(accountsChanged()));
+    Tp::AccountPtr newAccount = addAccount("mock", "mock", "extra");
+    QVERIFY(!newAccount.isNull());
+
+    QTRY_COMPARE(accountsChangedSpy.count(), 1);
+    QCOMPARE(TelepathyHelper::instance()->accounts().count(), 3);
+
+    bool accountFound = false;
+    Q_FOREACH(AccountEntry *entry, TelepathyHelper::instance()->accounts()) {
+        if (entry->accountId() == newAccount->uniqueIdentifier()) {
+            accountFound = true;
+            break;
+        }
+    }
+    QVERIFY(accountFound);
+
+    // now remove the extra account and make sure it is properly removed
+    accountsChangedSpy.clear();
+    QVERIFY(removeAccount(newAccount));
+    QTRY_COMPARE(accountsChangedSpy.count(), 1);
+    QCOMPARE(TelepathyHelper::instance()->accounts().count(), 2);
+    QCOMPARE(TelepathyHelper::instance()->accounts()[0]->accountId(), first->accountId());
+    QCOMPARE(TelepathyHelper::instance()->accounts()[1]->accountId(), second->accountId());
+}
+
+void TelepathyHelperTest::testAccountSorting()
+{
+    // create two accounts with modem-objpath parameters and make sure they are listed first
+    QVariantMap parameters;
+    parameters["modem-objpath"] = "/phonesim1";
+    Tp::AccountPtr firstAccount = addAccount("ofono", "ofono", "firstPhoneAccount", parameters);
+    QVERIFY(!firstAccount.isNull());
+
+    parameters["modem-objpath"] = "/phonesim2";
+    Tp::AccountPtr secondAccount = addAccount("ofono", "ofono", "secondPhoneAccount", parameters);
+    QVERIFY(!secondAccount.isNull());
+
+    // wait for the accounts to appear;
+    QTRY_COMPARE(TelepathyHelper::instance()->accounts().count(), 4);
+
+    // and check the order
+    QCOMPARE(TelepathyHelper::instance()->accounts()[0]->accountId(), firstAccount->uniqueIdentifier());
+    QCOMPARE(TelepathyHelper::instance()->accounts()[1]->accountId(), secondAccount->uniqueIdentifier());
+
+    // now add a third account that should go before the two others
+    parameters["modem-objpath"] = "/phonesim0";
+    Tp::AccountPtr thirdAccount = addAccount("ofono", "ofono", "thirdPhoneAccount", parameters);
+    QVERIFY(!thirdAccount.isNull());
+
+    // wait for the accounts to appear;
+    QTRY_COMPARE(TelepathyHelper::instance()->accounts().count(), 5);
+    QCOMPARE(TelepathyHelper::instance()->accounts()[0]->accountId(), thirdAccount->uniqueIdentifier());
+
+    // and remove the created accounts
+    QVERIFY(removeAccount(firstAccount));
+    QVERIFY(removeAccount(secondAccount));
+    QVERIFY(removeAccount(thirdAccount));
+}
+
+void TelepathyHelperTest::testAccountIds()
+{
+    QCOMPARE(TelepathyHelper::instance()->accountIds().count(), 2);
+    QVERIFY(TelepathyHelper::instance()->accountIds().contains("mock/mock/account0"));
+    QVERIFY(TelepathyHelper::instance()->accountIds().contains("mock/ofono/account0"));
+
+    // now check that new accounts are captured
+    QSignalSpy accountIdsChangedSpy(TelepathyHelper::instance(), SIGNAL(accountIdsChanged()));
+    Tp::AccountPtr newAccount = addAccount("mock", "mock", "extra");
+    QVERIFY(!newAccount.isNull());
+
+    QTRY_COMPARE(accountIdsChangedSpy.count(), 1);
+    QCOMPARE(TelepathyHelper::instance()->accountIds().count(), 3);
+
+    // just to make sure check that each account id matches one account
+    for (int i = 0; i < 3; ++i) {
+        QCOMPARE(TelepathyHelper::instance()->accountIds()[i], TelepathyHelper::instance()->accounts()[i]->accountId());
+    }
+
+    // now remove the extra account and make sure it is properly removed
+    accountIdsChangedSpy.clear();
+    QVERIFY(removeAccount(newAccount));
+    QTRY_COMPARE(accountIdsChangedSpy.count(), 1);
+    QCOMPARE(TelepathyHelper::instance()->accountIds().count(), 2);
+    QCOMPARE(TelepathyHelper::instance()->accountIds()[0], TelepathyHelper::instance()->accounts()[0]->accountId());
+    QCOMPARE(TelepathyHelper::instance()->accountIds()[1], TelepathyHelper::instance()->accounts()[1]->accountId());
+}
+
+void TelepathyHelperTest::testActiveAccounts()
+{
+    QSignalSpy activeAccountsSpy(TelepathyHelper::instance(), SIGNAL(activeAccountsChanged()));
+
+    // at startup, all accounts are active, so make sure we got two active accounts
+    QCOMPARE(TelepathyHelper::instance()->activeAccounts().count(), 2);
+
+    // now set one of the accounts as offline and make sure it is captured
+    mGenericController->setOnline(false);
+    QTRY_COMPARE(activeAccountsSpy.count(), 1);
+    QCOMPARE(TelepathyHelper::instance()->activeAccounts().count(), 1);
+    QCOMPARE(TelepathyHelper::instance()->activeAccounts()[0]->accountId(), QString("mock/ofono/account0"));
+
+    // set the other account offline to make sure
+    activeAccountsSpy.clear();
+    mPhoneController->setOnline(false);
+    QTRY_COMPARE(activeAccountsSpy.count(), 1);
+    QVERIFY(TelepathyHelper::instance()->activeAccounts().isEmpty());
+
+    // and set both accounts online again
+    activeAccountsSpy.clear();
+    mGenericController->setOnline(true);
+    mPhoneController->setOnline(true);
+    QTRY_COMPARE(activeAccountsSpy.count(), 2);
+    QCOMPARE(TelepathyHelper::instance()->activeAccounts().count(), 2);
+}
+
+void TelepathyHelperTest::testAccountForId()
+{
+    AccountEntry *genericAccount = TelepathyHelper::instance()->accountForId("mock/mock/account0");
+    QVERIFY(genericAccount);
+    QCOMPARE(genericAccount->accountId(), QString("mock/mock/account0"));
+
+    AccountEntry *phoneAccount = TelepathyHelper::instance()->accountForId("mock/ofono/account0");
+    QVERIFY(phoneAccount);
+    QCOMPARE(phoneAccount->accountId(), QString("mock/ofono/account0"));
+}
+
+void TelepathyHelperTest::testAccountForConnection()
+{
+    Q_FOREACH(AccountEntry *account, TelepathyHelper::instance()->accounts()) {
+        AccountEntry *entry = TelepathyHelper::instance()->accountForConnection(account->account()->connection());
+        QVERIFY(entry);
+        QCOMPARE(entry, account);
+    }
+}
+
+void TelepathyHelperTest::testEmergencyCallsAvailable()
+{
+    QSignalSpy emergencyCallsSpy(TelepathyHelper::instance(), SIGNAL(emergencyCallsAvailableChanged()));
+
+    // check that calls are available by default
+    QVERIFY(TelepathyHelper::instance()->emergencyCallsAvailable());
+
+    // set the generic account as "flightmode" and make sure it doesn't affect the emergencyCallsAvailable
+    mGenericController->setPresence("flightmode", "");
+    QTest::qWait(500);
+    QCOMPARE(emergencyCallsSpy.count(), 0);
+    QVERIFY(TelepathyHelper::instance()->emergencyCallsAvailable());
+
+    // now set the phone account as "flightmode", and see if the emergencyCallsAvailable value
+    mPhoneController->setPresence("flightmode", "");
+    QTRY_COMPARE(emergencyCallsSpy.count(), 1);
+    QVERIFY(!TelepathyHelper::instance()->emergencyCallsAvailable());
+
+    // set the generic account online and check if it affects the value
+    emergencyCallsSpy.clear();
+    mGenericController->setOnline(true);
+    QTest::qWait(500);
+    QCOMPARE(emergencyCallsSpy.count(), 0);
+    QVERIFY(!TelepathyHelper::instance()->emergencyCallsAvailable());
+
+    // and finally set the phone account back online
+    mPhoneController->setOnline(true);
+    QTRY_COMPARE(emergencyCallsSpy.count(), 1);
+    QVERIFY(TelepathyHelper::instance()->emergencyCallsAvailable());
+}
+
+// -------------- Helpers -----------------------
+
+void TelepathyHelperTest::onAccountManagerReady(Tp::PendingOperation *op)
+{
+    ready = true;
+}
+
+Tp::AccountPtr TelepathyHelperTest::addAccount(const QString &manager, const QString &protocol, const QString &displayName, const QVariantMap &parameters)
+{
+    bool finished = false;
+    Tp::AccountPtr account;
+    connect(mAccountManager->createAccount(manager, protocol, displayName, parameters), &Tp::PendingOperation::finished,
+            [&](Tp::PendingOperation *op) {
+        Tp::PendingAccount *pa = qobject_cast<Tp::PendingAccount*>(op);
+        if (op->isError() || !pa) {
+            qCritical() << "Failed to create account:" << op->errorName() << op->errorMessage();
+            finished = true;
+            return;
+        }
+
+        account = pa->account();
+        finished = true;
+    });
+
+    while (!finished) {
+        QTest::qWait(100);
+    }
+    return account;
+}
+
+bool TelepathyHelperTest::removeAccount(const Tp::AccountPtr &account)
+{
+    bool success = false;
+    bool finished = false;
+
+    connect(account->remove(), &Tp::PendingOperation::finished,
+            [&](Tp::PendingOperation *op) {
+        success = !op->isError();
+        finished = true;
+    });
+
+    while (!finished) {
+        QTest::qWait(100);
+    }
+    return success;
+}
+
+QTEST_MAIN(TelepathyHelperTest)
+#include "TelepathyHelperTest.moc"

