diff options
Diffstat (limited to 'libs/tdlib/td/test/tdclient.cpp')
-rw-r--r-- | libs/tdlib/td/test/tdclient.cpp | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/libs/tdlib/td/test/tdclient.cpp b/libs/tdlib/td/test/tdclient.cpp new file mode 100644 index 0000000000..7cef0fcbc0 --- /dev/null +++ b/libs/tdlib/td/test/tdclient.cpp @@ -0,0 +1,837 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "data.h" + +#include "td/actor/PromiseFuture.h" + +#include "td/telegram/ClientActor.h" + +#include "td/telegram/td_api.h" + +#include "td/utils/base64.h" +#include "td/utils/BufferedFd.h" +#include "td/utils/filesystem.h" +#include "td/utils/format.h" +#include "td/utils/logging.h" +#include "td/utils/misc.h" +#include "td/utils/port/FileFd.h" +#include "td/utils/port/path.h" +#include "td/utils/Random.h" +#include "td/utils/Slice.h" +#include "td/utils/Status.h" +#include "td/utils/tests.h" + +#include <cstdio> +#include <functional> +#include <map> +#include <memory> +#include <utility> + +REGISTER_TESTS(tdclient); + +namespace td { + +template <class T> +static void check_td_error(T &result) { + CHECK(result->get_id() != td_api::error::ID) << to_string(result); +} + +static void rmrf(CSlice path) { + td::walk_path(path, [](CSlice path, bool is_dir) { + if (is_dir) { + td::rmdir(path).ignore(); + } else { + td::unlink(path).ignore(); + } + }); +} + +class TestClient : public Actor { + public: + explicit TestClient(string name) : name_(std::move(name)) { + } + struct Update { + uint64 id; + tl_object_ptr<td_api::Object> object; + Update(uint64 id, tl_object_ptr<td_api::Object> object) : id(id), object(std::move(object)) { + } + }; + class Listener { + public: + Listener() = default; + Listener(const Listener &) = delete; + Listener &operator=(const Listener &) = delete; + Listener(Listener &&) = delete; + Listener &operator=(Listener &&) = delete; + virtual ~Listener() = default; + virtual void start_listen(TestClient *client) { + } + virtual void stop_listen() { + } + virtual void on_update(std::shared_ptr<Update> update) = 0; + }; + void close(Promise<> close_promise) { + close_promise_ = std::move(close_promise); + td_.reset(); + } + + unique_ptr<TdCallback> make_td_callback() { + class TdCallbackImpl : public TdCallback { + public: + explicit TdCallbackImpl(ActorId<TestClient> client) : client_(client) { + } + void on_result(uint64 id, tl_object_ptr<td_api::Object> result) override { + send_closure(client_, &TestClient::on_result, id, std::move(result)); + } + void on_error(uint64 id, tl_object_ptr<td_api::error> error) override { + send_closure(client_, &TestClient::on_error, id, std::move(error)); + } + void on_closed() override { + send_closure(client_, &TestClient::on_closed); + } + + private: + ActorId<TestClient> client_; + }; + return make_unique<TdCallbackImpl>(actor_id(this)); + } + + void add_listener(std::unique_ptr<Listener> listener) { + auto *ptr = listener.get(); + listeners_.push_back(std::move(listener)); + ptr->start_listen(this); + } + void remove_listener(Listener *listener) { + pending_remove_.push_back(listener); + } + void do_pending_remove_listeners() { + for (auto listener : pending_remove_) { + do_remove_listener(listener); + } + pending_remove_.clear(); + } + void do_remove_listener(Listener *listener) { + for (size_t i = 0; i < listeners_.size(); i++) { + if (listeners_[i].get() == listener) { + listener->stop_listen(); + listeners_.erase(listeners_.begin() + i); + break; + } + } + } + + void on_result(uint64 id, tl_object_ptr<td_api::Object> result) { + on_update(std::make_shared<Update>(id, std::move(result))); + } + void on_error(uint64 id, tl_object_ptr<td_api::error> error) { + on_update(std::make_shared<Update>(id, std::move(error))); + } + void on_update(std::shared_ptr<Update> update) { + for (auto &listener : listeners_) { + listener->on_update(update); + } + do_pending_remove_listeners(); + } + + void on_closed() { + stop(); + } + + void start_up() override { + rmrf(name_); + set_context(std::make_shared<td::ActorContext>()); + set_tag(name_); + LOG(INFO) << "START UP!"; + + td_ = create_actor<ClientActor>("Td-proxy", make_td_callback()); + } + + ActorOwn<ClientActor> td_; + + string name_; + + private: + std::vector<std::unique_ptr<Listener>> listeners_; + std::vector<Listener *> pending_remove_; + + Promise<> close_promise_; +}; + +class Task : public TestClient::Listener { + public: + void on_update(std::shared_ptr<TestClient::Update> update) override { + auto it = sent_queries_.find(update->id); + if (it != sent_queries_.end()) { + it->second(std::move(update->object)); + sent_queries_.erase(it); + } + process_update(update); + } + void start_listen(TestClient *client) override { + client_ = client; + start_up(); + } + virtual void process_update(std::shared_ptr<TestClient::Update> update) { + } + + template <class F> + void send_query(tl_object_ptr<td_api::Function> function, F callback) { + auto id = current_query_id_++; + sent_queries_[id] = std::forward<F>(callback); + send_closure(client_->td_, &ClientActor::request, id, std::move(function)); + } + + protected: + std::map<uint64, std::function<void(tl_object_ptr<td_api::Object>)>> sent_queries_; + TestClient *client_; + uint64 current_query_id_ = 1; + + virtual void start_up() { + } + void stop() { + client_->remove_listener(this); + } +}; + +class DoAuthentication : public Task { + public: + DoAuthentication(string name, string phone, string code, Promise<> promise) + : name_(std::move(name)), phone_(std::move(phone)), code_(std::move(code)), promise_(std::move(promise)) { + } + void start_up() override { + send_query(make_tl_object<td_api::getAuthorizationState>(), + [this](auto res) { this->process_authorization_state(std::move(res)); }); + } + void process_authorization_state(tl_object_ptr<td_api::Object> authorization_state) { + start_flag_ = true; + tl_object_ptr<td_api::Function> function; + switch (authorization_state->get_id()) { + case td_api::authorizationStateWaitEncryptionKey::ID: + function = make_tl_object<td_api::checkDatabaseEncryptionKey>(); + break; + case td_api::authorizationStateWaitPhoneNumber::ID: + function = make_tl_object<td_api::setAuthenticationPhoneNumber>(phone_, false, true); + break; + case td_api::authorizationStateWaitCode::ID: + function = make_tl_object<td_api::checkAuthenticationCode>(code_, name_, ""); + break; + case td_api::authorizationStateWaitTdlibParameters::ID: { + auto parameters = td_api::make_object<td_api::tdlibParameters>(); + parameters->use_test_dc_ = true; + parameters->database_directory_ = name_ + TD_DIR_SLASH; + parameters->use_message_database_ = true; + parameters->use_secret_chats_ = true; + parameters->api_id_ = 94575; + parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2"; + parameters->system_language_code_ = "en"; + parameters->device_model_ = "Desktop"; + parameters->system_version_ = "Unknown"; + parameters->application_version_ = "tdclient-test"; + parameters->ignore_file_names_ = false; + parameters->enable_storage_optimizer_ = true; + function = td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)); + break; + } + case td_api::authorizationStateReady::ID: + on_authorization_ready(); + return; + default: + CHECK(false) << "Unexpected authorization state " << to_string(authorization_state); + } + send_query(std::move(function), [this](auto res) { CHECK(res->get_id() == td_api::ok::ID) << to_string(res); }); + } + void on_authorization_ready() { + LOG(INFO) << "GOT AUTHORIZED"; + stop(); + } + + private: + string name_; + string phone_; + string code_; + Promise<> promise_; + bool start_flag_{false}; + void process_update(std::shared_ptr<TestClient::Update> update) override { + if (!start_flag_) { + return; + } + if (!update->object) { + return; + } + if (update->object->get_id() == td_api::updateAuthorizationState::ID) { + auto o = std::move(update->object); + process_authorization_state(std::move(static_cast<td_api::updateAuthorizationState &>(*o).authorization_state_)); + } + } +}; + +class SetUsername : public Task { + public: + SetUsername(string username, Promise<> promise) : username_(std::move(username)), promise_(std::move(promise)) { + } + + private: + string username_; + Promise<> promise_; + int32 self_id_; + string tag_; + + void start_up() override { + send_query(make_tl_object<td_api::getMe>(), [this](auto res) { this->process_me_user(std::move(res)); }); + } + + void process_me_user(tl_object_ptr<td_api::Object> res) { + CHECK(res->get_id() == td_api::user::ID); + auto user = move_tl_object_as<td_api::user>(res); + self_id_ = user->id_; + if (user->username_ != username_) { + LOG(INFO) << "SET USERNAME: " << username_; + send_query(make_tl_object<td_api::setUsername>(username_), [this](auto res) { + CHECK(res->get_id() == td_api::ok::ID); + this->send_self_message(); + }); + } else { + send_self_message(); + } + } + void send_self_message() { + tag_ = PSTRING() << format::as_hex(Random::secure_int64()); + + send_query(make_tl_object<td_api::createPrivateChat>(self_id_, false), [this](auto res) { + CHECK(res->get_id() == td_api::chat::ID); + auto chat = move_tl_object_as<td_api::chat>(res); + this->send_query( + make_tl_object<td_api::sendMessage>( + chat->id_, 0, false, false, nullptr, + make_tl_object<td_api::inputMessageText>( + make_tl_object<td_api::formattedText>(PSTRING() << tag_ << " INIT", Auto()), false, false)), + [](auto res) {}); + }); + } + + void process_update(std::shared_ptr<TestClient::Update> update) override { + if (!update->object) { + return; + } + if (update->object->get_id() == td_api::updateMessageSendSucceeded::ID) { + auto updateNewMessage = move_tl_object_as<td_api::updateMessageSendSucceeded>(update->object); + auto &message = updateNewMessage->message_; + if (message->content_->get_id() == td_api::messageText::ID) { + auto messageText = move_tl_object_as<td_api::messageText>(message->content_); + auto text = messageText->text_->text_; + if (text.substr(0, tag_.size()) == tag_) { + LOG(INFO) << "GOT SELF MESSAGE"; + return stop(); + } + } + } + } +}; + +class CheckTestA : public Task { + public: + CheckTestA(string tag, Promise<> promise) : tag_(std::move(tag)), promise_(std::move(promise)) { + } + + private: + string tag_; + Promise<> promise_; + string previous_text_; + int cnt_ = 20; + void process_update(std::shared_ptr<TestClient::Update> update) override { + if (update->object->get_id() == td_api::updateNewMessage::ID) { + auto updateNewMessage = move_tl_object_as<td_api::updateNewMessage>(update->object); + auto &message = updateNewMessage->message_; + if (message->content_->get_id() == td_api::messageText::ID) { + auto messageText = move_tl_object_as<td_api::messageText>(message->content_); + auto text = messageText->text_->text_; + if (text.substr(0, tag_.size()) == tag_) { + CHECK(text > previous_text_) << tag("now", text) << tag("previous", previous_text_); + previous_text_ = text; + cnt_--; + LOG(INFO) << "GOT " << tag("text", text) << tag("left", cnt_); + if (cnt_ == 0) { + return stop(); + } + } + } + } + } +}; + +class TestA : public Task { + public: + TestA(string tag, string username) : tag_(std::move(tag)), username_(std::move(username)) { + } + void start_up() override { + send_query(make_tl_object<td_api::searchPublicChat>(username_), [this](auto res) { + CHECK(res->get_id() == td_api::chat::ID); + auto chat = move_tl_object_as<td_api::chat>(res); + for (int i = 0; i < 20; i++) { + this->send_query(make_tl_object<td_api::sendMessage>( + chat->id_, 0, false, false, nullptr, + make_tl_object<td_api::inputMessageText>( + make_tl_object<td_api::formattedText>(PSTRING() << tag_ << " " << (1000 + i), Auto()), + false, false)), + [&](auto res) { this->stop(); }); + } + }); + } + + private: + string tag_; + string username_; +}; + +class TestSecretChat : public Task { + public: + TestSecretChat(string tag, string username) : tag_(std::move(tag)), username_(std::move(username)) { + } + + void start_up() override { + auto f = [this](auto res) { + CHECK(res->get_id() == td_api::chat::ID); + auto chat = move_tl_object_as<td_api::chat>(res); + this->chat_id_ = chat->id_; + this->secret_chat_id_ = move_tl_object_as<td_api::chatTypeSecret>(chat->type_)->secret_chat_id_; + }; + send_query(make_tl_object<td_api::searchPublicChat>(username_), [this, f = std::move(f)](auto res) mutable { + CHECK(res->get_id() == td_api::chat::ID); + auto chat = move_tl_object_as<td_api::chat>(res); + CHECK(chat->type_->get_id() == td_api::chatTypePrivate::ID); + auto info = move_tl_object_as<td_api::chatTypePrivate>(chat->type_); + this->send_query(make_tl_object<td_api::createNewSecretChat>(info->user_id_), std::move(f)); + }); + } + + void process_update(std::shared_ptr<TestClient::Update> update) override { + if (!update->object) { + return; + } + if (update->object->get_id() == td_api::updateSecretChat::ID) { + auto update_secret_chat = move_tl_object_as<td_api::updateSecretChat>(update->object); + if (update_secret_chat->secret_chat_->id_ != secret_chat_id_ || + update_secret_chat->secret_chat_->state_->get_id() != td_api::secretChatStateReady::ID) { + return; + } + LOG(INFO) << "SEND ENCRYPTED MESSAGES"; + for (int i = 0; i < 20; i++) { + send_query(make_tl_object<td_api::sendMessage>( + chat_id_, 0, false, false, nullptr, + make_tl_object<td_api::inputMessageText>( + make_tl_object<td_api::formattedText>(PSTRING() << tag_ << " " << (1000 + i), Auto()), false, + false)), + [](auto res) {}); + } + } + } + + private: + string tag_; + string username_; + int64 secret_chat_id_ = 0; + int64 chat_id_ = 0; +}; + +class TestFileGenerated : public Task { + public: + TestFileGenerated(string tag, string username) : tag_(std::move(tag)), username_(std::move(username)) { + } + + void start_up() override { + } + + void process_update(std::shared_ptr<TestClient::Update> update) override { + if (!update->object) { + return; + } + if (update->object->get_id() == td_api::updateNewMessage::ID) { + auto updateNewMessage = move_tl_object_as<td_api::updateNewMessage>(update->object); + auto &message = updateNewMessage->message_; + chat_id_ = message->chat_id_; + if (message->content_->get_id() == td_api::messageText::ID) { + auto messageText = move_tl_object_as<td_api::messageText>(message->content_); + auto text = messageText->text_->text_; + if (text.substr(0, tag_.size()) == tag_) { + if (text.substr(tag_.size() + 1) == "ONE_FILE") { + return one_file(); + } + } + } + } else if (update->object->get_id() == td_api::updateFileGenerationStart::ID) { + auto info = move_tl_object_as<td_api::updateFileGenerationStart>(update->object); + generate_file(info->generation_id_, info->original_path_, info->destination_path_, info->conversion_); + } else if (update->object->get_id() == td_api::updateFile::ID) { + auto file = move_tl_object_as<td_api::updateFile>(update->object); + LOG(INFO) << to_string(file); + } + } + void one_file() { + LOG(ERROR) << "Start ONE_FILE test"; + string file_path = string("test_documents") + TD_DIR_SLASH + "a.txt"; + mkpath(file_path).ensure(); + auto raw_file = + FileFd::open(file_path, FileFd::Flags::Create | FileFd::Flags::Truncate | FileFd::Flags::Write).move_as_ok(); + auto file = BufferedFd<FileFd>(std::move(raw_file)); + for (int i = 1; i < 100000; i++) { + file.write(PSLICE() << i << "\n").ensure(); + } + file.flush_write().ensure(); // important + file.close(); + this->send_query(make_tl_object<td_api::sendMessage>( + chat_id_, 0, false, false, nullptr, + make_tl_object<td_api::inputMessageDocument>( + make_tl_object<td_api::inputFileGenerated>(file_path, "square", 0), + make_tl_object<td_api::inputThumbnail>( + make_tl_object<td_api::inputFileGenerated>(file_path, "thumbnail", 0), 0, 0), + make_tl_object<td_api::formattedText>(tag_, Auto()))), + [](auto res) { check_td_error(res); }); + + this->send_query( + make_tl_object<td_api::sendMessage>(chat_id_, 0, false, false, nullptr, + make_tl_object<td_api::inputMessageDocument>( + make_tl_object<td_api::inputFileGenerated>(file_path, "square", 0), + nullptr, make_tl_object<td_api::formattedText>(tag_, Auto()))), + [](auto res) { check_td_error(res); }); + } + + friend class GenerateFile; + class GenerateFile : public Actor { + public: + GenerateFile(Task *parent, int64 id, string original_path, string destination_path, string conversion) + : parent_(parent) + , id_(id) + , original_path_(std::move(original_path)) + , destination_path_(std::move(destination_path)) + , conversion_(std::move(conversion)) { + } + + private: + Task *parent_; + int64 id_; + string original_path_; + string destination_path_; + string conversion_; + + FILE *from = nullptr; + FILE *to = nullptr; + + void start_up() override { + from = std::fopen(original_path_.c_str(), "rb"); + CHECK(from); + to = std::fopen(destination_path_.c_str(), "wb"); + CHECK(to); + yield(); + } + void loop() override { + int cnt = 0; + while (true) { + uint32 x; + auto r = std::fscanf(from, "%u", &x); + if (r != 1) { + return stop(); + } + std::fprintf(to, "%u\n", x * x); + if (++cnt >= 10000) { + break; + } + } + auto ready = std::ftell(to); + LOG(ERROR) << "READY: " << ready; + parent_->send_query(make_tl_object<td_api::setFileGenerationProgress>( + id_, 1039823 /*yeah, exact size of this file*/, narrow_cast<int32>(ready)), + [](auto result) { check_td_error(result); }); + set_timeout_in(0.02); + } + void tear_down() override { + std::fclose(from); + std::fclose(to); + parent_->send_query(make_tl_object<td_api::finishFileGeneration>(id_, nullptr), + [](auto result) { check_td_error(result); }); + } + }; + void generate_file(int64 id, string original_path, string destination_path, string conversion) { + LOG(ERROR) << "Generate file " << tag("id", id) << tag("original_path", original_path) + << tag("destination_path", destination_path) << tag("conversion", conversion); + if (conversion == "square") { + create_actor<GenerateFile>("GenerateFile", this, id, original_path, destination_path, conversion).release(); + } else if (conversion == "thumbnail") { + write_file(destination_path, base64url_decode(Slice(thumbnail, thumbnail_size)).ok()).ensure(); + this->send_query(make_tl_object<td_api::finishFileGeneration>(id, nullptr), + [](auto result) { check_td_error(result); }); + } else { + LOG(FATAL) << "unknown " << tag("conversion", conversion); + } + } + + private: + string tag_; + string username_; + int64 chat_id_ = 0; +}; + +class CheckTestC : public Task { + public: + CheckTestC(string username, string tag, Promise<> promise) + : username_(std::move(username)), tag_(std::move(tag)), promise_(std::move(promise)) { + } + + void start_up() override { + send_query(make_tl_object<td_api::searchPublicChat>(username_), [this](auto res) { + CHECK(res->get_id() == td_api::chat::ID); + auto chat = move_tl_object_as<td_api::chat>(res); + chat_id_ = chat->id_; + this->one_file(); + }); + } + + private: + string username_; + string tag_; + Promise<> promise_; + int64 chat_id_; + + void one_file() { + this->send_query( + make_tl_object<td_api::sendMessage>( + chat_id_, 0, false, false, nullptr, + make_tl_object<td_api::inputMessageText>( + make_tl_object<td_api::formattedText>(PSTRING() << tag_ << " ONE_FILE", Auto()), false, false)), + [](auto res) { check_td_error(res); }); + } + + void process_update(std::shared_ptr<TestClient::Update> update) override { + if (!update->object) { + return; + } + if (update->object->get_id() == td_api::updateNewMessage::ID) { + auto updateNewMessage = move_tl_object_as<td_api::updateNewMessage>(update->object); + auto &message = updateNewMessage->message_; + if (message->content_->get_id() == td_api::messageDocument::ID) { + auto messageDocument = move_tl_object_as<td_api::messageDocument>(message->content_); + auto text = messageDocument->caption_->text_; + if (text.substr(0, tag_.size()) == tag_) { + file_id_to_check_ = messageDocument->document_->document_->id_; + LOG(ERROR) << "GOT FILE " << to_string(messageDocument->document_->document_); + this->send_query(make_tl_object<td_api::downloadFile>(file_id_to_check_, 1), + [](auto res) { check_td_error(res); }); + } + } + } else if (update->object->get_id() == td_api::updateFile::ID) { + auto updateFile = move_tl_object_as<td_api::updateFile>(update->object); + if (updateFile->file_->id_ == file_id_to_check_ && (updateFile->file_->local_->is_downloading_completed_)) { + check_file(updateFile->file_->local_->path_); + } + } + } + + void check_file(CSlice path) { + FILE *from = std::fopen(path.c_str(), "rb"); + CHECK(from); + uint32 x; + uint32 y = 1; + while (std::fscanf(from, "%u", &x) == 1) { + CHECK(x == y * y); + y++; + } + std::fclose(from); + stop(); + } + int32 file_id_to_check_ = 0; +}; + +class LoginTestActor : public Actor { + public: + explicit LoginTestActor(Status *status) : status_(status) { + *status_ = Status::OK(); + } + + private: + Status *status_; + ActorOwn<TestClient> alice_; + ActorOwn<TestClient> bob_; + + string alice_phone_ = "9996636437"; + string bob_phone_ = "9996636438"; + string alice_username_ = "alice_" + alice_phone_; + string bob_username_ = "bob_" + bob_phone_; + + string stage_name_; + + void begin_stage(string stage_name, double timeout) { + LOG(WARNING) << "Begin stage '" << stage_name << "'"; + stage_name_ = std::move(stage_name); + set_timeout_in(timeout); + } + + void start_up() override { + begin_stage("Logging in", 160); + alice_ = create_actor<TestClient>("AliceClient", "alice"); + bob_ = create_actor<TestClient>("BobClient", "bob"); + + send_closure(alice_, &TestClient::add_listener, + std::make_unique<DoAuthentication>( + "alice", alice_phone_, "33333", + PromiseCreator::event(self_closure(this, &LoginTestActor::start_up_fence_dec)))); + + send_closure(bob_, &TestClient::add_listener, + std::make_unique<DoAuthentication>( + "bob", bob_phone_, "33333", + PromiseCreator::event(self_closure(this, &LoginTestActor::start_up_fence_dec)))); + } + + int start_up_fence_ = 3; + void start_up_fence_dec() { + --start_up_fence_; + if (start_up_fence_ == 0) { + init(); + } else if (start_up_fence_ == 1) { + return init(); + class WaitActor : public Actor { + public: + WaitActor(double timeout, Promise<> promise) : timeout_(timeout), promise_(std::move(promise)) { + } + void start_up() override { + set_timeout_in(timeout_); + } + void timeout_expired() override { + stop(); + } + + private: + double timeout_; + Promise<> promise_; + }; + create_actor<WaitActor>("WaitActor", 2, + PromiseCreator::event(self_closure(this, &LoginTestActor::start_up_fence_dec))) + .release(); + } + } + + void init() { + send_closure(alice_, &TestClient::add_listener, + std::make_unique<SetUsername>( + alice_username_, PromiseCreator::event(self_closure(this, &LoginTestActor::init_fence_dec)))); + send_closure(bob_, &TestClient::add_listener, + std::make_unique<SetUsername>( + bob_username_, PromiseCreator::event(self_closure(this, &LoginTestActor::init_fence_dec)))); + } + + int init_fence_ = 2; + void init_fence_dec() { + if (--init_fence_ == 0) { + test_a(); + } + } + + int32 test_a_fence_ = 2; + void test_a_fence() { + if (--test_a_fence_ == 0) { + test_b(); + } + } + + void test_a() { + begin_stage("Ready to create chats", 80); + string alice_tag = PSTRING() << format::as_hex(Random::secure_int64()); + string bob_tag = PSTRING() << format::as_hex(Random::secure_int64()); + + send_closure(bob_, &TestClient::add_listener, + std::make_unique<CheckTestA>( + alice_tag, PromiseCreator::event(self_closure(this, &LoginTestActor::test_a_fence)))); + send_closure(alice_, &TestClient::add_listener, + std::make_unique<CheckTestA>( + bob_tag, PromiseCreator::event(self_closure(this, &LoginTestActor::test_a_fence)))); + + send_closure(alice_, &TestClient::add_listener, std::make_unique<TestA>(alice_tag, bob_username_)); + send_closure(bob_, &TestClient::add_listener, std::make_unique<TestA>(bob_tag, alice_username_)); + // send_closure(alice_, &TestClient::add_listener, std::make_unique<TestChat>(bob_username_)); + } + + void timeout_expired() override { + LOG(FATAL) << "Timeout expired in stage '" << stage_name_ << "'"; + } + + int32 test_b_fence_ = 1; + void test_b_fence() { + if (--test_b_fence_ == 0) { + test_c(); + } + } + + int32 test_c_fence_ = 1; + void test_c_fence() { + if (--test_c_fence_ == 0) { + finish(); + } + } + + void test_b() { + begin_stage("Create secret chat", 40); + string tag = PSTRING() << format::as_hex(Random::secure_int64()); + + send_closure( + bob_, &TestClient::add_listener, + std::make_unique<CheckTestA>(tag, PromiseCreator::event(self_closure(this, &LoginTestActor::test_b_fence)))); + send_closure(alice_, &TestClient::add_listener, std::make_unique<TestSecretChat>(tag, bob_username_)); + } + + void test_c() { + begin_stage("Send generated file", 240); + string tag = PSTRING() << format::as_hex(Random::secure_int64()); + + send_closure(bob_, &TestClient::add_listener, + std::make_unique<CheckTestC>( + alice_username_, tag, PromiseCreator::event(self_closure(this, &LoginTestActor::test_c_fence)))); + send_closure(alice_, &TestClient::add_listener, std::make_unique<TestFileGenerated>(tag, bob_username_)); + } + + int32 finish_fence_ = 2; + void finish_fence() { + finish_fence_--; + if (finish_fence_ == 0) { + Scheduler::instance()->finish(); + stop(); + } + } + void finish() { + send_closure(alice_, &TestClient::close, PromiseCreator::event(self_closure(this, &LoginTestActor::finish_fence))); + send_closure(bob_, &TestClient::close, PromiseCreator::event(self_closure(this, &LoginTestActor::finish_fence))); + } +}; + +class Tdclient_login : public td::Test { + public: + using Test::Test; + bool step() final { + if (!is_inited_) { + SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG) + 2); + sched_.init(4); + sched_.create_actor_unsafe<LoginTestActor>(0, "LoginTestActor", &result_).release(); + sched_.start(); + is_inited_ = true; + } + + bool ret = sched_.run_main(10); + if (ret) { + return true; + } + sched_.finish(); + if (result_.is_error()) { + LOG(ERROR) << result_; + } + ASSERT_TRUE(result_.is_ok()); + return false; + } + + private: + bool is_inited_ = false; + ConcurrentScheduler sched_; + Status result_; +}; +Tdclient_login Tdclient_login("Tdclient_login"); +}; // namespace td |