summaryrefslogtreecommitdiffstats
path: root/src/lib/test-guid.c
blob: 0a13cf1142524227f3eff043763645b839912826 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */

#include "test-lib.h"
#include "guid.h"
#include "ioloop.h"

/*
 * We want earlier timestamps to compare as < with later timestamps, but
 * guid_128_cmp() doesn't do that because the timestamps in the guid are
 * stored in little-endian byte order.
 */
static int reverse_guid_128_cmp(const guid_128_t a, const guid_128_t b)
{
	int i;

	for (i = GUID_128_SIZE - 1; i >= 0; i--)
		if (a[i] != b[i])
			return (int)a[i] - (int)b[i];

	return 0;
}

static bool guid_128_has_sane_nsecs(const guid_128_t g)
{
	unsigned long nsecs;

	nsecs = le32_to_cpu_unaligned(g);

	return nsecs < 1000000000UL;
}

static inline void set_fake_time(time_t sec, long usec)
{
	ioloop_timeval.tv_sec = sec;
	ioloop_timeval.tv_usec = usec;
}

/*
 * We muck with the ioloop_timeval in various ways and make sure that the
 * guids that get generated make sense.  To make sure that the guid
 * generation code takes up our faked timestamp, we use a far-away time (Jan
 * 1 2038) as the base time.  We don't want to go beyond 32-bit signed
 * time_t for the base time to avoid issues on systems with 32-bit signed
 * time_t.
 *
 * While guids really only need to be unique, here we actually enforce that
 * they are increasing (as defined by reverse_guid_128_cmp()).  If guids are
 * always increasing, they will always be unique.
 */
static void test_ioloop_guid_128_generate(void)
{
	const time_t basetime = 2145909600; /* Jan 1 2038 */
	struct timeval saved_ioloop_timeval;
	guid_128_t guids[2];
	int i;

	/* save the ioloop_timeval before we start messing with it */
	saved_ioloop_timeval = ioloop_timeval;

	/*
	 * Generating multiple guids within a microsecond should keep
	 * incrementing them.
	 */
	test_begin("guid_128_generate() increasing guid within a usec");
	set_fake_time(basetime, 0);
	guid_128_generate(guids[1]);
	for (i = 0; i < 10; i++) {
		const int this = i % 2;
		const int prev = 1 - this;

		guid_128_generate(guids[this]);

		test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0);
		test_assert(guid_128_has_sane_nsecs(guids[this]));
	}
	test_end();

	/*
	 * If the current time changes by +1 usec, so should the guids.
	 */
	test_begin("guid_128_generate() increasing guid with usec fast-forward");
	for (i = 0; i < 10; i++) {
		const int this = i % 2;
		const int prev = 1 - this;

		set_fake_time(basetime, 1 + i);
		guid_128_generate(guids[this]);

		test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0);
		test_assert(guid_128_has_sane_nsecs(guids[this]));
	}
	test_end();

	/*
	 * If the current time changes by +1 sec, so should the guids.
	 */
	test_begin("guid_128_generate() increasing guid with sec fast-forward");
	for (i = 0; i < 10; i++) {
		const int this = i % 2;
		const int prev = 1 - this;

		set_fake_time(basetime + 1 + i, 0);
		guid_128_generate(guids[this]);

		test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0);
		test_assert(guid_128_has_sane_nsecs(guids[this]));
	}
	test_end();

	/*
	 * Requesting enough guids should increment the seconds but always
	 * produce valid nsecs.
	 *
	 * (Set a time that leaves us 1000 guids before seconds overflow and
	 * then ask for 2500 guids.)
	 */
	test_begin("guid_128_generate() proper guid nsec overflow");
	set_fake_time(basetime + 11, 999999L);
	for (i = 0; i < 2500; i++) {
		const int this = i % 2;
		const int prev = 1 - this;

		guid_128_generate(guids[this]);
		test_assert(reverse_guid_128_cmp(guids[prev], guids[this]) < 0);
		test_assert(guid_128_has_sane_nsecs(guids[this]));
	}
	test_end();

	/*
	 * When ahead by 1500 guids (see previous test), +1 usec shouldn't
	 * have any effect.
	 */
	test_begin("guid_128_generate() no effect with increasing time when ahead");
	set_fake_time(basetime + 12, 0);
	guid_128_generate(guids[0]);
	test_assert(reverse_guid_128_cmp(guids[1], guids[0]) < 0);
	test_assert(guid_128_has_sane_nsecs(guids[0]));
	test_end();

	/* not a test - just set a more convenient time */
	set_fake_time(basetime + 15, 500);
	guid_128_generate(guids[1]);
	test_assert(reverse_guid_128_cmp(guids[0], guids[1]) < 0);
	test_assert(guid_128_has_sane_nsecs(guids[1]));

	/*
	 * Time going backwards by 1 usec should have no effect on guids.
	 */
	test_begin("guid_128_generate() usec time-travel still increasing");
	set_fake_time(basetime + 15, 499);
	guid_128_generate(guids[0]);
	test_assert(reverse_guid_128_cmp(guids[1], guids[0]) < 0);
	test_assert(guid_128_has_sane_nsecs(guids[0]));
	test_end();

	/*
	 * Time going backwards by 1 sec should have no effect on guids.
	 */
	test_begin("guid_128_generate() sec time-travel still increasing");
	set_fake_time(basetime + 14, 499);
	guid_128_generate(guids[1]);
	test_assert(reverse_guid_128_cmp(guids[0], guids[1]) < 0);
	test_assert(guid_128_has_sane_nsecs(guids[1]));
	test_end();

	/* restore the previously saved value just in case */
	ioloop_timeval = saved_ioloop_timeval;
}

