feat(AbstractDb): add a way to deal with exceptions

This commit is contained in:
Ronan Abhamon 2018-01-24 17:18:53 +01:00
parent 8f24505766
commit 9072624acb
28 changed files with 883 additions and 1081 deletions

View file

@ -21,6 +21,7 @@
#define _L_GENERAL_H_
#ifdef __cplusplus
#include <memory>
#include <type_traits>
#endif
@ -101,6 +102,12 @@ void l_assert (const char *condition, const char *file, int line);
// Define an integer version like: 0xXXYYZZ, XX=MAJOR, YY=MINOR, and ZZ=PATCH.
#define L_VERSION(MAJOR, MINOR, PATCH) (((MAJOR) << 16) | ((MINOR) << 8) | (PATCH))
// Not available in C++11...
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args && ...args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// -----------------------------------------------------------------------------
// Data access.
// -----------------------------------------------------------------------------

View file

@ -21,7 +21,6 @@
#define _L_UTILS_H_
#include <ctime>
#include <memory>
#include <string>
#include <vector>

View file

@ -113,8 +113,6 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
db/main-db-p.h
db/main-db.h
db/session/db-session-p.h
db/session/db-session-provider.h
db/session/db-session.h
dial-plan/dial-plan-p.h
dial-plan/dial-plan.h
enums.h
@ -222,7 +220,6 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
db/main-db-event-key.cpp
db/main-db-key.cpp
db/main-db.cpp
db/session/db-session-provider.cpp
db/session/db-session.cpp
dial-plan/dial-plan.cpp
event-log/conference/conference-call-event.cpp

View file

@ -20,8 +20,6 @@
#ifndef _L_CHAT_ROOM_LISTENER_H_
#define _L_CHAT_ROOM_LISTENER_H_
#include <memory>
#include "chat/chat-room/abstract-chat-room.h"
// =============================================================================

View file

@ -21,7 +21,6 @@
#define _L_CPIM_GENERIC_HEADER_H_
#include <list>
#include <memory>
#include "cpim-header.h"

View file

@ -21,7 +21,6 @@
#define _L_CONFERENCE_INTERFACE_H_
#include <list>
#include <memory>
#include "linphone/utils/general.h"

View file

@ -20,8 +20,6 @@
#ifndef _L_LOCAL_CONFERENCE_EVENT_HANDLER_H_
#define _L_LOCAL_CONFERENCE_EVENT_HANDLER_H_
#include <memory>
#include "linphone/types.h"
#include "address/address.h"

View file

@ -20,8 +20,6 @@
#ifndef _L_CALL_SESSION_PARAMS_H_
#define _L_CALL_SESSION_PARAMS_H_
#include <memory>
#include "object/clonable-object.h"
#include "linphone/types.h"

View file

@ -20,8 +20,6 @@
#ifndef _L_MEDIA_SESSION_PARAMS_P_H_
#define _L_MEDIA_SESSION_PARAMS_P_H_
#include <memory>
#include "call-session-params-p.h"
#include "media-session-params.h"

View file

@ -20,7 +20,6 @@
#ifndef _L_PARTICIPANT_DEVICE_H_
#define _L_PARTICIPANT_DEVICE_H_
#include <memory>
#include <string>
#include "address/identity-address.h"

View file

@ -20,8 +20,6 @@
#ifndef _L_PARTICIPANT_P_H_
#define _L_PARTICIPANT_P_H_
#include <memory>
#include "object/object-p.h"
#include "conference/participant.h"

View file

@ -20,8 +20,6 @@
#ifndef _L_CORE_ACCESSOR_H_
#define _L_CORE_ACCESSOR_H_
#include <memory>
#include "linphone/utils/general.h"
// =============================================================================

View file

