summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/tdutils/test
diff options
context:
space:
mode:
authoraunsane <aunsane@gmail.com>2018-04-27 21:33:17 +0300
committeraunsane <aunsane@gmail.com>2018-04-27 21:33:17 +0300
commite1ec72eab6d00b3ba38e5932bc88920f103b6e4a (patch)
tree999de2725a83e30fbbf6576200525d4ef0c5fe38 /protocols/Telegram/tdlib/td/tdutils/test
parentb9ce1d4d98525490ca1a38e2d9fd4f3369adb3e0 (diff)
Telegram: initial commit
- tdlib moved to telegram dir
Diffstat (limited to 'protocols/Telegram/tdlib/td/tdutils/test')
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp58
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp205
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp117
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp115
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp36
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp96
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp166
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp41
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp113
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/heap.cpp178
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/json.cpp94
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/misc.cpp262
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/pq.cpp118
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/variant.cpp75
15 files changed, 1698 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp b/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp
new file mode 100644
index 0000000000..b617485462
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp
@@ -0,0 +1,24 @@
+//
+// 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 "td/utils/Enumerator.h"
+#include "td/utils/tests.h"
+
+TEST(Enumerator, simple) {
+ td::Enumerator<std::string> e;
+ auto b = e.add("b");
+ auto a = e.add("a");
+ auto d = e.add("d");
+ auto c = e.add("c");
+ ASSERT_STREQ(e.get(a), "a");
+ ASSERT_STREQ(e.get(b), "b");
+ ASSERT_STREQ(e.get(c), "c");
+ ASSERT_STREQ(e.get(d), "d");
+ ASSERT_EQ(a, e.add("a"));
+ ASSERT_EQ(b, e.add("b"));
+ ASSERT_EQ(c, e.add("c"));
+ ASSERT_EQ(d, e.add("d"));
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp b/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp
new file mode 100644
index 0000000000..36b0570530
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp
@@ -0,0 +1,58 @@
+//
+// 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 "td/utils/HazardPointers.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+
+#include <atomic>
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(HazardPointers, stress) {
+ struct Node {
+ std::atomic<std::string *> name_;
+ char pad[64];
+ };
+ int threads_n = 10;
+ std::vector<Node> nodes(threads_n);
+ td::HazardPointers<std::string> hazard_pointers(threads_n);
+ std::vector<td::thread> threads(threads_n);
+ int thread_id = 0;
+ for (auto &thread : threads) {
+ thread = td::thread([&, thread_id] {
+ auto holder = hazard_pointers.get_holder(thread_id, 0);
+ for (int i = 0; i < 1000000; i++) {
+ auto &node = nodes[td::Random::fast(0, threads_n - 1)];
+ auto *str = holder.protect(node.name_);
+ if (str) {
+ CHECK(*str == "one" || *str == "twotwo");
+ }
+ holder.clear();
+ if (td::Random::fast(0, 5) == 0) {
+ std::string *new_str = new std::string(td::Random::fast(0, 1) == 0 ? "one" : "twotwo");
+ if (node.name_.compare_exchange_strong(str, new_str, std::memory_order_acq_rel)) {
+ hazard_pointers.retire(thread_id, str);
+ } else {
+ delete new_str;
+ }
+ }
+ }
+ });
+ thread_id++;
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ LOG(ERROR) << "Undeleted pointers: " << hazard_pointers.to_delete_size_unsafe();
+ CHECK(static_cast<int>(hazard_pointers.to_delete_size_unsafe()) <= threads_n * threads_n);
+ for (int i = 0; i < threads_n; i++) {
+ hazard_pointers.retire(i);
+ }
+ CHECK(hazard_pointers.to_delete_size_unsafe() == 0);
+}
+#endif //!TD_THREAD_UNSUPPORTED
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp
new file mode 100644
index 0000000000..2da3f0cd3f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp
@@ -0,0 +1,205 @@
+//
+// 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 "td/utils/logging.h"
+#include "td/utils/MpmcQueue.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/tests.h"
+
+#include <algorithm>
+#include <tuple>
+
+TEST(OneValue, simple) {
+ {
+ std::string x{"hello"};
+ td::OneValue<std::string> value;
+ auto status = value.set_value(x);
+ CHECK(status);
+ CHECK(x.empty());
+ status = value.get_value(x);
+ CHECK(status);
+ CHECK(x == "hello");
+ }
+ {
+ td::OneValue<std::string> value;
+ std::string x;
+ auto status = value.get_value(x);
+ CHECK(!status);
+ CHECK(x.empty());
+ std::string y{"hello"};
+ status = value.set_value(y);
+ CHECK(!status);
+ CHECK(y == "hello");
+ }
+}
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(OneValue, stress) {
+ td::Stage run;
+ td::Stage check;
+
+ std::string from;
+ bool set_status;
+
+ std::string to;
+ bool get_status;
+ std::vector<td::thread> threads;
+ td::OneValue<std::string> value;
+ for (size_t i = 0; i < 2; i++) {
+ threads.push_back(td::thread([&, id = i] {
+ for (td::uint64 round = 1; round < 100000; round++) {
+ if (id == 0) {
+ value.reset();
+ to = "";
+ from = "";
+ }
+ run.wait(round * 2);
+ if (id == 0) {
+ from = "hello";
+ set_status = value.set_value(from);
+ } else {
+ get_status = value.get_value(to);
+ }
+ check.wait(round * 2);
+ if (id == 0) {
+ if (set_status) {
+ CHECK(get_status);
+ CHECK(from.empty());
+ CHECK(to == "hello") << to;
+ } else {
+ CHECK(!get_status);
+ CHECK(from == "hello");
+ CHECK(to.empty());
+ }
+ }
+ }
+ }));
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+#endif //!TD_THREAD_UNSUPPORTED
+
+TEST(MpmcQueueBlock, simple) {
+ // Test doesn't work now and it is ok, try_pop, logic changed
+ return;
+ td::MpmcQueueBlock<std::string> block(2);
+ std::string x = "hello";
+ using PushStatus = td::MpmcQueueBlock<std::string>::PushStatus;
+ using PopStatus = td::MpmcQueueBlock<std::string>::PopStatus;
+ auto push_status = block.push(x);
+ CHECK(push_status == PushStatus::Ok);
+ CHECK(x.empty());
+ auto pop_status = block.pop(x);
+ CHECK(pop_status == PopStatus::Ok);
+ CHECK(x == "hello");
+ pop_status = block.try_pop(x);
+ CHECK(pop_status == PopStatus::Empty);
+ x = "hello";
+ push_status = block.push(x);
+ CHECK(push_status == PushStatus::Ok);
+ x = "hello";
+ push_status = block.push(x);
+ CHECK(push_status == PushStatus::Closed);
+ CHECK(x == "hello");
+ x = "";
+ pop_status = block.try_pop(x);
+ CHECK(pop_status == PopStatus::Ok);
+ pop_status = block.try_pop(x);
+ CHECK(pop_status == PopStatus::Closed);
+}
+
+TEST(MpmcQueue, simple) {
+ td::MpmcQueue<int> q(2, 1);
+ for (int t = 0; t < 2; t++) {
+ for (int i = 0; i < 100; i++) {
+ q.push(i, 0);
+ }
+ for (int i = 0; i < 100; i++) {
+ int x = q.pop(0);
+ CHECK(x == i) << x << " expected " << i;
+ }
+ }
+}
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(MpmcQueue, multi_thread) {
+ size_t n = 10;
+ size_t m = 10;
+ struct Data {
+ size_t from{0};
+ size_t value{0};
+ };
+ struct ThreadData {
+ std::vector<Data> v;
+ char pad[64];
+ };
+ td::MpmcQueue<Data> q(1024, n + m + 1);
+ std::vector<td::thread> n_threads(n);
+ std::vector<td::thread> m_threads(m);
+ std::vector<ThreadData> thread_data(m);
+ size_t thread_id = 0;
+ for (auto &thread : m_threads) {
+ thread = td::thread([&, thread_id] {
+ while (true) {
+ auto data = q.pop(thread_id);
+ if (data.value == 0) {
+ return;
+ }
+ thread_data[thread_id].v.push_back(data);
+ }
+ });
+ thread_id++;
+ }
+ size_t qn = 100000;
+ for (auto &thread : n_threads) {
+ thread = td::thread([&, thread_id] {
+ for (size_t i = 0; i < qn; i++) {
+ Data data;
+ data.from = thread_id - m;
+ data.value = i + 1;
+ q.push(data, thread_id);
+ }
+ });
+ thread_id++;
+ }
+ for (auto &thread : n_threads) {
+ thread.join();
+ }
+ for (size_t i = 0; i < m; i++) {
+ Data data;
+ data.from = 0;
+ data.value = 0;
+ q.push(data, thread_id);
+ }
+ for (auto &thread : m_threads) {
+ thread.join();
+ }
+ std::vector<Data> all;
+ for (size_t i = 0; i < m; i++) {
+ std::vector<size_t> from(n, 0);
+ for (auto &data : thread_data[i].v) {
+ all.push_back(data);
+ CHECK(data.value > from[data.from]);
+ from[data.from] = data.value;
+ }
+ }
+ CHECK(all.size() == n * qn) << all.size();
+ std::sort(all.begin(), all.end(),
+ [](const auto &a, const auto &b) { return std::tie(a.from, a.value) < std::tie(b.from, b.value); });
+ for (size_t i = 0; i < n * qn; i++) {
+ CHECK(all[i].from == i / qn);
+ CHECK(all[i].value == i % qn + 1);
+ }
+ LOG(ERROR) << "Undeleted pointers: " << q.hazard_pointers_to_delele_size_unsafe();
+ CHECK(q.hazard_pointers_to_delele_size_unsafe() <= (n + m + 1) * (n + m + 1));
+ for (size_t id = 0; id < n + m + 1; id++) {
+ q.gc(id);
+ }
+ CHECK(q.hazard_pointers_to_delele_size_unsafe() == 0) << q.hazard_pointers_to_delele_size_unsafe();
+}
+#endif //!TD_THREAD_UNSUPPORTED
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp b/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp
new file mode 100644
index 0000000000..e27e217713
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp
@@ -0,0 +1,117 @@
+//
+// 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 "td/utils/MpmcWaiter.h"
+#include "td/utils/port/sleep.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+
+#include <atomic>
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(MpmcWaiter, stress_one_one) {
+ td::Stage run;
+ td::Stage check;
+
+ std::vector<td::thread> threads;
+ std::atomic<size_t> value;
+ size_t write_cnt = 10;
+ std::unique_ptr<td::MpmcWaiter> waiter;
+ size_t threads_n = 2;
+ for (size_t i = 0; i < threads_n; i++) {
+ threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
+ for (td::uint64 round = 1; round < 100000; round++) {
+ if (id == 0) {
+ value = 0;
+ waiter = std::make_unique<td::MpmcWaiter>();
+ write_cnt = td::Random::fast(1, 10);
+ }
+ run.wait(round * threads_n);
+ if (id == 1) {
+ for (size_t i = 0; i < write_cnt; i++) {
+ value.store(i + 1, std::memory_order_relaxed);
+ waiter->notify();
+ }
+ } else {
+ int yields = 0;
+ for (size_t i = 1; i <= write_cnt; i++) {
+ while (true) {
+ auto x = value.load(std::memory_order_relaxed);
+ if (x >= i) {
+ break;
+ }
+ yields = waiter->wait(yields, id);
+ }
+ yields = waiter->stop_wait(yields, id);
+ }
+ }
+ check.wait(round * threads_n);
+ }
+ }));
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+TEST(MpmcWaiter, stress) {
+ td::Stage run;
+ td::Stage check;
+
+ std::vector<td::thread> threads;
+ size_t write_n;
+ size_t read_n;
+ std::atomic<size_t> write_pos;
+ std::atomic<size_t> read_pos;
+ size_t end_pos;
+ size_t write_cnt;
+ size_t threads_n = 20;
+ std::unique_ptr<td::MpmcWaiter> waiter;
+ for (size_t i = 0; i < threads_n; i++) {
+ threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
+ for (td::uint64 round = 1; round < 1000; round++) {
+ if (id == 0) {
+ write_n = td::Random::fast(1, 10);
+ read_n = td::Random::fast(1, 10);
+ write_cnt = td::Random::fast(1, 50);
+ end_pos = write_n * write_cnt;
+ write_pos = 0;
+ read_pos = 0;
+ waiter = std::make_unique<td::MpmcWaiter>();
+ }
+ run.wait(round * threads_n);
+ if (id <= write_n) {
+ for (size_t i = 0; i < write_cnt; i++) {
+ if (td::Random::fast(0, 20) == 0) {
+ td::usleep_for(td::Random::fast(1, 300));
+ }
+ write_pos.fetch_add(1, std::memory_order_relaxed);
+ waiter->notify();
+ }
+ } else if (id > 10 && id - 10 <= read_n) {
+ int yields = 0;
+ while (true) {
+ auto x = read_pos.load(std::memory_order_relaxed);
+ if (x == end_pos) {
+ break;
+ }
+ if (x == write_pos.load(std::memory_order_relaxed)) {
+ yields = waiter->wait(yields, id);
+ continue;
+ }
+ yields = waiter->stop_wait(yields, id);
+ read_pos.compare_exchange_strong(x, x + 1, std::memory_order_relaxed);
+ }
+ }
+ check.wait(round * threads_n);
+ }
+ }));
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+#endif // !TD_THREAD_UNSUPPORTED
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp
new file mode 100644
index 0000000000..629e5b7223
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp
@@ -0,0 +1,115 @@
+//
+// 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 "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/MpscLinkQueue.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/tests.h"
+
+class NodeX : public td::MpscLinkQueueImpl::Node {
+ public:
+ explicit NodeX(int value) : value_(value) {
+ }
+ td::MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
+ return static_cast<td::MpscLinkQueueImpl::Node *>(this);
+ }
+ static NodeX *from_mpsc_link_queue_node(td::MpscLinkQueueImpl::Node *node) {
+ return static_cast<NodeX *>(node);
+ }
+ int value() {
+ return value_;
+ }
+
+ private:
+ int value_;
+};
+using QueueNode = td::MpscLinkQueueUniquePtrNode<NodeX>;
+
+QueueNode create_node(int value) {
+ return QueueNode(std::make_unique<NodeX>(value));
+}
+
+TEST(MpscLinkQueue, one_thread) {
+ td::MpscLinkQueue<QueueNode> queue;
+
+ {
+ queue.push(create_node(1));
+ queue.push(create_node(2));
+ queue.push(create_node(3));
+ td::MpscLinkQueue<QueueNode>::Reader reader;
+ queue.pop_all(reader);
+ queue.push(create_node(4));
+ queue.pop_all(reader);
+ std::vector<int> v;
+ while (auto node = reader.read()) {
+ v.push_back(node.value().value());
+ }
+ CHECK((v == std::vector<int>{1, 2, 3, 4})) << td::format::as_array(v);
+
+ v.clear();
+ queue.push(create_node(5));
+ queue.pop_all(reader);
+ while (auto node = reader.read()) {
+ v.push_back(node.value().value());
+ }
+ CHECK((v == std::vector<int>{5})) << td::format::as_array(v);
+ }
+
+ {
+ queue.push_unsafe(create_node(3));
+ queue.push_unsafe(create_node(2));
+ queue.push_unsafe(create_node(1));
+ queue.push_unsafe(create_node(0));
+ td::MpscLinkQueue<QueueNode>::Reader reader;
+ queue.pop_all_unsafe(reader);
+ std::vector<int> v;
+ while (auto node = reader.read()) {
+ v.push_back(node.value().value());
+ }
+ CHECK((v == std::vector<int>{3, 2, 1, 0})) << td::format::as_array(v);
+ }
+}
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(MpscLinkQueue, multi_thread) {
+ td::MpscLinkQueue<QueueNode> queue;
+ int threads_n = 10;
+ int queries_n = 1000000;
+ std::vector<int> next_value(threads_n);
+ std::vector<td::thread> threads(threads_n);
+ int thread_i = 0;
+ for (auto &thread : threads) {
+ thread = td::thread([&, id = thread_i] {
+ for (int i = 0; i < queries_n; i++) {
+ queue.push(create_node(i * threads_n + id));
+ }
+ });
+ thread_i++;
+ }
+
+ int active_threads = threads_n;
+
+ td::MpscLinkQueue<QueueNode>::Reader reader;
+ while (active_threads) {
+ queue.pop_all(reader);
+ while (auto value = reader.read()) {
+ auto x = value.value().value();
+ auto thread_id = x % threads_n;
+ x /= threads_n;
+ CHECK(next_value[thread_id] == x);
+ next_value[thread_id]++;
+ if (x + 1 == queries_n) {
+ active_threads--;
+ }
+ }
+ }
+
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+#endif //!TD_THREAD_UNSUPPORTED
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp b/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp
new file mode 100644
index 0000000000..6a5a20015f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp
@@ -0,0 +1,36 @@
+//
+// 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 "td/utils/OrderedEventsProcessor.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+TEST(OrderedEventsProcessor, random) {
+ int d = 5001;
+ int n = 1000000;
+ int offset = 1000000;
+ std::vector<std::pair<int, int>> v;
+ for (int i = 0; i < n; i++) {
+ auto shift = td::Random::fast(0, 1) ? td::Random::fast(0, d) : td::Random::fast(0, 1) * d;
+ v.push_back({i + shift, i + offset});
+ }
+ std::sort(v.begin(), v.end());
+
+ td::OrderedEventsProcessor<int> processor(offset);
+ int next_pos = offset;
+ for (auto p : v) {
+ int seq_no = p.second;
+ processor.add(seq_no, seq_no, [&](auto seq_no, int x) {
+ ASSERT_EQ(x, next_pos);
+ next_pos++;
+ });
+ }
+ ASSERT_EQ(next_pos, n + offset);
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp b/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp
new file mode 100644
index 0000000000..61d956f4e6
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp
@@ -0,0 +1,96 @@
+//
+// 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 "td/utils/logging.h"
+#include "td/utils/SharedObjectPool.h"
+#include "td/utils/tests.h"
+
+#include <memory>
+
+TEST(AtomicRefCnt, simple) {
+ td::detail::AtomicRefCnt cnt{0};
+ cnt.inc();
+ cnt.inc();
+ CHECK(!cnt.dec());
+ cnt.inc();
+ CHECK(!cnt.dec());
+ CHECK(cnt.dec());
+ cnt.inc();
+ CHECK(cnt.dec());
+}
+
+template <class T, class D>
+using Ptr = td::detail::SharedPtr<T, D>;
+class Deleter {
+ public:
+ template <class T>
+ void operator()(T *t) {
+ std::default_delete<T>()(t);
+ was_delete() = true;
+ }
+ static bool &was_delete() {
+ static bool flag = false;
+ return flag;
+ }
+};
+
+TEST(SharedPtr, simple) {
+ CHECK(!Deleter::was_delete());
+ Ptr<std::string, Deleter> ptr = Ptr<std::string, Deleter>::create("hello");
+ auto ptr2 = ptr;
+ CHECK(*ptr == "hello");
+ CHECK(*ptr2 == "hello");
+ ptr.reset();
+ CHECK(*ptr2 == "hello");
+ CHECK(ptr.empty());
+ Ptr<std::string, Deleter> ptr3 = std::move(ptr2);
+ CHECK(ptr2.empty());
+ CHECK(*ptr3 == "hello");
+ ptr = ptr3;
+ CHECK(*ptr3 == "hello");
+ ptr3.reset();
+ CHECK(*ptr == "hello");
+ ptr2 = std::move(ptr);
+ CHECK(ptr.empty());
+ CHECK(*ptr2 == "hello");
+ ptr2 = ptr2;
+ CHECK(*ptr2 == "hello");
+ CHECK(!Deleter::was_delete());
+ ptr2.reset();
+ CHECK(Deleter::was_delete());
+ CHECK(ptr2.empty());
+}
+
+TEST(SharedObjectPool, simple) {
+ class Node {
+ public:
+ Node() {
+ cnt()++;
+ };
+ ~Node() {
+ cnt()--;
+ }
+ static int &cnt() {
+ static int cnt_ = 0;
+ return cnt_;
+ }
+ };
+ {
+ td::SharedObjectPool<Node> pool;
+ pool.alloc();
+ pool.alloc();
+ pool.alloc();
+ pool.alloc();
+ pool.alloc();
+ CHECK(Node::cnt() == 0);
+ CHECK(pool.total_size() == 1);
+ CHECK(pool.calc_free_size() == 1);
+ pool.alloc(), pool.alloc(), pool.alloc();
+ CHECK(pool.total_size() == 3);
+ CHECK(pool.calc_free_size() == 3);
+ }
+ CHECK(Node::cnt() == 0);
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp b/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp
new file mode 100644
index 0000000000..faf4ef61a4
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp
@@ -0,0 +1,166 @@
+//
+// 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 "td/utils/base64.h"
+#include "td/utils/common.h"
+#include "td/utils/crypto.h"
+#include "td/utils/Slice.h"
+#include "td/utils/tests.h"
+
+#include <limits>
+
+static td::vector<td::string> strings{"", "1", "short test string", td::string(1000000, 'a')};
+
+#if TD_HAVE_OPENSSL
+TEST(Crypto, AesCtrState) {
+ td::vector<td::uint32> answers1{0u, 1141589763u, 596296607u, 3673001485u, 2302125528u,
+ 330967191u, 2047392231u, 3537459563u, 307747798u, 2149598133u};
+ td::vector<td::uint32> answers2{0u, 2053451992u, 1384063362u, 3266188502u, 2893295118u,
+ 780356167u, 1904947434u, 2043402406u, 472080809u, 1807109488u};
+
+ std::size_t i = 0;
+ for (auto length : {0, 1, 31, 32, 33, 9999, 10000, 10001, 999999, 1000001}) {
+ td::uint32 seed = length;
+ td::string s(length, '\0');
+ for (auto &c : s) {
+ seed = seed * 123457567u + 987651241u;
+ c = static_cast<char>((seed >> 23) & 255);
+ }
+
+ td::UInt256 key;
+ for (auto &c : key.raw) {
+ seed = seed * 123457567u + 987651241u;
+ c = (seed >> 23) & 255;
+ }
+ td::UInt128 iv;
+ for (auto &c : iv.raw) {
+ seed = seed * 123457567u + 987651241u;
+ c = (seed >> 23) & 255;
+ }
+
+ td::AesCtrState state;
+ state.init(key, iv);
+ td::string t(length, '\0');
+ state.encrypt(s, t);
+ ASSERT_EQ(answers1[i], td::crc32(t));
+ state.init(key, iv);
+ state.decrypt(t, t);
+ ASSERT_STREQ(s, t);
+
+ for (auto &c : iv.raw) {
+ c = 0xFF;
+ }
+ state.init(key, iv);
+ state.encrypt(s, t);
+ ASSERT_EQ(answers2[i], td::crc32(t));
+
+ i++;
+ }
+}
+
+TEST(Crypto, Sha256State) {
+ for (auto length : {0, 1, 31, 32, 33, 9999, 10000, 10001, 999999, 1000001}) {
+ auto s = td::rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), length);
+ td::UInt256 baseline;
+ td::sha256(s, td::MutableSlice(baseline.raw, 32));
+
+ td::Sha256State state;
+ td::sha256_init(&state);
+ auto v = td::rand_split(s);
+ for (auto &x : v) {
+ td::sha256_update(x, &state);
+ }
+ td::UInt256 result;
+ td::sha256_final(&state, td::MutableSlice(result.raw, 32));
+ ASSERT_TRUE(baseline == result);
+ }
+}
+
+TEST(Crypto, PBKDF) {
+ td::vector<td::string> passwords{"", "qwerty", std::string(1000, 'a')};
+ td::vector<td::string> salts{"", "qwerty", std::string(1000, 'a')};
+ td::vector<int> iteration_counts{1, 2, 1000};
+ td::vector<td::Slice> answers{
+ "984LZT0tcqQQjPWr6RL/3Xd2Ftu7J6cOggTzri0Pb60=", "lzmEEdaupDp3rO+SImq4J41NsGaL0denanJfdoCsRcU=",
+ "T8WKIcEAzhg1uPmZHXOLVpZdFLJOF2H73/xprF4LZno=", "NHxAnMhPOATsb1wV0cGDlAIs+ofzI6I4I8eGJeWN9Qw=",
+ "fjYi7waEPjbVYEuZ61/Nm2hbk/vRdShoJoXg4Ygnqe4=", "GhW6e95hGJSf+ID5IrSbvzWyBZ1l35A+UoL55Uh/njk=",
+ "BueLDpqSCEc0GWk83WgMwz3UsWwfvVKcvllETSB/Yq8=", "hgHgJZNWRh78PyPdVJsK8whgHOHQbNQiyaTuGDX2IFo=",
+ "T2xdyNT1GlcA4+MVNzOe7NCgSAAzNkanNsmuoSr+4xQ=", "/f6t++GUPE+e63+0TrlInL+UsmzRSAAFopa8BBBmb2w=",
+ "8Zn98QEAKS9wPOUlN09+pfm0SWs1IGeQxQkNMT/1k48=", "sURLQ/6UX/KVYedyQB21oAtMJ+STZ4iwpxfQtqmWkLw=",
+ "T9t/EJXFpPs2Lhca7IVGphTC/OdEloPMHw1UhDnXcyQ=", "TIrtN05E9KQL6Lp/wjtbsFS+KkWZ8jlGK0ErtaoitOg=",
+ "+1KcMBjyUNz5VMaIfE5wkGwS6I+IQ5FhK+Ou2HgtVoQ=", "h36ci1T0vGllCl/xJxq6vI7n28Bg40dilzWOKg6Jt8k=",
+ "9uwsHJsotTiTqqCYftN729Dg7QI2BijIjV2MvSEUAeE=", "/l+vd/XYgbioh1SfLMaGRr13udmY6TLSlG4OYmytwGU=",
+ "7qfZZBbMRLtgjqq7GHgWa/UfXPajW8NXpJ6/T3P1rxI=", "ufwz94p28WnoOFdbrb1oyQEzm/v0CV2b0xBVxeEPJGA=",
+ "T/PUUBX2vGMUsI6httlhbMHlGPMvqFBNzayU5voVlaw=", "viMvsvTg9GfQymF3AXZ8uFYTDa3qLrqJJk9w/74iZfg=",
+ "HQF+rOZMW4DAdgZz8kAMe28eyIi0rs3a3u/mUeGPNfs=", "7lBVA+GnSxWF/eOo+tyyTB7niMDl1MqP8yzo+xnHTyw=",
+ "aTWb7HQAxaTKhSiRPY3GuM1GVmq/FPuwWBU/TUpdy70=", "fbg8M/+Ht/oU+UAZ4dQcGPo+wgCCHaA+GM4tm5jnWcY=",
+ "DJbCGFMIR/5neAlpda8Td5zftK4NGekVrg2xjrKW/4c="};
+
+ std::size_t pos = 0;
+ for (auto &password : passwords) {
+ for (auto &salt : salts) {
+ for (auto &iteration_count : iteration_counts) {
+ char result[32];
+ td::pbkdf2_sha256(password, salt, iteration_count, {result, 32});
+ ASSERT_STREQ(answers[pos], td::base64_encode({result, 32}));
+ pos++;
+ }
+ }
+ }
+}
+
+TEST(Crypto, sha1) {
+ td::vector<td::Slice> answers{"2jmj7l5rSw0yVb/vlWAYkK/YBwk=", "NWoZK3kTsExUV00Ywo1G5jlUKKs=",
+ "uRysQwoax0pNJeBC3+zpQzJy1rA=", "NKqXPNTE2qT2Husr260nMWU0AW8="};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ unsigned char output[20];
+ td::sha1(strings[i], output);
+ ASSERT_STREQ(answers[i], td::base64_encode(td::Slice(output, 20)));
+ }
+}
+
+TEST(Crypto, sha256) {
+ td::vector<td::Slice> answers{
+ "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", "a4ayc/80/OGda4BO/1o/V0etpOqiLx1JwB5S3beHW0s=",
+ "yPMaY7Q8PKPwCsw64UnDD5mhRcituEJgzLZMvr0O8pY=", "zcduXJkU+5KBocfihNc+Z/GAmkiklyAOBG05zMcRLNA="};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ td::string output(32, '\0');
+ td::sha256(strings[i], output);
+ ASSERT_STREQ(answers[i], td::base64_encode(output));
+ }
+}
+
+TEST(Crypto, md5) {
+ td::vector<td::Slice> answers{
+ "1B2M2Y8AsgTpgAmY7PhCfg==", "xMpCOKC5I4INzFCab3WEmw==", "vwBninYbDRkgk+uA7GMiIQ==", "dwfWrk4CfHDuoqk1wilvIQ=="};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ td::string output(16, '\0');
+ td::md5(strings[i], output);
+ ASSERT_STREQ(answers[i], td::base64_encode(output));
+ }
+}
+#endif
+
+#if TD_HAVE_ZLIB
+TEST(Crypto, crc32) {
+ td::vector<td::uint32> answers{0u, 2212294583u, 3013144151u, 3693461436u};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ ASSERT_EQ(answers[i], td::crc32(strings[i]));
+ }
+}
+#endif
+
+TEST(Crypto, crc64) {
+ td::vector<td::uint64> answers{0ull, 3039664240384658157ull, 17549519902062861804ull, 8794730974279819706ull};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ ASSERT_EQ(answers[i], td::crc64(strings[i]));
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp b/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp
new file mode 100644
index 0000000000..a0a92c14eb
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp
@@ -0,0 +1,41 @@
+//
+// 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 "td/utils/filesystem.h"
+#include "td/utils/tests.h"
+
+TEST(Misc, clean_filename) {
+ using td::clean_filename;
+ ASSERT_STREQ(clean_filename("-1234567"), "-1234567");
+ ASSERT_STREQ(clean_filename(".git"), "git");
+ ASSERT_STREQ(clean_filename("../../.git"), "git");
+ ASSERT_STREQ(clean_filename(".././.."), "");
+ ASSERT_STREQ(clean_filename("../"), "");
+ ASSERT_STREQ(clean_filename(".."), "");
+ ASSERT_STREQ(clean_filename("test/git/ as dsa . a"), "as dsa.a");
+ ASSERT_STREQ(clean_filename(" . "), "");
+ ASSERT_STREQ(clean_filename("!@#$%^&*()_+-=[]{;|:\"}'<>?,.`~"), "!@#$%^ ()_+-=[]{; } ,.~");
+ ASSERT_STREQ(clean_filename("!@#$%^&*()_+-=[]{}\\|:\";'<>?,.`~"), "; ,.~");
+ ASSERT_STREQ(clean_filename("عرفها بعد قد. هذا مع تاريخ اليميني واندونيسيا،, لعدم تاريخ لهيمنة الى"),
+ "عرفها بعد قد.هذا مع تاريخ اليميني");
+ ASSERT_STREQ(
+ clean_filename(
+ "012345678901234567890123456789012345678901234567890123456789adsasdasdsaa.01234567890123456789asdasdasdasd"),
+ "012345678901234567890123456789012345678901234567890123456789.01234567890123456789");
+ ASSERT_STREQ(clean_filename("01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. "
+ "0123456789`<><<>><><>0123456789asdasdasdasd"),
+ "01234567890123456789012345678901234567890123456789.0123456789");
+ ASSERT_STREQ(clean_filename("01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. "
+ "0123456789`<><><>0123456789asdasdasdasd"),
+ "01234567890123456789012345678901234567890123456789.0123456789 012");
+ ASSERT_STREQ(clean_filename("C:/document.tar.gz"), "document.tar.gz");
+ ASSERT_STREQ(clean_filename("test...."), "test");
+ ASSERT_STREQ(clean_filename("....test"), "test");
+ ASSERT_STREQ(clean_filename("test.exe...."), "test.exe"); // extension has changed
+ ASSERT_STREQ(clean_filename("test.exe01234567890123456789...."),
+ "test.exe01234567890123456789"); // extension may be more then 20 characters
+ ASSERT_STREQ(clean_filename("....test....asdf"), "test.asdf");
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp b/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp
new file mode 100644
index 0000000000..e4bd81eb0d
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp
@@ -0,0 +1,113 @@
+//
+// 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 "td/utils/buffer.h"
+#include "td/utils/ByteFlow.h"
+#include "td/utils/Gzip.h"
+#include "td/utils/GzipByteFlow.h"
+#include "td/utils/logging.h"
+#include "td/utils/Status.h"
+#include "td/utils/tests.h"
+
+static void encode_decode(td::string s) {
+ auto r = td::gzencode(s, 2);
+ ASSERT_TRUE(!r.empty());
+ if (r.empty()) {
+ return;
+ }
+ auto new_s = td::gzdecode(r.as_slice());
+ ASSERT_TRUE(!new_s.empty());
+ if (new_s.empty()) {
+ return;
+ }
+ ASSERT_EQ(s, new_s.as_slice().str());
+}
+
+TEST(Gzip, gzencode_gzdecode) {
+ auto str = td::rand_string(0, 127, 1000);
+ encode_decode(str);
+ str = td::rand_string('a', 'z', 1000000);
+ encode_decode(str);
+ str = td::string(1000000, 'a');
+ encode_decode(str);
+}
+
+TEST(Gzip, flow) {
+ auto str = td::rand_string('a', 'z', 1000000);
+ auto parts = td::rand_split(str);
+
+ auto input_writer = td::ChainBufferWriter::create_empty();
+ auto input = input_writer.extract_reader();
+ td::ByteFlowSource source(&input);
+ td::GzipByteFlow gzip_flow(td::Gzip::Encode);
+ gzip_flow = td::GzipByteFlow(td::Gzip::Encode);
+ td::ByteFlowSink sink;
+
+ source >> gzip_flow >> sink;
+
+ ASSERT_TRUE(!sink.is_ready());
+ for (auto &part : parts) {
+ input_writer.append(part);
+ source.wakeup();
+ }
+ ASSERT_TRUE(!sink.is_ready());
+ source.close_input(td::Status::OK());
+ ASSERT_TRUE(sink.is_ready());
+ ASSERT_TRUE(sink.status().is_ok());
+ auto res = sink.result()->move_as_buffer_slice().as_slice().str();
+ ASSERT_TRUE(!res.empty());
+ ASSERT_EQ(td::gzencode(str, 2).as_slice().str(), res);
+}
+TEST(Gzip, flow_error) {
+ auto str = td::rand_string('a', 'z', 1000000);
+ auto zip = td::gzencode(str).as_slice().str();
+ zip.resize(zip.size() - 1);
+ auto parts = td::rand_split(zip);
+
+ auto input_writer = td::ChainBufferWriter();
+ auto input = input_writer.extract_reader();
+ td::ByteFlowSource source(&input);
+ td::GzipByteFlow gzip_flow(td::Gzip::Decode);
+ td::ByteFlowSink sink;
+
+ source >> gzip_flow >> sink;
+
+ ASSERT_TRUE(!sink.is_ready());
+ for (auto &part : parts) {
+ input_writer.append(part);
+ source.wakeup();
+ }
+ ASSERT_TRUE(!sink.is_ready());
+ source.close_input(td::Status::OK());
+ ASSERT_TRUE(sink.is_ready());
+ ASSERT_TRUE(!sink.status().is_ok());
+}
+
+TEST(Gzip, encode_decode_flow) {
+ auto str = td::rand_string('a', 'z', 1000000);
+ auto parts = td::rand_split(str);
+ auto input_writer = td::ChainBufferWriter::create_empty();
+ auto input = input_writer.extract_reader();
+ td::ByteFlowSource source(&input);
+ td::GzipByteFlow gzip_encode_flow(td::Gzip::Encode);
+ td::GzipByteFlow gzip_decode_flow(td::Gzip::Decode);
+ td::GzipByteFlow gzip_encode_flow2(td::Gzip::Encode);
+ td::GzipByteFlow gzip_decode_flow2(td::Gzip::Decode);
+ td::ByteFlowSink sink;
+ source >> gzip_encode_flow >> gzip_decode_flow >> gzip_encode_flow2 >> gzip_decode_flow2 >> sink;
+
+ ASSERT_TRUE(!sink.is_ready());
+ for (auto &part : parts) {
+ input_writer.append(part);
+ source.wakeup();
+ }
+ ASSERT_TRUE(!sink.is_ready());
+ source.close_input(td::Status::OK());
+ ASSERT_TRUE(sink.is_ready());
+ LOG_IF(ERROR, sink.status().is_error()) << sink.status();
+ ASSERT_TRUE(sink.status().is_ok());
+ ASSERT_EQ(str, sink.result()->move_as_buffer_slice().as_slice().str());
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp b/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp
new file mode 100644
index 0000000000..0dcfcf98ff
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp
@@ -0,0 +1,178 @@
+//
+// 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 "td/utils/tests.h"
+
+#include "td/utils/common.h"
+#include "td/utils/Heap.h"
+#include "td/utils/logging.h"
+#include "td/utils/Random.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <set>
+#include <utility>
+
+REGISTER_TESTS(heap)
+
+using namespace td;
+
+TEST(Heap, sort_random_perm) {
+ int n = 1000000;
+ std::vector<int> v(n);
+ for (int i = 0; i < n; i++) {
+ v[i] = i;
+ }
+ std::srand(123);
+ std::random_shuffle(v.begin(), v.end());
+ std::vector<HeapNode> nodes(n);
+ KHeap<int> kheap;
+ for (int i = 0; i < n; i++) {
+ kheap.insert(v[i], &nodes[i]);
+ }
+ for (int i = 0; i < n; i++) {
+ ASSERT_EQ(i, kheap.top_key());
+ kheap.pop();
+ }
+};
+
+class CheckedHeap {
+ public:
+ void set_max_size(int max_size) {
+ nodes.resize(max_size);
+ free_ids.resize(max_size);
+ rev_ids.resize(max_size);
+ for (int i = 0; i < max_size; i++) {
+ free_ids[i] = max_size - i - 1;
+ nodes[i].value = i;
+ }
+ }
+ static void xx(int key, const HeapNode *heap_node) {
+ const Node *node = static_cast<const Node *>(heap_node);
+ std::fprintf(stderr, "(%d;%d)", node->key, node->value);
+ }
+ void check() const {
+ for (auto p : set_heap) {
+ std::fprintf(stderr, "(%d;%d)", p.first, p.second);
+ }
+ std::fprintf(stderr, "\n");
+ kheap.for_each(xx);
+ std::fprintf(stderr, "\n");
+ kheap.check();
+ }
+ int random_id() const {
+ CHECK(!empty());
+ return ids[Random::fast(0, static_cast<int>(ids.size() - 1))];
+ }
+ size_t size() const {
+ return ids.size();
+ }
+ bool empty() const {
+ return ids.empty();
+ }
+
+ int top_key() const {
+ CHECK(!empty());
+ int res = set_heap.begin()->first;
+ ASSERT_EQ(set_heap.size(), kheap.size());
+ ASSERT_EQ(res, kheap.top_key());
+ return res;
+ }
+ int insert(int key) {
+ // std::fprintf(stderr, "insert %d\n", key);
+ int id;
+ if (free_ids.empty()) {
+ UNREACHABLE();
+ id = static_cast<int>(nodes.size());
+ nodes.emplace_back(key, id);
+ rev_ids.push_back(-1);
+ } else {
+ id = free_ids.back();
+ free_ids.pop_back();
+ nodes[id].key = key;
+ }
+ rev_ids[id] = static_cast<int>(ids.size());
+ ids.push_back(id);
+ kheap.insert(key, &nodes[id]);
+ set_heap.emplace(key, id);
+ return id;
+ }
+ void fix_key(int new_key, int id) {
+ // std::fprintf(stderr, "fix key %d %d (old_key = %d)\n", new_key, id, nodes[id].key);
+ set_heap.erase(std::make_pair(nodes[id].key, id));
+ nodes[id].key = new_key;
+ kheap.fix(new_key, &nodes[id]);
+ set_heap.emplace(new_key, id);
+ }
+ void erase(int id) {
+ // std::fprintf(stderr, "erase %d\n", id);
+ int pos = rev_ids[id];
+ CHECK(pos != -1);
+ ids[pos] = ids.back();
+ rev_ids[ids[pos]] = pos;
+ ids.pop_back();
+ rev_ids[id] = -1;
+ free_ids.push_back(id);
+
+ kheap.erase(&nodes[id]);
+ set_heap.erase(std::make_pair(nodes[id].key, id));
+ }
+ void pop() {
+ // std::fprintf(stderr, "pop\n");
+ CHECK(!empty());
+ Node *node = static_cast<Node *>(kheap.pop());
+ int id = node->value;
+ ASSERT_EQ(node->key, set_heap.begin()->first);
+
+ int pos = rev_ids[id];
+ CHECK(pos != -1);
+ ids[pos] = ids.back();
+ rev_ids[ids[pos]] = pos;
+ ids.pop_back();
+ rev_ids[id] = -1;
+ free_ids.push_back(id);
+
+ set_heap.erase(std::make_pair(nodes[id].key, id));
+ }
+
+ private:
+ struct Node : public HeapNode {
+ Node() = default;
+ Node(int key, int value) : key(key), value(value) {
+ }
+ int key = 0;
+ int value = 0;
+ };
+ vector<int> ids;
+ vector<int> rev_ids;
+ vector<int> free_ids;
+ vector<Node> nodes;
+ std::set<std::pair<int, int>> set_heap;
+ KHeap<int> kheap;
+};
+
+TEST(Heap, random_events) {
+ CheckedHeap heap;
+ heap.set_max_size(1000);
+ for (int i = 0; i < 300000; i++) {
+ if (!heap.empty()) {
+ heap.top_key();
+ }
+
+ int x = Random::fast(0, 4);
+ if (heap.empty() || (x < 2 && heap.size() < 1000)) {
+ heap.insert(Random::fast(0, 99));
+ } else if (x < 3) {
+ heap.fix_key(Random::fast(0, 99), heap.random_id());
+ } else if (x < 4) {
+ heap.erase(heap.random_id());
+ } else if (x < 5) {
+ heap.pop();
+ }
+ // heap.check();
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/json.cpp b/protocols/Telegram/tdlib/td/tdutils/test/json.cpp
new file mode 100644
index 0000000000..deca81a791
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/json.cpp
@@ -0,0 +1,94 @@
+//
+// 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 "td/utils/tests.h"
+
+#include "td/utils/JsonBuilder.h"
+#include "td/utils/logging.h"
+#include "td/utils/StringBuilder.h"
+
+#include <tuple>
+#include <utility>
+
+REGISTER_TESTS(json)
+
+using namespace td;
+
+static void decode_encode(string str, string result = "") {
+ auto str_copy = str;
+ auto r_value = json_decode(str_copy);
+ ASSERT_TRUE(r_value.is_ok());
+ if (r_value.is_error()) {
+ LOG(INFO) << r_value.error();
+ return;
+ }
+ auto new_str = json_encode<string>(r_value.ok());
+ if (result.empty()) {
+ result = str;
+ }
+ ASSERT_EQ(result, new_str);
+}
+
+TEST(JSON, array) {
+ char tmp[1000];
+ StringBuilder sb({tmp, sizeof(tmp)});
+ JsonBuilder jb(std::move(sb));
+ jb.enter_value().enter_array() << "Hello" << -123;
+ ASSERT_EQ(jb.string_builder().is_error(), false);
+ auto encoded = jb.string_builder().as_cslice().str();
+ ASSERT_EQ("[\"Hello\",-123]", encoded);
+ decode_encode(encoded);
+}
+TEST(JSON, object) {
+ char tmp[1000];
+ StringBuilder sb({tmp, sizeof(tmp)});
+ JsonBuilder jb(std::move(sb));
+ auto c = jb.enter_object();
+ c << std::tie("key", "value");
+ c << std::make_pair("1", 2);
+ c.leave();
+ ASSERT_EQ(jb.string_builder().is_error(), false);
+ auto encoded = jb.string_builder().as_cslice().str();
+ ASSERT_EQ("{\"key\":\"value\",\"1\":2}", encoded);
+ decode_encode(encoded);
+}
+
+TEST(JSON, nested) {
+ char tmp[1000];
+ StringBuilder sb({tmp, sizeof(tmp)});
+ JsonBuilder jb(std::move(sb));
+ {
+ auto a = jb.enter_array();
+ a << 1;
+ { a.enter_value().enter_array() << 2; }
+ a << 3;
+ }
+ ASSERT_EQ(jb.string_builder().is_error(), false);
+ auto encoded = jb.string_builder().as_cslice().str();
+ ASSERT_EQ("[1,[2],3]", encoded);
+ decode_encode(encoded);
+}
+
+TEST(JSON, kphp) {
+ decode_encode("[]");
+ decode_encode("[[]]");
+ decode_encode("{}");
+ decode_encode("{}");
+ decode_encode("\"\\n\"");
+ decode_encode(
+ "\""
+ "some long string \\t \\r \\\\ \\n \\f \\\" "
+ "\\u1234"
+ "\"");
+ decode_encode(
+ "{\"keyboard\":[[\"\\u2022 abcdefg\"],[\"\\u2022 hijklmnop\"],[\"\\u2022 "
+ "qrstuvwxyz\"]],\"one_time_keyboard\":true}");
+ decode_encode(
+ " \n { \"keyboard\" : \n [[ \"\\u2022 abcdefg\" ] , \n [ \"\\u2022 hijklmnop\" \n ],[ \n \"\\u2022 "
+ "qrstuvwxyz\"]], \n \"one_time_keyboard\"\n:\ntrue\n}\n \n",
+ "{\"keyboard\":[[\"\\u2022 abcdefg\"],[\"\\u2022 hijklmnop\"],[\"\\u2022 "
+ "qrstuvwxyz\"]],\"one_time_keyboard\":true}");
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp b/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp
new file mode 100644
index 0000000000..dd1f1ec457
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp
@@ -0,0 +1,262 @@
+//
+// 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 "td/utils/base64.h"
+#include "td/utils/HttpUrl.h"
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/EventFd.h"
+#include "td/utils/port/FileFd.h"
+#include "td/utils/port/path.h"
+#include "td/utils/port/sleep.h"
+#include "td/utils/port/Stat.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/Slice.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/tests.h"
+
+#include <atomic>
+#include <clocale>
+#include <limits>
+#include <locale>
+
+using namespace td;
+
+#if TD_LINUX || TD_DARWIN
+TEST(Misc, update_atime_saves_mtime) {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
+ std::string name = "test_file";
+ unlink(name).ignore();
+ auto r_file = FileFd::open(name, FileFd::Read | FileFd::Flags::Create | FileFd::Flags::Truncate);
+ LOG_IF(ERROR, r_file.is_error()) << r_file.error();
+ ASSERT_TRUE(r_file.is_ok());
+ r_file.move_as_ok().close();
+
+ auto info = stat(name).ok();
+ int32 tests_ok = 0;
+ int32 tests_wa = 0;
+ for (int i = 0; i < 10000; i++) {
+ update_atime(name).ensure();
+ auto new_info = stat(name).ok();
+ if (info.mtime_nsec_ == new_info.mtime_nsec_) {
+ tests_ok++;
+ } else {
+ tests_wa++;
+ info.mtime_nsec_ = new_info.mtime_nsec_;
+ }
+ ASSERT_EQ(info.mtime_nsec_, new_info.mtime_nsec_);
+ usleep_for(Random::fast(0, 1000));
+ }
+ if (tests_wa > 0) {
+ LOG(ERROR) << "Access time was unexpectedly updated " << tests_wa << " times";
+ }
+ unlink(name).ensure();
+}
+
+TEST(Misc, update_atime_change_atime) {
+ SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
+ std::string name = "test_file";
+ unlink(name).ignore();
+ auto r_file = FileFd::open(name, FileFd::Read | FileFd::Flags::Create | FileFd::Flags::Truncate);
+ LOG_IF(ERROR, r_file.is_error()) << r_file.error();
+ ASSERT_TRUE(r_file.is_ok());
+ r_file.move_as_ok().close();
+ auto info = stat(name).ok();
+ // not enough for fat and e.t.c.
+ usleep_for(5000000);
+ update_atime(name).ensure();
+ auto new_info = stat(name).ok();
+ if (info.atime_nsec_ == new_info.atime_nsec_) {
+ LOG(ERROR) << "Access time was unexpectedly not changed";
+ }
+ unlink(name).ensure();
+}
+#endif
+
+TEST(Misc, errno_tls_bug) {
+ // That's a problem that should be avoided
+ // errno = 0;
+ // impl_.alloc(123);
+ // CHECK(errno == 0);
+
+#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
+ EventFd test_event_fd;
+ test_event_fd.init();
+ std::atomic<int> s(0);
+ s = 1;
+ td::thread th([&] {
+ while (s != 1) {
+ }
+ test_event_fd.acquire();
+ });
+ th.join();
+
+ for (int i = 0; i < 1000; i++) {
+ vector<EventFd> events(10);
+ vector<td::thread> threads;
+ for (auto &event : events) {
+ event.init();
+ event.release();
+ }
+ for (auto &event : events) {
+ threads.push_back(td::thread([&] {
+ {
+ EventFd tmp;
+ tmp.init();
+ tmp.acquire();
+ }
+ event.acquire();
+ }));
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ }
+#endif
+}
+
+TEST(Misc, base64) {
+ ASSERT_TRUE(is_base64("dGVzdA==") == true);
+ ASSERT_TRUE(is_base64("dGVzdB==") == false);
+ ASSERT_TRUE(is_base64("dGVzdA=") == false);
+ ASSERT_TRUE(is_base64("dGVzdA") == false);
+ ASSERT_TRUE(is_base64("dGVz") == true);
+ ASSERT_TRUE(is_base64("") == true);
+ ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == true);
+ ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=") == false);
+ ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
+ ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == false);
+ ASSERT_TRUE(is_base64("====") == false);
+
+ ASSERT_TRUE(is_base64url("dGVzdA==") == true);
+ ASSERT_TRUE(is_base64url("dGVzdB==") == false);
+ ASSERT_TRUE(is_base64url("dGVzdA=") == false);
+ ASSERT_TRUE(is_base64url("dGVzdA") == true);
+ ASSERT_TRUE(is_base64url("dGVz") == true);
+ ASSERT_TRUE(is_base64url("") == true);
+ ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == true);
+ ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=") == false);
+ ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
+ ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == false);
+ ASSERT_TRUE(is_base64url("====") == false);
+
+ for (int l = 0; l < 300000; l += l / 20 + l / 1000 * 500 + 1) {
+ for (int t = 0; t < 10; t++) {
+ string s = rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), l);
+ string encoded = base64url_encode(s);
+ auto decoded = base64url_decode(encoded);
+ ASSERT_TRUE(decoded.is_ok());
+ ASSERT_TRUE(decoded.ok() == s);
+
+ encoded = base64_encode(s);
+ decoded = base64_decode(encoded);
+ ASSERT_TRUE(decoded.is_ok());
+ ASSERT_TRUE(decoded.ok() == s);
+ }
+ }
+
+ ASSERT_TRUE(base64url_decode("dGVzdA").is_ok());
+ ASSERT_TRUE(base64url_decode("dGVzdB").is_error());
+ ASSERT_TRUE(base64_encode(base64url_decode("dGVzdA").ok()) == "dGVzdA==");
+ ASSERT_TRUE(base64_encode("any carnal pleas") == "YW55IGNhcm5hbCBwbGVhcw==");
+ ASSERT_TRUE(base64_encode("any carnal pleasu") == "YW55IGNhcm5hbCBwbGVhc3U=");
+ ASSERT_TRUE(base64_encode("any carnal pleasur") == "YW55IGNhcm5hbCBwbGVhc3Vy");
+ ASSERT_TRUE(base64_encode(" /'.;.';≤.];,].',[.;/,.;/]/..;!@#!*(%?::;!%\";") ==
+ "ICAgICAgLycuOy4nO+KJpC5dOyxdLicsWy47LywuOy9dLy4uOyFAIyEqKCU/"
+ "Ojo7ISUiOw==");
+}
+
+TEST(Misc, to_integer) {
+ ASSERT_EQ(to_integer<int32>("-1234567"), -1234567);
+ ASSERT_EQ(to_integer<int64>("-1234567"), -1234567);
+ ASSERT_EQ(to_integer<uint32>("-1234567"), 0u);
+ ASSERT_EQ(to_integer<int16>("-1234567"), 10617);
+ ASSERT_EQ(to_integer<uint16>("-1234567"), 0u);
+ ASSERT_EQ(to_integer<int16>("-1254567"), -9383);
+ ASSERT_EQ(to_integer<uint16>("1254567"), 9383u);
+ ASSERT_EQ(to_integer<int64>("-12345678910111213"), -12345678910111213);
+ ASSERT_EQ(to_integer<uint64>("12345678910111213"), 12345678910111213ull);
+
+ ASSERT_EQ(to_integer_safe<int32>("-1234567").ok(), -1234567);
+ ASSERT_EQ(to_integer_safe<int64>("-1234567").ok(), -1234567);
+ ASSERT_TRUE(to_integer_safe<uint32>("-1234567").is_error());
+ ASSERT_TRUE(to_integer_safe<int16>("-1234567").is_error());
+ ASSERT_TRUE(to_integer_safe<uint16>("-1234567").is_error());
+ ASSERT_TRUE(to_integer_safe<int16>("-1254567").is_error());
+ ASSERT_TRUE(to_integer_safe<uint16>("1254567").is_error());
+ ASSERT_EQ(to_integer_safe<int64>("-12345678910111213").ok(), -12345678910111213);
+ ASSERT_EQ(to_integer_safe<uint64>("12345678910111213").ok(), 12345678910111213ull);
+ ASSERT_TRUE(to_integer_safe<uint64>("-12345678910111213").is_error());
+}
+
+static void test_to_double_one(CSlice str, Slice expected, int precision = 6) {
+ auto result = PSTRING() << td::StringBuilder::FixedDouble(to_double(str), precision);
+ if (expected != result) {
+ LOG(ERROR) << "To double conversion failed: have " << str << ", expected " << expected << ", parsed "
+ << to_double(str) << ", got " << result;
+ }
+}
+
+static void test_to_double() {
+ test_to_double_one("0", "0.000000");
+ test_to_double_one("1", "1.000000");
+ test_to_double_one("-10", "-10.000000");
+ test_to_double_one("1.234", "1.234000");
+ test_to_double_one("-1.234e2", "-123.400000");
+ test_to_double_one("inf", "inf");
+ test_to_double_one(" inF asdasd", "inf");
+ test_to_double_one(" inFasdasd", "0.000000");
+ test_to_double_one(" NaN", "nan");
+ test_to_double_one(" 12345678910111213141516171819 asdasd", "12345678910111213670658736128.000000");
+ test_to_double_one("1.234567891011121314E123",
+ "1234567891011121363209105003376291141757777526749278953577304234065881343284952489418916814035346"
+ "625663604561924259911303168.000000");
+ test_to_double_one("1.234567891011121314E-9", "0.000000");
+ test_to_double_one("123456789", "123456789.000000");
+ test_to_double_one("-1,234567891011121314E123", "-1.000000");
+ test_to_double_one("123456789", "123456789", 0);
+ test_to_double_one("1.23456789", "1", 0);
+ test_to_double_one("1.23456789", "1.2", 1);
+ test_to_double_one("1.23456789", "1.23", 2);
+ test_to_double_one("1.23456789", "1.235", 3);
+ test_to_double_one("1.23456789", "1.2346", 4);
+ test_to_double_one("1.23456789", "1.23457", 5);
+ test_to_double_one("1.23456789", "1.234568", 6);
+ test_to_double_one("1.23456789", "1.2345679", 7);
+ test_to_double_one("1.23456789", "1.23456789", 8);
+ test_to_double_one("1.23456789", "1.234567890", 9);
+ test_to_double_one("1.23456789", "1.2345678900", 10);
+}
+
+TEST(Misc, to_double) {
+ test_to_double();
+ const char *locale_name = (std::setlocale(LC_ALL, "fr-FR") == nullptr ? "" : "fr-FR");
+ std::locale new_locale(locale_name);
+ std::locale::global(new_locale);
+ test_to_double();
+ std::locale::global(std::locale::classic());
+ test_to_double();
+}
+
+static void test_get_url_query_file_name_one(const char *prefix, const char *suffix, const char *file_name) {
+ auto path = string(prefix) + string(file_name) + string(suffix);
+ ASSERT_STREQ(file_name, get_url_query_file_name(path));
+ ASSERT_STREQ(file_name, get_url_file_name("http://telegram.org" + path));
+ ASSERT_STREQ(file_name, get_url_file_name("http://telegram.org:80" + path));
+ ASSERT_STREQ(file_name, get_url_file_name("telegram.org" + path));
+}
+
+TEST(Misc, get_url_query_file_name) {
+ for (auto suffix : {"?t=1#test", "#test?t=1", "#?t=1", "?t=1#", "#test", "?t=1", "#", "?", ""}) {
+ test_get_url_query_file_name_one("", suffix, "");
+ test_get_url_query_file_name_one("/", suffix, "");
+ test_get_url_query_file_name_one("/a/adasd/", suffix, "");
+ test_get_url_query_file_name_one("/a/lklrjetn/", suffix, "adasd.asdas");
+ test_get_url_query_file_name_one("/", suffix, "a123asadas");
+ test_get_url_query_file_name_one("/", suffix, "\\a\\1\\2\\3\\a\\s\\a\\das");
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp b/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp
new file mode 100644
index 0000000000..5210cc2638
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp
@@ -0,0 +1,118 @@
+//
+// 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 "td/utils/tests.h"
+
+#include "td/utils/BigNum.h"
+#include "td/utils/common.h"
+#include "td/utils/crypto.h"
+#include "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+REGISTER_TESTS(pq)
+
+using namespace td;
+
+#if TD_HAVE_OPENSSL
+static bool is_prime(uint64 x) {
+ for (uint64 d = 2; d < x && d * d <= x; d++) {
+ if (x % d == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::vector<uint64> gen_primes(uint64 L, uint64 R, int limit = 0) {
+ std::vector<uint64> res;
+ for (auto x = L; x <= R && (limit <= 0 || res.size() < static_cast<std::size_t>(limit)); x++) {
+ if (is_prime(x)) {
+ res.push_back(x);
+ }
+ }
+ return res;
+}
+
+static std::vector<uint64> gen_primes() {
+ std::vector<uint64> result;
+ append(result, gen_primes(1, 100));
+ append(result, gen_primes((1ull << 31) - 500000, std::numeric_limits<uint64>::max(), 5));
+ append(result, gen_primes((1ull << 32) - 500000, std::numeric_limits<uint64>::max(), 5));
+ append(result, gen_primes((1ull << 39) - 500000, std::numeric_limits<uint64>::max(), 1));
+ return result;
+}
+
+using PqQuery = std::pair<uint64, uint64>;
+static bool cmp(const PqQuery &a, const PqQuery &b) {
+ return a.first * a.second < b.first * b.second;
+}
+static std::vector<PqQuery> gen_pq_queries() {
+ std::vector<PqQuery> res;
+ auto primes = gen_primes();
+ for (auto q : primes) {
+ for (auto p : primes) {
+ if (p > q) {
+ break;
+ }
+ res.emplace_back(p, q);
+ }
+ }
+ std::sort(res.begin(), res.end(), cmp);
+ return res;
+}
+
+static void test_pq(uint64 first, uint64 second) {
+ BigNum p = BigNum::from_decimal(PSLICE() << first);
+ BigNum q = BigNum::from_decimal(PSLICE() << second);
+
+ BigNum pq;
+ BigNumContext context;
+ BigNum::mul(pq, p, q, context);
+ std::string pq_str = pq.to_binary();
+
+ std::string p_str, q_str;
+ int err = td::pq_factorize(pq_str, &p_str, &q_str);
+ CHECK(err == 0) << first << " * " << second;
+
+ BigNum p_res = BigNum::from_binary(p_str);
+ BigNum q_res = BigNum::from_binary(q_str);
+
+ CHECK(p_str == p.to_binary()) << td::tag("got", p_res.to_decimal()) << td::tag("expected", first);
+ CHECK(q_str == q.to_binary()) << td::tag("got", q_res.to_decimal()) << td::tag("expected", second);
+}
+#endif
+
+TEST(CryptoPQ, hands) {
+ ASSERT_EQ(1ull, td::pq_factorize(0));
+ ASSERT_EQ(1ull, td::pq_factorize(1));
+ ASSERT_EQ(1ull, td::pq_factorize(2));
+ ASSERT_EQ(1ull, td::pq_factorize(3));
+ ASSERT_EQ(2ull, td::pq_factorize(4));
+ ASSERT_EQ(1ull, td::pq_factorize(5));
+ ASSERT_EQ(3ull, td::pq_factorize(7 * 3));
+ ASSERT_EQ(179424611ull, td::pq_factorize(179424611ull * 179424673ull));
+
+#if TD_HAVE_OPENSSL
+ test_pq(4294467311, 4294467449);
+#endif
+}
+
+#if TD_HAVE_OPENSSL
+TEST(CryptoPQ, generated_slow) {
+ for (int i = 0; i < 100000; i++) {
+ test_pq(2, 2);
+ }
+ auto queries = gen_pq_queries();
+ for (auto query : queries) {
+ test_pq(query.first, query.second);
+ }
+}
+#endif \ No newline at end of file
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp b/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp
new file mode 100644
index 0000000000..5c5e18d1d8
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp
@@ -0,0 +1,75 @@
+//
+// 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 "td/utils/Slice.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/tests.h"
+#include "td/utils/Variant.h"
+
+REGISTER_TESTS(variant);
+
+using namespace td;
+
+static const size_t BUF_SIZE = 1024 * 1024;
+static char buf[BUF_SIZE], buf2[BUF_SIZE];
+static StringBuilder sb(MutableSlice(buf, BUF_SIZE - 1));
+static StringBuilder sb2(MutableSlice(buf2, BUF_SIZE - 1));
+
+static std::string move_sb() {
+ auto res = sb.as_cslice().str();
+ sb.clear();
+ return res;
+}
+
+static std::string name(int id) {
+ if (id == 1) {
+ return "A";
+ }
+ if (id == 2) {
+ return "B";
+ }
+ if (id == 3) {
+ return "C";
+ }
+ return "";
+}
+
+template <int id>
+class Class {
+ public:
+ Class() {
+ sb << "+" << name(id);
+ }
+ Class(const Class &) = delete;
+ Class &operator=(const Class &) = delete;
+ Class(Class &&) = delete;
+ Class &operator=(Class &&) = delete;
+ ~Class() {
+ sb << "-" << name(id);
+ }
+};
+
+using A = Class<1>;
+using B = Class<2>;
+using C = Class<3>;
+
+TEST(Variant, simple) {
+ {
+ Variant<std::unique_ptr<A>, std::unique_ptr<B>, std::unique_ptr<C>> abc;
+ ASSERT_STREQ("", sb.as_cslice());
+ abc = std::make_unique<A>();
+ ASSERT_STREQ("+A", sb.as_cslice());
+ sb.clear();
+ abc = std::make_unique<B>();
+ ASSERT_STREQ("+B-A", sb.as_cslice());
+ sb.clear();
+ abc = std::make_unique<C>();
+ ASSERT_STREQ("+C-B", sb.as_cslice());
+ sb.clear();
+ }
+ ASSERT_STREQ("-C", move_sb());
+ sb.clear();
+};