tests/internaltest.cc

Go to the documentation of this file.
00001 /* internaltest.cc: test of the Xapian internals
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2002,2003,2006,2007 Olly Betts
00006  * Copyright 2006 Lemur Consulting Ltd
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License as
00010  * published by the Free Software Foundation; either version 2 of the
00011  * License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00021  * USA
00022  */
00023 
00024 #include <config.h>
00025 
00026 // Need to call Error::get_errno() for testing.
00027 #define XAPIAN_DEPRECATED(D) D
00028 #include <xapian.h>
00029 
00030 #include <float.h>
00031 #include "safeerrno.h"
00032 
00033 #include <string>
00034 #include <list>
00035 
00036 using namespace std;
00037 
00038 #include "autoptr.h"
00039 #include "testsuite.h"
00040 #include "testutils.h"
00041 
00042 #include "serialise.h"
00043 #include "serialise-double.h"
00044 #include "omqueryinternal.h"
00045 #include "utils.h"
00046 
00047 static bool test_except1()
00048 {
00049     try {
00050         throw 1;
00051     } catch (int) {
00052     }
00053     return true;
00054 }
00055 
00056 class Test_Exception {
00057     public:
00058         int value;
00059         Test_Exception(int value_) : value(value_) {}
00060 };
00061 
00062 // test that nested exceptions work correctly.
00063 static bool test_exception1()
00064 {
00065     try {
00066         try {
00067             throw Test_Exception(1);
00068         } catch (...) {
00069             try {
00070                 throw Test_Exception(2);
00071             } catch (...) {
00072             }
00073             throw;
00074         }
00075     } catch (Test_Exception & e) {
00076         TEST_EQUAL(e.value, 1);
00077         return true;
00078     }
00079     return false;
00080 }
00081 
00082 // ###########################################
00083 // # Tests of the reference counted pointers #
00084 // ###########################################
00085 
00086 class test_refcnt : public Xapian::Internal::RefCntBase {
00087     private:
00088         bool &deleted;
00089     public:
00090         test_refcnt(bool &deleted_) : deleted(deleted_) {
00091             tout << "constructor\n";
00092         }
00093         Xapian::Internal::RefCntPtr<const test_refcnt> test() {
00094             return Xapian::Internal::RefCntPtr<const test_refcnt>(this);
00095         }
00096         ~test_refcnt() {
00097             deleted = true;
00098             tout << "destructor\n";
00099         }
00100 };
00101 
00102 static bool test_refcnt1()
00103 {
00104     bool deleted = false;
00105 
00106     test_refcnt *p = new test_refcnt(deleted);
00107 
00108     TEST_EQUAL(p->ref_count, 0);
00109 
00110     {
00111         Xapian::Internal::RefCntPtr<test_refcnt> rcp(p);
00112 
00113         TEST_EQUAL(rcp->ref_count, 1);
00114         
00115         {
00116             Xapian::Internal::RefCntPtr<test_refcnt> rcp2;
00117             rcp2 = rcp;
00118             TEST_EQUAL(rcp->ref_count, 2);
00119             // rcp2 goes out of scope here
00120         }
00121         
00122         TEST_AND_EXPLAIN(!deleted, "Object prematurely deleted!");
00123         TEST_EQUAL(rcp->ref_count, 1);
00124         // rcp goes out of scope here
00125     }
00126     
00127     TEST_AND_EXPLAIN(deleted, "Object not properly deleted");
00128 
00129     return true;
00130 }
00131 
00132 // This is a regression test - a RefCntPtr used to delete the object pointed
00133 // to if you assigned it to itself and the reference count was 1.
00134 static bool test_refcnt2()
00135 {
00136     bool deleted = false;
00137 
00138     test_refcnt *p = new test_refcnt(deleted);
00139 
00140     Xapian::Internal::RefCntPtr<test_refcnt> rcp(p);
00141     
00142     rcp = rcp;
00143     
00144     TEST_AND_EXPLAIN(!deleted, "Object deleted by self-assignment");
00145 
00146     return true;
00147 }
00148 
00149 // Class for testing AutoPtr<>.
00150 class test_autoptr {
00151     bool &deleted;
00152   public:
00153     test_autoptr(bool &deleted_) : deleted(deleted_) {
00154         tout << "test_autoptr constructor\n";
00155     }
00156     ~test_autoptr() {
00157         deleted = true;
00158         tout << "test_autoptr destructor\n";
00159     }
00160 };
00161 
00162 // Test autoptr self-assignment.
00163 static bool test_autoptr1()
00164 {
00165     bool deleted = false;
00166 
00167     test_autoptr * raw_ptr = new test_autoptr(deleted);
00168     {
00169         AutoPtr<test_autoptr> ptr(raw_ptr);
00170 
00171         TEST_EQUAL(ptr.get(), raw_ptr);
00172 
00173         TEST(!deleted);
00174 
00175         ptr = ptr;
00176 
00177         TEST_EQUAL(ptr.get(), raw_ptr);
00178 
00179         TEST(!deleted);
00180     }
00181 
00182     TEST(deleted);
00183     
00184     return true;
00185 }
00186 
00187 // test string comparisions
00188 static bool test_stringcomp1()
00189 {
00190     bool success = true;
00191 
00192     string s1;
00193     string s2;
00194 
00195     s1 = "foo";
00196     s2 = "foo";
00197 
00198     if ((s1 != s2) || (s1 > s2)) {
00199         success = false;
00200         tout << "String comparisons BADLY wrong" << endl;
00201     }
00202 
00203     s1 += '\0';
00204 
00205     if ((s1 == s2) || (s1 < s2)) {
00206         success = false;
00207         tout << "String comparisions don't cope with extra nulls" << endl;
00208     }
00209 
00210     s2 += '\0';
00211 
00212     s1 += 'a';
00213     s2 += 'z';
00214 
00215     if ((s1.length() != 5) || (s2.length() != 5)) {
00216         success = false;
00217         tout << "Lengths with added nulls wrong" << endl;
00218     }
00219 
00220     if ((s1 == s2) || !(s1 < s2)) {
00221         success = false;
00222         tout << "Characters after a null ignored in comparisons" << endl;
00223     }
00224 
00225     return success;
00226 }
00227 
00228 static bool test_tostring1()
00229 {
00230     TEST_EQUAL(om_tostring(0), "0");
00231     TEST_EQUAL(om_tostring(10), "10");
00232     TEST_EQUAL(om_tostring(10u), "10");
00233     TEST_EQUAL(om_tostring(-10), "-10");
00234     TEST_EQUAL(om_tostring(0xffffffff), "4294967295");
00235     TEST_EQUAL(om_tostring(0x7fffffff), "2147483647");
00236     TEST_EQUAL(om_tostring(0x7fffffffu), "2147483647");
00237     TEST_EQUAL(om_tostring(-0x7fffffff), "-2147483647");
00238 
00239 #ifdef __WIN32__
00240     /* Test the 64 bit integer conversion to string.
00241      * (Currently only exists for windows.)
00242      */
00243     TEST_EQUAL(om_tostring(10ll), "10");
00244     TEST_EQUAL(om_tostring(-10ll), "-10");
00245     TEST_EQUAL(om_tostring(0x200000000ll), "8589934592");
00246 // We don't currently have an "unsigned long long" version since it's not required
00247 // anywhere in the library.
00248 //    TEST_EQUAL(om_tostring(0x200000000ull), "8589934592");
00249 #endif
00250 
00251     return true;
00252 }
00253 
00254 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00255 // Check serialisation of lengths.
00256 static bool test_serialiselength1()
00257 {
00258     size_t n = 0;
00259     while (n < 0xff000000) {
00260         string s = encode_length(n);
00261         const char *p = s.data();
00262         const char *p_end = p + s.size();
00263         size_t decoded_n = decode_length(&p, p_end, false);
00264         if (n != decoded_n || p != p_end) tout << "[" << s << "]" << endl;
00265         TEST_EQUAL(n, decoded_n);
00266         TEST_EQUAL(p_end - p, 0);
00267         if (n < 5000) {
00268             ++n;
00269         } else {
00270             n += 53643;
00271         }
00272     }
00273 
00274     return true;
00275 }
00276 
00277 // Regression test: vetting the remaining buffer length
00278 static bool test_serialiselength2()
00279 {
00280     // Special case tests for 0
00281     {
00282         string s = encode_length(0);
00283         {
00284             const char *p = s.data();
00285             const char *p_end = p + s.size();
00286             TEST(decode_length(&p, p_end, true) == 0);
00287             TEST(p == p_end);
00288         }
00289         s += 'x';
00290         {
00291             const char *p = s.data();
00292             const char *p_end = p + s.size();
00293             TEST(decode_length(&p, p_end, true) == 0);
00294             TEST_EQUAL(p_end - p, 1);
00295         }
00296     }
00297     // Special case tests for 1
00298     {
00299         string s = encode_length(1);
00300         TEST_EXCEPTION(Xapian::NetworkError,
00301             const char *p = s.data();
00302             const char *p_end = p + s.size();
00303             TEST(decode_length(&p, p_end, true) == 1);
00304         );
00305         s += 'x';
00306         {
00307             const char *p = s.data();
00308             const char *p_end = p + s.size();
00309             TEST(decode_length(&p, p_end, true) == 1);
00310             TEST_EQUAL(p_end - p, 1);
00311         }
00312         s += 'x';
00313         {
00314             const char *p = s.data();
00315             const char *p_end = p + s.size();
00316             TEST(decode_length(&p, p_end, true) == 1);
00317             TEST_EQUAL(p_end - p, 2);
00318         }
00319     }
00320     // Nothing magic here, just test a range of odd and even values.
00321     for (size_t n = 2; n < 1000; n = (n + 1) * 2 + (n >> 1)) {
00322         string s = encode_length(n);
00323         TEST_EXCEPTION(Xapian::NetworkError,
00324             const char *p = s.data();
00325             const char *p_end = p + s.size();
00326             TEST(decode_length(&p, p_end, true) == n);
00327         );
00328         s.append(n-1, 'x');
00329         TEST_EXCEPTION(Xapian::NetworkError,
00330             const char *p = s.data();
00331             const char *p_end = p + s.size();
00332             TEST(decode_length(&p, p_end, true) == n);
00333         );
00334         s += 'x';
00335         {
00336             const char *p = s.data();
00337             const char *p_end = p + s.size();
00338             TEST(decode_length(&p, p_end, true) == n);
00339             TEST_EQUAL(size_t(p_end - p), n);
00340         }
00341         s += 'x';
00342         {
00343             const char *p = s.data();
00344             const char *p_end = p + s.size();
00345             TEST(decode_length(&p, p_end, true) == n);
00346             TEST_EQUAL(size_t(p_end - p), n + 1);
00347         }
00348     }
00349 
00350     return true;
00351 }
00352 #endif
00353 
00354 static void check_double_serialisation(double u)
00355 {
00356     string encoded = serialise_double(u);
00357     const char * ptr = encoded.data();
00358     const char * end = ptr + encoded.size();
00359     double v = unserialise_double(&ptr, end);
00360     if (ptr != end || u != v) {
00361         tout << u << " -> " << v << ", difference = " << v - u << endl;
00362         tout << "FLT_RADIX = " << FLT_RADIX << endl;
00363         tout << "DBL_MAX_EXP = " << DBL_MAX_EXP << endl;
00364     }
00365     TEST(ptr == end);
00366     TEST_EQUAL(u, v);
00367 }
00368 
00369 // Check serialisation of doubles.
00370 static bool test_serialisedouble1()
00371 {
00372     static const double test_values[] = {
00373         3.14159265,
00374         1e57,
00375         123.1,
00376         257.12,
00377         1234.567e123,
00378         255.5,
00379         256.125,
00380         257.03125,
00381     };
00382 
00383     check_double_serialisation(0.0);
00384     check_double_serialisation(1.0);
00385     check_double_serialisation(-1.0);
00386     check_double_serialisation(DBL_MAX);
00387     check_double_serialisation(-DBL_MAX);
00388     check_double_serialisation(DBL_MIN);
00389     check_double_serialisation(-DBL_MIN);
00390 
00391     const double *p;
00392     for (p = test_values; p < test_values + sizeof(test_values) / sizeof(double); ++p) {
00393         double val = *p;
00394         check_double_serialisation(val);
00395         check_double_serialisation(-val);
00396         check_double_serialisation(1.0 / val);
00397         check_double_serialisation(-1.0 / val);
00398     }
00399 
00400     return true;
00401 }
00402 
00403 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00404 // Check serialisation of documents.
00405 static bool test_serialisedoc1()
00406 {
00407     Xapian::Document doc;
00408 
00409     string s;
00410 
00411     s = serialise_document(doc);
00412     TEST(serialise_document(unserialise_document(s)) == s);
00413 
00414     doc.set_data("helllooooo");
00415     doc.add_term("term");
00416     doc.add_value(1, "foo");
00417 
00418     s = serialise_document(doc);
00419     TEST(serialise_document(unserialise_document(s)) == s);
00420 
00421     return true;
00422 }
00423 
00424 // Check serialisation of queries.
00425 static bool test_serialisequery1()
00426 {
00427     string s;
00428     list<Xapian::Query> queries;
00429 
00430     queries.push_back(Xapian::Query("foo"));
00431 
00432     // Regression test for bug in 0.9.10 and earlier.
00433     queries.push_back(Xapian::Query("foo", 1, 1));
00434 
00435     queries.push_back(Xapian::Query(Xapian::Query::OP_OR,
00436                                     Xapian::Query("foo", 1, 1),
00437                                     Xapian::Query("bar", 1, 1)));
00438 
00439     const char * words[] = { "paragraph", "word" };
00440     queries.push_back(Xapian::Query(Xapian::Query::OP_OR, words, words + 2));
00441 
00442     const char * words2[] = { "milk", "on", "fridge" };
00443     queries.push_back(Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT,
00444                                     Xapian::Query(Xapian::Query::OP_OR,
00445                                                   Xapian::Query("leave"),
00446                                                   Xapian::Query(Xapian::Query::OP_PHRASE, words2, words2 + 3)
00447                                                  ),
00448                                     2.5
00449                                    ));
00450 
00451     list<Xapian::Query>::const_iterator query;
00452     for (query = queries.begin(); query != queries.end(); query++) {
00453         Xapian::Query::Internal * qint;
00454 
00455         s = query->internal->serialise();
00456         qint = Xapian::Query::Internal::unserialise(s);
00457 
00458         TEST(qint->serialise() == s);
00459         delete qint;
00460     }
00461 
00462     return true;
00463 }
00464 
00465 // Check serialisation of Xapian::Error.
00466 static bool test_serialiseerror1()
00467 {
00468     string enoent_msg(strerror(ENOENT));
00469     Xapian::DatabaseOpeningError e("Failed to open database", ENOENT);
00470     // Regression test for bug in 1.0.0 - it didn't convert errno values for
00471     // get_description() if they hadn't already been converted.
00472     TEST_STRINGS_EQUAL(e.get_description(), "DatabaseOpeningError: Failed to open database (" + enoent_msg + ")");
00473 
00474     TEST_EQUAL(e.get_errno(), ENOENT);
00475     TEST_STRINGS_EQUAL(e.get_error_string(), enoent_msg);
00476 
00477     string serialisation = serialise_error(e);
00478 
00479     // Test if unserialise_error() throws with a flag to avoid the possibility
00480     // of an "unreachable code" warning when we get around to marking
00481     // unserialise_error() as "noreturn".
00482     bool threw = false;
00483     try {
00484         // unserialise_error throws an exception.
00485         unserialise_error(serialisation, "", "");
00486     } catch (Xapian::Error & ecaught) {
00487         TEST_EQUAL(ecaught.get_errno(), 0); // errno values aren't portable.
00488         TEST_STRINGS_EQUAL(ecaught.get_error_string(), enoent_msg);
00489         threw = true;
00490     }
00491     TEST(threw);
00492 
00493     // Check that the original is still OK.
00494     TEST_STRINGS_EQUAL(e.get_error_string(), enoent_msg);
00495 
00496     // Regression test - in 1.0.0, copying used to duplicate the error_string
00497     // pointer, resulting in double calls to free().
00498     Xapian::DatabaseOpeningError ecopy(e);
00499     TEST_STRINGS_EQUAL(ecopy.get_error_string(), enoent_msg);
00500 
00501     return true;
00502 }
00503 #endif
00504 
00505 // By default Sun's C++ compiler doesn't call the destructor on a
00506 // temporary object until the end of the block (contrary to what
00507 // ISO C++ requires).  This is done in the name of "compatibility".
00508 // Passing -features=tmplife to CC fixes this.  This check ensures
00509 // that this actually works for Sun's C++ and any other compilers
00510 // that might have this problem.
00511 struct TempDtorTest {
00512     static int count;
00513     static TempDtorTest factory() { return TempDtorTest(); }
00514     TempDtorTest() { ++count; }
00515     ~TempDtorTest() { --count; }
00516 };
00517 
00518 int TempDtorTest::count = 0;
00519 
00520 static bool test_temporarydtor1()
00521 {
00522     TEST_EQUAL(TempDtorTest::count, 0);
00523     TempDtorTest::factory();
00524     TEST_EQUAL(TempDtorTest::count, 0);
00525 
00526     return true;
00527 }
00528 
00529 // ##################################################################
00530 // # End of actual tests                                            #
00531 // ##################################################################
00532 
00534 test_desc tests[] = {
00535     {"except1",                 test_except1},
00536     {"exception1",              test_exception1},
00537     {"refcnt1",                 test_refcnt1},
00538     {"refcnt2",                 test_refcnt2},
00539     {"autoptr1",                test_autoptr1},
00540     {"stringcomp1",             test_stringcomp1},
00541     {"temporarydtor1",          test_temporarydtor1},
00542     {"tostring1",               test_tostring1},
00543     {"serialisedouble1",        test_serialisedouble1},
00544 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00545     {"serialiselength1",        test_serialiselength1},
00546     {"serialiselength2",        test_serialiselength2},
00547     {"serialisedoc1",           test_serialisedoc1},
00548     {"serialisequery1",         test_serialisequery1},
00549     {"serialiseerror1",         test_serialiseerror1},
00550 #endif
00551     {0, 0}
00552 };
00553 
00554 int main(int argc, char **argv)
00555 {
00556     test_driver::parse_command_line(argc, argv);
00557     return test_driver::run(tests);
00558 }

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.