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