2 * @brief Unit tests of non-Xapian-specific internal code.
4 /* Copyright (C) 2006,2007,2009,2010,2012,2015,2016 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
29 #define XAPIAN_UNITTEST
30 static const char * unittest_assertion_failed
= NULL
;
31 #define UNITTEST_CHECK_EXCEPTION \
32 if (unittest_assertion_failed) { \
33 const char * unittest_assertion_failed_ = unittest_assertion_failed;\
34 unittest_assertion_failed = NULL;\
35 throw unittest_assertion_failed_;\
38 #include "testsuite.h"
42 #define UNITTEST_ASSERT_LOCATION__(LINE,MSG) __FILE__":"#LINE": "#MSG
43 #define UNITTEST_ASSERT_LOCATION_(LINE,MSG) UNITTEST_ASSERT_LOCATION__(LINE,MSG)
44 #define UNITTEST_ASSERT_LOCATION(MSG) UNITTEST_ASSERT_LOCATION_(__LINE__,MSG)
45 #define UNITTEST_ASSERT_NOTHROW(COND, RET) \
48 unittest_assertion_failed = UNITTEST_ASSERT_LOCATION(COND);\
53 // Utility code we use:
54 #include "../common/stringutils.h"
55 #include "../common/log2.h"
57 // Simpler version of TEST_EXCEPTION macro.
58 #define TEST_EXCEPTION(TYPE, CODE) \
62 UNITTEST_CHECK_EXCEPTION \
63 FAIL_TEST("Expected exception "#TYPE" not thrown"); \
64 } catch (const TYPE &) { \
68 // Code we're unit testing:
69 #include "../common/errno_to_string.cc"
70 #include "../common/fileutils.cc"
71 #include "../common/serialise-double.cc"
72 #include "../common/str.cc"
73 #include "../net/length.cc"
74 #include "../net/serialise-error.cc"
75 #include "../api/error.cc"
76 #include "../api/sortable-serialise.cc"
78 // Stub replacement, which doesn't deal with escaping or producing valid UTF-8.
79 // The full implementation needs Xapian::Utf8Iterator and
80 // Xapian::Unicode::append_utf8().
82 description_append(std::string
& desc
, const std::string
&s
)
87 DEFINE_TESTCASE_(simple_exceptions_work1
) {
97 class TestException
{ };
99 DEFINE_TESTCASE_(class_exceptions_work1
) {
101 throw TestException();
102 } catch (const TestException
&) {
109 r_r_p(string a
, const string
& b
)
111 resolve_relative_path(a
, b
);
115 DEFINE_TESTCASE_(resolverelativepath1
) {
116 TEST_EQUAL(r_r_p("/abs/o/lute", ""), "/abs/o/lute");
117 TEST_EQUAL(r_r_p("/abs/o/lute", "/"), "/abs/o/lute");
118 TEST_EQUAL(r_r_p("/abs/o/lute", "//"), "/abs/o/lute");
119 TEST_EQUAL(r_r_p("/abs/o/lute", "foo"), "/abs/o/lute");
120 TEST_EQUAL(r_r_p("/abs/o/lute", "foo/"), "/abs/o/lute");
121 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo"), "/abs/o/lute");
122 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo/"), "/abs/o/lute");
123 TEST_EQUAL(r_r_p("/abs/o/lute", "foo/bar"), "/abs/o/lute");
124 TEST_EQUAL(r_r_p("/abs/o/lute", "foo/bar/"), "/abs/o/lute");
125 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo/bar"), "/abs/o/lute");
126 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo/bar/"), "/abs/o/lute");
127 TEST_EQUAL(r_r_p("rel/a/tive", ""), "rel/a/tive");
128 TEST_EQUAL(r_r_p("rel/a/tive", "/"), "/rel/a/tive");
129 TEST_EQUAL(r_r_p("rel/a/tive", "//"), "//rel/a/tive");
130 TEST_EQUAL(r_r_p("rel/a/tive", "foo"), "rel/a/tive");
131 TEST_EQUAL(r_r_p("rel/a/tive", "foo/"), "foo/rel/a/tive");
132 TEST_EQUAL(r_r_p("rel/a/tive", "/foo"), "/rel/a/tive");
133 TEST_EQUAL(r_r_p("rel/a/tive", "/foo/"), "/foo/rel/a/tive");
134 TEST_EQUAL(r_r_p("rel/a/tive", "foo/bar"), "foo/rel/a/tive");
135 TEST_EQUAL(r_r_p("rel/a/tive", "foo/bar/"), "foo/bar/rel/a/tive");
136 TEST_EQUAL(r_r_p("rel/a/tive", "/foo/bar"), "/foo/rel/a/tive");
137 TEST_EQUAL(r_r_p("rel/a/tive", "/foo/bar/"), "/foo/bar/rel/a/tive");
139 TEST_EQUAL(r_r_p("/abs/o/lute", "/foo\\bar"), "/abs/o/lute");
140 TEST_EQUAL(r_r_p("rel/a/tive", "/foo\\bar"), "/rel/a/tive");
142 TEST_EQUAL(r_r_p("\\dos\\path", ""), "\\dos\\path");
143 TEST_EQUAL(r_r_p("\\dos\\path", "/"), "\\dos\\path");
144 TEST_EQUAL(r_r_p("\\dos\\path", "\\"), "\\dos\\path");
145 TEST_EQUAL(r_r_p("\\dos\\path", "c:"), "c:\\dos\\path");
146 TEST_EQUAL(r_r_p("\\dos\\path", "c:\\"), "c:\\dos\\path");
147 TEST_EQUAL(r_r_p("\\dos\\path", "c:\\temp"), "c:\\dos\\path");
148 TEST_EQUAL(r_r_p("\\dos\\path", "c:\\temp\\"), "c:\\dos\\path");
149 TEST_EQUAL(r_r_p("rel/a/tive", "\\"), "\\rel/a/tive");
150 TEST_EQUAL(r_r_p("rel/a/tive", "foo\\"), "foo\\rel/a/tive");
151 TEST_EQUAL(r_r_p("rel\\a\\tive", "/foo/"), "/foo/rel\\a\\tive");
152 TEST_EQUAL(r_r_p("rel/a/tive", "c:/foo/bar"), "c:/foo/rel/a/tive");
153 TEST_EQUAL(r_r_p("rel/a/tive", "c:foo/bar/"), "c:foo/bar/rel/a/tive");
154 TEST_EQUAL(r_r_p("rel/a/tive", "c:"), "c:rel/a/tive");
155 TEST_EQUAL(r_r_p("rel/a/tive", "c:\\"), "c:\\rel/a/tive");
156 TEST_EQUAL(r_r_p("C:rel/a/tive", "c:\\foo\\bar"), "C:\\foo\\rel/a/tive");
157 TEST_EQUAL(r_r_p("C:rel/a/tive", "c:"), "C:rel/a/tive");
158 // This one is impossible to reliably resolve without knowing the current
159 // drive - if it is C:, then the answer is: "C:/abs/o/rel/a/tive"
160 TEST_EQUAL(r_r_p("C:rel/a/tive", "/abs/o/lute"), "C:rel/a/tive");
162 TEST_EQUAL(r_r_p("\\\\SRV\\VOL\\FILE", "/a/b"), "\\\\SRV\\VOL\\FILE");
163 TEST_EQUAL(r_r_p("rel/a/tive", "\\\\SRV\\VOL\\DIR\\FILE"), "\\\\SRV\\VOL\\DIR\\rel/a/tive");
164 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\SRV\\VOL\\FILE"), "\\\\SRV\\VOL/abs/o/lute");
165 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\S\\V\\FILE"), "\\\\S\\V/abs/o/lute");
166 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\S\\V\\"), "\\\\S\\V/abs/o/lute");
167 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\S\\V"), "\\\\S\\V/abs/o/lute");
168 TEST_EQUAL(r_r_p("//SRV/VOL/FILE", "/a/b"), "//SRV/VOL/FILE");
169 TEST_EQUAL(r_r_p("rel/a/tive", "//SRV/VOL/DIR/FILE"), "//SRV/VOL/DIR/rel/a/tive");
170 TEST_EQUAL(r_r_p("/abs/o/lute", "//SRV/VOL/FILE"), "//SRV/VOL/abs/o/lute");
171 TEST_EQUAL(r_r_p("/abs/o/lute", "//S/V/FILE"), "//S/V/abs/o/lute");
172 TEST_EQUAL(r_r_p("/abs/o/lute", "//S/V/"), "//S/V/abs/o/lute");
173 TEST_EQUAL(r_r_p("/abs/o/lute", "//S/V"), "//S/V/abs/o/lute");
174 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\C:\\wibble"), "\\\\?\\C:\\abs\\o\\lute");
175 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\UNC\\S\\V"), "\\\\?\\UNC\\S\\V\\abs\\o\\lute");
176 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\UNC\\S\\V\\"), "\\\\?\\UNC\\S\\V\\abs\\o\\lute");
177 TEST_EQUAL(r_r_p("/abs/o/lute", "\\\\?\\UNC\\S\\V\\TMP\\README.TXT"), "\\\\?\\UNC\\S\\V\\abs\\o\\lute");
178 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\C:\\wibble"), "\\\\?\\C:\\r\\elativ\\e");
179 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\C:\\wibble\\wobble"), "\\\\?\\C:\\wibble\\r\\elativ\\e");
180 #if 0 // Is this a valid testcase? It fails, but isn't relevant to Xapian.
181 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\UNC\\S\\V"), "\\\\?\\UNC\\S\\V\\r\\elativ\\e");
183 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\UNC\\S\\V\\"), "\\\\?\\UNC\\S\\V\\r\\elativ\\e");
184 TEST_EQUAL(r_r_p("r/elativ/e", "\\\\?\\UNC\\S\\V\\TMP\\README.TXT"), "\\\\?\\UNC\\S\\V\\TMP\\r\\elativ\\e");
190 check_double_serialisation(double u
)
192 // Commonly C++ string implementations keep the string nul-terminated, and
193 // encoded.data() returns a pointer to a buffer including the nul (the same
194 // as encoded.c_str()). This means that valgrind won't catch a read one
195 // past the end of the serialised value, so we copy just the serialised
196 // value into a temporary buffer.
198 string encoded
= serialise_double(u
);
199 TEST(encoded
.size() < sizeof(buf
));
200 memcpy(buf
, encoded
.data(), encoded
.size());
201 // Put a NULL pointer either side, to catch incrementing/decrementing at
202 // the wrong level of indirection (regression test for a bug in an
203 // unreleased version).
204 const char * ptr
[3] = { NULL
, buf
, NULL
};
205 const char * end
= ptr
[1] + encoded
.size();
206 double v
= unserialise_double(&(ptr
[1]), end
);
207 if (ptr
[1] != end
|| u
!= v
) {
208 cout
<< u
<< " -> " << v
<< ", difference = " << v
- u
<< endl
;
209 cout
<< "FLT_RADIX = " << FLT_RADIX
<< endl
;
210 cout
<< "DBL_MAX_EXP = " << DBL_MAX_EXP
<< endl
;
212 TEST_EQUAL(static_cast<const void*>(ptr
[1]), static_cast<const void*>(end
));
215 // Check serialisation of doubles.
216 DEFINE_TESTCASE_(serialisedouble1
) {
217 static const double test_values
[] = {
228 check_double_serialisation(0.0);
229 check_double_serialisation(1.0);
230 check_double_serialisation(-1.0);
231 check_double_serialisation(DBL_MAX
);
232 check_double_serialisation(-DBL_MAX
);
233 check_double_serialisation(DBL_MIN
);
234 check_double_serialisation(-DBL_MIN
);
237 for (p
= test_values
; p
< test_values
+ sizeof(test_values
) / sizeof(double); ++p
) {
239 check_double_serialisation(val
);
240 check_double_serialisation(-val
);
241 check_double_serialisation(1.0 / val
);
242 check_double_serialisation(-1.0 / val
);
248 #ifdef XAPIAN_HAS_REMOTE_BACKEND
249 // Check serialisation of lengths.
250 static bool test_serialiselength1()
253 while (n
< 0xff000000) {
254 string s
= encode_length(n
);
255 const char *p
= s
.data();
256 const char *p_end
= p
+ s
.size();
258 decode_length(&p
, p_end
, decoded_n
);
259 if (n
!= decoded_n
|| p
!= p_end
) tout
<< "[" << s
<< "]" << endl
;
260 TEST_EQUAL(n
, decoded_n
);
261 TEST_EQUAL(p_end
- p
, 0);
272 // Regression test: vetting the remaining buffer length
273 static bool test_serialiselength2()
275 // Special case tests for 0
277 string s
= encode_length(0);
279 const char *p
= s
.data();
280 const char *p_end
= p
+ s
.size();
282 decode_length_and_check(&p
, p_end
, r
);
288 const char *p
= s
.data();
289 const char *p_end
= p
+ s
.size();
291 decode_length_and_check(&p
, p_end
, r
);
293 TEST_EQUAL(p_end
- p
, 1);
296 // Special case tests for 1
298 string s
= encode_length(1);
299 TEST_EXCEPTION(Xapian_NetworkError
,
300 const char *p
= s
.data();
301 const char *p_end
= p
+ s
.size();
303 decode_length_and_check(&p
, p_end
, r
);
308 const char *p
= s
.data();
309 const char *p_end
= p
+ s
.size();
311 decode_length_and_check(&p
, p_end
, r
);
313 TEST_EQUAL(p_end
- p
, 1);
317 const char *p
= s
.data();
318 const char *p_end
= p
+ s
.size();
320 decode_length_and_check(&p
, p_end
, r
);
322 TEST_EQUAL(p_end
- p
, 2);
325 // Nothing magic here, just test a range of odd and even values.
326 for (size_t n
= 2; n
< 1000; n
= (n
+ 1) * 2 + (n
>> 1)) {
327 string s
= encode_length(n
);
328 TEST_EXCEPTION(Xapian_NetworkError
,
329 const char *p
= s
.data();
330 const char *p_end
= p
+ s
.size();
332 decode_length_and_check(&p
, p_end
, r
);
335 s
.append(n
- 1, 'x');
336 TEST_EXCEPTION(Xapian_NetworkError
,
337 const char *p
= s
.data();
338 const char *p_end
= p
+ s
.size();
340 decode_length_and_check(&p
, p_end
, r
);
345 const char *p
= s
.data();
346 const char *p_end
= p
+ s
.size();
348 decode_length_and_check(&p
, p_end
, r
);
350 TEST_EQUAL(size_t(p_end
- p
), n
);
354 const char *p
= s
.data();
355 const char *p_end
= p
+ s
.size();
357 decode_length_and_check(&p
, p_end
, r
);
359 TEST_EQUAL(size_t(p_end
- p
), n
+ 1);
366 // Check serialisation of Xapian::Error.
367 static bool test_serialiseerror1()
369 string
enoent_msg(strerror(ENOENT
));
370 Xapian::DatabaseOpeningError
e("Failed to open database", ENOENT
);
371 // Regression test for bug in 1.0.0 - it didn't convert errno values for
372 // get_description() if they hadn't already been converted.
373 TEST_STRINGS_EQUAL(e
.get_description(), "DatabaseOpeningError: Failed to open database (" + enoent_msg
+ ")");
375 TEST_STRINGS_EQUAL(e
.get_error_string(), enoent_msg
);
377 string serialisation
= serialise_error(e
);
379 // Test if unserialise_error() throws with a flag to avoid the possibility
380 // of an "unreachable code" warning when we get around to marking
381 // unserialise_error() as "noreturn".
384 // unserialise_error throws an exception.
385 unserialise_error(serialisation
, "", "");
386 } catch (const Xapian::Error
& ecaught
) {
387 TEST_STRINGS_EQUAL(ecaught
.get_error_string(), enoent_msg
);
392 // Check that the original is still OK.
393 TEST_STRINGS_EQUAL(e
.get_error_string(), enoent_msg
);
395 // Regression test - in 1.0.0, copying used to duplicate the error_string
396 // pointer, resulting in double calls to free().
397 Xapian::DatabaseOpeningError
ecopy(e
);
398 TEST_STRINGS_EQUAL(ecopy
.get_error_string(), enoent_msg
);
404 // Test log2() (which might be our replacement version).
405 static bool test_log2()
407 TEST_EQUAL(log2(1.0), 0.0);
408 TEST_EQUAL(log2(2.0), 1.0);
409 TEST_EQUAL(log2(1024.0), 10.0);
410 TEST_EQUAL(log2(0.5), -1.0);
414 static const double test_sortableserialise_numbers
[] = {
422 -3.14159265358979323846,
454 3.14159265358979323846,
463 64 // Magic number which we stop at.
466 // Test serialisation and unserialisation of various numbers.
467 // This is actually a public API, but we want extra assertions in the code
469 static bool test_sortableserialise1()
473 bool started
= false;
474 for (const double *p
= test_sortableserialise_numbers
; *p
!= 64; ++p
) {
476 tout
<< "Number: " << num
<< '\n';
477 string str
= Xapian::sortable_serialise(num
);
478 tout
<< "String: " << str
<< '\n';
479 TEST_EQUAL(Xapian::sortable_unserialise(str
), num
);
485 } else if (prevnum
> num
) {
491 } else if (prevstr
> str
) {
495 TEST_AND_EXPLAIN(num_cmp
== str_cmp
,
496 "Numbers " << prevnum
<< " and " << num
<<
497 " don't sort the same way as their string "
508 static bool test_tostring1()
510 TEST_EQUAL(str(0), "0");
511 TEST_EQUAL(str(0u), "0");
512 TEST_EQUAL(str(1), "1");
513 TEST_EQUAL(str(1u), "1");
514 TEST_EQUAL(str(9), "9");
515 TEST_EQUAL(str(9u), "9");
516 TEST_EQUAL(str(10), "10");
517 TEST_EQUAL(str(10u), "10");
518 TEST_EQUAL(str(-1), "-1");
519 TEST_EQUAL(str(-9), "-9");
520 TEST_EQUAL(str(-10), "-10");
521 TEST_EQUAL(str(0xffffffff), "4294967295");
522 TEST_EQUAL(str(0x7fffffff), "2147483647");
523 TEST_EQUAL(str(0x7fffffffu
), "2147483647");
524 TEST_EQUAL(str(-0x7fffffff), "-2147483647");
527 /* Test the 64 bit integer conversion to string.
528 * (Currently only exists for windows.)
530 TEST_EQUAL(str(10ll), "10");
531 TEST_EQUAL(str(-10ll), "-10");
532 TEST_EQUAL(str(0x200000000ll
), "8589934592");
533 // We don't currently have an "unsigned long long" version since it's not required
534 // anywhere in the library.
535 // TEST_EQUAL(str(0x200000000ull), "8589934592");
541 /// Regression test for bug fixed in 1.1.1.
542 static bool test_strbool1()
544 TEST_EQUAL(str(true), "1");
545 TEST_EQUAL(str(false), "0");
549 static const test_desc tests
[] = {
550 TESTCASE(simple_exceptions_work1
),
551 TESTCASE(class_exceptions_work1
),
552 TESTCASE(resolverelativepath1
),
553 TESTCASE(serialisedouble1
),
554 #ifdef XAPIAN_HAS_REMOTE_BACKEND
555 TESTCASE(serialiselength1
),
556 TESTCASE(serialiselength2
),
557 TESTCASE(serialiseerror1
),
560 TESTCASE(sortableserialise1
),
566 int main(int argc
, char **argv
)
568 test_driver::parse_command_line(argc
, argv
);
569 return test_driver::run(tests
);
570 } catch (const char * e
) {