1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/platform/basic.h"
32
33 #include "mongo/bson/bsonmisc.h"
34 #include "mongo/bson/bsonobjbuilder.h"
35 #include "mongo/bson/bsontypes.h"
36 #include "mongo/stdx/unordered_set.h"
37 #include "mongo/unittest/unittest.h"
38 #include "mongo/util/uuid.h"
39
40 namespace mongo {
41 namespace {
42
TEST(UUIDTest,UUIDCollisionTest)43 TEST(UUIDTest, UUIDCollisionTest) {
44 // Generate some UUIDs and check that they do not collide.
45 // NOTE: if this test fails, it is not necessarily a bug. However, if it
46 // begins to fail often and on specific platforms, we should investigate
47 // the quality of our entropy on those systems.
48 stdx::unordered_set<UUID, UUID::Hash> uuids;
49 for (int i = 0; i < 10000; i++) {
50 ASSERT(uuids.emplace(UUID::gen()).second);
51 }
52 }
53
TEST(UUIDTest,isUUIDStringTest)54 TEST(UUIDTest, isUUIDStringTest) {
55 // Several valid strings
56 ASSERT(UUID::isUUIDString("00000000-0000-4000-8000-000000000000"));
57 ASSERT(UUID::isUUIDString("01234567-9abc-4def-9012-3456789abcde"));
58 ASSERT(UUID::isUUIDString("dddddddd-eeee-4fff-aaaa-bbbbbbbbbbbb"));
59 ASSERT(UUID::isUUIDString("A9A9A9A9-BEDF-4DD9-B001-222345716283"));
60
61 // No version or variant set
62 ASSERT(UUID::isUUIDString("00000000-0000-0000-0000-000000000000"));
63
64 // Mixed casing is weird, but technically legal
65 ASSERT(UUID::isUUIDString("abcdefAB-CDEF-4000-AaAa-FDFfdffd9991"));
66
67 // Wrong number of Hyphens
68 ASSERT(!UUID::isUUIDString("00000000-0000-4000-8000-0000000000-00"));
69 ASSERT(!UUID::isUUIDString("000000000000-4000-8000-000000000000"));
70 ASSERT(!UUID::isUUIDString("00000000000040008000000000000000"));
71
72 // Hyphens in the wrong places
73 ASSERT(!UUID::isUUIDString("dddddd-ddeeee-4fff-aaaa-bbbbbbbbbbbb"));
74 ASSERT(!UUID::isUUIDString("ddddddd-deeee-4fff-aaaa-bbbbbbbbbbbb"));
75 ASSERT(!UUID::isUUIDString("d-d-d-dddddeeee4fffaaaa-bbbbbbbbbbbb"));
76
77 // Illegal characters
78 ASSERT(!UUID::isUUIDString("samsamsa-sams-4sam-8sam-samsamsamsam"));
79
80 // Too short
81 ASSERT(!UUID::isUUIDString("A9A9A9A9-BEDF-4DD9-B001"));
82 ASSERT(!UUID::isUUIDString("dddddddd-eeee-4fff-aaaa-bbbbbbbbbbb"));
83
84 // Too long
85 ASSERT(!UUID::isUUIDString("01234567-9abc-4def-9012-3456789abcdef"));
86 ASSERT(!UUID::isUUIDString("0123004567-9abc-4def-9012-3456789abcdef0000"));
87 }
88
TEST(UUIDTest,toAndFromString)89 TEST(UUIDTest, toAndFromString) {
90 // String -> UUID -> string
91 auto s1 = "00000000-0000-4000-8000-000000000000";
92 auto uuid1Res = UUID::parse(s1);
93 ASSERT_OK(uuid1Res);
94 auto uuid1 = uuid1Res.getValue();
95 ASSERT(UUID::isUUIDString(s1));
96 ASSERT(UUID::isUUIDString(uuid1.toString()));
97 ASSERT_EQUALS(uuid1.toString(), s1);
98
99 // UUID -> string -> UUID
100 auto uuid2 = UUID::gen();
101 auto s2 = uuid2.toString();
102 ASSERT(UUID::isUUIDString(s2));
103
104 auto uuid2FromStringRes = UUID::parse(s2);
105 ASSERT_OK(uuid2FromStringRes);
106 auto uuid2FromString = uuid2FromStringRes.getValue();
107 ASSERT_EQUALS(uuid2FromString, uuid2);
108 ASSERT_EQUALS(uuid2FromString.toString(), s2);
109
110 // Two UUIDs constructed from the same string are equal
111 auto s3 = "01234567-9abc-4def-9012-3456789abcde";
112 auto uuid3Res = UUID::parse(s3);
113 auto uuid3AgainRes = UUID::parse(s3);
114 ASSERT_OK(uuid3Res);
115 ASSERT_OK(uuid3AgainRes);
116 ASSERT_EQUALS(uuid3Res.getValue(), uuid3AgainRes.getValue());
117
118 // Two UUIDs constructed from differently cased string are equal
119 auto sLower = "00000000-aaaa-4000-8000-000000000000";
120 auto sUpper = "00000000-AAAA-4000-8000-000000000000";
121 auto uuidLowerRes = UUID::parse(sLower);
122 auto uuidUpperRes = UUID::parse(sUpper);
123 ASSERT_OK(uuidLowerRes);
124 ASSERT_OK(uuidUpperRes);
125 auto uuidLower = uuidLowerRes.getValue();
126 auto uuidUpper = uuidUpperRes.getValue();
127 ASSERT_EQUALS(uuidLower, uuidUpper);
128
129 // Casing is not preserved on round trip, both become lowercase
130 ASSERT_EQUALS(uuidLower.toString(), uuidUpper.toString());
131 ASSERT_EQUALS(uuidLower.toString(), sLower);
132 ASSERT_EQUALS(uuidUpper.toString(), sLower);
133 ASSERT_NOT_EQUALS(uuidUpper.toString(), sUpper);
134
135 // UUIDs constructed from different strings are not equal
136 auto s4 = "01234567-9abc-4def-9012-3456789abcde";
137 auto s5 = "01234567-0000-4def-9012-3456789abcde";
138 ASSERT_NOT_EQUALS(UUID::parse(s4).getValue(), UUID::parse(s5).getValue());
139
140 // UUIDs cannot be constructed from invalid strings
141 ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse("00000000000040008000000000000000"));
142 ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse("d-d-d-dddddeeee4fffaaaa-bbbbbbbbbbbb"));
143 ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse("samsamsa-sams-4sam-8sam-samsamsamsam"));
144 }
145
TEST(UUIDTest,toAndFromCDR)146 TEST(UUIDTest, toAndFromCDR) {
147 UUID uuid = UUID::gen();
148 ConstDataRange cdr = uuid.toCDR();
149 UUID roundtripped = UUID::fromCDR(cdr);
150 ASSERT_EQUALS(roundtripped, uuid);
151 }
152
TEST(UUIDTest,toAndFromBSON)153 TEST(UUIDTest, toAndFromBSON) {
154 // UUID -> BSON -> UUID
155 UUID uuid = UUID::gen();
156 auto uuidBSON = uuid.toBSON();
157 auto uuid2Res = UUID::parse(uuidBSON.getField("uuid"));
158 ASSERT_OK(uuid2Res);
159 auto uuid2 = uuid2Res.getValue();
160 ASSERT_EQUALS(uuid, uuid2Res);
161
162 // BSON -> UUID -> BSON
163 uint8_t uuidBytes[] = {0, 0, 0, 0, 0, 0, 0x40, 0, 0x80, 0, 0, 0, 0, 0, 0, 0};
164 auto bson = BSON("uuid" << BSONBinData(uuidBytes, 16, newUUID));
165 auto uuidFromBSON = UUID::parse(bson.getField("uuid"));
166 ASSERT_OK(uuidFromBSON);
167
168 auto uuidBSON2 = uuid2.toBSON();
169 ASSERT_EQUALS(uuidBSON.woCompare(uuidBSON2), 0);
170
171 // UUIDs cannot be constructed from invalid BSON elements
172 auto bson2 = BSON("uuid"
173 << "sam");
174 ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse(bson2.getField("uuid")));
175 auto bson3 = BSON("uuid"
176 << "dddddddd-eeee-4fff-aaaa-bbbbbbbbbbbb");
177 ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse(bson3.getField("uuid")));
178 auto bson4 = BSON("uuid" << 14);
179 ASSERT_EQUALS(ErrorCodes::InvalidUUID, UUID::parse(bson4.getField("uuid")));
180 }
181
TEST(UUIDTest,toBSONUsingBSONMacro)182 TEST(UUIDTest, toBSONUsingBSONMacro) {
183 auto uuid = UUID::gen();
184 auto bson = BSON("myuuid" << uuid);
185
186 BSONObjBuilder bob;
187 uuid.appendToBuilder(&bob, "myuuid");
188 auto expectedBson = bob.obj();
189
190 ASSERT_BSONOBJ_EQ(expectedBson, bson);
191 }
192
193 } // namespace
194 } // namespace mongo
195