| // 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 |