Signed-off-by: Benny Halevy <bhalevy@scylladb.com> Message-Id: <20220216081623.830627-2-bhalevy@scylladb.com>
263 lines
9.9 KiB
C++
263 lines
9.9 KiB
C++
/*
|
|
* Copyright (C) 2014-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#define BOOST_TEST_MODULE core
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <utility>
|
|
#include "utils/UUID_gen.hh"
|
|
#include "types.hh"
|
|
|
|
BOOST_AUTO_TEST_CASE(test_generation_of_name_based_UUID) {
|
|
auto uuid = utils::UUID_gen::get_name_UUID("systembatchlog");
|
|
BOOST_REQUIRE_EQUAL(uuid.to_sstring(), "0290003c-977e-397c-ac3e-fdfdc01d626b");
|
|
}
|
|
|
|
using utils::UUID;
|
|
using namespace std::rel_ops;
|
|
|
|
BOOST_AUTO_TEST_CASE(test_UUID_comparison) {
|
|
static const std::initializer_list<std::pair<UUID, UUID>> uuid_pairs = {
|
|
|
|
{ UUID("ffeeddcc-aa99-8877-6655-443322110000"), UUID("00000000-0000-0000-0000-000000000000") },
|
|
{ UUID("0feeddcc-aa99-8877-6655-443322110000"), UUID("00000000-0000-0000-0000-000000000000") },
|
|
{ UUID("00000000-0000-0000-0000-000000000001"), UUID("00000000-0000-0000-0000-000000000000") },
|
|
|
|
{ UUID("ffeeddcc-aa99-8877-6655-443322110001"), UUID("ffeeddcc-aa99-8877-6655-443322110000") },
|
|
{ UUID("0feeddcc-aa99-8877-6655-443322110000"), UUID("0eeeddcc-aa99-8877-6655-443322110000") },
|
|
{ UUID("0290003c-987e-397c-ac3e-fdfdc01d626b"), UUID("0290003c-977e-397c-ac3e-fdfdc01d626b") },
|
|
|
|
};
|
|
|
|
for (auto& p : uuid_pairs) {
|
|
BOOST_REQUIRE_GT(p.first, p.second);
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_from_string) {
|
|
auto check = [] (sstring_view sv) {
|
|
auto uuid = UUID(sv);
|
|
BOOST_CHECK_EQUAL(uuid.version(), 4);
|
|
BOOST_CHECK_EQUAL(uuid.to_sstring(), sv);
|
|
BOOST_CHECK_EQUAL((uuid.get_least_significant_bits() >> 62) & 0x3, 2);
|
|
};
|
|
|
|
check("b1415756-49c3-4fa8-9b72-d1b867b032af");
|
|
check("85859d5c-fcf3-4b0b-9089-197b8b06735c");
|
|
check("e596c2f2-d29d-44a0-bb89-0a90ff928490");
|
|
check("f28f86f5-cbc2-4526-ba25-db90c226ec6a");
|
|
check("ce84997b-6ea2-4468-9f02-8a65abf4141a");
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_make_random_uuid) {
|
|
std::vector<UUID> uuids;
|
|
for (auto i = 0; i < 100; i++) {
|
|
auto uuid = utils::make_random_uuid();
|
|
BOOST_CHECK_EQUAL(uuid.version(), 4);
|
|
BOOST_CHECK_EQUAL((uuid.get_least_significant_bits() >> 62) & 0x3, 2);
|
|
uuids.emplace_back(uuid);
|
|
}
|
|
std::sort(uuids.begin(), uuids.end());
|
|
BOOST_CHECK(std::unique(uuids.begin(), uuids.end()) == uuids.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_get_time_uuid) {
|
|
using namespace std::chrono;
|
|
|
|
auto uuid = utils::UUID_gen::get_time_UUID();
|
|
BOOST_CHECK(uuid.is_timestamp());
|
|
|
|
auto tp = system_clock::now();
|
|
uuid = utils::UUID_gen::get_time_UUID(tp);
|
|
BOOST_CHECK(uuid.is_timestamp());
|
|
|
|
auto millis = duration_cast<milliseconds>(tp.time_since_epoch());
|
|
uuid = utils::UUID_gen::get_time_UUID(millis);
|
|
BOOST_CHECK(uuid.is_timestamp());
|
|
|
|
auto unix_timestamp = utils::UUID_gen::unix_timestamp(uuid);
|
|
BOOST_CHECK(unix_timestamp == millis);
|
|
}
|
|
|
|
std::strong_ordering timeuuid_legacy_tri_compare(bytes_view o1, bytes_view o2) {
|
|
auto compare_pos = [&] (unsigned pos, int mask, std::strong_ordering ifequal) {
|
|
auto d = (o1[pos] & mask) <=> (o2[pos] & mask);
|
|
return d != 0 ? d : ifequal;
|
|
};
|
|
auto res = compare_pos(6, 0xf,
|
|
compare_pos(7, 0xff,
|
|
compare_pos(4, 0xff,
|
|
compare_pos(5, 0xff,
|
|
compare_pos(0, 0xff,
|
|
compare_pos(1, 0xff,
|
|
compare_pos(2, 0xff,
|
|
compare_pos(3, 0xff, std::strong_ordering::equal))))))));
|
|
if (res == 0) {
|
|
res = lexicographical_tri_compare(o1.begin(), o1.end(), o2.begin(), o2.end(),
|
|
[] (const int8_t& a, const int8_t& b) { return a <=> b; });
|
|
}
|
|
return res;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_timeuuid_msb_is_monotonic) {
|
|
using utils::UUID, utils::UUID_gen;
|
|
auto uuid = UUID_gen::get_time_UUID();
|
|
auto first = uuid.serialize();
|
|
int64_t scale_list[] = { 1, 10, 10000, 10000000, 0 };
|
|
auto str = [&scale_list](int64_t *scale) {
|
|
static std::string name_list[] = { " 100ns", "mc", "ms", "s" };
|
|
return name_list[scale - scale_list];
|
|
};
|
|
|
|
for (int64_t *scale = scale_list; *scale; scale++) {
|
|
int step = 1; /* + (random() % 169) ; */
|
|
auto prev = first;
|
|
for (int64_t i = 1; i < 3697; i += step) {
|
|
auto next = UUID(UUID_gen::create_time(UUID_gen::decimicroseconds{uuid.timestamp() + (i * *scale)}), 0).serialize();
|
|
bool t1 = utils::timeuuid_tri_compare(next, prev) > 0;
|
|
bool t2 = utils::timeuuid_tri_compare(next, first) > 0;
|
|
if (!t1 || !t2) {
|
|
BOOST_CHECK_MESSAGE(t1 && t2, format("a UUID {}{} later is not great than at test start: {} {}", i, str(scale), t1, t2));
|
|
}
|
|
prev = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_timeuuid_tri_compare_legacy) {
|
|
using utils::UUID, utils::UUID_gen;
|
|
auto uuid = UUID_gen::get_time_UUID();
|
|
auto first = uuid.serialize();
|
|
int64_t scale_list[] = { 1, 10, 10000, 10000000, 0 };
|
|
auto str = [&scale_list](int64_t *scale) {
|
|
static std::string name_list[] = { " 100ns", "mc", "ms", "s" };
|
|
return name_list[scale - scale_list];
|
|
};
|
|
|
|
|
|
for (int64_t *scale = scale_list; *scale; scale++) {
|
|
int step = 1; /* + (random() % 169) ; */
|
|
auto prev = first;
|
|
for (int64_t i = 1; i < 3697; i += step) {
|
|
auto next = UUID(UUID_gen::create_time(UUID_gen::decimicroseconds{uuid.timestamp() + (i * *scale)}), 0).serialize();
|
|
bool t1 = utils::timeuuid_tri_compare(next, prev) == timeuuid_legacy_tri_compare(next, prev);
|
|
bool t2 = utils::timeuuid_tri_compare(next, first) == timeuuid_legacy_tri_compare(next, first);
|
|
if (!t1 || !t2) {
|
|
BOOST_CHECK_MESSAGE(t1 && t2, format("a UUID {}{} later violates compare order", i, str(scale)));
|
|
}
|
|
prev = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(test_timeuuid_submicro_is_monotonic) {
|
|
const int64_t PAD6 = 0xFFFF'FFFF'FFFF;
|
|
using namespace std::chrono;
|
|
using utils::UUID, utils::UUID_gen;
|
|
UUID current_timeuuid = UUID_gen::get_time_UUID();
|
|
// Node identifier must be set to avoid collisions
|
|
BOOST_CHECK((current_timeuuid.get_least_significant_bits() & PAD6) != 0);
|
|
int64_t micros = UUID_gen::micros_timestamp(current_timeuuid);
|
|
int maxsubmicro = (1 << 17) - 1;
|
|
int step = 1 + (random() % 169);
|
|
auto prev = UUID_gen::get_time_UUID_bytes_from_micros_and_submicros(std::chrono::microseconds{micros}, 0);
|
|
auto prev_timeuuid = UUID_gen::get_UUID(prev.data());
|
|
// Check prev_timeuuid node identifier is set. It uses
|
|
// a spoof MAC address, not the same as a standard UUID.
|
|
BOOST_CHECK((prev_timeuuid.get_least_significant_bits() & PAD6) != 0);
|
|
auto check_is_valid_timeuuid = [&](UUID uuid) {
|
|
// UUID is version 1
|
|
BOOST_CHECK(uuid.is_timestamp());
|
|
// UUID preserves the original microsecond time
|
|
BOOST_CHECK(UUID_gen::micros_timestamp(uuid) == micros);
|
|
// UUID 100nsec time grows monotonically
|
|
BOOST_CHECK(uuid.timestamp() >= prev_timeuuid.timestamp());
|
|
// UUID node is the same for all generated UUIDs
|
|
BOOST_CHECK((prev_timeuuid.get_least_significant_bits() & PAD6) ==
|
|
(uuid.get_least_significant_bits() & PAD6));
|
|
};
|
|
for (int i = 1; i <= maxsubmicro; i += step) {
|
|
auto uuid = UUID_gen::get_time_UUID_bytes_from_micros_and_submicros(
|
|
std::chrono::microseconds{micros}, i);
|
|
check_is_valid_timeuuid(UUID_gen::get_UUID(uuid.data()));
|
|
// UUID submicro part grows monotonically
|
|
BOOST_CHECK(utils::timeuuid_tri_compare({uuid.data(), 16}, {prev.data(), 16}) > 0);
|
|
prev = uuid;
|
|
}
|
|
BOOST_CHECK_EXCEPTION(UUID_gen::get_time_UUID_bytes_from_micros_and_submicros(std::chrono::microseconds{micros}, 2 * maxsubmicro),
|
|
utils::timeuuid_submicro_out_of_range, [](auto& x) -> bool { return true; });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_min_time_uuid) {
|
|
using namespace std::chrono;
|
|
|
|
auto tp = system_clock::now();
|
|
auto millis = duration_cast<milliseconds>(tp.time_since_epoch());
|
|
auto uuid = utils::UUID_gen::min_time_UUID(millis);
|
|
BOOST_CHECK(uuid.is_timestamp());
|
|
|
|
auto unix_timestamp = utils::UUID_gen::unix_timestamp(uuid);
|
|
BOOST_CHECK(unix_timestamp == millis);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_max_time_uuid) {
|
|
using namespace std::chrono;
|
|
|
|
auto tp = system_clock::now();
|
|
auto millis = duration_cast<milliseconds>(tp.time_since_epoch());
|
|
auto uuid = utils::UUID_gen::max_time_UUID(millis);
|
|
BOOST_CHECK(uuid.is_timestamp());
|
|
|
|
auto unix_timestamp = utils::UUID_gen::unix_timestamp(uuid);
|
|
BOOST_CHECK(unix_timestamp == millis);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_negate) {
|
|
using namespace utils;
|
|
|
|
auto original_uuid = UUID_gen::get_time_UUID();
|
|
BOOST_TEST_MESSAGE(fmt::format("original_uuid: {}", original_uuid));
|
|
|
|
auto negated_uuid = UUID_gen::negate(original_uuid);
|
|
BOOST_TEST_MESSAGE(fmt::format("negated_uuid: {}", negated_uuid));
|
|
|
|
BOOST_REQUIRE(original_uuid != negated_uuid);
|
|
|
|
auto re_negated_uuid = UUID_gen::negate(negated_uuid);
|
|
BOOST_TEST_MESSAGE(fmt::format("re_negated_uuid: {}", re_negated_uuid));
|
|
|
|
BOOST_REQUIRE(original_uuid == re_negated_uuid);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_null_uuid) {
|
|
// Verify that the default-constructed UUID is null
|
|
utils::UUID uuid;
|
|
BOOST_CHECK(uuid.is_null());
|
|
BOOST_CHECK(!uuid);
|
|
|
|
// Verify that the null_uuid is indeed null
|
|
uuid = utils::null_uuid();
|
|
BOOST_CHECK(uuid.is_null());
|
|
BOOST_CHECK(!uuid);
|
|
|
|
// Verify that the default constructed
|
|
// UUID and the null_uuid are equal.
|
|
BOOST_REQUIRE_EQUAL(UUID(), utils::null_uuid());
|
|
|
|
// Verify that a random uuid is not null
|
|
uuid = utils::make_random_uuid();
|
|
BOOST_CHECK(!uuid.is_null());
|
|
BOOST_CHECK(uuid);
|
|
|
|
// Verify that a time uuid is not null
|
|
uuid = utils::UUID_gen::get_time_UUID();
|
|
BOOST_CHECK(!uuid.is_null());
|
|
BOOST_CHECK(uuid);
|
|
}
|