@ -17,18 +17,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef SOCI_ENABLED
#include <soci/soci.h>
#endif // ifdef SOCI_ENABLED
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif // ifdef __APPLE__
#include "linphone/utils/utils.h"
#include "abstract-db-p.h"
#include "db/session/db-session-provider.h"
#include "logger/logger.h"
// =============================================================================
@ -40,28 +33,30 @@ LINPHONE_BEGIN_NAMESPACE
AbstractDb::AbstractDb (AbstractDbPrivate &p) : Object(p) {}
// Force static sqlite3 linking for IOS and Android.
#if TARGET_OS_IPHONE || defined(__ANDROID__)
#if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
extern "C" void register_factory_sqlite3();
#endif // TARGET_OS_IPHONE || defined(__ANDROID__)
#endif // if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
bool AbstractDb::connect (Backend backend, const string &parameters) {
L_D();
#if TARGET_OS_IPHONE || defined(__ANDROID__)
#if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
if (backend == Sqlite3)
register_factory_sqlite3();
#endif // defined(TARGET_OS_IPHONE) || defined(__ANDROID__)
#endif // if defined(SOCI_ENABLED) && (TARGET_OS_IPHONE || defined(__ANDROID__))
d->backend = backend;
d->dbSession = DbSessionProvider::getInstance()->getSession(
d->dbSession = DbSession(
(backend == Mysql ? "mysql://" : "sqlite3://") + parameters
);
if (d->dbSession) {
try {
enableForeignKeys(false);
init();
enableForeignKeys(true);
#ifdef SOCI_ENABLED
d->dbSession.enableForeignKeys(false);
init();
d->dbSession.enableForeignKeys(true);
#endif // ifdef SOCI_ENABLED
} catch (const exception &e) {
lWarning() << "Unable to init database: " << e.what();
@ -73,9 +68,46 @@ bool AbstractDb::connect (Backend backend, const string &parameters) {
return d->dbSession;
}
bool AbstractDb::isConnected () const {
void AbstractDb::disconnect () {
L_D();
return d->dbSession;
d->dbSession = DbSession();
}
bool AbstractDb::forceReconnect () {
L_D();
if (!d->dbSession) {
lWarning() << "Unable to reconnect. Not a valid database session.";
return false;
}
#ifdef SOCI_ENABLED
constexpr int retryCount = 2;
lInfo() << "Trying sql backend reconnect...";
try {
soci::session *session = d->dbSession.getBackendSession();
session->close();
for (int i = 0; i < retryCount; ++i) {
try {
lInfo() << "Reconnect... Try: " << i;
session->reconnect();
lInfo() << "Database reconnection successful!";
return true;
} catch (const soci::soci_error &e) {
if (e.get_error_category() != soci::soci_error::connection_error)
throw e;
}
}
} catch (const exception &e) {
lError() << "Unable to reconnect: `" << e.what() << "`.";
return false;
}
lError() << "Database reconnection failed!";
#endif // ifdef SOCI_ENABLED
return false;
}
AbstractDb::Backend AbstractDb::getBackend () const {
@ -93,134 +125,4 @@ void AbstractDb::init () {
// Nothing.
}
// -----------------------------------------------------------------------------
string AbstractDb::primaryKeyStr (const string &type) const {
L_D();
switch (d->backend) {
case Mysql:
return " " + type + " AUTO_INCREMENT PRIMARY KEY";
case Sqlite3:
// See: ROWIDs and the INTEGER PRIMARY KEY
// https://www.sqlite.org/lang_createtable.html
return " INTEGER PRIMARY KEY ASC";
}
L_ASSERT(false);
return "";
}
string AbstractDb::primaryKeyRefStr (const string &type) const {
L_D();
switch (d->backend) {
case Mysql:
return " " + type;
case Sqlite3:
return " INTEGER";
}
L_ASSERT(false);
return "";
}
string AbstractDb::varcharPrimaryKeyStr (int length) const {
L_D();
switch (d->backend) {
case Mysql:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
case Sqlite3:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
}
L_ASSERT(false);
return "";
}
string AbstractDb::timestampType () const {
L_D();
switch (d->backend) {
case Mysql:
return " TIMESTAMP";
case Sqlite3:
return " DATE";
}
L_ASSERT(false);
return "";
}
string AbstractDb::noLimitValue () const {
L_D();
switch (d->backend) {
case Mysql:
return "9999999999999999999";
case Sqlite3:
return "-1";
}
L_ASSERT(false);
return "";
}
long long AbstractDb::getLastInsertId () const {
long long id = 0;
#ifdef SOCI_ENABLED
L_D();
string sql;
switch (d->backend) {
case Mysql:
sql = "SELECT LAST_INSERT_ID()";
break;
case Sqlite3:
sql = "SELECT last_insert_rowid()";
break;
}
soci::session *session = d->dbSession.getBackendSession<soci::session>();
*session << sql, soci::into(id);
#endif // ifdef SOCI_ENABLED
return id;
}
void AbstractDb::enableForeignKeys (bool status) {
#ifdef SOCI_ENABLED
L_D();
soci::session *session = d->dbSession.getBackendSession<soci::session>();
switch (d->backend) {
case Mysql:
*session << string("SET FOREIGN_KEY_CHECKS = ") + (status ? "1" : "0");
break;
case Sqlite3:
*session << string("PRAGMA foreign_keys = ") + (status ? "ON" : "OFF");
break;
}
#endif // ifdef SOCI_ENABLED
}
bool AbstractDb::checkTableExists (const string &table) const {
#ifdef SOCI_ENABLED
L_D();
soci::session *session = d->dbSession.getBackendSession<soci::session>();
switch (d->backend) {
case Mysql:
*session << "SHOW TABLES LIKE :table", soci::use(table);
return session->got_data() > 0;
case Sqlite3:
*session << "SELECT name FROM sqlite_master WHERE type='table' AND name=:table", soci::use(table);
return session->got_data() > 0;
}
#endif // ifdef SOCI_ENABLED
L_ASSERT(false);
return false;
}
LINPHONE_END_NAMESPACE

View file

@ -38,9 +38,9 @@ public:
virtual ~AbstractDb () = default;
bool connect (Backend backend, const std::string &parameters);
bool disconnect ();
void disconnect ();
bool isConnected () const;
bool forceReconnect ();
Backend getBackend () const;
@ -51,20 +51,6 @@ protected:
virtual void init ();
std::string primaryKeyStr (const std::string &type = "INT") const;
std::string primaryKeyRefStr (const std::string &type = "INT") const;
std::string varcharPrimaryKeyStr (int length) const;
std::string timestampType () const;
std::string noLimitValue () const;
long long getLastInsertId () const;
void enableForeignKeys (bool status);
bool checkTableExists (const std::string &table) const;
private:
L_DECLARE_PRIVATE(AbstractDb);
L_DISABLE_COPY(AbstractDb);

View file

@ -20,8 +20,6 @@
#ifndef _L_MAIN_DB_KEY_H_
#define _L_MAIN_DB_KEY_H_
#include <memory>
#include "object/clonable-object.h"
// =============================================================================

View file

@ -20,6 +20,8 @@
#ifndef _L_MAIN_DB_P_H_
#define _L_MAIN_DB_P_H_
#include <unordered_map>
#include "abstract/abstract-db-p.h"
#include "event-log/event-log.h"
#include "main-db.h"

File diff suppressed because it is too large Load diff

View file

@ -20,8 +20,6 @@
#ifndef _L_DB_SESSION_P_H_
#define _L_DB_SESSION_P_H_
#include <memory>
#include "db-session.h"
#include "object/clonable-object-p.h"
@ -33,10 +31,15 @@ class DbSessionPrivate : public ClonableObjectPrivate {
friend class DbSessionProvider;
private:
bool isValid = false;
enum class Backend {
None,
Mysql,
Sqlite3
} backend;
DbSession::Type type = DbSession::None;
std::shared_ptr<void> backendSession;
#ifdef SOCI_ENABLED
std::unique_ptr<soci::session> backendSession;
#endif // ifdef SOCI_ENABLED
L_DECLARE_PUBLIC(DbSession);
};

View file

@ -1,58 +0,0 @@
/*
* db-session-provider.cpp
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* This program 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef SOCI_ENABLED
#include <soci/soci.h>
#endif // ifdef SOCI_ENABLED
#include "db-session-p.h"
#include "logger/logger.h"
#include "object/object-p.h"
#include "db-session-provider.h"
// =============================================================================
using namespace std;
LINPHONE_BEGIN_NAMESPACE
DbSessionProvider::DbSessionProvider () : Singleton(*new ObjectPrivate) {}
DbSession DbSessionProvider::getSession (const string &uri) {
DbSession session(DbSession::None);
#ifdef SOCI_ENABLED
try {
session = DbSession(DbSession::Soci);
DbSessionPrivate *p = session.getPrivate();
p->backendSession = make_shared<soci::session>(uri);
p->isValid = true;
} catch (const exception &e) {
session = DbSession(DbSession::None);
lWarning() << "Unable to get db session: " << e.what();
}
#else
lWarning() << "Unable to get db session: soci not enabled.";
#endif // ifdef SOCI_ENABLED
return session;
}
LINPHONE_END_NAMESPACE

View file

@ -1,46 +0,0 @@
/*
* db-session-provider.h
* Copyright (C) 2010-2018 Belledonne Communications SARL
*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* This program 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _L_DB_SESSION_PROVIDER_H_
#define _L_DB_SESSION_PROVIDER_H_
#include "db-session.h"
#include "object/singleton.h"
// =============================================================================
LINPHONE_BEGIN_NAMESPACE
class DbSessionProviderPrivate;
class DbSessionProvider : public Singleton<DbSessionProvider> {
friend class Singleton<DbSessionProvider>;
public:
DbSession getSession (const std::string &uri);
private:
DbSessionProvider ();
L_DISABLE_COPY(DbSessionProvider);
};
LINPHONE_END_NAMESPACE
#endif // ifndef _L_DB_SESSION_PROVIDER_H_

View file

@ -17,7 +17,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "linphone/utils/utils.h"
#include "db-session-p.h"
#include "logger/logger.h"
// =============================================================================
@ -25,26 +28,176 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
DbSession::DbSession (Type type) : ClonableObject(*new DbSessionPrivate) {
L_D();
d->type = type;
DbSession::DbSession () : ClonableObject(*new DbSessionPrivate) {}
DbSession::DbSession (const string &uri) : DbSession() {
#ifdef SOCI_ENABLED
try {
L_D();
d->backendSession = make_unique<soci::session>(uri);
d->backend = !uri.find("mysql") ? DbSessionPrivate::Backend::Mysql : DbSessionPrivate::Backend::Sqlite3;
} catch (const exception &e) {
lWarning() << "Unable to build db session with uri: " << e.what();
}
#else
lWarning() << "Unable to build db session with uri: soci not enabled.";
#endif // ifdef SOCI_ENABLED
}
DbSession::operator bool () const {
L_D();
return d->isValid;
return d->backend != DbSessionPrivate::Backend::None;
}
L_USE_DEFAULT_CLONABLE_OBJECT_SHARED_IMPL(DbSession);
DbSession::Type DbSession::getBackendType () const {
#ifdef SOCI_ENABLED
soci::session *DbSession::getBackendSession () const {
L_D();
return d->backendSession.get();
}
#endif // ifdef SOCI_ENABLED
string DbSession::primaryKeyStr (const string &type) const {
L_D();
return d->type;
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " " + type + " AUTO_INCREMENT PRIMARY KEY";
case DbSessionPrivate::Backend::Sqlite3:
// See: ROWIDs and the INTEGER PRIMARY KEY
// https://www.sqlite.org/lang_createtable.html
return " INTEGER PRIMARY KEY ASC";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
void *DbSession::getBackendSession () const {
string DbSession::primaryKeyRefStr (const string &type) const {
L_D();
return d->backendSession.get();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " " + type;
case DbSessionPrivate::Backend::Sqlite3:
return " INTEGER";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::varcharPrimaryKeyStr (int length) const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
case DbSessionPrivate::Backend::Sqlite3:
return " VARCHAR(" + Utils::toString(length) + ") PRIMARY KEY";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::timestampType () const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " TIMESTAMP";
case DbSessionPrivate::Backend::Sqlite3:
return " DATE";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::noLimitValue () const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return "9999999999999999999";
case DbSessionPrivate::Backend::Sqlite3:
return "-1";
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
long long DbSession::getLastInsertId () const {
long long id = 0;
L_D();
string sql;
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
sql = "SELECT LAST_INSERT_ID()";
break;
case DbSessionPrivate::Backend::Sqlite3:
sql = "SELECT last_insert_rowid()";
break;
case DbSessionPrivate::Backend::None:
break;
}
#ifdef SOCI_ENABLED
*d->backendSession << sql, soci::into(id);
#endif // ifdef SOCI_ENABLED
return id;
}
void DbSession::enableForeignKeys (bool status) {
#ifdef SOCI_ENABLED
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
*d->backendSession << string("SET FOREIGN_KEY_CHECKS = ") + (status ? "1" : "0");
break;
case DbSessionPrivate::Backend::Sqlite3:
*d->backendSession << string("PRAGMA foreign_keys = ") + (status ? "ON" : "OFF");
break;
case DbSessionPrivate::Backend::None:
break;
}
#endif // ifdef SOCI_ENABLED
}
bool DbSession::checkTableExists (const string &table) const {
#ifdef SOCI_ENABLED
L_D();
soci::session *session = d->backendSession.get();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
*session << "SHOW TABLES LIKE :table", soci::use(table);
return session->got_data() > 0;
case DbSessionPrivate::Backend::Sqlite3:
*session << "SELECT name FROM sqlite_master WHERE type='table' AND name=:table", soci::use(table);
return session->got_data() > 0;
case DbSessionPrivate::Backend::None:
return false;
}
#endif // ifdef SOCI_ENABLED
L_ASSERT(false);
return false;
}
LINPHONE_END_NAMESPACE

View file

@ -20,16 +20,14 @@
#ifndef _L_DB_SESSION_H_
#define _L_DB_SESSION_H_
#ifdef SOCI_ENABLED
#include <soci/soci.h>
#endif // ifdef SOCI_ENABLED
#include "object/clonable-object.h"
// =============================================================================
#ifdef SOCI_ENABLED
namespace soci {
class session;
}
#endif // ifdef SOCI_ENABLED
LINPHONE_BEGIN_NAMESPACE
class DbSessionPrivate;
@ -38,54 +36,36 @@ class DbSession : public ClonableObject {
friend class DbSessionProvider;
public:
enum Type {
None,
Soci
};
explicit DbSession (Type type = None);
DbSession ();
explicit DbSession (const std::string &uri);
DbSession (const DbSession &src);
DbSession &operator= (const DbSession &src);
operator bool () const;
Type getBackendType () const;
#ifdef SOCI_ENABLED
soci::session *getBackendSession () const;
#endif // ifdef SOCI_ENABLED
template<typename T>
T *getBackendSession () const;
std::string primaryKeyStr (const std::string &type = "INT") const;
std::string primaryKeyRefStr (const std::string &type = "INT") const;
std::string varcharPrimaryKeyStr (int length) const;
std::string timestampType () const;
std::string noLimitValue () const;
long long getLastInsertId () const;
void enableForeignKeys (bool status);
bool checkTableExists (const std::string &table) const;
private:
void *getBackendSession () const;
L_DECLARE_PRIVATE(DbSession);
};
// -----------------------------------------------------------------------------
template<typename T>
struct TypeOfDbSession {
static const DbSession::Type type = DbSession::None;
};
#ifdef SOCI_ENABLED
template<>
struct TypeOfDbSession<::soci::session> {
static const DbSession::Type type = DbSession::Soci;
};
#endif // ifdef SOCI_ENABLED
template<typename T>
T *DbSession::getBackendSession () const {
typedef TypeOfDbSession<T> Type;
static_assert(Type::type != DbSession::None, "Unable to get backend session, invalid type.");
if (getBackendType() != Type::type)
return nullptr;
return static_cast<T *>(getBackendSession());
}
LINPHONE_END_NAMESPACE
#endif // ifndef _L_DB_SESSION_H_

View file

@ -20,8 +20,6 @@
#ifndef _L_CONFERENCE_CALL_EVENT_H_
#define _L_CONFERENCE_CALL_EVENT_H_
#include <memory>
#include "event-log/event-log.h"
// =============================================================================

View file

@ -20,8 +20,6 @@
#ifndef _L_CHAT_MESSAGE_EVENT_H_
#define _L_CHAT_MESSAGE_EVENT_H_
#include <memory>
#include "conference-event.h"
// =============================================================================

View file

@ -21,7 +21,6 @@
#define _L_EVENT_LOG_H_
#include <ctime>
#include <memory>
#include "linphone/enums/event-log-enums.h"
#include "linphone/utils/enum-generator.h"

View file

@ -18,7 +18,6 @@
*/
#include <chrono>
#include <memory>
#include <bctoolbox/logging.h>

View file

@ -20,7 +20,6 @@
#ifndef _L_OBJECT_H_
#define _L_OBJECT_H_
#include <memory>
#include <mutex>
#include "base-object.h"

View file

@ -64,11 +64,6 @@ private:
// -----------------------------------------------------------------------------
static void open_database () {
MainDbProvider provider;
BC_ASSERT_TRUE(provider.getMainDb().isConnected());
}
static void get_events_count () {
MainDbProvider provider;
const MainDb &mainDb = provider.getMainDb();
@ -193,7 +188,6 @@ static void get_conference_notified_events () {
}
test_t main_db_tests[] = {
TEST_NO_TAG("Open database", open_database),
TEST_NO_TAG("Get events count", get_events_count),
TEST_NO_TAG("Get messages count", get_messages_count),
TEST_NO_TAG("Get unread messages count", get_unread_messages_count),