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

Documentation for Xapian (version 1.0.18).
Generated on 15 Feb 2010 by Doxygen 1.5.2.