2
0
mirror of https://github.com/ars3niy/tdlib-purple synced 2025-09-01 14:35:11 +00:00

Added a real test case with login sequence

This commit is contained in:
Arseniy Lartsev
2020-05-06 20:19:26 +02:00
parent 37a03df751
commit df08fa12f9
10 changed files with 1413 additions and 22 deletions

View File

@@ -44,6 +44,14 @@ Copy the .so to libpurple plugins directory.
It's good to have telegram-purple installed as well since its icon is used at the moment. It's good to have telegram-purple installed as well since its icon is used at the moment.
## Regression test
Build google test library and `make install` it somewhere
Run cmake with '-DGTEST_PATH=/path/to/gtest'
`make run-tests` or `test/tests` or `valgrind test/tests`
## GPL compatibility: building tdlib with OpenSSL 3.0 ## GPL compatibility: building tdlib with OpenSSL 3.0
OpenSSL versions prior to 3.0 branch have license with advertisement clause, making it incompatible with GPL. If this is a concern, a possible solution is to build with OpenSSL 3.0 which uses Apache 2.0 license. OpenSSL versions prior to 3.0 branch have license with advertisement clause, making it incompatible with GPL. If this is a concern, a possible solution is to build with OpenSSL 3.0 which uses Apache 2.0 license.

View File

@@ -275,8 +275,9 @@ void PurpleTdClient::setPurpleConnectionInProgress()
purple_debug_misc(config::pluginId, "Connection in progress\n"); purple_debug_misc(config::pluginId, "Connection in progress\n");
PurpleConnection *gc = purple_account_get_connection(m_account); PurpleConnection *gc = purple_account_get_connection(m_account);
if (PURPLE_CONNECTION_IS_CONNECTED(gc))
purple_blist_remove_account(m_account);
purple_connection_set_state (gc, PURPLE_CONNECTING); purple_connection_set_state (gc, PURPLE_CONNECTING);
purple_blist_remove_account(m_account);
purple_connection_update_progress(gc, "Connecting", 1, 3); purple_connection_update_progress(gc, "Connecting", 1, 3);
} }

View File

