blob: 935b705e60a56aeca4a52384e35b18762c192b4f [file]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "uuid/uuid.h"
#include <cstdint>
#include <string>
#include <type_traits>
#include "absl/log/check.h"
#include "absl/numeric/int128.h"
#include "absl/random/random.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace credentio {
namespace {
using ::absl::StatusCode::kInvalidArgument;
using ::absl_testing::IsOk;
using ::absl_testing::IsOkAndHolds;
using ::absl_testing::StatusIs;
using ::testing::HasSubstr;
TEST(UuidTest, IsTriviallyDestructible) {
// The type is not POD because it has a default constructor that initializes
// the value to kInvalid. Without such as constructor, Uuids declared on the
// stack would have an indeterminate value. However, the type is trivially
// destructible, which means the Google Style Guide allows static constants
// of type Uuid.
static_assert(std::is_trivially_destructible_v<Uuid>,
"Uuid is not trivially destructible");
}
TEST(UuidTest, ConstexprFromStringOrDie) {
constexpr auto kUuid =
Uuid::FromStringOrDie("03020100-0504-0706-0908-0f0e0d0c0b0a");
EXPECT_FALSE(kUuid.IsMagic());
}
TEST(UuidTest, CompareData) {
const Uuid id0_0(0, 0);
const Uuid id0_1(0, 1);
const Uuid id1_0(1, 0);
EXPECT_TRUE(id0_0 == id0_0);
EXPECT_FALSE(id0_0 != id0_0);
EXPECT_FALSE(id0_0 < id0_0);
EXPECT_TRUE(id0_0 <= id0_0);
EXPECT_FALSE(id0_0 == id0_1);
EXPECT_TRUE(id0_0 != id0_1);
EXPECT_TRUE(id0_0 < id0_1);
EXPECT_TRUE(id0_0 <= id0_1);
EXPECT_FALSE(id0_0 == id1_0);
EXPECT_TRUE(id0_0 != id1_0);
EXPECT_TRUE(id0_0 < id1_0);
EXPECT_TRUE(id0_0 <= id1_0);
EXPECT_FALSE(id0_1 == id0_0);
EXPECT_TRUE(id0_1 != id0_0);
EXPECT_FALSE(id0_1 < id0_0);
EXPECT_FALSE(id0_1 <= id0_0);
EXPECT_FALSE(id1_0 == id0_0);
EXPECT_TRUE(id1_0 != id0_0);
EXPECT_FALSE(id1_0 < id0_0);
EXPECT_FALSE(id1_0 <= id0_0);
}
TEST(UuidTest, Invalid) {
Uuid id;
EXPECT_EQ(Uuid::kInvalid, id);
}
TEST(UuidTest, IsValid) {
EXPECT_FALSE(Uuid(0).IsValid());
EXPECT_FALSE(Uuid(1).IsValid());
absl::uint128 i = Uuid(Uuid::kValidMin).ToRawNumber();
i--;
EXPECT_FALSE(Uuid(i).IsValid());
i++;
EXPECT_TRUE(Uuid(i).IsValid());
EXPECT_TRUE(Uuid(Uuid::kValidMax).IsValid());
}
TEST(UuidTest, Magic) {
// This test will not compile unless 0 < key < 1000.
static constexpr uint64_t key = 27;
// Four different ways of specifying the same static constant.
static const Uuid m1 = Uuid::Magic<key>();
static const Uuid m2 = Uuid(key);
static const Uuid m3 = Uuid(0, key);
static const Uuid m4 = Uuid(absl::uint128(key));
EXPECT_TRUE(m1 == m2);
EXPECT_TRUE(m2 == m3);
EXPECT_TRUE(m3 == m4);
}
TEST(UuidTest, IsMagic) {
// This test will not compile unless 0 < magic_key < 1000.
static constexpr uint64_t magic_key = 27;
// Four different ways of specifying the same static constant.
EXPECT_TRUE(Uuid::Magic<magic_key>().IsMagic());
EXPECT_TRUE(Uuid(magic_key).IsMagic());
EXPECT_TRUE(Uuid(0, magic_key).IsMagic());
EXPECT_TRUE(Uuid(absl::uint128(magic_key)).IsMagic());
EXPECT_FALSE(Uuid(1001).IsMagic());
EXPECT_FALSE(Uuid(0, 1001).IsMagic());
EXPECT_FALSE(Uuid(absl::uint128(1001)).IsMagic());
}
TEST(UuidTest, Number) {
absl::uint128 i1 = absl::MakeUint128(0, 1);
Uuid id1(1);
EXPECT_EQ(i1, id1.ToRawNumber());
EXPECT_EQ(0, id1.high64());
EXPECT_EQ(1, id1.low64());
absl::uint128 i2 = absl::MakeUint128(0, 1275);
Uuid id2(0, 1275);
EXPECT_EQ(i2, id2.ToRawNumber());
EXPECT_EQ(0, id2.high64());
EXPECT_EQ(1275, id2.low64());
absl::uint128 i3 = absl::MakeUint128(1, 0);
Uuid id3(1, 0);
EXPECT_EQ(i3, id3.ToRawNumber());
EXPECT_EQ(1, id3.high64());
EXPECT_EQ(0, id3.low64());
absl::uint128 i4 = absl::MakeUint128(2, 14);
Uuid id4(2, 14);
EXPECT_EQ(i4, id4.ToRawNumber());
EXPECT_EQ(2, id4.high64());
EXPECT_EQ(14, id4.low64());
absl::BitGen rng;
const auto rand64 = [&rng]() { return absl::Uniform<uint64_t>(rng); };
for (int i = 0; i != 100; i++) {
uint64_t high = rand64();
uint64_t low = rand64();
absl::uint128 j = absl::MakeUint128(high, low);
Uuid id(high, low);
EXPECT_EQ(j, id.ToRawNumber());
EXPECT_EQ(high, id.high64());
EXPECT_EQ(low, id.low64());
}
}
TEST(UuidTest, Compare) {
EXPECT_TRUE(Uuid(1) == Uuid(1));
EXPECT_FALSE(Uuid(1) == Uuid(2));
EXPECT_FALSE(Uuid(1) != Uuid(1));
EXPECT_TRUE(Uuid(1) != Uuid(2));
EXPECT_TRUE(Uuid(1) < Uuid(2));
EXPECT_FALSE(Uuid(2) < Uuid(1));
absl::BitGen rng;
const auto rand64 = [&rng]() { return absl::Uniform<uint64_t>(rng); };
for (int i = 0; i != 100; i++) {
absl::uint128 i1 = absl::MakeUint128(rand64(), rand64());
absl::uint128 i2 = absl::MakeUint128(rand64(), rand64());
Uuid id1(i1);
Uuid id2(i2);
const Uuid sid1(absl::Uint128High64(i1), absl::Uint128Low64(i1));
const Uuid sid2(absl::Uint128High64(i2), absl::Uint128Low64(i2));
EXPECT_EQ(id1, id1);
EXPECT_EQ(id1, sid1);
EXPECT_EQ(sid1, id1);
if (i1 < i2) {
EXPECT_TRUE(id1 < id2);
EXPECT_FALSE(id2 < id1);
EXPECT_FALSE(id1 == id2);
EXPECT_TRUE(id1 < sid2);
EXPECT_FALSE(id2 < sid1);
EXPECT_FALSE(id1 == sid2);
EXPECT_TRUE(sid1 < id2);
EXPECT_FALSE(sid2 < id1);
EXPECT_FALSE(sid1 == id2);
} else if (i2 < i1) {
EXPECT_TRUE(id2 < id1);
EXPECT_FALSE(id1 < id2);
EXPECT_FALSE(id1 == id2);
EXPECT_TRUE(id2 < sid1);
EXPECT_FALSE(id1 < sid2);
EXPECT_FALSE(id1 == sid2);
EXPECT_TRUE(sid2 < id1);
EXPECT_FALSE(sid1 < id2);
EXPECT_FALSE(sid1 == id2);
} else {
EXPECT_EQ(id1, id2);
EXPECT_EQ(id1, sid2);
EXPECT_EQ(sid1, id2);
}
}
}
TEST(UuidTest, String) {
Uuid id(0x0f0e0d0c0b0a0908ULL, 0x0706050403020100ULL);
EXPECT_EQ("03020100-0504-0706-0908-0f0e0d0c0b0a", id.ToString());
EXPECT_EQ(id, Uuid::FromStringOrDie("3020100-504-706-908-f0e0d0c0b0a"));
EXPECT_EQ(id, Uuid::FromStringOrDie("3020100-504-706-908-F0E0D0C0B0A"));
EXPECT_EQ(id, Uuid::FromStringOrDie("03020100-0504-0706-0908-0f0e0d0c0b0a"));
}
TEST(UuidTest, StringSuccess) {
Uuid id(0x0f0e0d0c0b0a0908ULL, 0x0706050403020100ULL);
EXPECT_EQ("03020100-0504-0706-0908-0f0e0d0c0b0a", id.ToString());
EXPECT_THAT(Uuid::FromString("3020100-504-706-908-f0e0d0c0b0a"),
IsOkAndHolds(id));
EXPECT_THAT(Uuid::FromString("3020100-504-706-908-F0E0D0C0B0A"),
IsOkAndHolds(id));
}
TEST(UuidTest, StringFail) {
auto is_invalid = StatusIs(kInvalidArgument, HasSubstr("Syntax error"));
EXPECT_THAT(Uuid::FromString("I am not a Uuid"), is_invalid);
EXPECT_THAT(Uuid::FromString(""), is_invalid);
EXPECT_THAT(Uuid::FromString("00-00-00"), is_invalid);
// Individual components too long.
EXPECT_THAT(Uuid::FromString("111111118-1111-1111-1111-111111111111"),
is_invalid);
EXPECT_THAT(Uuid::FromString("11111111-11118-1111-1111-111111111111"),
is_invalid);
EXPECT_THAT(Uuid::FromString("11111111-1111-11118-1111-111111111111"),
is_invalid);
EXPECT_THAT(Uuid::FromString("11111111-1111-1111-11118-111111111111"),
is_invalid);
EXPECT_THAT(Uuid::FromString("11111111-1111-1111-1111-1111111111118"),
is_invalid);
// Missing components
EXPECT_THAT(Uuid::FromString("-1111-1111-1111-111111111111"), is_invalid);
EXPECT_THAT(Uuid::FromString("11111111-1111--1111-111111111111"), is_invalid);
EXPECT_THAT(Uuid::FromString("11111111-1111-1111-1111-"), is_invalid);
EXPECT_THAT(Uuid::FromString("----"), is_invalid);
// Extra component
EXPECT_THAT(Uuid::FromString("11111111-1111-1111-1111-111111111111-1"),
is_invalid);
}
TEST(UuidTest, StringDeath) {
EXPECT_DEATH(Uuid::FromStringOrDie("wrong"), "Syntax error");
EXPECT_DEATH(Uuid::FromStringOrDie("03020100-0504-0706-0908-0f0e0d0c0b0Z"),
"Syntax error");
EXPECT_DEATH(Uuid::FromStringOrDie("Z3020100-0504-0706-0908-0f0e0d0c0b0a"),
"Syntax error");
}
TEST(UuidTest, Bytes) {
Uuid id(0x0f0e0d0c0b0a0908ULL, 0x0706050403020100ULL);
EXPECT_EQ("03020100-0504-0706-0908-0f0e0d0c0b0a", id.ToString());
std::string s;
s.assign("\x03\x02\x01\x00\x05\x04\x07\x06\x09\x08\x0f\x0e\x0d\x0c\x0b\x0a",
16);
EXPECT_EQ(s, id.ToProtoBytes());
auto id2 = Uuid::FromProtoBytes(s);
EXPECT_THAT(id2.status(), IsOk());
EXPECT_EQ(id, id2.value());
}
TEST(UuidTest, InvalidBytes) {
// Uuid::kInvalid is represented with the empty string.
Uuid id;
EXPECT_EQ(Uuid::kInvalid, id);
EXPECT_EQ("", id.ToProtoBytes());
auto id2 = Uuid::FromProtoBytes("");
EXPECT_THAT(id2.status(), IsOk());
EXPECT_EQ(Uuid::kInvalid, id2.value());
}
TEST(UuidTest, ParseError) {
auto is_invalid = StatusIs(kInvalidArgument, HasSubstr("Syntax error"));
std::string s;
s.resize(15);
EXPECT_THAT(Uuid::FromProtoBytes(s), is_invalid);
s.resize(17);
EXPECT_THAT(Uuid::FromProtoBytes(s), is_invalid);
s.resize(16);
EXPECT_THAT(Uuid::FromProtoBytes(s).status(), IsOk());
}
} // namespace
} // namespace credentio