// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/utils/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/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 #include #include #include #include #include #if TD_HAVE_ABSL #include #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 s{0}; s = 1; td::thread th([&] { while (s != 1) { } test_event_fd.acquire(); }); th.join(); for (int i = 0; i < 1000; i++) { vector events(10); vector 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(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::min(), std::numeric_limits::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("-1234567"), -1234567); ASSERT_EQ(to_integer("-1234567"), -1234567); ASSERT_EQ(to_integer("-1234567"), 0u); ASSERT_EQ(to_integer("-1234567"), 10617); ASSERT_EQ(to_integer("-1234567"), 0u); ASSERT_EQ(to_integer("-1254567"), -9383); ASSERT_EQ(to_integer("1254567"), 9383u); ASSERT_EQ(to_integer("-12345678910111213"), -12345678910111213); ASSERT_EQ(to_integer("12345678910111213"), 12345678910111213ull); ASSERT_EQ(to_integer_safe("-1234567").ok(), -1234567); ASSERT_EQ(to_integer_safe("-1234567").ok(), -1234567); ASSERT_TRUE(to_integer_safe("-1234567").is_error()); ASSERT_TRUE(to_integer_safe("-1234567").is_error()); ASSERT_TRUE(to_integer_safe("-1234567").is_error()); ASSERT_TRUE(to_integer_safe("-1254567").is_error()); ASSERT_TRUE(to_integer_safe("1254567").is_error()); ASSERT_EQ(to_integer_safe("-12345678910111213").ok(), -12345678910111213); ASSERT_EQ(to_integer_safe("12345678910111213").ok(), 12345678910111213ull); ASSERT_TRUE(to_integer_safe("-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 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 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 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; 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(buf) = 123; ASSERT_EQ(123, as((const char *)buf)); ASSERT_EQ(123, as((char *)buf)); char buf2[100]; as(buf2) = as(buf); ASSERT_EQ(123, as((const char *)buf2)); ASSERT_EQ(123, as((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, bswap32(0x78563412u)); ASSERT_EQ(0x12345678abcdef67ull, 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 threads; std::vector> 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 parts = {0, 1, 2000, 2001, std::numeric_limits::max(), std::numeric_limits::max() - 1, std::numeric_limits::max(), static_cast(std::numeric_limits::max()) + 1}; std::vector signed_parts = {0, 1, 2000, 2001, -1, -2000, -2001, std::numeric_limits::max(), std::numeric_limits::max() - 1, std::numeric_limits::min(), std::numeric_limits::min() + 1, std::numeric_limits::max(), static_cast(std::numeric_limits::max()) + 1, std::numeric_limits::max() - 1, std::numeric_limits::min(), std::numeric_limits::min() + 1, static_cast(std::numeric_limits::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 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({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