Factor out UUID handling into new uuid class
[xapian.git] / xapian-core / tests / unittest.cc
blobf4534dac21f72b9bd961d3af2bb6037f4cf68389
1 /** @file unittest.cc
2 * @brief Unit tests of non-Xapian-specific internal code.
3 */
4 /* Copyright (C) 2006,2007,2009,2010,2012,2015,2016,2018 Olly Betts
5 * Copyright (C) 2007 Richard Boulton
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <config.h>
25 #include <cctype>
26 #include <cfloat>
27 #include <climits>
28 #include <cmath>
29 #include <cstring>
30 #include <iostream>
32 #include "safeunistd.h"
34 #define XAPIAN_UNITTEST
35 static const char * unittest_assertion_failed = NULL;
36 #define UNITTEST_CHECK_EXCEPTION \
37 if (unittest_assertion_failed) { \
38 const char * unittest_assertion_failed_ = unittest_assertion_failed;\
39 unittest_assertion_failed = NULL;\
40 throw unittest_assertion_failed_;\
43 #include "testsuite.h"
45 using namespace std;
47 #define UNITTEST_ASSERT_LOCATION__(LINE,MSG) __FILE__":"#LINE": "#MSG
48 #define UNITTEST_ASSERT_LOCATION_(LINE,MSG) UNITTEST_ASSERT_LOCATION__(LINE,MSG)
49 #define UNITTEST_ASSERT_LOCATION(MSG) UNITTEST_ASSERT_LOCATION_(__LINE__,MSG)
50 #define UNITTEST_ASSERT_NOTHROW(COND, RET) \
51 do {\
52 if (rare(!(COND))) {\
53 unittest_assertion_failed = UNITTEST_ASSERT_LOCATION(COND);\
54 return RET;\
56 } while (false)
58 // Utility code we use:
59 #include "../common/stringutils.h"
60 #include "../common/log2.h"
62 // Simpler version of TEST_EXCEPTION macro.
63 #define TEST_EXCEPTION(TYPE, CODE) \
64 do { \
65 try { \
66 CODE; \
67 UNITTEST_CHECK_EXCEPTION \
68 FAIL_TEST("Expected exception "#TYPE" not thrown"); \
69 } catch (const TYPE &) { \
70 } \
71 } while (0)
73 // Code we're unit testing:
74 #include "../common/closefrom.cc"
75 #include "../common/errno_to_string.cc"
76 #include "../common/fileutils.cc"
77 #include "../common/overflow.h"
78 #include "../common/serialise-double.cc"
79 #include "../common/str.cc"
80 #include "../backends/uuids.cc"
81 #include "../net/length.cc"
82 #include "../net/serialise-error.cc"
83 #include "../api/error.cc"
84 #include "../api/sortable-serialise.cc"
86 // fileutils.cc uses opendir(), etc though not in a function we currently test.
87 #include "../common/msvc_dirent.cc"
89 // The UUID code uses hexdigit().
90 #include "../api/constinfo.cc"
92 // Stub replacement, which doesn't deal with escaping or producing valid UTF-8.
93 // The full implementation needs Xapian::Utf8Iterator and
94 // Xapian::Unicode::append_utf8().
95 void
96 description_append(std::string & desc, const std::string &s)
98 desc += s;
101 DEFINE_TESTCASE_(simple_exceptions_work1) {
102 try {
103 throw 42;
104 } catch (int val) {
105 TEST_EQUAL(val, 42);
106 return true;
108 return false;
111 class TestException { };
113 DEFINE_TESTCASE_(class_exceptions_work1) {
114 try {
115 throw TestException();
116 } catch (const TestException &) {
117 return true;
119 return false;
122 inline string
123 r_r_p(string a, const string & b)
125 resolve_relative_path(a, b);
126 return a;
129 DEFINE_TESTCASE_(resolverelativepath1) {
130 TEST_EQUAL(r_r_p("/abs/o/lute", ""), "/abs/o/lute");
131 TEST_EQUAL(r_r_p("/abs/o/lute", "/"), "/abs/o/lute");
132 TEST_EQUAL(r_r_p("/abs/o/lute", "//"), "/abs/o/lute");
133 TEST_EQUAL(r_r_p("/abs/o/lute", "foo"), "/abs/o/lute");
134 TEST_EQUAL(r_r_p("/abs/o/lute", "foo/"), "/abs/o/lute");
135 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo"), "/abs/o/lute");
136 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo/"), "/abs/o/lute");
137 TEST_EQUAL(r_r_p("/abs/o/lute", "foo/bar"), "/abs/o/lute");
138 TEST_EQUAL(r_r_p("/abs/o/lute", "foo/bar/"), "/abs/o/lute");
139 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo/bar"), "/abs/o/lute");
140 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo/bar/"), "/abs/o/lute");
141 TEST_EQUAL(r_r_p("rel/a/tive", ""), "rel/a/tive");
142 TEST_EQUAL(r_r_p("rel/a/tive", "/"), "/rel/a/tive");
143 TEST_EQUAL(r_r_p("rel/a/tive", "//"), "//rel/a/tive");
144 TEST_EQUAL(r_r_p("rel/a/tive", "foo"), "rel/a/tive");
145 TEST_EQUAL(r_r_p("rel/a/tive", "foo/"), "foo/rel/a/tive");
146 TEST_EQUAL(r_r_p("rel/a/tive", "/foo"), "/rel/a/tive");
147 TEST_EQUAL(r_r_p("rel/a/tive", "/foo/"), "/foo/rel/a/tive");
148 TEST_EQUAL(r_r_p("rel/a/tive", "foo/bar"), "foo/rel/a/tive");
149 TEST_EQUAL(r_r_p("rel/a/tive", "foo/bar/"), "foo/bar/rel/a/tive");
150 TEST_EQUAL(r_r_p("rel/a/tive", "/foo/bar"), "/foo/rel/a/tive");
151 TEST_EQUAL(r_r_p("rel/a/tive", "/foo/bar/"), "/foo/bar/rel/a/tive");
152 #ifndef __WIN32__
153 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo\\bar"), "/abs/o/lute");
154 TEST_EQUAL(r_r_p("rel/a/tive", "/foo\\bar"), "/rel/a/tive");
155 #else
156 TEST_EQUAL(r_r_p("\\dos\\path", ""), "\\dos\\path");
157 TEST_EQUAL(r_r_p("\\dos\\path", "/"), "\\dos\\path");
158 TEST_EQUAL(r_r_p("\\dos\\path", "\\"), "\\dos\\path");
159 TEST_EQUAL(r_r_p("\\dos\\path", "c:"), "c:\\dos\\path");
160 TEST_EQUAL(r_r_p("\\dos\\path", "c:\\"), "c:\\dos\\path");
161 TEST_EQUAL(r_r_p("\\dos\\path", "c:\\temp"), "c:\\dos\\path");
162 TEST_EQUAL(r_r_p("\\dos\\path", "c:\\temp\\"), "c:\\dos\\path");
163 TEST_EQUAL(r_r_p("rel/a/tive", "\\"), "\\rel/a/tive");
164 TEST_EQUAL(r_r_p("rel/a/tive", "foo\\"), "foo\\rel/a/tive");
165 TEST_EQUAL(r_r_p("rel\\a\\tive", "/foo/"), "/foo/rel\\a\\tive");
166 TEST_EQUAL(r_r_p("rel/a/tive", "c:/foo/bar"), "c:/foo/rel/a/tive");
167 TEST_EQUAL(r_r_p("rel/a/tive", "c:foo/bar/"), "c:foo/bar/rel/a/tive");
168 TEST_EQUAL(r_r_p("rel/a/tive", "c:"), "c:rel/a/tive");
169 TEST_EQUAL(r_r_p("rel/a/tive", "c:\\"), "c:\\rel/a/tive");
170 TEST_EQUAL(r_r_p("C:rel/a/tive", "c:\\foo\\bar"), "C:\\foo\\rel/a/tive");
171 TEST_EQUAL(r_r_p("C:rel/a/tive", "c:"), "C:rel/a/tive");
172 // This one is impossible to reliably resolve without knowing the current
173 // drive - if it is C:, then the answer is: "C:/abs/o/rel/a/tive"
174 TEST_EQUAL(r_r_p("C:rel/a/tive", "/abs/o/lute"), "C:rel/a/tive");
175 // UNC paths tests:
176 TEST_EQUAL(r_r_p("\\\\SRV\\VOL\\FILE", "/a/b"), "\\\\SRV\\VOL\\FILE");
177 TEST_EQUAL(r_r_p("rel/a/tive", "\\\\SRV\\VOL\\DIR\\FILE"), "\\\\SRV\\VOL\\DIR\\rel/a/tive");
178 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\SRV\\VOL\\FILE"), "\\\\SRV\\VOL/abs/o/lute");
179 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\S\\V\\FILE"), "\\\\S\\V/abs/o/lute");
180 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\S\\V\\"), "\\\\S\\V/abs/o/lute");
181 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\S\\V"), "\\\\S\\V/abs/o/lute");
182 TEST_EQUAL(r_r_p("//SRV/VOL/FILE", "/a/b"), "//SRV/VOL/FILE");
183 TEST_EQUAL(r_r_p("rel/a/tive", "//SRV/VOL/DIR/FILE"), "//SRV/VOL/DIR/rel/a/tive");
184 TEST_EQUAL(r_r_p("/abs/o/lute", "//SRV/VOL/FILE"), "//SRV/VOL/abs/o/lute");
185 TEST_EQUAL(r_r_p("/abs/o/lute", "//S/V/FILE"), "//S/V/abs/o/lute");
186 TEST_EQUAL(r_r_p("/abs/o/lute", "//S/V/"), "//S/V/abs/o/lute");
187 TEST_EQUAL(r_r_p("/abs/o/lute", "//S/V"), "//S/V/abs/o/lute");
188 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\C:\\wibble"), "\\\\?\\C:\\abs\\o\\lute");
189 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\UNC\\S\\V"), "\\\\?\\UNC\\S\\V\\abs\\o\\lute");
190 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\UNC\\S\\V\\"), "\\\\?\\UNC\\S\\V\\abs\\o\\lute");
191 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\UNC\\S\\V\\TMP\\README.TXT"), "\\\\?\\UNC\\S\\V\\abs\\o\\lute");
192 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\C:\\wibble"), "\\\\?\\C:\\r\\elativ\\e");
193 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\C:\\wibble\\wobble"), "\\\\?\\C:\\wibble\\r\\elativ\\e");
194 #if 0 // Is this a valid testcase? It fails, but isn't relevant to Xapian.
195 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\UNC\\S\\V"), "\\\\?\\UNC\\S\\V\\r\\elativ\\e");
196 #endif
197 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\UNC\\S\\V\\"), "\\\\?\\UNC\\S\\V\\r\\elativ\\e");
198 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\UNC\\S\\V\\TMP\\README.TXT"), "\\\\?\\UNC\\S\\V\\TMP\\r\\elativ\\e");
199 #endif
200 return true;
203 static void
204 check_double_serialisation(double u)
206 // Commonly C++ string implementations keep the string nul-terminated, and
207 // encoded.data() returns a pointer to a buffer including the nul (the same
208 // as encoded.c_str()). This means that valgrind won't catch a read one
209 // past the end of the serialised value, so we copy just the serialised
210 // value into a temporary buffer.
211 char buf[16];
212 string encoded = serialise_double(u);
213 TEST(encoded.size() < sizeof(buf));
214 memcpy(buf, encoded.data(), encoded.size());
215 // Put a NULL pointer either side, to catch incrementing/decrementing at
216 // the wrong level of indirection (regression test for a bug in an
217 // unreleased version).
218 const char * ptr[3] = { NULL, buf, NULL };
219 const char * end = ptr[1] + encoded.size();
220 double v = unserialise_double(&(ptr[1]), end);
221 if (ptr[1] != end || u != v) {
222 cout << u << " -> " << v << ", difference = " << v - u << endl;
223 cout << "FLT_RADIX = " << FLT_RADIX << endl;
224 cout << "DBL_MAX_EXP = " << DBL_MAX_EXP << endl;
226 TEST_EQUAL(static_cast<const void*>(ptr[1]), static_cast<const void*>(end));
229 // Check serialisation of doubles.
230 DEFINE_TESTCASE_(serialisedouble1) {
231 static const double test_values[] = {
232 3.14159265,
233 1e57,
234 123.1,
235 257.12,
236 1234.567e123,
237 255.5,
238 256.125,
239 257.03125,
242 check_double_serialisation(0.0);
243 check_double_serialisation(1.0);
244 check_double_serialisation(-1.0);
245 check_double_serialisation(DBL_MAX);
246 check_double_serialisation(-DBL_MAX);
247 check_double_serialisation(DBL_MIN);
248 check_double_serialisation(-DBL_MIN);
250 const double *p;
251 for (p = test_values; p < test_values + sizeof(test_values) / sizeof(double); ++p) {
252 double val = *p;
253 check_double_serialisation(val);
254 check_double_serialisation(-val);
255 check_double_serialisation(1.0 / val);
256 check_double_serialisation(-1.0 / val);
259 return true;
262 #ifdef XAPIAN_HAS_REMOTE_BACKEND
263 // Check serialisation of lengths.
264 static bool test_serialiselength1()
266 size_t n = 0;
267 while (n < 0xff000000) {
268 string s = encode_length(n);
269 const char *p = s.data();
270 const char *p_end = p + s.size();
271 size_t decoded_n;
272 decode_length(&p, p_end, decoded_n);
273 if (n != decoded_n || p != p_end) tout << "[" << s << "]" << endl;
274 TEST_EQUAL(n, decoded_n);
275 TEST_EQUAL(p_end - p, 0);
276 if (n < 5000) {
277 ++n;
278 } else {
279 n += 53643;
283 return true;
286 // Regression test: vetting the remaining buffer length
287 static bool test_serialiselength2()
289 // Special case tests for 0
291 string s = encode_length(0);
293 const char *p = s.data();
294 const char *p_end = p + s.size();
295 size_t r;
296 decode_length_and_check(&p, p_end, r);
297 TEST(r == 0);
298 TEST(p == p_end);
300 s += 'x';
302 const char *p = s.data();
303 const char *p_end = p + s.size();
304 size_t r;
305 decode_length_and_check(&p, p_end, r);
306 TEST(r == 0);
307 TEST_EQUAL(p_end - p, 1);
310 // Special case tests for 1
312 string s = encode_length(1);
313 TEST_EXCEPTION(Xapian_NetworkError,
314 const char *p = s.data();
315 const char *p_end = p + s.size();
316 size_t r;
317 decode_length_and_check(&p, p_end, r);
318 (void)r;
320 s += 'x';
322 const char *p = s.data();
323 const char *p_end = p + s.size();
324 size_t r;
325 decode_length_and_check(&p, p_end, r);
326 TEST(r == 1);
327 TEST_EQUAL(p_end - p, 1);
329 s += 'x';
331 const char *p = s.data();
332 const char *p_end = p + s.size();
333 size_t r;
334 decode_length_and_check(&p, p_end, r);
335 TEST(r == 1);
336 TEST_EQUAL(p_end - p, 2);
339 // Nothing magic here, just test a range of odd and even values.
340 for (size_t n = 2; n < 1000; n = (n + 1) * 2 + (n >> 1)) {
341 string s = encode_length(n);
342 TEST_EXCEPTION(Xapian_NetworkError,
343 const char *p = s.data();
344 const char *p_end = p + s.size();
345 size_t r;
346 decode_length_and_check(&p, p_end, r);
347 (void)r;
349 s.append(n - 1, 'x');
350 TEST_EXCEPTION(Xapian_NetworkError,
351 const char *p = s.data();
352 const char *p_end = p + s.size();
353 size_t r;
354 decode_length_and_check(&p, p_end, r);
355 (void)r;
357 s += 'x';
359 const char *p = s.data();
360 const char *p_end = p + s.size();
361 size_t r;
362 decode_length_and_check(&p, p_end, r);
363 TEST(r == n);
364 TEST_EQUAL(size_t(p_end - p), n);
366 s += 'x';
368 const char *p = s.data();
369 const char *p_end = p + s.size();
370 size_t r;
371 decode_length_and_check(&p, p_end, r);
372 TEST(r == n);
373 TEST_EQUAL(size_t(p_end - p), n + 1);
377 return true;
380 // Check serialisation of Xapian::Error.
381 static bool test_serialiseerror1()
383 string enoent_msg(strerror(ENOENT));
384 Xapian::DatabaseOpeningError e("Failed to open database", ENOENT);
385 // Regression test for bug in 1.0.0 - it didn't convert errno values for
386 // get_description() if they hadn't already been converted.
387 TEST_STRINGS_EQUAL(e.get_description(), "DatabaseOpeningError: Failed to open database (" + enoent_msg + ")");
389 TEST_STRINGS_EQUAL(e.get_error_string(), enoent_msg);
391 string serialisation = serialise_error(e);
393 // Test if unserialise_error() throws with a flag to avoid the possibility
394 // of an "unreachable code" warning when we get around to marking
395 // unserialise_error() as "noreturn".
396 bool threw = false;
397 try {
398 // unserialise_error throws an exception.
399 unserialise_error(serialisation, "", "");
400 } catch (const Xapian::Error & ecaught) {
401 TEST_STRINGS_EQUAL(ecaught.get_error_string(), enoent_msg);
402 threw = true;
404 TEST(threw);
406 // Check that the original is still OK.
407 TEST_STRINGS_EQUAL(e.get_error_string(), enoent_msg);
409 // Regression test - in 1.0.0, copying used to duplicate the error_string
410 // pointer, resulting in double calls to free().
411 Xapian::DatabaseOpeningError ecopy(e);
412 TEST_STRINGS_EQUAL(ecopy.get_error_string(), enoent_msg);
414 return true;
416 #endif
418 // Test log2() (which might be our replacement version).
419 static bool test_log2()
421 TEST_EQUAL(log2(1.0), 0.0);
422 TEST_EQUAL(log2(2.0), 1.0);
423 TEST_EQUAL(log2(1024.0), 10.0);
424 TEST_EQUAL(log2(0.5), -1.0);
425 return true;
428 static const double test_sortableserialise_numbers[] = {
429 #ifdef INFINITY
430 -INFINITY,
431 #endif
432 -HUGE_VAL,
433 -DBL_MAX,
434 -exp2(1022),
435 -1024.5,
436 -3.14159265358979323846,
439 -1.8,
440 -1.1,
442 -0.5,
443 -0.2,
444 -0.1,
445 -0.000005,
446 -0.000002,
447 -0.000001,
448 -exp2(-1023),
449 -exp2(-1024),
450 -exp2(-1074),
451 -DBL_MIN,
453 DBL_MIN,
454 exp2(-1074),
455 exp2(-1024),
456 exp2(-1023),
457 0.000001,
458 0.000002,
459 0.000005,
460 0.1,
461 0.2,
462 0.5,
464 1.1,
465 1.8,
468 3.14159265358979323846,
469 1024.5,
470 exp2(1022),
471 DBL_MAX,
472 HUGE_VAL,
473 #ifdef INFINITY
474 INFINITY,
475 #endif
477 64 // Magic number which we stop at.
480 // Test serialisation and unserialisation of various numbers.
481 // This is actually a public API, but we want extra assertions in the code
482 // while we test it.
483 static bool test_sortableserialise1()
485 double prevnum = 0;
486 string prevstr;
487 bool started = false;
488 for (const double *p = test_sortableserialise_numbers; *p != 64; ++p) {
489 double num = *p;
490 tout << "Number: " << num << '\n';
491 string str = Xapian::sortable_serialise(num);
492 tout << "String: " << str << '\n';
493 TEST_EQUAL(Xapian::sortable_unserialise(str), num);
495 if (started) {
496 int num_cmp = 0;
497 if (prevnum < num) {
498 num_cmp = -1;
499 } else if (prevnum > num) {
500 num_cmp = 1;
502 int str_cmp = 0;
503 if (prevstr < str) {
504 str_cmp = -1;
505 } else if (prevstr > str) {
506 str_cmp = 1;
509 TEST_AND_EXPLAIN(num_cmp == str_cmp,
510 "Numbers " << prevnum << " and " << num <<
511 " don't sort the same way as their string "
512 "counterparts");
515 prevnum = num;
516 prevstr = str;
517 started = true;
519 return true;
522 static bool test_tostring1()
524 TEST_EQUAL(str(0), "0");
525 TEST_EQUAL(str(0u), "0");
526 TEST_EQUAL(str(1), "1");
527 TEST_EQUAL(str(1u), "1");
528 TEST_EQUAL(str(9), "9");
529 TEST_EQUAL(str(9u), "9");
530 TEST_EQUAL(str(10), "10");
531 TEST_EQUAL(str(10u), "10");
532 TEST_EQUAL(str(-1), "-1");
533 TEST_EQUAL(str(-9), "-9");
534 TEST_EQUAL(str(-10), "-10");
535 TEST_EQUAL(str(0xffffffff), "4294967295");
536 TEST_EQUAL(str(0x7fffffff), "2147483647");
537 TEST_EQUAL(str(0x7fffffffu), "2147483647");
538 TEST_EQUAL(str(-0x7fffffff), "-2147483647");
540 #ifdef __WIN32__
541 /* Test the 64 bit integer conversion to string.
542 * (Currently only exists for windows.)
544 TEST_EQUAL(str(10ll), "10");
545 TEST_EQUAL(str(-10ll), "-10");
546 TEST_EQUAL(str(0x200000000ll), "8589934592");
547 // We don't currently have an "unsigned long long" version since it's not required
548 // anywhere in the library.
549 // TEST_EQUAL(str(0x200000000ull), "8589934592");
550 #endif
552 return true;
555 /// Regression test for bug fixed in 1.1.1.
556 static bool test_strbool1()
558 TEST_EQUAL(str(true), "1");
559 TEST_EQUAL(str(false), "0");
560 return true;
563 static bool test_closefrom1()
565 #ifndef __WIN32__
566 // Simple test.
567 closefrom(8);
569 // Simple test when there are definitely no fds to close.
570 closefrom(42);
572 // Test passing a really high threshold.
573 closefrom(INT_MAX);
575 // Open some fds and check the expected ones are closed.
576 TEST_EQUAL(dup2(1, 14), 14);
577 TEST_EQUAL(dup2(1, 15), 15);
578 TEST_EQUAL(dup2(1, 18), 18);
579 closefrom(15);
580 TEST_EQUAL(close(14), 0);
581 TEST(close(15) == -1 && errno == EBADF);
582 TEST(close(18) == -1 && errno == EBADF);
583 #endif
584 return true;
587 static bool test_uuid1()
589 Uuid uuid, uuid2;
591 // Test a generated uuid.
592 uuid.generate();
593 TEST(!uuid.is_null());
594 string str = uuid.to_string();
595 TEST_EQUAL(str.size(), 36);
596 TEST_NOT_EQUAL(str, "00000000-0000-0000-0000-000000000000");
597 // Check UUID pattern is correct and that upper case is not used.
598 for (int i = 0; i != 8; ++i) {
599 unsigned char ch = str[i];
600 TEST(isxdigit(ch));
601 TEST(!isupper(ch));
603 TEST_EQUAL(str[8], '-');
604 for (int i = 9; i != 13; ++i) {
605 unsigned char ch = str[i];
606 TEST(isxdigit(ch));
607 TEST(!isupper(ch));
609 TEST_EQUAL(str[13], '-');
610 for (int i = 14; i != 18; ++i) {
611 unsigned char ch = str[i];
612 TEST(isxdigit(ch));
613 TEST(!isupper(ch));
615 TEST_EQUAL(str[18], '-');
616 for (int i = 19; i != 23; ++i) {
617 unsigned char ch = str[i];
618 TEST(isxdigit(ch));
619 TEST(!isupper(ch));
621 TEST_EQUAL(str[23], '-');
622 for (int i = 24; i != 36; ++i) {
623 unsigned char ch = str[i];
624 TEST(isxdigit(ch));
625 TEST(!isupper(ch));
628 uuid2.parse(str);
629 TEST(memcmp(uuid.data(), uuid2.data(), uuid.BINARY_SIZE) == 0);
631 // Check the variant is "10x" and the version between 1 and 5. Mostly this
632 // is to catch bugs where the platform's API for generating UUIDs uses a
633 // different endianness for fields (which we've run into under both WIN32
634 // and FreeBSD).
635 TEST_EQUAL(uuid.data()[8] & 0xc0, 0x80);
636 TEST_REL(str[19], >=, '8');
637 TEST_REL(str[19], <=, 'b');
638 TEST_REL(uuid.data()[6], >=, 0x10);
639 TEST_REL(uuid.data()[6], <=, 0x5f);
640 TEST_REL(str[14], >=, '1');
641 TEST_REL(str[14], <=, '5');
643 // Test generating another uuid gives us a different non-null uuid.
644 uuid2.generate();
645 TEST(!uuid2.is_null());
646 TEST(memcmp(uuid.data(), uuid2.data(), uuid.BINARY_SIZE) != 0);
648 // Test null uuid.
649 uuid.clear();
650 TEST(uuid.is_null());
651 str = uuid.to_string();
652 TEST_EQUAL(str, "00000000-0000-0000-0000-000000000000");
653 uuid2.generate();
654 TEST(!uuid2.is_null());
655 uuid2.parse(str);
656 TEST(memcmp(uuid.data(), uuid2.data(), uuid.BINARY_SIZE) == 0);
658 return true;
661 // Classes used by movesupport1 test
662 class A : public Xapian::Internal::intrusive_base {
663 int x = 0;
664 public:
665 explicit A(int x_) : x(x_) {}
667 int get_x() const {
668 return x;
672 class B : public Xapian::Internal::opt_intrusive_base {
673 int x = 0;
674 bool & alive;
675 public:
676 B(int x_, bool & alive_) : x(x_), alive(alive_) {
677 alive = true;
680 ~B() {
681 alive = false;
684 int get_x() const {
685 return x;
688 B * release() {
689 opt_intrusive_base::release();
690 return this;
694 static bool test_movesupport1()
697 // Test move semantics support for intrusive_ptr class
698 Xapian::Internal::intrusive_ptr<A> p1(new A{5});
699 Xapian::Internal::intrusive_ptr<A> p3;
701 // Test move constructor
702 Xapian::Internal::intrusive_ptr<A> p2(std::move(p1));
703 TEST_EQUAL(p2->get_x(), 5);
704 TEST_EQUAL(p1.get(), 0);
706 // Test move assignment
707 p3 = std::move(p2);
708 TEST_EQUAL(p3->get_x(), 5);
709 TEST_EQUAL(p2.get(), 0);
712 bool alive = false;
714 // Same test for opt_intrusive_ptr class
715 B * b1 = new B{5, alive};
716 b1->release();
717 Xapian::Internal::opt_intrusive_ptr<B> p1(b1);
718 Xapian::Internal::opt_intrusive_ptr<B> p3;
720 // Test move constructor
721 Xapian::Internal::opt_intrusive_ptr<B> p2(std::move(p1));
722 TEST_EQUAL(p2->get_x(), 5);
723 TEST_EQUAL(p1.get(), 0);
724 TEST_EQUAL(alive, true);
726 // Test move assignment
727 p3 = std::move(p2);
728 TEST_EQUAL(p3->get_x(), 5);
729 TEST_EQUAL(p2.get(), 0);
730 TEST_EQUAL(alive, true);
732 // Test that object b1 has been deleted.
733 TEST_EQUAL(alive, false);
734 return true;
737 static bool test_addoverflows1()
739 unsigned long res;
740 TEST(!add_overflows(0UL, 0UL, res));
741 TEST_EQUAL(res, 0);
743 TEST(add_overflows(ULONG_MAX, 1UL, res));
744 TEST_EQUAL(res, 0);
746 TEST(add_overflows(1UL, ULONG_MAX, res));
747 TEST_EQUAL(res, 0);
749 TEST(add_overflows(ULONG_MAX, ULONG_MAX, res));
750 TEST_EQUAL(res, ULONG_MAX - 1UL);
752 return true;
755 static bool test_muloverflows1()
757 unsigned long res;
758 TEST(!mul_overflows(0UL, 0UL, res));
759 TEST_EQUAL(res, 0);
761 TEST(!mul_overflows(ULONG_MAX, 0UL, res));
762 TEST_EQUAL(res, 0);
764 TEST(!mul_overflows(0UL, ULONG_MAX, res));
765 TEST_EQUAL(res, 0);
767 TEST(!mul_overflows(ULONG_MAX, 1UL, res));
768 TEST_EQUAL(res, ULONG_MAX);
770 TEST(!mul_overflows(1UL, ULONG_MAX, res));
771 TEST_EQUAL(res, ULONG_MAX);
773 TEST(mul_overflows((ULONG_MAX >> 1UL) + 1UL, 2UL, res));
774 TEST_EQUAL(res, 0);
776 TEST(mul_overflows(2UL, (ULONG_MAX >> 1UL) + 1UL, res));
777 TEST_EQUAL(res, 0);
779 TEST(mul_overflows(ULONG_MAX, ULONG_MAX, res));
781 return true;
784 static const test_desc tests[] = {
785 TESTCASE(simple_exceptions_work1),
786 TESTCASE(class_exceptions_work1),
787 TESTCASE(resolverelativepath1),
788 TESTCASE(serialisedouble1),
789 #ifdef XAPIAN_HAS_REMOTE_BACKEND
790 TESTCASE(serialiselength1),
791 TESTCASE(serialiselength2),
792 TESTCASE(serialiseerror1),
793 #endif
794 TESTCASE(log2),
795 TESTCASE(sortableserialise1),
796 TESTCASE(tostring1),
797 TESTCASE(strbool1),
798 TESTCASE(closefrom1),
799 TESTCASE(uuid1),
800 TESTCASE(movesupport1),
801 TESTCASE(addoverflows1),
802 TESTCASE(muloverflows1),
803 END_OF_TESTCASES
806 int main(int argc, char **argv)
807 try {
808 test_driver::parse_command_line(argc, argv);
809 return test_driver::run(tests);
810 } catch (const char * e) {
811 cout << e << endl;
812 return 1;