void test_guid(void)
{
	static const guid_128_t test_guid =
	{ 0x01, 0x23, 0x45, 0x67, 0x89,
	  0xab, 0xcd, 0xef,
	  0xAB, 0xCD, 0xEF,
	  0x00, 0x00, 0x00, 0x00, 0x00 };
	guid_128_t guid1, guid2, guid3;
	const char *str;
	char guidbuf[GUID_128_SIZE*2 + 2];
	unsigned int i;

	test_begin("guid_128_generate()");
	guid_128_generate(guid1);
	guid_128_generate(guid2);
	test_assert(!guid_128_equals(guid1, guid2));
	test_assert(guid_128_cmp(guid1, guid2) != 0);
	test_end();

	test_begin("guid_128_is_empty()");
	test_assert(!guid_128_is_empty(guid1));
	test_assert(!guid_128_is_empty(guid2));
	guid_128_generate(guid3);
	guid_128_empty(guid3);
	test_assert(guid_128_is_empty(guid3));
	test_end();

	test_begin("guid_128_copy()");
	guid_128_copy(guid3, guid1);
	test_assert(guid_128_equals(guid3, guid1));
	test_assert(!guid_128_equals(guid3, guid2));
	guid_128_copy(guid3, guid2);
	test_assert(!guid_128_equals(guid3, guid1));
	test_assert(guid_128_equals(guid3, guid2));
	test_end();

	test_begin("guid_128_to_string()");
	str = guid_128_to_string(guid1);
	test_assert(guid_128_from_string(str, guid3) == 0);
	test_assert(guid_128_equals(guid3, guid1));
	test_end();

	test_begin("guid_128_from_string()");
	/* empty */
	memset(guidbuf, '0', GUID_128_SIZE*2);
	guidbuf[GUID_128_SIZE*2] = '\0';
	guidbuf[GUID_128_SIZE*2+1] = '\0';
	test_assert(guid_128_from_string(guidbuf, guid3) == 0);
	test_assert(guid_128_is_empty(guid3));
	/* too large */
	guidbuf[GUID_128_SIZE*2] = '0';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	/* too small */
	guidbuf[GUID_128_SIZE*2-1] = '\0';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	/* reset to normal */
	guidbuf[GUID_128_SIZE*2-1] = '0';
	guidbuf[GUID_128_SIZE*2] = '\0';
	test_assert(guid_128_from_string(guidbuf, guid3) == 0);
	/* upper + lowercase hex chars */
	i_assert(GUID_128_SIZE*2 > 16 + 6);
	for (i = 0; i < 10; i++)
		guidbuf[i] = '0' + i;
	for (i = 0; i < 6; i++)
		guidbuf[10 + i] = 'a' + i;
	for (i = 0; i < 6; i++)
		guidbuf[16 + i] = 'A' + i;
	test_assert(guid_128_from_string(guidbuf, guid3) == 0);
	test_assert(guid_128_equals(guid3, test_guid));
	/* non-hex chars */
	guidbuf[0] = 'g';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	guidbuf[0] = ' ';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);

	test_assert(guid_128_from_uuid_string("fee0ceac-0327-11e7-ad39-52540078f374", guid3) == 0);
	test_assert(guid_128_from_uuid_string("fee0ceac032711e7ad3952540078f374", guid2) == 0);
	test_assert(guid_128_cmp(guid3, guid2) == 0);
	test_assert(guid_128_from_uuid_string("{fee0ceac-0327-11e7-ad39-52540078f374}", guid2) == 0);
	test_assert(guid_128_cmp(guid3, guid2) == 0);
	test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_RECORD), "fee0ceac-0327-11e7-ad39-52540078f374")==0);
	test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_COMPACT), "fee0ceac032711e7ad3952540078f374")==0);
	test_assert(strcmp(guid_128_to_uuid_string(guid3, FORMAT_MICROSOFT), "{fee0ceac-0327-11e7-ad39-52540078f374}")==0);
	/* failure test */
	test_assert(guid_128_from_uuid_string("fe-e0ceac-0327-11e7-ad39-52540078f374", guid3) < 0);

	test_end();

	test_ioloop_guid_128_generate();
}