@@ -8,6 +8,7 @@ add_executable(tests EXCLUDE_FROM_ALL
testsuite.cpp testsuite.cpp
test-transceiver.cpp test-transceiver.cpp
libpurple-mock.cpp libpurple-mock.cpp
printout.cpp
../tdlib-purple.cpp ../tdlib-purple.cpp
../td-client.cpp ../td-client.cpp

View File

@@ -64,7 +64,7 @@ void purple_account_destroy(PurpleAccount *account)
{ {
free(account->username); free(account->username);
free(account->alias); free(account->alias);
free(account); delete account;
} }
void purple_blist_add_account(PurpleAccount *account) void purple_blist_add_account(PurpleAccount *account)
@@ -89,7 +89,7 @@ void purple_blist_remove_buddy(PurpleBuddy *buddy)
// TODO add event // TODO add event
free(buddy->name); free(buddy->name);
free(buddy->alias); free(buddy->alias);
free(buddy); delete buddy;
} }
const char *purple_buddy_get_alias_only(PurpleBuddy *buddy) const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
@@ -176,7 +176,7 @@ PurpleConversation *purple_conversation_new(PurpleConversationType type,
void purple_conversation_destroy(PurpleConversation *conv) void purple_conversation_destroy(PurpleConversation *conv)
{ {
free(conv->name); free(conv->name);
free(conv); delete conv;
} }
void purple_conversation_write(PurpleConversation *conv, const char *who, void purple_conversation_write(PurpleConversation *conv, const char *who,
@@ -309,7 +309,7 @@ void purple_xfer_unref(PurpleXfer *xfer)
{ {
if (--xfer->ref == 0) { if (--xfer->ref == 0) {
free(xfer->who); free(xfer->who);
free(xfer); delete xfer;
} }
} }

1219
test/printout.cpp Normal file

File diff suppressed because it is too large Load Diff

9
test/printout.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef _PRINTOUT_H
#define _PRINTOUT_H
#include <td/telegram/td_api.hpp>
std::string requestToString(const td::td_api::Function &req);
std::string responseToString(const td::td_api::Object &object);
#endif

View File

@@ -1,17 +1,15 @@
#include "test-transceiver.h" #include "test-transceiver.h"
#include "printout.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
static std::string requestToString(const td::td_api::Function &req) using namespace td::td_api;
{
return "whatever";
}
void TestTransceiver::send(td::Client::Request &&request) void TestTransceiver::send(td::Client::Request &&request)
{ {
m_requests.push(std::move(request)); m_requests.push(std::move(request));
} }
void TestTransceiver::verifyRequest(const td::td_api::Function &request) void TestTransceiver::verifyRequest(const Function &request)
{ {
m_lastRequestIds.clear(); m_lastRequestIds.clear();
verifyRequestImpl(request); verifyRequestImpl(request);
@@ -20,10 +18,10 @@ void TestTransceiver::verifyRequest(const td::td_api::Function &request)
verifyNoRequests(); verifyNoRequests();
} }
void TestTransceiver::verifyRequests(const std::vector<td::td_api::Function> &requests) void TestTransceiver::verifyRequests(const std::vector<Function> &requests)
{ {
m_lastRequestIds.clear(); m_lastRequestIds.clear();
for (const td::td_api::Function &req: requests) { for (const Function &req: requests) {
verifyRequestImpl(req); verifyRequestImpl(req);
m_lastRequestIds.push_back(m_requests.front().id); m_lastRequestIds.push_back(m_requests.front().id);
m_requests.pop(); m_requests.pop();
@@ -31,12 +29,67 @@ void TestTransceiver::verifyRequests(const std::vector<td::td_api::Function> &re
verifyNoRequests(); verifyNoRequests();
} }
void TestTransceiver::verifyRequestImpl(const td::td_api::Function &request) static void compare(const Function &actual, const Function &expected)
{
}
static void compare(const setTdlibParameters &actual, const setTdlibParameters &expected)
{
EXPECT_EQ(expected.parameters_->database_directory_, actual.parameters_->database_directory_);
}
static void compare(const checkDatabaseEncryptionKey &actual, const checkDatabaseEncryptionKey &expected)
{
EXPECT_EQ(expected.encryption_key_, actual.encryption_key_);
}
static void compare(const setAuthenticationPhoneNumber &actual, const setAuthenticationPhoneNumber &expected)
{
EXPECT_EQ(expected.phone_number_, actual.phone_number_);
EXPECT_TRUE((expected.settings_ != nullptr) == (actual.settings_ != nullptr));
}
static void compareRequests(const Function &actual, const Function &expected)
{
EXPECT_EQ(expected.get_id(), actual.get_id()) << "Wrong request type: expected " << requestToString(expected);
#define C(class) case class::ID: \
compare(static_cast<const class &>(actual), static_cast<const class &>(expected)); \
break;
switch (actual.get_id()) {
C(setTdlibParameters)
C(checkDatabaseEncryptionKey)
C(setAuthenticationPhoneNumber)
default:
compare(actual, expected);
break;
}
}
void TestTransceiver::verifyRequestImpl(const Function &request)
{ {
EXPECT_FALSE(m_requests.empty()) << "Missing request: expected " << requestToString(request); EXPECT_FALSE(m_requests.empty()) << "Missing request: expected " << requestToString(request);
std::cout << "Received request " << m_requests.front().id << ": " << requestToString(*m_requests.front().function) << "\n";
compareRequests(*m_requests.front().function, request);
} }
void TestTransceiver::verifyNoRequests() void TestTransceiver::verifyNoRequests()
{ {
EXPECT_TRUE(m_requests.empty()) << "Unexpected request: " << requestToString(*m_requests.front().function); EXPECT_TRUE(m_requests.empty()) << "Unexpected request: " << requestToString(*m_requests.front().function);
} }
void TestTransceiver::update(object_ptr<Object> object)
{
std::cout << "Sending update: " << responseToString(*object) << "\n";
receive({0, std::move(object)});
}
void TestTransceiver::reply(object_ptr<Object> object)
{
EXPECT_GE(1u, m_lastRequestIds.size()) << "No requests to reply to";
std::cout << "Replying to request " << m_lastRequestIds.front() << ": " << responseToString(*object) << "\n";
receive({m_lastRequestIds.front(), std::move(object)});
m_lastRequestIds.erase(m_lastRequestIds.begin());
}

View File

@@ -11,6 +11,8 @@ public:
void verifyRequest(const td::td_api::Function &request); void verifyRequest(const td::td_api::Function &request);
void verifyRequests(const std::vector<td::td_api::Function> &requests); void verifyRequests(const std::vector<td::td_api::Function> &requests);
void verifyNoRequests(); void verifyNoRequests();
void update(td::td_api::object_ptr<td::td_api::Object> object);
void reply(td::td_api::object_ptr<td::td_api::Object> object);
private: private:
std::queue<td::Client::Request> m_requests; std::queue<td::Client::Request> m_requests;
std::vector<uint64_t> m_lastRequestIds; std::vector<uint64_t> m_lastRequestIds;

View File

@@ -2,32 +2,128 @@
#include "tdlib-purple.h" #include "tdlib-purple.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
using namespace td::td_api;
class CommTest: public testing::Test { class CommTest: public testing::Test {
public: public:
CommTest(); CommTest();
~CommTest();
private: private:
const std::string phoneNumber = "1234567"; const std::string phoneNumber = "1234567";
const int selfId = 1;
const std::string selfFirstName = "Isaac";
const std::string selfLastName = "Newton";
TestTransceiver tgl; TestTransceiver tgl;
PurplePlugin purplePlugin; PurplePlugin purplePlugin;
PurpleAccount *account; PurpleAccount *account;
PurpleConnection *connection;
protected:
void SetUp() override;
void TearDown() override;
void login();
}; };
CommTest::CommTest() CommTest::CommTest()
{ {
tgprpl_set_test_backend(&tgl); tgprpl_set_test_backend(&tgl);
purple_init_plugin(&purplePlugin); purple_init_plugin(&purplePlugin);
account = purple_account_new(phoneNumber.c_str(), NULL);
((PurplePluginProtocolInfo *)purplePlugin.info->extra_info)->login(account);
} }
CommTest::~CommTest() void CommTest::SetUp()
{ {
account = purple_account_new(phoneNumber.c_str(), NULL);
connection = new PurpleConnection;
account->gc = connection;
}
void CommTest::TearDown()
{
tgl.verifyNoRequests();
if (purple_connection_get_protocol_data(connection))
((PurplePluginProtocolInfo *)purplePlugin.info->extra_info)->close(connection);
delete connection;
purple_account_destroy(account); purple_account_destroy(account);
} }
TEST_F(CommTest, dummy) void CommTest::login()
{ {
EXPECT_EQ(4, 2*2); ((PurplePluginProtocolInfo *)purplePlugin.info->extra_info)->login(account);
tgl.update(make_object<updateAuthorizationState>(make_object<authorizationStateWaitTdlibParameters>()));
tgl.verifyRequest(setTdlibParameters(make_object<tdlibParameters>(
false,
std::string(purple_user_dir()) + G_DIR_SEPARATOR_S +
"tdlib" + G_DIR_SEPARATOR_S + phoneNumber,
"",
false,
false,
false,
false,
0,
"",
"",
"",
"",
"",
false,
false
)));
tgl.reply(make_object<ok>());
// TODO: what if is_encrypted = false?
tgl.update(make_object<updateAuthorizationState>(make_object<authorizationStateWaitEncryptionKey>(true)));
tgl.verifyRequest(checkDatabaseEncryptionKey(""));
tgl.reply(make_object<ok>());
tgl.update(make_object<updateAuthorizationState>(make_object<authorizationStateWaitPhoneNumber>()));
tgl.verifyRequest(setAuthenticationPhoneNumber(phoneNumber, nullptr));
tgl.reply(make_object<ok>());
tgl.update(make_object<updateAuthorizationState>(make_object<authorizationStateReady>()));
tgl.verifyNoRequests();
tgl.update(make_object<updateConnectionState>(make_object<connectionStateConnecting>()));
tgl.verifyNoRequests();
// TODO: verify purple_connection_set_state and purple_connection_update_progress
tgl.update(make_object<updateConnectionState>(make_object<connectionStateUpdating>()));
tgl.verifyNoRequests();
// TODO: verify purple_connection_update_progress
tgl.update(make_object<updateConnectionState>(make_object<connectionStateReady>()));
tgl.verifyRequest(getContacts());
tgl.update(make_object<updateUser>(make_object<user>(
selfId,
selfFirstName,
selfLastName,
"",
phoneNumber,
make_object<userStatusOffline>(),
nullptr,
false,
false,
false,
false,
"",
false,
true,
make_object<userTypeRegular>(),
""
)));
tgl.verifyNoRequests();
// TODO: test sending some users and chats
tgl.reply(make_object<users>());
tgl.verifyRequest(getChats());
// TODO: test sending some chats
tgl.reply(make_object<chats>());
// TODO: verfy purple_account_set_alias
}
TEST_F(CommTest, login)
{
login();
} }

View File

@@ -65,8 +65,10 @@ TdTransceiver::TdTransceiver(PurpleTdClient *owner, UpdateCb updateCb, ITranscei
TdTransceiver::~TdTransceiver() TdTransceiver::~TdTransceiver()
{ {
m_stopThread = true; m_stopThread = true;
m_impl->m_client->send({UINT64_MAX, td::td_api::make_object<td::td_api::close>()}); if (!m_testBackend) {
m_pollThread.join(); m_impl->m_client->send({UINT64_MAX, td::td_api::make_object<td::td_api::close>()});
m_pollThread.join();
}
// Orphan m_impl - if the background thread generated idle callbacks while we were waiting for // Orphan m_impl - if the background thread generated idle callbacks while we were waiting for
// it to quit, those callbacks will be called after this destructor return (doing nothing, as // it to quit, those callbacks will be called after this destructor return (doing nothing, as