mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
266
tdutils/test/ConcurrentHashMap.cpp
Normal file
266
tdutils/test/ConcurrentHashMap.cpp
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/ConcurrentHashTable.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
|
||||
#if TD_HAVE_ABSL
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#else
|
||||
#include <unordered_map>
|
||||
#endif
|
||||
|
||||
#if TD_WITH_LIBCUCKOO
|
||||
#include <third-party/libcuckoo/libcuckoo/cuckoohash_map.hh>
|
||||
#endif
|
||||
|
||||
#if TD_WITH_JUNCTION
|
||||
#include <junction/ConcurrentMap_Grampa.h>
|
||||
#include <junction/ConcurrentMap_Leapfrog.h>
|
||||
#include <junction/ConcurrentMap_Linear.h>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
// Non resizable HashMap. Just an example
|
||||
template <class KeyT, class ValueT>
|
||||
class ArrayHashMap {
|
||||
public:
|
||||
explicit ArrayHashMap(size_t n) : array_(n) {
|
||||
}
|
||||
struct Node {
|
||||
std::atomic<KeyT> key{KeyT{}};
|
||||
std::atomic<ValueT> value{ValueT{}};
|
||||
};
|
||||
static std::string get_name() {
|
||||
return "ArrayHashMap";
|
||||
}
|
||||
KeyT empty_key() const {
|
||||
return KeyT{};
|
||||
}
|
||||
|
||||
void insert(KeyT key, ValueT value) {
|
||||
array_.with_value(key, true, [&](auto &node_value) { node_value.store(value, std::memory_order_release); });
|
||||
}
|
||||
ValueT find(KeyT key, ValueT value) {
|
||||
array_.with_value(key, false, [&](auto &node_value) { value = node_value.load(std::memory_order_acquire); });
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
AtomicHashArray<KeyT, std::atomic<ValueT>> array_;
|
||||
};
|
||||
|
||||
template <class KeyT, class ValueT>
|
||||
class ConcurrentHashMapMutex {
|
||||
public:
|
||||
explicit ConcurrentHashMapMutex(size_t) {
|
||||
}
|
||||
static std::string get_name() {
|
||||
return "ConcurrentHashMapMutex";
|
||||
}
|
||||
void insert(KeyT key, ValueT value) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
hash_map_.emplace(key, value);
|
||||
}
|
||||
ValueT find(KeyT key, ValueT default_value) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
auto it = hash_map_.find(key);
|
||||
if (it == hash_map_.end()) {
|
||||
return default_value;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
#if TD_HAVE_ABSL
|
||||
absl::flat_hash_map<KeyT, ValueT> hash_map_;
|
||||
#else
|
||||
std::unordered_map<KeyT, ValueT> hash_map_;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class KeyT, class ValueT>
|
||||
class ConcurrentHashMapSpinlock {
|
||||
public:
|
||||
explicit ConcurrentHashMapSpinlock(size_t) {
|
||||
}
|
||||
static std::string get_name() {
|
||||
return "ConcurrentHashMapSpinlock";
|
||||
}
|
||||
void insert(KeyT key, ValueT value) {
|
||||
auto guard = spinlock_.lock();
|
||||
hash_map_.emplace(key, value);
|
||||
}
|
||||
ValueT find(KeyT key, ValueT default_value) {
|
||||
auto guard = spinlock_.lock();
|
||||
auto it = hash_map_.find(key);
|
||||
if (it == hash_map_.end()) {
|
||||
return default_value;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
SpinLock spinlock_;
|
||||
#if TD_HAVE_ABSL
|
||||
absl::flat_hash_map<KeyT, ValueT> hash_map_;
|
||||
#else
|
||||
std::unordered_map<KeyT, ValueT> hash_map_;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if TD_WITH_LIBCUCKOO
|
||||
template <class KeyT, class ValueT>
|
||||
class ConcurrentHashMapLibcuckoo {
|
||||
public:
|
||||
explicit ConcurrentHashMapLibcuckoo(size_t) {
|
||||
}
|
||||
static std::string get_name() {
|
||||
return "ConcurrentHashMapLibcuckoo";
|
||||
}
|
||||
void insert(KeyT key, ValueT value) {
|
||||
hash_map_.insert(key, value);
|
||||
}
|
||||
ValueT find(KeyT key, ValueT default_value) {
|
||||
hash_map_.find(key, default_value);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
private:
|
||||
cuckoohash_map<KeyT, ValueT> hash_map_;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_WITH_JUNCTION
|
||||
template <class KeyT, class ValueT>
|
||||
class ConcurrentHashMapJunction {
|
||||
public:
|
||||
explicit ConcurrentHashMapJunction(size_t size) : hash_map_() {
|
||||
}
|
||||
static std::string get_name() {
|
||||
return "ConcurrentHashMapJunction";
|
||||
}
|
||||
void insert(KeyT key, ValueT value) {
|
||||
hash_map_.assign(key, value);
|
||||
}
|
||||
ValueT find(KeyT key, ValueT default_value) {
|
||||
return hash_map_.get(key);
|
||||
}
|
||||
|
||||
ConcurrentHashMapJunction(const ConcurrentHashMapJunction &) = delete;
|
||||
ConcurrentHashMapJunction &operator=(const ConcurrentHashMapJunction &) = delete;
|
||||
ConcurrentHashMapJunction(ConcurrentHashMapJunction &&other) = delete;
|
||||
ConcurrentHashMapJunction &operator=(ConcurrentHashMapJunction &&) = delete;
|
||||
~ConcurrentHashMapJunction() {
|
||||
junction::DefaultQSBR.flush();
|
||||
}
|
||||
|
||||
private:
|
||||
junction::ConcurrentMap_Leapfrog<KeyT, ValueT> hash_map_;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace td
|
||||
|
||||
template <class HashMap>
|
||||
class HashMapBenchmark : public td::Benchmark {
|
||||
struct Query {
|
||||
int key;
|
||||
int value;
|
||||
};
|
||||
std::vector<Query> queries;
|
||||
td::unique_ptr<HashMap> hash_map;
|
||||
|
||||
size_t threads_n = 16;
|
||||
int mod_;
|
||||
static constexpr size_t mul_ = 7273; //1000000000 + 7;
|
||||
int n_;
|
||||
|
||||
public:
|
||||
explicit HashMapBenchmark(size_t threads_n) : threads_n(threads_n) {
|
||||
}
|
||||
std::string get_description() const override {
|
||||
return hash_map->get_name();
|
||||
}
|
||||
void start_up_n(int n) override {
|
||||
n *= (int)threads_n;
|
||||
n_ = n;
|
||||
hash_map = td::make_unique<HashMap>(n * 2);
|
||||
}
|
||||
|
||||
void run(int n) override {
|
||||
n = n_;
|
||||
std::vector<td::thread> threads;
|
||||
|
||||
for (size_t i = 0; i < threads_n; i++) {
|
||||
size_t l = n * i / threads_n;
|
||||
size_t r = n * (i + 1) / threads_n;
|
||||
threads.emplace_back([l, r, this] {
|
||||
for (size_t i = l; i < r; i++) {
|
||||
auto x = int((i + 1) * mul_ % n_) + 3;
|
||||
auto y = int(i + 2);
|
||||
hash_map->insert(x, y);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() override {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
auto x = int((i + 1) * mul_ % n_) + 3;
|
||||
auto y = int(i + 2);
|
||||
ASSERT_EQ(y, hash_map->find(x, -1));
|
||||
}
|
||||
queries.clear();
|
||||
hash_map.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <class HashMap>
|
||||
static void bench_hash_map() {
|
||||
td::bench(HashMapBenchmark<HashMap>(16));
|
||||
td::bench(HashMapBenchmark<HashMap>(1));
|
||||
}
|
||||
|
||||
TEST(ConcurrentHashMap, Benchmark) {
|
||||
bench_hash_map<td::ConcurrentHashMap<int, int>>();
|
||||
bench_hash_map<td::ArrayHashMap<int, int>>();
|
||||
bench_hash_map<td::ConcurrentHashMapSpinlock<int, int>>();
|
||||
bench_hash_map<td::ConcurrentHashMapMutex<int, int>>();
|
||||
#if TD_WITH_LIBCUCKOO
|
||||
bench_hash_map<td::ConcurrentHashMapLibcuckoo<int, int>>();
|
||||
#endif
|
||||
#if TD_WITH_JUNCTION
|
||||
bench_hash_map<td::ConcurrentHashMapJunction<int, int>>();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
36
tdutils/test/Enumerator.cpp
Normal file
36
tdutils/test/Enumerator.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#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"));
|
||||
}
|
80
tdutils/test/EpochBasedMemoryReclamation.cpp
Normal file
80
tdutils/test/EpochBasedMemoryReclamation.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/EpochBasedMemoryReclamation.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(EpochBaseMemoryReclamation, stress) {
|
||||
struct Node {
|
||||
std::atomic<std::string *> name_{nullptr};
|
||||
char pad[64];
|
||||
};
|
||||
|
||||
int threads_n = 10;
|
||||
std::vector<Node> nodes(threads_n);
|
||||
td::EpochBasedMemoryReclamation<std::string> ebmr(threads_n + 1);
|
||||
auto locker = ebmr.get_locker(threads_n);
|
||||
locker.lock();
|
||||
locker.unlock();
|
||||
std::vector<td::thread> threads(threads_n);
|
||||
int thread_id = 0;
|
||||
for (auto &thread : threads) {
|
||||
thread = td::thread([&, thread_id] {
|
||||
auto locker = ebmr.get_locker(thread_id);
|
||||
locker.lock();
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
auto &node = nodes[td::Random::fast(0, threads_n - 1)];
|
||||
auto *str = node.name_.load(std::memory_order_acquire);
|
||||
if (str) {
|
||||
CHECK(*str == "one" || *str == "twotwo");
|
||||
}
|
||||
if ((i + 1) % 100 == 0) {
|
||||
locker.retire();
|
||||
}
|
||||
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)) {
|
||||
locker.retire(str);
|
||||
} else {
|
||||
delete new_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
locker.retire_sync();
|
||||
locker.unlock();
|
||||
});
|
||||
thread_id++;
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
LOG(ERROR) << "Undeleted pointers: " << ebmr.to_delete_size_unsafe();
|
||||
//CHECK(static_cast<int>(ebmr.to_delete_size_unsafe()) <= threads_n * threads_n);
|
||||
for (int i = 0; i < threads_n; i++) {
|
||||
ebmr.get_locker(i).retire_sync();
|
||||
}
|
||||
CHECK(ebmr.to_delete_size_unsafe() == 0);
|
||||
}
|
||||
#endif //!TD_THREAD_UNSUPPORTED
|
72
tdutils/test/HazardPointers.cpp
Normal file
72
tdutils/test/HazardPointers.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/HazardPointers.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
TEST(HazardPointers, stress) {
|
||||
struct Node {
|
||||
std::atomic<std::string *> name_{nullptr};
|
||||
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] {
|
||||
std::remove_reference_t<decltype(hazard_pointers)>::Holder holder(hazard_pointers, 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 == td::Slice("one") || *str == td::Slice("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
|
219
tdutils/test/MpmcQueue.cpp
Normal file
219
tdutils/test/MpmcQueue.cpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/common.h"
|
||||
#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());
|
||||
LOG_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
|
||||
/*
|
||||
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);
|
||||
LOG_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;
|
||||
}
|
||||
}
|
||||
LOG_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(INFO) << "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);
|
||||
}
|
||||
LOG_CHECK(q.hazard_pointers_to_delele_size_unsafe() == 0) << q.hazard_pointers_to_delele_size_unsafe();
|
||||
}
|
||||
#endif //!TD_THREAD_UNSUPPORTED
|
129
tdutils/test/MpmcWaiter.cpp
Normal file
129
tdutils/test/MpmcWaiter.cpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#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{0};
|
||||
size_t write_cnt = 10;
|
||||
td::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 = td::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{0};
|
||||
std::atomic<size_t> read_pos{0};
|
||||
size_t end_pos;
|
||||
size_t write_cnt;
|
||||
size_t threads_n = 20;
|
||||
td::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 = td::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
|
128
tdutils/test/MpscLinkQueue.cpp
Normal file
128
tdutils/test/MpscLinkQueue.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/common.h"
|
||||
#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(td::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());
|
||||
}
|
||||
LOG_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());
|
||||
}
|
||||
LOG_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());
|
||||
}
|
||||
LOG_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
|
48
tdutils/test/OrderedEventsProcessor.cpp
Normal file
48
tdutils/test/OrderedEventsProcessor.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#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);
|
||||
}
|
117
tdutils/test/SharedObjectPool.cpp
Normal file
117
tdutils/test/SharedObjectPool.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/common.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");
|
||||
#if TD_CLANG
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
ptr2 = ptr2;
|
||||
#if TD_CLANG
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
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;
|
||||
{ auto ptr1 = pool.alloc(); }
|
||||
{ auto ptr2 = pool.alloc(); }
|
||||
{ auto ptr3 = pool.alloc(); }
|
||||
{ auto ptr4 = pool.alloc(); }
|
||||
{ auto ptr5 = pool.alloc(); }
|
||||
CHECK(Node::cnt() == 0);
|
||||
CHECK(pool.total_size() == 1);
|
||||
CHECK(pool.calc_free_size() == 1);
|
||||
{ auto ptr6 = pool.alloc(), ptr7 = pool.alloc(), ptr8 = pool.alloc(); }
|
||||
CHECK(pool.total_size() == 3);
|
||||
CHECK(pool.calc_free_size() == 3);
|
||||
}
|
||||
CHECK(Node::cnt() == 0);
|
||||
}
|
97
tdutils/test/SharedSlice.cpp
Normal file
97
tdutils/test/SharedSlice.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/tests.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
|
||||
using namespace td;
|
||||
|
||||
TEST(SharedSlice, Hands) {
|
||||
{
|
||||
SharedSlice h("hello");
|
||||
ASSERT_EQ("hello", h.as_slice());
|
||||
// auto g = h; // CE
|
||||
auto g = h.clone();
|
||||
ASSERT_EQ("hello", g.as_slice());
|
||||
}
|
||||
|
||||
{
|
||||
SharedSlice h("hello");
|
||||
UniqueSharedSlice g(std::move(h));
|
||||
ASSERT_EQ("", h.as_slice());
|
||||
ASSERT_EQ("hello", g.as_slice());
|
||||
}
|
||||
{
|
||||
SharedSlice h("hello");
|
||||
SharedSlice t = h.clone();
|
||||
UniqueSharedSlice g(std::move(h));
|
||||
ASSERT_EQ("", h.as_slice());
|
||||
ASSERT_EQ("hello", g.as_slice());
|
||||
ASSERT_EQ("hello", t.as_slice());
|
||||
}
|
||||
|
||||
{
|
||||
UniqueSharedSlice g(5);
|
||||
g.as_mutable_slice().copy_from("hello");
|
||||
SharedSlice h(std::move(g));
|
||||
ASSERT_EQ("hello", h);
|
||||
ASSERT_EQ("", g);
|
||||
}
|
||||
|
||||
{
|
||||
UniqueSlice h("hello");
|
||||
UniqueSlice g(std::move(h));
|
||||
ASSERT_EQ("", h.as_slice());
|
||||
ASSERT_EQ("hello", g.as_slice());
|
||||
}
|
||||
|
||||
{
|
||||
SecureString h("hello");
|
||||
SecureString g(std::move(h));
|
||||
ASSERT_EQ("", h.as_slice());
|
||||
ASSERT_EQ("hello", g.as_slice());
|
||||
}
|
||||
|
||||
{
|
||||
Stage stage;
|
||||
SharedSlice a, b;
|
||||
std::vector<td::thread> threads(2);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
threads[i] = td::thread([i, &stage, &a, &b] {
|
||||
for (int j = 0; j < 10000; j++) {
|
||||
if (i == 0) {
|
||||
a = SharedSlice("hello");
|
||||
b = a.clone();
|
||||
}
|
||||
stage.wait((2 * j + 1) * 2);
|
||||
if (i == 0) {
|
||||
ASSERT_EQ('h', a[0]);
|
||||
a.clear();
|
||||
} else {
|
||||
UniqueSharedSlice c(std::move(b));
|
||||
c.as_mutable_slice()[0] = '!';
|
||||
}
|
||||
stage.wait((2 * j + 2) * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
70
tdutils/test/buffer.cpp
Normal file
70
tdutils/test/buffer.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
using namespace td;
|
||||
|
||||
TEST(Buffer, buffer_builder) {
|
||||
{
|
||||
BufferBuilder builder;
|
||||
builder.append("b");
|
||||
builder.prepend("a");
|
||||
builder.append("c");
|
||||
ASSERT_EQ(builder.extract().as_slice(), "abc");
|
||||
}
|
||||
{
|
||||
BufferBuilder builder{"hello", 0, 0};
|
||||
ASSERT_EQ(builder.extract().as_slice(), "hello");
|
||||
}
|
||||
{
|
||||
BufferBuilder builder{"hello", 1, 1};
|
||||
builder.prepend("A ");
|
||||
builder.append(" B");
|
||||
ASSERT_EQ(builder.extract().as_slice(), "A hello B");
|
||||
}
|
||||
{
|
||||
std::string str = rand_string('a', 'z', 10000);
|
||||
auto splitted_str = rand_split(str);
|
||||
|
||||
int l = Random::fast(0, static_cast<int32>(splitted_str.size() - 1));
|
||||
int r = l;
|
||||
BufferBuilder builder(splitted_str[l], 123, 1000);
|
||||
while (l != 0 || r != static_cast<int32>(splitted_str.size()) - 1) {
|
||||
if (l == 0 || (Random::fast(0, 1) == 1 && r != static_cast<int32>(splitted_str.size() - 1))) {
|
||||
r++;
|
||||
if (Random::fast(0, 1) == 1) {
|
||||
builder.append(splitted_str[r]);
|
||||
} else {
|
||||
builder.append(BufferSlice(splitted_str[r]));
|
||||
}
|
||||
} else {
|
||||
l--;
|
||||
if (Random::fast(0, 1) == 1) {
|
||||
builder.prepend(splitted_str[l]);
|
||||
} else {
|
||||
builder.prepend(BufferSlice(splitted_str[l]));
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(builder.extract().as_slice(), str);
|
||||
}
|
||||
}
|
304
tdutils/test/crypto.cpp
Normal file
304
tdutils/test/crypto.cpp
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/tests.h"
|
||||
#include "td/utils/UInt.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(as_slice(key), as_slice(iv));
|
||||
td::string t(length, '\0');
|
||||
state.encrypt(s, t);
|
||||
ASSERT_EQ(answers1[i], td::crc32(t));
|
||||
state.init(as_slice(key), as_slice(iv));
|
||||
state.decrypt(t, t);
|
||||
ASSERT_STREQ(s, t);
|
||||
|
||||
for (auto &c : iv.raw) {
|
||||
c = 0xFF;
|
||||
}
|
||||
state.init(as_slice(key), as_slice(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, as_slice(baseline));
|
||||
|
||||
td::Sha256State state;
|
||||
state.init();
|
||||
td::Sha256State state2 = std::move(state);
|
||||
auto v = td::rand_split(s);
|
||||
for (auto &x : v) {
|
||||
state2.feed(x);
|
||||
}
|
||||
state = std::move(state2);
|
||||
td::UInt256 result;
|
||||
state.extract(as_slice(result));
|
||||
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
|
||||
|
||||
#if TD_HAVE_CRC32C
|
||||
TEST(Crypto, crc32c) {
|
||||
td::vector<td::uint32> answers{0u, 2432014819u, 1077264849u, 1131405888u};
|
||||
|
||||
for (std::size_t i = 0; i < strings.size(); i++) {
|
||||
ASSERT_EQ(answers[i], td::crc32c(strings[i]));
|
||||
|
||||
auto v = td::rand_split(strings[i]);
|
||||
td::uint32 a = 0;
|
||||
td::uint32 b = 0;
|
||||
for (auto &x : v) {
|
||||
a = td::crc32c_extend(a, x);
|
||||
auto x_crc = td::crc32c(x);
|
||||
b = td::crc32c_extend(b, x_crc, x.size());
|
||||
}
|
||||
ASSERT_EQ(answers[i], a);
|
||||
ASSERT_EQ(answers[i], b);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Crypto, crc32c_benchmark) {
|
||||
class Crc32cExtendBenchmark : public td::Benchmark {
|
||||
public:
|
||||
explicit Crc32cExtendBenchmark(size_t chunk_size) : chunk_size_(chunk_size) {
|
||||
}
|
||||
std::string get_description() const override {
|
||||
return PSTRING() << "Crc32c with chunk_size=" << chunk_size_;
|
||||
}
|
||||
void start_up_n(int n) override {
|
||||
if (n > (1 << 20)) {
|
||||
cnt_ = n / (1 << 20);
|
||||
n = (1 << 20);
|
||||
} else {
|
||||
cnt_ = 1;
|
||||
}
|
||||
data_ = std::string(n, 'a');
|
||||
}
|
||||
void run(int n) override {
|
||||
td::uint32 res = 0;
|
||||
for (int i = 0; i < cnt_; i++) {
|
||||
td::Slice data(data_);
|
||||
while (!data.empty()) {
|
||||
auto head = data.substr(0, chunk_size_);
|
||||
data = data.substr(head.size());
|
||||
res = td::crc32c_extend(res, head);
|
||||
}
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t chunk_size_;
|
||||
std::string data_;
|
||||
int cnt_;
|
||||
};
|
||||
bench(Crc32cExtendBenchmark(2));
|
||||
bench(Crc32cExtendBenchmark(8));
|
||||
bench(Crc32cExtendBenchmark(32));
|
||||
bench(Crc32cExtendBenchmark(128));
|
||||
bench(Crc32cExtendBenchmark(65536));
|
||||
}
|
||||
#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]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Crypto, crc16) {
|
||||
td::vector<td::uint16> answers{0, 9842, 25046, 37023};
|
||||
|
||||
for (std::size_t i = 0; i < strings.size(); i++) {
|
||||
ASSERT_EQ(answers[i], td::crc16(strings[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static td::Slice rsa_private_key = R"ABCD(
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeYT5/prmLEa2Q
|
||||
tZND+UwTmif8kl2VlXaMCjj1k1lJJq8BqS8cVM2vPnOPzFoiC2LYykhm4kk7goCC
|
||||
ZH6wez9yakg28fcq0Ycv0x8DL1K+VKHJuwIhVfQs//IY1/cBOrMESc+NQowPbv1t
|
||||
TIFxBO2gebnpLuseht8ix7XtpGC4qAaHN2aEvT2cRsnA76TAK1RVxf1OYGUFBDzY
|
||||
318WpVZfVIjcQ7K9+eU6b2Yb84VLlvJXw3e1rvw+fBzx2EjpD4zhXy11YppWDyV6
|
||||
HEb2hs3cGS/LbHfHvdcSfil2omaJP97MDEEY2HFxjR/E5CEf2suvPzX4XS3RE+S3
|
||||
2aEJaaQbAgMBAAECggEAKo3XRNwls0wNt5xXcvF4smOUdUuY5u/0AHZQUgYBVvM1
|
||||
GA9E+ZnsxjUgLgs/0DX3k16aHj39H4sohksuxxy+lmlqKkGBN8tioC85RwW+Qre1
|
||||
QgIsNS7ai+XqcQCavrx51z88nV53qNhnXIwAVR1JT6Ubg1i8G1pZxrEKyk/jRlJd
|
||||
mGjf6vjitH//PPkghPJ/D42k93YRcy+duOgqYDQpLZp8DiEGfYrX10B1H7HrWLV+
|
||||
Wp5KO1YXtKgQUplj6kYy72bVajbxYTvzgjaaKsh74jBO0uT3tHTtXG0dcKGb0VR/
|
||||
cqP/1H/lC9bAnAqAGefNusGJQZIElvTsrpIQXOeZsQKBgQD2W04S+FjqYYFjnEFX
|
||||
6eL4it01afs5M3/C6CcI5JQtN6p+Na4NCSILol33xwhakn87zqdADHawBYQVQ8Uw
|
||||
dPurl805wfkzN3AbfdDmtx0IJ8vK4HFpktRjfpwBVhlVtm1doAYFqqsuCF2vWW1t
|
||||
mM2YOSq4AnRHCeBb/P6kRIW0MwKBgQDnFawKKqiC4tuyBOkkEhexlm7x9he0md7D
|
||||
3Z2hc3Bmdcq1niw4wBq3HUxGLReGCcSr5epKSQwkunlTn5ZSC6Rmbe4zxsGIwbb3
|
||||
5W3342swBaoxEIuBokBvZ/xUOXVwiqKj+S/NzVkZcnT6K9V/HnUCQR+JBbQxFQaX
|
||||
iiezcjKoeQKBgCIVUcDoIQ0UPl10ocmy7xbpx177calhSZzCl5vwW9vBptHdRV5C
|
||||
VDZ92ThNjgdR205/8b23u7fwm2yBusdQd/0ufFMwVfTTB6yWBI/W56pYLya7VJWB
|
||||
nebB/n1k1w53tbvNRugDy7kLqUJ4Qd521ILp7dIVbNbjM+omH2jEnibnAoGBAIM5
|
||||
a1jaoJay/M86uqohHBNcuePtO8jzF+1iDAGC7HFCsrov+CzB6mnR2V6AfLtBEM4M
|
||||
4d8NXDf/LKawGUy+D72a74m3dG+UkbJ0Nt5t5pB+pwb1vkL/QFgDVOb/OhGOqI01
|
||||
FFBqLA6nUIZAHhzxzsBY+u90rb6xkey8J49faiUBAoGAaMgOgEvQB5H19ZL5tMkl
|
||||
A/DKtTz/NFzN4Zw/vNPVb7eNn4jg9M25d9xqvL4acOa+nuV3nLHbcUWE1/7STXw1
|
||||
gT58CvoEmD1AiP95nup+HKHENJ1DWMgF5MDfVQwGCvWP5/Qy89ybr0eG8HjbldbN
|
||||
MpSmzz2wOz152oGdOd3syT4=
|
||||
-----END PRIVATE KEY-----
|
||||
)ABCD";
|
||||
|
||||
static td::Slice rsa_public_key = R"ABCD(
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3mE+f6a5ixGtkLWTQ/lM
|
||||
E5on/JJdlZV2jAo49ZNZSSavAakvHFTNrz5zj8xaIgti2MpIZuJJO4KAgmR+sHs/
|
||||
cmpINvH3KtGHL9MfAy9SvlShybsCIVX0LP/yGNf3ATqzBEnPjUKMD279bUyBcQTt
|
||||
oHm56S7rHobfIse17aRguKgGhzdmhL09nEbJwO+kwCtUVcX9TmBlBQQ82N9fFqVW
|
||||
X1SI3EOyvfnlOm9mG/OFS5byV8N3ta78Pnwc8dhI6Q+M4V8tdWKaVg8lehxG9obN
|
||||
3Bkvy2x3x73XEn4pdqJmiT/ezAxBGNhxcY0fxOQhH9rLrz81+F0t0RPkt9mhCWmk
|
||||
GwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
)ABCD";
|
||||
|
||||
TEST(Crypto, rsa) {
|
||||
auto value = td::rand_string('a', 'z', 200);
|
||||
auto encrypted_value = td::rsa_encrypt_pkcs1_oaep(rsa_public_key, value).move_as_ok();
|
||||
auto decrypted_value = td::rsa_decrypt_pkcs1_oaep(rsa_private_key, encrypted_value.as_slice()).move_as_ok();
|
||||
ASSERT_TRUE(decrypted_value.as_slice().truncate(value.size()) == value);
|
||||
}
|
54
tdutils/test/filesystem.cpp
Normal file
54
tdutils/test/filesystem.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#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");
|
||||
ASSERT_STREQ(clean_filename("കറുപ്പ്.txt"), "കറപപ.txt");
|
||||
}
|
125
tdutils/test/gzip.cpp
Normal file
125
tdutils/test/gzip.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#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);
|
||||
|
||||
td::ChainBufferWriter input_writer;
|
||||
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);
|
||||
td::ChainBufferWriter input_writer;
|
||||
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());
|
||||
}
|
189
tdutils/test/heap.cpp
Normal file
189
tdutils/test/heap.cpp
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Heap.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();
|
||||
}
|
||||
}
|
107
tdutils/test/json.cpp
Normal file
107
tdutils/test/json.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Slice.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(MutableSlice{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(MutableSlice{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(MutableSlice{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}");
|
||||
}
|
136
tdutils/test/log.cpp
Normal file
136
tdutils/test/log.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/tests.h"
|
||||
#include "td/utils/FileLog.h"
|
||||
#include "td/utils/TsFileLog.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
// Thread safe logging with tests
|
||||
//
|
||||
// LOG uses thread local LogInterface
|
||||
// void append(CSlice slice, int log_level);
|
||||
//
|
||||
|
||||
template <class Log>
|
||||
class LogBenchmark : public td::Benchmark {
|
||||
public:
|
||||
explicit LogBenchmark(std::string name, int threads_n, std::function<td::unique_ptr<Log>()> creator)
|
||||
: name_(std::move(name)), threads_n_(threads_n), creator_(std::move(creator)) {
|
||||
}
|
||||
std::string get_description() const override {
|
||||
return PSTRING() << name_ << " " << td::tag("threads_n", threads_n_);
|
||||
}
|
||||
void start_up() override {
|
||||
log_ = creator_();
|
||||
threads_.resize(threads_n_);
|
||||
}
|
||||
void tear_down() override {
|
||||
for (auto path : log_->get_file_paths()) {
|
||||
td::unlink(path).ignore();
|
||||
}
|
||||
log_.reset();
|
||||
}
|
||||
void run(int n) override {
|
||||
for (auto &thread : threads_) {
|
||||
thread = td::thread([this, n] { this->run_thread(n); });
|
||||
}
|
||||
for (auto &thread : threads_) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void run_thread(int n) {
|
||||
auto str = PSTRING() << "#" << n << " : fsjklfdjsklfjdsklfjdksl\n";
|
||||
for (int i = 0; i < n; i++) {
|
||||
log_->append(str);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
td::unique_ptr<Log> log_;
|
||||
int threads_n_{0};
|
||||
std::function<td::unique_ptr<Log>()> creator_;
|
||||
std::vector<td::thread> threads_;
|
||||
};
|
||||
|
||||
template <class F>
|
||||
void bench_log(std::string name, int threads_n, F &&f) {
|
||||
bench(LogBenchmark<typename decltype(f())::element_type>(std::move(name), threads_n, std::move(f)));
|
||||
};
|
||||
|
||||
TEST(Log, TsLogger) {
|
||||
bench_log("NewTsFileLog", 4, [] { return td::TsFileLog::create("tmplog").move_as_ok(); });
|
||||
bench_log("TsFileLog", 8, [] {
|
||||
class FileLog : public td::LogInterface {
|
||||
public:
|
||||
FileLog() {
|
||||
file_log_.init("tmplog", std::numeric_limits<td::int64>::max(), false);
|
||||
ts_log_.init(&file_log_);
|
||||
}
|
||||
~FileLog() {
|
||||
}
|
||||
void append(td::CSlice slice) override {
|
||||
ts_log_.append(slice, -1);
|
||||
}
|
||||
std::vector<std::string> get_file_paths() override {
|
||||
return file_log_.get_file_paths();
|
||||
}
|
||||
|
||||
private:
|
||||
td::FileLog file_log_;
|
||||
td::TsLog ts_log_{nullptr};
|
||||
};
|
||||
return td::make_unique<FileLog>();
|
||||
});
|
||||
|
||||
bench_log("noop", 4, [] {
|
||||
class NoopLog : public td::LogInterface {
|
||||
public:
|
||||
void append(td::CSlice slice) override {
|
||||
}
|
||||
};
|
||||
return td::make_unique<NoopLog>();
|
||||
});
|
||||
|
||||
bench_log("FileLog", 4, [] {
|
||||
class FileLog : public td::LogInterface {
|
||||
public:
|
||||
FileLog() {
|
||||
file_log_.init("tmplog", std::numeric_limits<td::int64>::max(), false);
|
||||
}
|
||||
~FileLog() {
|
||||
}
|
||||
void append(td::CSlice slice) override {
|
||||
file_log_.append(slice, -1);
|
||||
}
|
||||
std::vector<std::string> get_file_paths() override {
|
||||
return file_log_.get_file_paths();
|
||||
}
|
||||
|
||||
private:
|
||||
td::FileLog file_log_;
|
||||
};
|
||||
return td::make_unique<FileLog>();
|
||||
});
|
||||
}
|
939
tdutils/test/misc.cpp
Normal file
939
tdutils/test/misc.cpp
Normal file
|
@ -0,0 +1,939 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/as.h"
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/BigNum.h"
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/CancellationToken.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Hash.h"
|
||||
#include "td/utils/HashMap.h"
|
||||
#include "td/utils/HashSet.h"
|
||||
#include "td/utils/HttpUrl.h"
|
||||
#include "td/utils/invoke.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/IPAddress.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/port/wstring_convert.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/tests.h"
|
||||
#include "td/utils/Time.h"
|
||||
#include "td/utils/translit.h"
|
||||
#include "td/utils/uint128.h"
|
||||
#include "td/utils/unicode.h"
|
||||
#include "td/utils/utf8.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <clocale>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#if TD_HAVE_ABSL
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#endif
|
||||
|
||||
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, get_last_argument) {
|
||||
auto a = make_unique<int>(5);
|
||||
ASSERT_EQ(*get_last_argument(std::move(a)), 5);
|
||||
ASSERT_EQ(*get_last_argument(1, 2, 3, 4, a), 5);
|
||||
ASSERT_EQ(*get_last_argument(a), 5);
|
||||
auto b = get_last_argument(1, 2, 3, std::move(a));
|
||||
ASSERT_TRUE(!a);
|
||||
ASSERT_EQ(*b, 5);
|
||||
}
|
||||
|
||||
TEST(Misc, call_n_arguments) {
|
||||
auto f = [](int, int) {};
|
||||
call_n_arguments<2>(f, 1, 3, 4);
|
||||
}
|
||||
|
||||
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);
|
||||
auto host_locale = std::locale::global(new_locale);
|
||||
test_to_double();
|
||||
new_locale = std::locale::global(std::locale::classic());
|
||||
test_to_double();
|
||||
auto classic_locale = std::locale::global(host_locale);
|
||||
test_to_double();
|
||||
}
|
||||
|
||||
TEST(Misc, print_int) {
|
||||
ASSERT_STREQ("-9223372036854775808", PSLICE() << -9223372036854775807 - 1);
|
||||
ASSERT_STREQ("-2147483649", PSLICE() << -2147483649ll);
|
||||
ASSERT_STREQ("-2147483648", PSLICE() << -2147483647 - 1);
|
||||
ASSERT_STREQ("-2147483647", PSLICE() << -2147483647);
|
||||
ASSERT_STREQ("-123456789", PSLICE() << -123456789);
|
||||
ASSERT_STREQ("-1", PSLICE() << -1);
|
||||
ASSERT_STREQ("0", PSLICE() << 0);
|
||||
ASSERT_STREQ("1", PSLICE() << 1);
|
||||
ASSERT_STREQ("9", PSLICE() << 9);
|
||||
ASSERT_STREQ("10", PSLICE() << 10);
|
||||
ASSERT_STREQ("2147483647", PSLICE() << 2147483647);
|
||||
ASSERT_STREQ("2147483648", PSLICE() << 2147483648ll);
|
||||
ASSERT_STREQ("2147483649", PSLICE() << 2147483649ll);
|
||||
ASSERT_STREQ("9223372036854775807", PSLICE() << 9223372036854775807ll);
|
||||
}
|
||||
|
||||
TEST(Misc, print_uint) {
|
||||
ASSERT_STREQ("0", PSLICE() << 0u);
|
||||
ASSERT_STREQ("1", PSLICE() << 1u);
|
||||
ASSERT_STREQ("9", PSLICE() << 9u);
|
||||
ASSERT_STREQ("10", PSLICE() << 10u);
|
||||
ASSERT_STREQ("2147483647", PSLICE() << 2147483647u);
|
||||
ASSERT_STREQ("2147483648", PSLICE() << 2147483648u);
|
||||
ASSERT_STREQ("2147483649", PSLICE() << 2147483649u);
|
||||
ASSERT_STREQ("9223372036854775807", PSLICE() << 9223372036854775807u);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_idn_to_ascii_one(string host, string result) {
|
||||
if (result != idn_to_ascii(host).ok()) {
|
||||
LOG(ERROR) << "Failed to convert " << host << " to " << result << ", got \"" << idn_to_ascii(host).ok() << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Misc, idn_to_ascii) {
|
||||
test_idn_to_ascii_one("::::::::::::::::::::::::::::::::::::::@/", "::::::::::::::::::::::::::::::::::::::@/");
|
||||
test_idn_to_ascii_one("", "");
|
||||
test_idn_to_ascii_one("%30", "%30");
|
||||
test_idn_to_ascii_one("127.0.0.1", "127.0.0.1");
|
||||
test_idn_to_ascii_one("fe80::", "fe80::");
|
||||
test_idn_to_ascii_one("fe80:0:0:0:200:f8ff:fe21:67cf", "fe80:0:0:0:200:f8ff:fe21:67cf");
|
||||
test_idn_to_ascii_one("2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d", "2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d");
|
||||
test_idn_to_ascii_one("::ffff:192.0.2.1", "::ffff:192.0.2.1");
|
||||
test_idn_to_ascii_one("ABCDEF", "abcdef");
|
||||
test_idn_to_ascii_one("abcdef", "abcdef");
|
||||
test_idn_to_ascii_one("abæcdöef", "xn--abcdef-qua4k");
|
||||
test_idn_to_ascii_one("schön", "xn--schn-7qa");
|
||||
test_idn_to_ascii_one("ยจฆฟคฏข", "xn--22cdfh1b8fsa");
|
||||
test_idn_to_ascii_one("☺", "xn--74h");
|
||||
test_idn_to_ascii_one("правда", "xn--80aafi6cg");
|
||||
test_idn_to_ascii_one("büücher", "xn--bcher-kvaa");
|
||||
test_idn_to_ascii_one("BüüCHER", "xn--bcher-kvaa");
|
||||
test_idn_to_ascii_one("bücüher", "xn--bcher-kvab");
|
||||
test_idn_to_ascii_one("bücherü", "xn--bcher-kvae");
|
||||
test_idn_to_ascii_one("ýbücher", "xn--bcher-kvaf");
|
||||
test_idn_to_ascii_one("übücher", "xn--bcher-jvab");
|
||||
test_idn_to_ascii_one("bücher.tld", "xn--bcher-kva.tld");
|
||||
test_idn_to_ascii_one("кто.рф", "xn--j1ail.xn--p1ai");
|
||||
test_idn_to_ascii_one("wіkіреdіа.org", "xn--wkd-8cdx9d7hbd.org");
|
||||
test_idn_to_ascii_one("cnwin2k8中国.avol.com", "xn--cnwin2k8-sd0mx14e.avol.com");
|
||||
test_idn_to_ascii_one("win-2k12r2-addc.阿伯测阿伯测ad.hai.com", "win-2k12r2-addc.xn--ad-tl3ca3569aba8944eca.hai.com");
|
||||
test_idn_to_ascii_one("✌.ws", "xn--7bi.ws");
|
||||
// test_idn_to_ascii_one("✌️.ws", "xn--7bi.ws"); // needs nameprep to succeed
|
||||
test_idn_to_ascii_one("⛧", "xn--59h");
|
||||
test_idn_to_ascii_one("--рф.рф", "xn-----mmcq.xn--p1ai");
|
||||
ASSERT_TRUE(idn_to_ascii("\xc0").is_error());
|
||||
}
|
||||
|
||||
#if TD_WINDOWS
|
||||
static void test_to_wstring_one(string str) {
|
||||
ASSERT_STREQ(str, from_wstring(to_wstring(str).ok()).ok());
|
||||
}
|
||||
|
||||
TEST(Misc, to_wstring) {
|
||||
test_to_wstring_one("");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
test_to_wstring_one("test");
|
||||
test_to_wstring_one("тест");
|
||||
}
|
||||
string str;
|
||||
for (uint32 i = 0; i <= 0xD7FF; i++) {
|
||||
append_utf8_character(str, i);
|
||||
}
|
||||
for (uint32 i = 0xE000; i <= 0x10FFFF; i++) {
|
||||
append_utf8_character(str, i);
|
||||
}
|
||||
test_to_wstring_one(str);
|
||||
ASSERT_TRUE(to_wstring("\xc0").is_error());
|
||||
auto emoji = to_wstring("🏟").ok();
|
||||
ASSERT_TRUE(from_wstring(emoji).ok() == "🏟");
|
||||
ASSERT_TRUE(emoji.size() == 2);
|
||||
auto emoji2 = emoji;
|
||||
emoji[0] = emoji[1];
|
||||
emoji2[1] = emoji2[0];
|
||||
ASSERT_TRUE(from_wstring(emoji).is_error());
|
||||
ASSERT_TRUE(from_wstring(emoji2).is_error());
|
||||
emoji2[0] = emoji[0];
|
||||
ASSERT_TRUE(from_wstring(emoji2).is_error());
|
||||
}
|
||||
#endif
|
||||
|
||||
static void test_translit(string word, vector<string> result, bool allow_partial = true) {
|
||||
ASSERT_EQ(result, get_word_transliterations(word, allow_partial));
|
||||
}
|
||||
|
||||
TEST(Misc, translit) {
|
||||
test_translit("word", {"word", "ворд"});
|
||||
test_translit("", {});
|
||||
test_translit("ььььььььь", {"ььььььььь"});
|
||||
test_translit("крыло", {"krylo", "крыло"});
|
||||
test_translit("krylo", {"krylo", "крило"});
|
||||
test_translit("crylo", {"crylo", "крило"});
|
||||
test_translit("cheiia", {"cheiia", "кхеииа", "чейия"});
|
||||
test_translit("cheii", {"cheii", "кхеии", "чейи", "чейий", "чейия"});
|
||||
test_translit("s", {"s", "с", "ш", "щ"});
|
||||
test_translit("y", {"e", "y", "е", "и", "ю", "я"});
|
||||
test_translit("j", {"e", "j", "е", "й", "ю", "я"});
|
||||
test_translit("yo", {"e", "yo", "е", "ио"});
|
||||
test_translit("artjom", {"artem", "artjom", "артем", "артйом"});
|
||||
test_translit("artyom", {"artem", "artyom", "артем", "артиом"});
|
||||
test_translit("arty", {"arte", "arty", "арте", "арти", "артю", "артя"});
|
||||
test_translit("льи", {"li", "lia", "ly", "льи"});
|
||||
test_translit("y", {"y", "и"}, false);
|
||||
test_translit("yo", {"e", "yo", "е", "ио"}, false);
|
||||
}
|
||||
|
||||
static void test_unicode(uint32 (*func)(uint32)) {
|
||||
for (uint32 i = 0; i <= 0x110000; i++) {
|
||||
auto res = func(i);
|
||||
CHECK(res <= 0x10ffff);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Misc, unicode) {
|
||||
test_unicode(prepare_search_character);
|
||||
test_unicode(unicode_to_lower);
|
||||
test_unicode(remove_diacritics);
|
||||
}
|
||||
|
||||
TEST(BigNum, from_decimal) {
|
||||
ASSERT_TRUE(BigNum::from_decimal("").is_error());
|
||||
ASSERT_TRUE(BigNum::from_decimal("a").is_error());
|
||||
ASSERT_TRUE(BigNum::from_decimal("123a").is_error());
|
||||
ASSERT_TRUE(BigNum::from_decimal("-123a").is_error());
|
||||
// ASSERT_TRUE(BigNum::from_decimal("-").is_error());
|
||||
ASSERT_TRUE(BigNum::from_decimal("123").is_ok());
|
||||
ASSERT_TRUE(BigNum::from_decimal("-123").is_ok());
|
||||
ASSERT_TRUE(BigNum::from_decimal("0").is_ok());
|
||||
ASSERT_TRUE(BigNum::from_decimal("-0").is_ok());
|
||||
ASSERT_TRUE(BigNum::from_decimal("-999999999999999999999999999999999999999999999999").is_ok());
|
||||
ASSERT_TRUE(BigNum::from_decimal("999999999999999999999999999999999999999999999999").is_ok());
|
||||
}
|
||||
|
||||
static void test_get_ipv4(uint32 ip) {
|
||||
td::IPAddress ip_address;
|
||||
ip_address.init_ipv4_port(td::IPAddress::ipv4_to_str(ip), 80).ensure();
|
||||
ASSERT_EQ(ip_address.get_ipv4(), ip);
|
||||
}
|
||||
|
||||
TEST(Misc, IPAddress_get_ipv4) {
|
||||
test_get_ipv4(0x00000000);
|
||||
test_get_ipv4(0x010000FF);
|
||||
test_get_ipv4(0xFF000001);
|
||||
test_get_ipv4(0x01020304);
|
||||
test_get_ipv4(0x04030201);
|
||||
test_get_ipv4(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
static void test_is_reserved(string ip, bool is_reserved) {
|
||||
IPAddress ip_address;
|
||||
ip_address.init_ipv4_port(ip, 80).ensure();
|
||||
ASSERT_EQ(is_reserved, ip_address.is_reserved());
|
||||
}
|
||||
|
||||
TEST(Misc, IPAddress_is_reserved) {
|
||||
test_is_reserved("0.0.0.0", true);
|
||||
test_is_reserved("0.255.255.255", true);
|
||||
test_is_reserved("1.0.0.0", false);
|
||||
test_is_reserved("5.0.0.0", false);
|
||||
test_is_reserved("9.255.255.255", false);
|
||||
test_is_reserved("10.0.0.0", true);
|
||||
test_is_reserved("10.255.255.255", true);
|
||||
test_is_reserved("11.0.0.0", false);
|
||||
test_is_reserved("100.63.255.255", false);
|
||||
test_is_reserved("100.64.0.0", true);
|
||||
test_is_reserved("100.127.255.255", true);
|
||||
test_is_reserved("100.128.0.0", false);
|
||||
test_is_reserved("126.255.255.255", false);
|
||||
test_is_reserved("127.0.0.0", true);
|
||||
test_is_reserved("127.255.255.255", true);
|
||||
test_is_reserved("128.0.0.0", false);
|
||||
test_is_reserved("169.253.255.255", false);
|
||||
test_is_reserved("169.254.0.0", true);
|
||||
test_is_reserved("169.254.255.255", true);
|
||||
test_is_reserved("169.255.0.0", false);
|
||||
test_is_reserved("172.15.255.255", false);
|
||||
test_is_reserved("172.16.0.0", true);
|
||||
test_is_reserved("172.31.255.255", true);
|
||||
test_is_reserved("172.32.0.0", false);
|
||||
test_is_reserved("191.255.255.255", false);
|
||||
test_is_reserved("192.0.0.0", true);
|
||||
test_is_reserved("192.0.0.255", true);
|
||||
test_is_reserved("192.0.1.0", false);
|
||||
test_is_reserved("192.0.1.255", false);
|
||||
test_is_reserved("192.0.2.0", true);
|
||||
test_is_reserved("192.0.2.255", true);
|
||||
test_is_reserved("192.0.3.0", false);
|
||||
test_is_reserved("192.88.98.255", false);
|
||||
test_is_reserved("192.88.99.0", true);
|
||||
test_is_reserved("192.88.99.255", true);
|
||||
test_is_reserved("192.88.100.0", false);
|
||||
test_is_reserved("192.167.255.255", false);
|
||||
test_is_reserved("192.168.0.0", true);
|
||||
test_is_reserved("192.168.255.255", true);
|
||||
test_is_reserved("192.169.0.0", false);
|
||||
test_is_reserved("198.17.255.255", false);
|
||||
test_is_reserved("198.18.0.0", true);
|
||||
test_is_reserved("198.19.255.255", true);
|
||||
test_is_reserved("198.20.0.0", false);
|
||||
test_is_reserved("198.51.99.255", false);
|
||||
test_is_reserved("198.51.100.0", true);
|
||||
test_is_reserved("198.51.100.255", true);
|
||||
test_is_reserved("198.51.101.0", false);
|
||||
test_is_reserved("203.0.112.255", false);
|
||||
test_is_reserved("203.0.113.0", true);
|
||||
test_is_reserved("203.0.113.255", true);
|
||||
test_is_reserved("203.0.114.0", false);
|
||||
test_is_reserved("223.255.255.255", false);
|
||||
test_is_reserved("224.0.0.0", true);
|
||||
test_is_reserved("239.255.255.255", true);
|
||||
test_is_reserved("240.0.0.0", true);
|
||||
test_is_reserved("255.255.255.254", true);
|
||||
test_is_reserved("255.255.255.255", true);
|
||||
}
|
||||
|
||||
static void test_split(Slice str, std::pair<Slice, Slice> expected) {
|
||||
ASSERT_EQ(expected, td::split(str));
|
||||
}
|
||||
|
||||
TEST(Misc, split) {
|
||||
test_split("", {"", ""});
|
||||
test_split(" ", {"", ""});
|
||||
test_split("abcdef", {"abcdef", ""});
|
||||
test_split("abc def", {"abc", "def"});
|
||||
test_split("a bcdef", {"a", "bcdef"});
|
||||
test_split(" abcdef", {"", "abcdef"});
|
||||
test_split("abcdef ", {"abcdef", ""});
|
||||
test_split("ab cd ef", {"ab", "cd ef"});
|
||||
test_split("ab cdef ", {"ab", "cdef "});
|
||||
test_split(" abcd ef", {"", "abcd ef"});
|
||||
test_split(" abcdef ", {"", "abcdef "});
|
||||
}
|
||||
|
||||
static void test_full_split(Slice str, vector<Slice> expected) {
|
||||
ASSERT_EQ(expected, td::full_split(str));
|
||||
}
|
||||
|
||||
TEST(Misc, full_split) {
|
||||
test_full_split("", {});
|
||||
test_full_split(" ", {"", ""});
|
||||
test_full_split(" ", {"", "", ""});
|
||||
test_full_split("abcdef", {"abcdef"});
|
||||
test_full_split("abc def", {"abc", "def"});
|
||||
test_full_split("a bcdef", {"a", "bcdef"});
|
||||
test_full_split(" abcdef", {"", "abcdef"});
|
||||
test_full_split("abcdef ", {"abcdef", ""});
|
||||
test_full_split("ab cd ef", {"ab", "cd", "ef"});
|
||||
test_full_split("ab cdef ", {"ab", "cdef", ""});
|
||||
test_full_split(" abcd ef", {"", "abcd", "ef"});
|
||||
test_full_split(" abcdef ", {"", "abcdef", ""});
|
||||
test_full_split(" ab cd ef ", {"", "ab", "cd", "ef", ""});
|
||||
test_full_split(" ab cd ef ", {"", "", "ab", "", "cd", "", "ef", "", ""});
|
||||
}
|
||||
|
||||
TEST(Misc, StringBuilder) {
|
||||
auto small_str = std::string{"abcdefghij"};
|
||||
auto big_str = std::string(1000, 'a');
|
||||
using V = std::vector<std::string>;
|
||||
for (auto use_buf : {false, true}) {
|
||||
for (size_t initial_buffer_size : {0, 1, 5, 10, 100, 1000, 2000}) {
|
||||
for (auto test : {V{small_str}, V{small_str, big_str, big_str, small_str}, V{big_str, small_str, big_str}}) {
|
||||
std::string buf(initial_buffer_size, '\0');
|
||||
td::StringBuilder sb(buf, use_buf);
|
||||
std::string res;
|
||||
for (auto x : test) {
|
||||
res += x;
|
||||
sb << x;
|
||||
}
|
||||
if (use_buf) {
|
||||
ASSERT_EQ(res, sb.as_cslice());
|
||||
} else {
|
||||
auto got = sb.as_cslice();
|
||||
res.resize(got.size());
|
||||
ASSERT_EQ(res, got);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Misc, As) {
|
||||
char buf[100];
|
||||
as<int>(buf) = 123;
|
||||
ASSERT_EQ(123, as<int>((const char *)buf));
|
||||
ASSERT_EQ(123, as<int>((char *)buf));
|
||||
char buf2[100];
|
||||
as<int>(buf2) = as<int>(buf);
|
||||
ASSERT_EQ(123, as<int>((const char *)buf2));
|
||||
ASSERT_EQ(123, as<int>((char *)buf2));
|
||||
}
|
||||
|
||||
TEST(Misc, Regression) {
|
||||
string name = "regression_db";
|
||||
RegressionTester::destroy(name);
|
||||
|
||||
{
|
||||
auto tester = RegressionTester::create(name);
|
||||
tester->save_db();
|
||||
tester->verify_test("one_plus_one", "two").ensure();
|
||||
tester->verify_test("one_plus_one", "two").ensure();
|
||||
tester->verify_test("two_plus_one", "three").ensure();
|
||||
tester->verify_test("one_plus_one", "two").ensure();
|
||||
tester->verify_test("two_plus_one", "three").ensure();
|
||||
tester->save_db();
|
||||
}
|
||||
{
|
||||
auto tester = RegressionTester::create(name);
|
||||
tester->save_db();
|
||||
tester->verify_test("one_plus_one", "two").ensure();
|
||||
tester->verify_test("one_plus_one", "two").ensure();
|
||||
tester->verify_test("two_plus_one", "three").ensure();
|
||||
tester->verify_test("one_plus_one", "two").ensure();
|
||||
tester->verify_test("two_plus_one", "three").ensure();
|
||||
tester->save_db();
|
||||
tester->verify_test("one_plus_one", "three").ensure_error();
|
||||
tester->verify_test("two_plus_one", "two").ensure_error();
|
||||
}
|
||||
{
|
||||
auto tester = RegressionTester::create(name);
|
||||
tester->verify_test("one_plus_one", "three").ensure_error();
|
||||
tester->verify_test("two_plus_one", "two").ensure_error();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Misc, Bits) {
|
||||
ASSERT_EQ(32, count_leading_zeroes32(0));
|
||||
ASSERT_EQ(64, count_leading_zeroes64(0));
|
||||
ASSERT_EQ(32, count_trailing_zeroes32(0));
|
||||
ASSERT_EQ(64, count_trailing_zeroes64(0));
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
ASSERT_EQ(31 - i, count_leading_zeroes32(1u << i));
|
||||
ASSERT_EQ(i, count_trailing_zeroes32(1u << i));
|
||||
ASSERT_EQ(31 - i, count_leading_zeroes_non_zero32(1u << i));
|
||||
ASSERT_EQ(i, count_trailing_zeroes_non_zero32(1u << i));
|
||||
}
|
||||
for (int i = 0; i < 64; i++) {
|
||||
ASSERT_EQ(63 - i, count_leading_zeroes64(1ull << i));
|
||||
ASSERT_EQ(i, count_trailing_zeroes64(1ull << i));
|
||||
ASSERT_EQ(63 - i, count_leading_zeroes_non_zero64(1ull << i));
|
||||
ASSERT_EQ(i, count_trailing_zeroes_non_zero64(1ull << i));
|
||||
}
|
||||
|
||||
ASSERT_EQ(0x12345678u, td::bswap32(0x78563412u));
|
||||
ASSERT_EQ(0x12345678abcdef67ull, td::bswap64(0x67efcdab78563412ull));
|
||||
|
||||
ASSERT_EQ(0, count_bits32(0));
|
||||
ASSERT_EQ(0, count_bits64(0));
|
||||
ASSERT_EQ(4, count_bits32((1u << 31) | 7));
|
||||
ASSERT_EQ(4, count_bits64((1ull << 63) | 7));
|
||||
}
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
TEST(Misc, Time) {
|
||||
Stage run;
|
||||
Stage check;
|
||||
Stage finish;
|
||||
|
||||
size_t threads_n = 3;
|
||||
std::vector<thread> threads;
|
||||
std::vector<std::atomic<double>> ts(threads_n);
|
||||
for (size_t i = 0; i < threads_n; i++) {
|
||||
threads.emplace_back([&, thread_id = i] {
|
||||
for (uint64 round = 1; round < 100000; round++) {
|
||||
ts[thread_id] = 0;
|
||||
run.wait(round * threads_n);
|
||||
ts[thread_id] = Time::now();
|
||||
check.wait(round * threads_n);
|
||||
for (auto &ts_ref : ts) {
|
||||
auto other_ts = ts_ref.load();
|
||||
if (other_ts != 0) {
|
||||
ASSERT_TRUE(other_ts <= Time::now_cached());
|
||||
}
|
||||
}
|
||||
|
||||
finish.wait(round * threads_n);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Misc, uint128) {
|
||||
std::vector<uint64> parts = {0,
|
||||
1,
|
||||
2000,
|
||||
2001,
|
||||
std::numeric_limits<uint64>::max(),
|
||||
std::numeric_limits<uint64>::max() - 1,
|
||||
std::numeric_limits<uint32>::max(),
|
||||
static_cast<uint64>(std::numeric_limits<uint32>::max()) + 1};
|
||||
std::vector<int64> signed_parts = {0,
|
||||
1,
|
||||
2000,
|
||||
2001,
|
||||
-1,
|
||||
-2000,
|
||||
-2001,
|
||||
std::numeric_limits<int64>::max(),
|
||||
std::numeric_limits<int64>::max() - 1,
|
||||
std::numeric_limits<int64>::min(),
|
||||
std::numeric_limits<int64>::min() + 1,
|
||||
std::numeric_limits<int32>::max(),
|
||||
static_cast<int64>(std::numeric_limits<int32>::max()) + 1,
|
||||
std::numeric_limits<int32>::max() - 1,
|
||||
std::numeric_limits<int32>::min(),
|
||||
std::numeric_limits<int32>::min() + 1,
|
||||
static_cast<int64>(std::numeric_limits<int32>::min()) - 1};
|
||||
|
||||
#if TD_HAVE_INT128
|
||||
auto to_intrinsic = [](uint128_emulated num) { return uint128_intrinsic(num.hi(), num.lo()); };
|
||||
auto eq = [](uint128_emulated a, uint128_intrinsic b) { return a.hi() == b.hi() && a.lo() == b.lo(); };
|
||||
auto ensure_eq = [&](uint128_emulated a, uint128_intrinsic b) {
|
||||
if (!eq(a, b)) {
|
||||
LOG(FATAL) << "[" << a.hi() << ";" << a.lo() << "] vs [" << b.hi() << ";" << b.lo() << "]";
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
std::vector<uint128_emulated> nums;
|
||||
for (auto hi : parts) {
|
||||
for (auto lo : parts) {
|
||||
auto a = uint128_emulated(hi, lo);
|
||||
#if TD_HAVE_INT128
|
||||
auto ia = uint128_intrinsic(hi, lo);
|
||||
ensure_eq(a, ia);
|
||||
#endif
|
||||
nums.push_back(a);
|
||||
nums.pop_back();
|
||||
nums.push_back({hi, lo});
|
||||
}
|
||||
}
|
||||
|
||||
#if TD_HAVE_INT128
|
||||
for (auto a : nums) {
|
||||
auto ia = to_intrinsic(a);
|
||||
ensure_eq(a, ia);
|
||||
CHECK(a.is_zero() == ia.is_zero());
|
||||
for (int i = 0; i <= 130; i++) {
|
||||
ensure_eq(a.shl(i), ia.shl(i));
|
||||
ensure_eq(a.shr(i), ia.shr(i));
|
||||
}
|
||||
for (auto b : parts) {
|
||||
ensure_eq(a.mult(b), ia.mult(b));
|
||||
}
|
||||
for (auto b : signed_parts) {
|
||||
ensure_eq(a.mult_signed(b), ia.mult_signed(b));
|
||||
if (b == 0) {
|
||||
continue;
|
||||
}
|
||||
int64 q, r;
|
||||
a.divmod_signed(b, &q, &r);
|
||||
int64 iq, ir;
|
||||
ia.divmod_signed(b, &iq, &ir);
|
||||
ASSERT_EQ(q, iq);
|
||||
ASSERT_EQ(r, ir);
|
||||
}
|
||||
for (auto b : nums) {
|
||||
auto ib = to_intrinsic(b);
|
||||
//LOG(ERROR) << ia.hi() << ";" << ia.lo() << " " << ib.hi() << ";" << ib.lo();
|
||||
ensure_eq(a.mult(b), ia.mult(ib));
|
||||
ensure_eq(a.add(b), ia.add(ib));
|
||||
ensure_eq(a.sub(b), ia.sub(ib));
|
||||
if (!b.is_zero()) {
|
||||
ensure_eq(a.div(b), ia.div(ib));
|
||||
ensure_eq(a.mod(b), ia.mod(ib));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto signed_part : signed_parts) {
|
||||
auto a = uint128_emulated::from_signed(signed_part);
|
||||
auto ia = uint128_intrinsic::from_signed(signed_part);
|
||||
ensure_eq(a, ia);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <template <class T> class HashT, class ValueT>
|
||||
Status test_hash(const std::vector<ValueT> &values) {
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
for (size_t j = i; j < values.size(); j++) {
|
||||
auto &a = values[i];
|
||||
auto &b = values[j];
|
||||
auto a_hash = HashT<ValueT>()(a);
|
||||
auto b_hash = HashT<ValueT>()(b);
|
||||
if (a == b) {
|
||||
if (a_hash != b_hash) {
|
||||
return Status::Error("Hash differs for same values");
|
||||
}
|
||||
} else {
|
||||
if (a_hash == b_hash) {
|
||||
return Status::Error("Hash is the same for different values");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
class BadValue {
|
||||
public:
|
||||
explicit BadValue(size_t value) : value_(value) {
|
||||
}
|
||||
|
||||
template <class H>
|
||||
friend H AbslHashValue(H hasher, const BadValue &value) {
|
||||
return hasher;
|
||||
}
|
||||
bool operator==(const BadValue &other) const {
|
||||
return value_ == other.value_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t value_;
|
||||
};
|
||||
|
||||
class ValueA {
|
||||
public:
|
||||
explicit ValueA(size_t value) : value_(value) {
|
||||
}
|
||||
template <class H>
|
||||
friend H AbslHashValue(H hasher, ValueA value) {
|
||||
return H::combine(std::move(hasher), value.value_);
|
||||
}
|
||||
bool operator==(const ValueA &other) const {
|
||||
return value_ == other.value_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t value_;
|
||||
};
|
||||
|
||||
class ValueB {
|
||||
public:
|
||||
explicit ValueB(size_t value) : value_(value) {
|
||||
}
|
||||
|
||||
template <class H>
|
||||
friend H AbslHashValue(H hasher, ValueB value) {
|
||||
return H::combine(std::move(hasher), value.value_);
|
||||
}
|
||||
bool operator==(const ValueB &other) const {
|
||||
return value_ == other.value_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t value_;
|
||||
};
|
||||
|
||||
template <template <class T> class HashT>
|
||||
static void test_hash() {
|
||||
// Just check that the following compiles
|
||||
AbslHashValue(Hasher(), ValueA{1});
|
||||
HashT<ValueA>()(ValueA{1});
|
||||
std::unordered_map<ValueA, int, HashT<ValueA>> s;
|
||||
s[ValueA{1}] = 1;
|
||||
HashMap<ValueA, int> su;
|
||||
su[ValueA{1}] = 1;
|
||||
HashSet<ValueA> su2;
|
||||
su2.insert(ValueA{1});
|
||||
#if TD_HAVE_ABSL
|
||||
std::unordered_map<ValueA, int, absl::Hash<ValueA>> x;
|
||||
absl::flat_hash_map<ValueA, int, HashT<ValueA>> sa;
|
||||
sa[ValueA{1}] = 1;
|
||||
#endif
|
||||
|
||||
test_hash<HashT, size_t>({1, 2, 3, 4, 5}).ensure();
|
||||
test_hash<HashT, BadValue>({BadValue{1}, BadValue{2}}).ensure_error();
|
||||
test_hash<HashT, ValueA>({ValueA{1}, ValueA{2}}).ensure();
|
||||
test_hash<HashT, ValueB>({ValueB{1}, ValueB{2}}).ensure();
|
||||
test_hash<HashT, std::pair<int, int>>({{1, 1}, {1, 2}}).ensure();
|
||||
// FIXME: use some better hash
|
||||
//test_hash<HashT, std::pair<int, int>>({{1, 1}, {1, 2}, {2, 1}, {2, 2}}).ensure();
|
||||
}
|
||||
|
||||
TEST(Misc, Hasher) {
|
||||
test_hash<TdHash>();
|
||||
#if TD_HAVE_ABSL
|
||||
test_hash<AbslHash>();
|
||||
#endif
|
||||
}
|
||||
TEST(Misc, CancellationToken) {
|
||||
CancellationTokenSource source;
|
||||
source.cancel();
|
||||
auto token1 = source.get_cancellation_token();
|
||||
auto token2 = source.get_cancellation_token();
|
||||
CHECK(!token1);
|
||||
source.cancel();
|
||||
CHECK(token1);
|
||||
CHECK(token2);
|
||||
auto token3 = source.get_cancellation_token();
|
||||
CHECK(!token3);
|
||||
source.cancel();
|
||||
CHECK(token3);
|
||||
|
||||
auto token4 = source.get_cancellation_token();
|
||||
CHECK(!token4);
|
||||
source = CancellationTokenSource{};
|
||||
CHECK(token4);
|
||||
}
|
216
tdutils/test/port.cpp
Normal file
216
tdutils/test/port.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
#include "td/utils/port/IoSlice.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/port/signals.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
using namespace td;
|
||||
|
||||
TEST(Port, files) {
|
||||
CSlice main_dir = "test_dir";
|
||||
rmrf(main_dir).ignore();
|
||||
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error());
|
||||
ASSERT_TRUE(walk_path(main_dir, [](CSlice name, WalkPath::Type type) { UNREACHABLE(); }).is_error());
|
||||
mkdir(main_dir).ensure();
|
||||
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "A").ensure();
|
||||
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B").ensure();
|
||||
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B" << TD_DIR_SLASH << "D").ensure();
|
||||
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "C").ensure();
|
||||
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error());
|
||||
std::string fd_path = PSTRING() << main_dir << TD_DIR_SLASH << "t.txt";
|
||||
std::string fd2_path = PSTRING() << main_dir << TD_DIR_SLASH << "C" << TD_DIR_SLASH << "t2.txt";
|
||||
|
||||
auto fd = FileFd::open(fd_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
||||
auto fd2 = FileFd::open(fd2_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
||||
fd2.close();
|
||||
|
||||
int cnt = 0;
|
||||
const int ITER_COUNT = 1000;
|
||||
for (int i = 0; i < ITER_COUNT; i++) {
|
||||
walk_path(main_dir,
|
||||
[&](CSlice name, WalkPath::Type type) {
|
||||
if (type == WalkPath::Type::NotDir) {
|
||||
ASSERT_TRUE(name == fd_path || name == fd2_path);
|
||||
}
|
||||
cnt++;
|
||||
})
|
||||
.ensure();
|
||||
}
|
||||
ASSERT_EQ((5 * 2 + 2) * ITER_COUNT, cnt);
|
||||
bool was_abort = false;
|
||||
walk_path(main_dir,
|
||||
[&](CSlice name, WalkPath::Type type) {
|
||||
CHECK(!was_abort);
|
||||
if (type == WalkPath::Type::EnterDir && ends_with(name, PSLICE() << TD_DIR_SLASH << "B")) {
|
||||
was_abort = true;
|
||||
return WalkPath::Action::Abort;
|
||||
}
|
||||
return WalkPath::Action::Continue;
|
||||
})
|
||||
.ensure();
|
||||
CHECK(was_abort);
|
||||
|
||||
cnt = 0;
|
||||
bool is_first_dir = true;
|
||||
walk_path(main_dir,
|
||||
[&](CSlice name, WalkPath::Type type) {
|
||||
cnt++;
|
||||
if (type == WalkPath::Type::EnterDir) {
|
||||
if (is_first_dir) {
|
||||
is_first_dir = false;
|
||||
} else {
|
||||
return WalkPath::Action::SkipDir;
|
||||
}
|
||||
}
|
||||
return WalkPath::Action::Continue;
|
||||
})
|
||||
.ensure();
|
||||
ASSERT_EQ(6, cnt);
|
||||
|
||||
ASSERT_EQ(0u, fd.get_size().move_as_ok());
|
||||
ASSERT_EQ(12u, fd.write("Hello world!").move_as_ok());
|
||||
ASSERT_EQ(4u, fd.pwrite("abcd", 1).move_as_ok());
|
||||
char buf[100];
|
||||
MutableSlice buf_slice(buf, sizeof(buf));
|
||||
ASSERT_TRUE(fd.pread(buf_slice.substr(0, 4), 2).is_error());
|
||||
fd.seek(11).ensure();
|
||||
ASSERT_EQ(2u, fd.write("?!").move_as_ok());
|
||||
|
||||
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Read | FileFd::CreateNew).is_error());
|
||||
fd = FileFd::open(fd_path, FileFd::Read | FileFd::Create).move_as_ok();
|
||||
ASSERT_EQ(13u, fd.get_size().move_as_ok());
|
||||
ASSERT_EQ(4u, fd.pread(buf_slice.substr(0, 4), 1).move_as_ok());
|
||||
ASSERT_STREQ("abcd", buf_slice.substr(0, 4));
|
||||
|
||||
fd.seek(0).ensure();
|
||||
ASSERT_EQ(13u, fd.read(buf_slice.substr(0, 13)).move_as_ok());
|
||||
ASSERT_STREQ("Habcd world?!", buf_slice.substr(0, 13));
|
||||
}
|
||||
|
||||
TEST(Port, Writev) {
|
||||
std::vector<IoSlice> vec;
|
||||
CSlice test_file_path = "test.txt";
|
||||
unlink(test_file_path).ignore();
|
||||
auto fd = FileFd::open(test_file_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
||||
vec.push_back(as_io_slice("a"));
|
||||
vec.push_back(as_io_slice("b"));
|
||||
vec.push_back(as_io_slice("cd"));
|
||||
ASSERT_EQ(4u, fd.writev(vec).move_as_ok());
|
||||
vec.clear();
|
||||
vec.push_back(as_io_slice("efg"));
|
||||
vec.push_back(as_io_slice(""));
|
||||
vec.push_back(as_io_slice("hi"));
|
||||
ASSERT_EQ(5u, fd.writev(vec).move_as_ok());
|
||||
fd.close();
|
||||
fd = FileFd::open(test_file_path, FileFd::Read).move_as_ok();
|
||||
Slice expected_content = "abcdefghi";
|
||||
ASSERT_EQ(static_cast<int64>(expected_content.size()), fd.get_size().ok());
|
||||
std::string content(expected_content.size(), '\0');
|
||||
ASSERT_EQ(content.size(), fd.read(content).move_as_ok());
|
||||
ASSERT_EQ(expected_content, content);
|
||||
}
|
||||
|
||||
#if TD_PORT_POSIX && !TD_THREAD_UNSUPPORTED
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
static std::mutex m;
|
||||
static std::vector<std::string> ptrs;
|
||||
static std::vector<int *> addrs;
|
||||
static TD_THREAD_LOCAL int thread_id;
|
||||
|
||||
static void on_user_signal(int sig) {
|
||||
int addr;
|
||||
addrs[thread_id] = &addr;
|
||||
char ptr[10];
|
||||
snprintf(ptr, 6, "%d", thread_id);
|
||||
std::unique_lock<std::mutex> guard(m);
|
||||
ptrs.push_back(std::string(ptr));
|
||||
}
|
||||
|
||||
TEST(Post, SignalsAndThread) {
|
||||
setup_signals_alt_stack().ensure();
|
||||
set_signal_handler(SignalType::User, on_user_signal).ensure();
|
||||
std::vector<std::string> ans = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
|
||||
{
|
||||
std::vector<td::thread> threads;
|
||||
int thread_n = 10;
|
||||
std::vector<Stage> stages(thread_n);
|
||||
ptrs.clear();
|
||||
addrs.resize(thread_n);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
threads.emplace_back([&, i] {
|
||||
setup_signals_alt_stack().ensure();
|
||||
if (i != 0) {
|
||||
stages[i].wait(2);
|
||||
}
|
||||
thread_id = i;
|
||||
pthread_kill(pthread_self(), SIGUSR1);
|
||||
if (i + 1 < thread_n) {
|
||||
stages[i + 1].wait(2);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &t : threads) {
|
||||
t.join();
|
||||
}
|
||||
CHECK(ptrs == ans);
|
||||
|
||||
LOG(ERROR) << ptrs;
|
||||
//LOG(ERROR) << std::set<int *>(addrs.begin(), addrs.end()).size();
|
||||
//LOG(ERROR) << addrs;
|
||||
}
|
||||
|
||||
{
|
||||
Stage stage;
|
||||
std::vector<td::thread> threads;
|
||||
int thread_n = 10;
|
||||
ptrs.clear();
|
||||
addrs.resize(thread_n);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
threads.emplace_back([&, i] {
|
||||
stage.wait(thread_n);
|
||||
thread_id = i;
|
||||
pthread_kill(pthread_self(), SIGUSR1);
|
||||
//kill(pid_t(syscall(SYS_gettid)), SIGUSR1);
|
||||
});
|
||||
}
|
||||
for (auto &t : threads) {
|
||||
t.join();
|
||||
}
|
||||
std::sort(ptrs.begin(), ptrs.end());
|
||||
CHECK(ptrs == ans);
|
||||
std::sort(addrs.begin(), addrs.end());
|
||||
ASSERT_TRUE(std::unique(addrs.begin(), addrs.end()) == addrs.end());
|
||||
//LOG(ERROR) << addrs;
|
||||
}
|
||||
}
|
||||
#endif
|
130
tdutils/test/pq.cpp
Normal file
130
tdutils/test/pq.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#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).move_as_ok();
|
||||
BigNum q = BigNum::from_decimal(PSLICE() << second).move_as_ok();
|
||||
|
||||
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);
|
||||
LOG_CHECK(err == 0) << first << " * " << second;
|
||||
|
||||
BigNum p_res = BigNum::from_binary(p_str);
|
||||
BigNum q_res = BigNum::from_binary(q_str);
|
||||
|
||||
LOG_CHECK(p_str == p.to_binary()) << td::tag("got", p_res.to_decimal()) << td::tag("expected", first);
|
||||
LOG_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
|
85
tdutils/test/variant.cpp
Normal file
85
tdutils/test/variant.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/tests.h"
|
||||
#include "td/utils/Variant.h"
|
||||
|
||||
REGISTER_TESTS(variant);
|
||||
|
||||
static const size_t BUF_SIZE = 1024 * 1024;
|
||||
static char buf[BUF_SIZE], buf2[BUF_SIZE];
|
||||
static td::StringBuilder sb(td::MutableSlice(buf, BUF_SIZE - 1));
|
||||
static td::StringBuilder sb2(td::MutableSlice(buf2, BUF_SIZE - 1));
|
||||
|
||||
static td::string move_sb() {
|
||||
auto res = sb.as_cslice().str();
|
||||
sb.clear();
|
||||
return res;
|
||||
}
|
||||
|
||||
static td::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) {
|
||||
{
|
||||
td::Variant<td::unique_ptr<A>, td::unique_ptr<B>, td::unique_ptr<C>> abc;
|
||||
ASSERT_STREQ("", sb.as_cslice());
|
||||
abc = td::make_unique<A>();
|
||||
ASSERT_STREQ("+A", sb.as_cslice());
|
||||
sb.clear();
|
||||
abc = td::make_unique<B>();
|
||||
ASSERT_STREQ("+B-A", sb.as_cslice());
|
||||
sb.clear();
|
||||
abc = td::make_unique<C>();
|
||||
ASSERT_STREQ("+C-B", sb.as_cslice());
|
||||
sb.clear();
|
||||
}
|
||||
ASSERT_STREQ("-C", move_sb());
|
||||
sb.clear();
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue