tests/btreetest.cc

Go to the documentation of this file.
00001 /* btreetest.cc: test of the btree manager
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2002,2003,2004,2005,2006,2007 Olly Betts
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2 of the
00010  * License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00020  * USA
00021  */
00022 
00023 #include <config.h>
00024 
00025 #include "safeerrno.h"
00026 
00027 #include "btreecheck.h"
00028 #include "unixcmds.h"
00029 #include "testsuite.h"
00030 #include "testutils.h"
00031 #include "utils.h"
00032 
00033 #include <algorithm>
00034 #include <fstream>
00035 #include <string>
00036 
00037 using namespace std;
00038 
00039 #include <sys/types.h>
00040 #include "safesysstat.h"
00041 
00042 #define BTREE_CHECK(DIR, OPTS) BtreeCheck::check(DIR, OPTS, tout)
00043 
00044 static string tmpdir;
00045 static string datadir;
00046 
00047 static void make_dir(const string & filename)
00048 {
00049     if (mkdir(filename, 0700) == -1 && errno != EEXIST) {
00050         tout << "Couldn't create directory `" << filename << "' ("
00051              << strerror(errno) << ")";
00052     }
00053 }
00054 
00056 static off_t get_filesize(const string &filename)
00057 {
00058     struct stat buf;
00059     int result = stat(filename, &buf);
00060     if (result) return -1;
00061     return buf.st_size;
00062 }
00063 
00064 static int process_lines(Btree & btree, ifstream &f)
00065 {
00066     int count = 0;
00067     while (true) {
00068         string s;
00069         if (!getline(f, s)) return count;
00070         if (s.empty()) continue;
00071         if (s[0] == '+') {
00072             string::size_type sp = s.find(' ');
00073             btree.add(s.substr(1, min(sp - 1, BTREE_MAX_KEY_LEN)),
00074                       s.substr(sp + 1));
00075             ++count;
00076         } else if (s[0] == '-') {
00077             btree.del(s.substr(1, BTREE_MAX_KEY_LEN));
00078             --count;
00079         } else {
00080             throw "No '+' or '-' on line `" + s + "'";
00081         }
00082     }
00083 }
00084 
00085 static int do_update(const string & btree_dir,
00086                      const string & datafile,
00087                      bool full_compact = false)
00088 {
00089     Btree btree(btree_dir, false);
00090     btree.open();
00091 
00092     if (full_compact) {
00093         tout << "Compact mode\n";
00094         btree.set_full_compaction(true);
00095     }
00096     
00097     int count;
00098     {
00099         ifstream f(datafile.c_str());
00100         TEST_AND_EXPLAIN(f.is_open(), "File " << datafile << " not found");
00101         count = process_lines(btree, f);
00102     }
00103 
00104     btree.commit(btree.get_open_revision_number() + 1);
00105 
00106     return count;
00107 }
00108 
00109 static void do_create(const string & btree_dir, int block_size = 2048)
00110 {
00111     if (btree_dir.empty()) return;
00112 
00113     // NetBSD mkdir() doesn't cope with a trailing slash.
00114     string no_slash = btree_dir;
00115     if (no_slash[no_slash.size() - 1] == '/')
00116         no_slash.resize(no_slash.size() - 1);
00117     rm_rf(no_slash);
00118     make_dir(no_slash);
00119 
00120     Btree dummy(btree_dir, false);
00121     dummy.create(block_size);
00122     tout << btree_dir << "/DB created with block size " << block_size << "\n";
00123 }
00124 
00125 static void unlink_table(const string & path)
00126 {
00127     unlink(path + "DB");
00128     unlink(path + "baseA");
00129     unlink(path + "baseB");
00130 }
00131 
00132 static void check_table_values_hello(Btree & table, const string &world)
00133 {
00134     string tag;
00135 
00136     // Check exact reads
00137     tag = "foo";
00138     TEST(table.get_exact_entry("hello", tag));
00139     TEST_EQUAL(tag, world);
00140 
00141     tag = "foo";
00142     TEST(!table.get_exact_entry("jello", tag));
00143     TEST_EQUAL(tag, "foo");
00144 
00145     tag = "foo";
00146     TEST(!table.get_exact_entry("bello", tag));
00147     TEST_EQUAL(tag, "foo");
00148     
00149     Bcursor * cursor = table.cursor_get();
00150 
00151     // Check normal reads
00152     tag = "foo";
00153     TEST(cursor->find_entry("hello"));
00154     TEST_EQUAL(cursor->current_key, "hello");
00155     cursor->read_tag();
00156     TEST_EQUAL(cursor->current_tag, world);
00157 
00158     tag = "foo";
00159     TEST(!cursor->find_entry("jello"));
00160     TEST_EQUAL(cursor->current_key, "hello");
00161     cursor->read_tag();
00162     TEST_EQUAL(cursor->current_tag, world);
00163 
00164     tag = "foo";
00165     TEST(!cursor->find_entry("bello"));
00166     TEST_EQUAL(cursor->current_key, "");
00167     cursor->read_tag();
00168     TEST_EQUAL(cursor->current_tag, "");
00169 
00170     delete cursor;
00171 }
00172 
00174 static void check_table_values_empty(Btree & table)
00175 {
00176     string tag;
00177 
00178     // Check exact reads
00179     tag = "foo";
00180     TEST(!table.get_exact_entry("hello", tag));
00181     TEST_EQUAL(tag, "foo");
00182 
00183     tag = "foo";
00184     TEST(!table.get_exact_entry("jello", tag));
00185     TEST_EQUAL(tag, "foo");
00186 
00187     tag = "foo";
00188     TEST(!table.get_exact_entry("bello", tag));
00189     TEST_EQUAL(tag, "foo");
00190     
00191     Bcursor * cursor = table.cursor_get();
00192     
00193     // Check normal reads
00194     tag = "foo";
00195     TEST(!cursor->find_entry("hello"));
00196     TEST_EQUAL(cursor->current_key, "");
00197     cursor->read_tag();
00198     TEST_EQUAL(cursor->current_tag, "");
00199 
00200     tag = "foo";
00201     TEST(!cursor->find_entry("jello"));
00202     TEST_EQUAL(cursor->current_key, "");
00203     cursor->read_tag();
00204     TEST_EQUAL(cursor->current_tag, "");
00205 
00206     tag = "foo";
00207     TEST(!cursor->find_entry("bello"));
00208     TEST_EQUAL(cursor->current_key, "");
00209     cursor->read_tag();
00210     TEST_EQUAL(cursor->current_tag, "");
00211 
00212     delete cursor;
00213 }
00214 
00216 static bool test_simple1()
00217 {
00218     const string path = tmpdir + "/test_simple1_";
00219     Btree btree(path, true);
00220     btree.create(8192);
00221     btree.open();
00222 
00223     string key = "foo";
00224     {
00225         Bcursor cursor(&btree);
00226         int found = cursor.find_entry(key);
00227         TEST(!found);
00228     }
00229     {
00230         Bcursor cursor(&btree);
00231         int found = cursor.find_entry(key);
00232         TEST(!found);
00233     }
00234 
00235     return true;
00236 }
00237 
00239 static bool test_insertdelete1()
00240 {
00241     const string btree_dir = tmpdir + "/B/";
00242     do_create(btree_dir);
00243     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00244 
00245     if (!file_exists(datadir + "ord+") || !file_exists(datadir + "ord-"))
00246         SKIP_TEST("Data files not present");
00247 
00248     unsigned int count = do_update(btree_dir, datadir + "ord+");
00249     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00250     {
00251         Btree btree(btree_dir, true);
00252         btree.open();
00253         TEST_EQUAL(count, btree.get_entry_count());
00254     }
00255 
00256     count += do_update(btree_dir, datadir + "ord-");
00257     BTREE_CHECK(btree_dir, OPT_SHOW_STATS | OPT_SHORT_TREE);
00258     {
00259         Btree btree(btree_dir, true);
00260         btree.open();
00261         TEST_EQUAL(btree.get_entry_count(), 0);
00262         TEST_EQUAL(count, btree.get_entry_count());
00263     }
00264 
00265     return true;
00266 }
00267 
00269 static bool test_sequent1()
00270 {
00271     const string btree_dir = tmpdir + "/B/";
00272     do_create(btree_dir);
00273     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00274 
00275     if (!file_exists(datadir + "ordnum+") || !file_exists(datadir + "ordnum-"))
00276         SKIP_TEST("Data files not present");
00277 
00278     do_update(btree_dir, datadir + "ord+");
00279     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00280 
00281     do_update(btree_dir, datadir + "ordnum+");
00282     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00283 
00284     do_update(btree_dir, datadir + "ord-");
00285     BTREE_CHECK(btree_dir, OPT_SHOW_STATS | OPT_SHORT_TREE);
00286 
00287     do_update(btree_dir, datadir + "ordnum-");
00288     BTREE_CHECK(btree_dir, OPT_SHOW_STATS | OPT_SHORT_TREE);
00289 
00290     Btree btree(btree_dir, true);
00291     btree.open();
00292     TEST_EQUAL(btree.get_entry_count(), 0);
00293 
00294     return true;
00295 }
00296 
00297 static bool test_emptykey1()
00298 {
00299     const string btree_dir = tmpdir + "/B/";
00300     do_create(btree_dir);
00301     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00302 
00303     {
00304         Btree btree(btree_dir, false);
00305         btree.open();
00306 
00307         tout << "Setting tag to jam" << endl;
00308         btree.add("", "jam");
00309         btree.commit(btree.get_open_revision_number() + 1);
00310     }
00311     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00312 
00313     {
00314         Btree btree(btree_dir, false);
00315         btree.open();
00316         TEST_EQUAL(btree.get_entry_count(), 0);
00317 
00318         tout << "Setting tag to marmite" << endl;
00319         btree.add("", "marmite");
00320         btree.commit(btree.get_open_revision_number() + 1);
00321     }
00322     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00323 
00324     {
00325         Btree btree(btree_dir, false);
00326         btree.open();
00327         TEST_EQUAL(btree.get_entry_count(), 0);
00328 
00329         tout << "Deleting tag" << endl;
00330         btree.del("");
00331         btree.commit(btree.get_open_revision_number() + 1);
00332     }
00333     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00334 
00335     {
00336         Btree btree(btree_dir, false);
00337         btree.open();
00338         TEST_EQUAL(btree.get_entry_count(), 0);
00339 
00340         tout << "Setting tag to butter" << endl;
00341         btree.add("", "butter");
00342         btree.add("test", "me");
00343         btree.commit(btree.get_open_revision_number() + 1);
00344     }
00345     BTREE_CHECK(btree_dir, OPT_SHOW_STATS);
00346 
00347     Btree btree(btree_dir, true);
00348     btree.open();
00349     TEST_EQUAL(btree.get_entry_count(), 1);
00350  
00351     return true;
00352 }
00353 
00355 static bool test_table1()
00356 {
00357     const string tablename = tmpdir + "/test_table1_";
00358     unlink_table(tablename);
00359     {
00360         Btree table0(tablename, true);
00361         TEST_EXCEPTION(Xapian::DatabaseOpeningError, table0.open());
00362         TEST_EXCEPTION(Xapian::DatabaseOpeningError, table0.open(10));
00363     }
00364     Btree rw_table(tablename, false);
00365     rw_table.create(8192);
00366     rw_table.open();
00367     Btree ro_table(tablename, true);
00368     ro_table.open();
00369 
00370     quartz_revision_number_t rev1 = ro_table.get_open_revision_number();
00371     quartz_revision_number_t rev2 = rw_table.get_open_revision_number();
00372 
00373     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00374     TEST_EQUAL(rev2, rw_table.get_open_revision_number());
00375     TEST_EQUAL(ro_table.get_entry_count(), 0);
00376     TEST_EQUAL(rw_table.get_entry_count(), 0);
00377 
00378     // Check adding no entries
00379 
00380 #ifdef XAPIAN_DEBUG
00381     TEST_EXCEPTION(Xapian::AssertionError,
00382                    ro_table.commit(ro_table.get_latest_revision_number() + 1));
00383 #endif
00384     rw_table.commit(rw_table.get_latest_revision_number() + 1);
00385 
00386     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00387     TEST_NOT_EQUAL(rev2, rw_table.get_open_revision_number());
00388     rev1 = ro_table.get_open_revision_number();
00389     rev2 = rw_table.get_open_revision_number();
00390     TEST_EQUAL(ro_table.get_entry_count(), 0);
00391     TEST_EQUAL(rw_table.get_entry_count(), 0);
00392 
00393     // Check adding some entries
00394 #ifdef XAPIAN_DEBUG
00395     TEST_EXCEPTION(Xapian::AssertionError, ro_table.add("hello", "world"));
00396 #endif
00397     rw_table.add("hello", "world");
00398     rw_table.commit(rw_table.get_latest_revision_number() + 1);
00399 
00400     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00401     TEST_NOT_EQUAL(rev2, rw_table.get_open_revision_number());
00402     rev1 = ro_table.get_open_revision_number();
00403     rev2 = rw_table.get_open_revision_number();
00404     TEST_EQUAL(ro_table.get_entry_count(), 0);
00405     TEST_EQUAL(rw_table.get_entry_count(), 1);
00406 
00407     // Check getting the entries out again
00408     check_table_values_empty(ro_table);
00409     check_table_values_hello(rw_table, "world");
00410 
00411     // Check adding the same entries
00412 #ifdef XAPIAN_DEBUG
00413     TEST_EXCEPTION(Xapian::AssertionError, ro_table.add("hello", "world"));
00414 #endif
00415     rw_table.add("hello", "world");
00416     rw_table.commit(rw_table.get_latest_revision_number() + 1);
00417 
00418     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00419     TEST_NOT_EQUAL(rev2, rw_table.get_open_revision_number());
00420     rev1 = ro_table.get_open_revision_number();
00421     rev2 = rw_table.get_open_revision_number();
00422     TEST_EQUAL(ro_table.get_entry_count(), 0);
00423     TEST_EQUAL(rw_table.get_entry_count(), 1);
00424 
00425     // Check getting the entries out again
00426     check_table_values_empty(ro_table);
00427     check_table_values_hello(rw_table, "world");
00428 
00429 #ifdef XAPIAN_DEBUG
00430     // Check adding an entry with a null key
00431     // Can't add a key to a read-only table anyway, empty or not!
00432     TEST_EXCEPTION(Xapian::AssertionError, ro_table.add("", "world"));
00433     // Empty keys aren't allowed (we no longer enforce this so the
00434     // magic empty key can be set).
00435     //TEST_EXCEPTION(Xapian::AssertionError, rw_table.add("", "world"));
00436 #endif
00437 
00438     // Check changing an entry, to a null tag
00439 #ifdef XAPIAN_DEBUG
00440     TEST_EXCEPTION(Xapian::AssertionError, ro_table.add("hello", ""));
00441 #endif
00442     rw_table.add("hello", "");
00443     rw_table.commit(rw_table.get_latest_revision_number() + 1);
00444 
00445     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00446     TEST_NOT_EQUAL(rev2, rw_table.get_open_revision_number());
00447     rev1 = ro_table.get_open_revision_number();
00448     rev2 = rw_table.get_open_revision_number();
00449     TEST_EQUAL(ro_table.get_entry_count(), 0);
00450     TEST_EQUAL(rw_table.get_entry_count(), 1);
00451     
00452     // Check getting the entries out again
00453     check_table_values_empty(ro_table);
00454     check_table_values_hello(rw_table, "");
00455 
00456     // Check deleting an entry
00457 #ifdef XAPIAN_DEBUG
00458     TEST_EXCEPTION(Xapian::AssertionError, ro_table.del("hello"));
00459 #endif
00460     rw_table.del("hello");
00461     rw_table.commit(rw_table.get_latest_revision_number() + 1);
00462 
00463     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00464     TEST_NOT_EQUAL(rev2, rw_table.get_open_revision_number());
00465     rev1 = ro_table.get_open_revision_number();
00466     rev2 = rw_table.get_open_revision_number();
00467     TEST_EQUAL(ro_table.get_entry_count(), 0);
00468     TEST_EQUAL(rw_table.get_entry_count(), 0);
00469 
00470     // Check the entries in the table
00471     check_table_values_empty(ro_table);
00472     check_table_values_empty(rw_table);
00473     
00474     // Check find_entry when looking for something between two elements
00475     rw_table.add("hello", "world");
00476     rw_table.add("whooo", "world");
00477 
00478     rw_table.commit(rw_table.get_latest_revision_number() + 1);
00479 
00480     TEST_EQUAL(rev1, ro_table.get_open_revision_number());
00481     TEST_NOT_EQUAL(rev2, rw_table.get_open_revision_number());
00482     rev1 = ro_table.get_open_revision_number();
00483     rev2 = rw_table.get_open_revision_number();
00484     TEST_EQUAL(ro_table.get_entry_count(), 0);
00485     TEST_EQUAL(rw_table.get_entry_count(), 2);
00486 
00487     // Check the entries in the table
00488     check_table_values_empty(ro_table);
00489     check_table_values_hello(rw_table, "world");
00490     
00491     return true;
00492 }
00493 
00495 static bool test_table2()
00496 {
00497     const string tablename = tmpdir + "/test_table2_";
00498     unlink_table(tablename);
00499 
00500     Btree table(tablename, false);
00501     table.create(8192);
00502     table.open();
00503     TEST_EQUAL(get_filesize(tmpdir + "/test_table2_DB"), 0);
00504 
00505     table.commit(table.get_latest_revision_number() + 1);
00506     TEST_EQUAL(get_filesize(tmpdir + "/test_table2_DB"), 0);
00507 
00508     table.commit(table.get_latest_revision_number() + 1);
00509     TEST_EQUAL(get_filesize(tmpdir + "/test_table2_DB"), 0);
00510 
00511     table.commit(table.get_latest_revision_number() + 1);
00512     TEST_EQUAL(get_filesize(tmpdir + "/test_table2_DB"), 0);
00513 
00514     table.add("foo", "bar");
00515     table.commit(table.get_latest_revision_number() + 1);
00516     TEST_EQUAL(get_filesize(tmpdir + "/test_table2_DB"), 8192);
00517 
00518     return true;
00519 }
00520 
00522 static bool test_table3()
00523 {
00524     const string tablename = tmpdir + "/test_table3_";
00525     unlink_table(tablename);
00526 
00527     Btree table(tablename, false);
00528     table.create(8192);
00529     table.open();
00530 
00531     table.commit(table.get_latest_revision_number() + 1);
00532 
00533     table.add("trad", string(2200, 'a'));
00534     table.add("trade", string(3800, 'b'));
00535     table.add("tradea", string(2000, 'c'));
00536 
00537     table.commit(table.get_latest_revision_number() + 1);
00538 
00539     {
00540         Bcursor * cursor = table.cursor_get();
00541         TEST(cursor->find_entry("trade"));
00542         TEST_EQUAL(cursor->current_key, "trade");
00543         cursor->read_tag();
00544         TEST_EQUAL(cursor->current_tag.size(), 3800);
00545 
00546         cursor->next();
00547         TEST_EQUAL(cursor->current_key, "tradea");
00548         delete cursor;
00549     }
00550 
00551     table.add("trade", string(4000, 'd'));
00552     table.commit(table.get_latest_revision_number() + 1);
00553 
00554     {
00555         Bcursor * cursor = table.cursor_get();
00556         TEST(cursor->find_entry("trade"));
00557         TEST_EQUAL(cursor->current_key, "trade");
00558         cursor->read_tag();
00559         TEST_EQUAL(cursor->current_tag.size(), 4000);
00560 
00561         cursor->next();
00562         TEST_EQUAL(cursor->current_key, "tradea");
00563         delete cursor;
00564     }
00565 
00566     return true;
00567 }
00568 
00570 static bool test_table4()
00571 {
00572     const string tablename = tmpdir + "/test_table4_";
00573     unlink_table(tablename);
00574     Btree table_rw(tablename, false);
00575     table_rw.create(8192);
00576     table_rw.open();
00577     Btree table_ro(tablename, true);
00578     table_ro.open();
00579 
00580     TEST_EQUAL(table_ro.get_entry_count(), 0);
00581     TEST_EQUAL(table_rw.get_entry_count(), 0);
00582 
00583     table_rw.del("foo1");
00584     TEST_EQUAL(table_ro.get_entry_count(), 0);
00585     TEST_EQUAL(table_rw.get_entry_count(), 0);
00586 
00587     table_rw.add("foo1", "");
00588     TEST_EQUAL(table_ro.get_entry_count(), 0);
00589     TEST_EQUAL(table_rw.get_entry_count(), 1);
00590 
00591     quartz_revision_number_t new_revision =
00592             table_ro.get_latest_revision_number() + 1;
00593     table_rw.commit(new_revision);
00594     table_ro.open();
00595     TEST_EQUAL(table_ro.get_entry_count(), 1);
00596     TEST_EQUAL(table_rw.get_entry_count(), 1);
00597 
00598     table_rw.add("foo1", "");
00599     TEST_EQUAL(table_ro.get_entry_count(), 1);
00600     TEST_EQUAL(table_rw.get_entry_count(), 1);
00601 
00602     table_rw.del("foo1");
00603     TEST_EQUAL(table_ro.get_entry_count(), 1);
00604     TEST_EQUAL(table_rw.get_entry_count(), 0);
00605 
00606     table_rw.del("foo1");
00607     TEST_EQUAL(table_ro.get_entry_count(), 1);
00608     TEST_EQUAL(table_rw.get_entry_count(), 0);
00609 
00610     table_rw.add("bar", "");
00611     TEST_EQUAL(table_ro.get_entry_count(), 1);
00612     TEST_EQUAL(table_rw.get_entry_count(), 1);
00613 
00614     table_rw.add("bar2", "");
00615     TEST_EQUAL(table_ro.get_entry_count(), 1);
00616     TEST_EQUAL(table_rw.get_entry_count(), 2);
00617 
00618     new_revision += 1;
00619     table_rw.commit(new_revision);
00620     table_ro.open();
00621 
00622     TEST_EQUAL(table_ro.get_entry_count(), 2);
00623     TEST_EQUAL(table_rw.get_entry_count(), 2);
00624 
00625     return true;
00626 }
00627 
00629 static bool test_table5()
00630 {
00631     const string tablename = tmpdir + "/test_table5_";
00632     unlink_table(tablename);
00633     quartz_revision_number_t new_revision;
00634     quartz_revision_number_t old_revision;
00635     {
00636         // Open table and add a few documents
00637         Btree table_rw(tablename, false);
00638         table_rw.create(8192);
00639         table_rw.open();
00640         Btree table_ro(tablename, true);
00641         table_ro.open();
00642 
00643         TEST_EQUAL(table_ro.get_entry_count(), 0);
00644         TEST_EQUAL(table_rw.get_entry_count(), 0);
00645 
00646         table_rw.add("foo1", "bar1");
00647         table_rw.add("foo2", "bar2");
00648         table_rw.add("foo3", "bar3");
00649 
00650         new_revision = table_ro.get_latest_revision_number() + 1;
00651         table_rw.commit(new_revision);
00652         table_ro.open();
00653 
00654         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00655         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00656     }
00657     {
00658         // Reopen and check that the documents are still there.
00659         Btree table_rw(tablename, false);
00660         table_rw.open();
00661         Btree table_ro(tablename, true);
00662         table_ro.open();
00663 
00664         TEST_EQUAL(table_ro.get_entry_count(), 3);
00665         TEST_EQUAL(table_rw.get_entry_count(), 3);
00666 
00667         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00668         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00669 
00670         Bcursor * cursor = table_rw.cursor_get();
00671         TEST(!cursor->find_entry("foo"));
00672         TEST_EQUAL(cursor->current_key, "");
00673         cursor->read_tag();
00674         TEST_EQUAL(cursor->current_tag, "");
00675 
00676         cursor->next();
00677         TEST(!cursor->after_end());
00678         TEST_EQUAL(cursor->current_key, "foo1");
00679         cursor->read_tag();
00680         TEST_EQUAL(cursor->current_tag, "bar1");
00681 
00682         cursor->next();
00683         TEST(!cursor->after_end());
00684         TEST_EQUAL(cursor->current_key, "foo2");
00685         cursor->read_tag();
00686         TEST_EQUAL(cursor->current_tag, "bar2");
00687 
00688         cursor->next();
00689         TEST(!cursor->after_end());
00690         TEST_EQUAL(cursor->current_key, "foo3");
00691         cursor->read_tag();
00692         TEST_EQUAL(cursor->current_tag, "bar3");
00693 
00694         cursor->next();
00695         TEST(cursor->after_end());
00696 
00697         // Add a new tag
00698         table_rw.add("foo25", "bar25");
00699         old_revision = new_revision;
00700         new_revision += 1;
00701         table_rw.commit(new_revision);
00702         table_ro.open();
00703 
00704         TEST_EQUAL(table_ro.get_entry_count(), 4);
00705         TEST_EQUAL(table_rw.get_entry_count(), 4);
00706 
00707         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00708         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00709         delete cursor;
00710     }
00711     {
00712         // Open old revision
00713         Btree table_rw(tablename, false);
00714         TEST(table_rw.open(old_revision));
00715         Btree table_ro(tablename, true);
00716         table_ro.open(old_revision);
00717 
00718         TEST_EQUAL(table_ro.get_entry_count(), 3);
00719         TEST_EQUAL(table_rw.get_entry_count(), 3);
00720 
00721         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00722         TEST_EQUAL(old_revision, table_ro.get_open_revision_number());
00723 
00724         // Add a new tag
00725         table_rw.add("foo26", "bar26");
00726         new_revision += 1;
00727         table_rw.commit(new_revision);
00728         table_ro.open();
00729 
00730         TEST_EQUAL(table_ro.get_entry_count(), 4);
00731         TEST_EQUAL(table_rw.get_entry_count(), 4);
00732 
00733         // Add another new tag, but don't apply this one.
00734         table_rw.add("foo37", "bar37");
00735         TEST_EQUAL(table_ro.get_entry_count(), 4);
00736         TEST_EQUAL(table_rw.get_entry_count(), 5);
00737 
00738         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00739         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00740     }
00741     {
00742         // Reopen and check that the documents are still there.
00743         Btree table_rw(tablename, false);
00744         table_rw.open();
00745         Btree table_ro(tablename, true);
00746         table_ro.open();
00747 
00748         TEST_EQUAL(table_ro.get_entry_count(), 4);
00749         TEST_EQUAL(table_rw.get_entry_count(), 4);
00750 
00751         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00752         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00753 
00754         Bcursor * cursor = table_rw.cursor_get();
00755         TEST(!cursor->find_entry("foo"));
00756         TEST_EQUAL(cursor->current_key, "");
00757         cursor->read_tag();
00758         TEST_EQUAL(cursor->current_tag, "");
00759 
00760         cursor->next();
00761         TEST(!cursor->after_end());
00762         TEST_EQUAL(cursor->current_key, "foo1");
00763         cursor->read_tag();
00764         TEST_EQUAL(cursor->current_tag, "bar1");
00765 
00766         cursor->next();
00767         TEST(!cursor->after_end());
00768         TEST_EQUAL(cursor->current_key, "foo2");
00769         cursor->read_tag();
00770         TEST_EQUAL(cursor->current_tag, "bar2");
00771 
00772         cursor->next();
00773         TEST(!cursor->after_end());
00774         TEST_EQUAL(cursor->current_key, "foo26");
00775         cursor->read_tag();
00776         TEST_EQUAL(cursor->current_tag, "bar26");
00777 
00778         cursor->next();
00779         TEST(!cursor->after_end());
00780         TEST_EQUAL(cursor->current_key, "foo3");
00781         cursor->read_tag();
00782         TEST_EQUAL(cursor->current_tag, "bar3");
00783 
00784         cursor->next();
00785         TEST(cursor->after_end());
00786         delete cursor;
00787     }
00788     {
00789         // Check that opening a nonexistent revision returns false (and doesn't
00790         // throw an exception).
00791         Btree table_ro(tablename, false);
00792         TEST(!table_ro.open(new_revision + 10));
00793     }
00794 
00795     return true;
00796 }
00797 
00799 static bool test_table6()
00800 {
00801     const string tablename = tmpdir + "/test_table6_";
00802     unlink_table(tablename);
00803     quartz_revision_number_t new_revision;
00804     {
00805         // Open table and add a couple of documents
00806         Btree table_rw(tablename, false);
00807         table_rw.create(8192);
00808         table_rw.open();
00809         Btree table_ro(tablename, true);
00810         table_ro.open();
00811 
00812         TEST_EQUAL(table_ro.get_entry_count(), 0);
00813         TEST_EQUAL(table_rw.get_entry_count(), 0);
00814 
00815         table_rw.add("foo1", "bar1");
00816 
00817         table_rw.add("foo2", "bar2");
00818         table_rw.cancel();
00819 
00820         table_rw.add("foo3", "bar3");
00821 
00822         new_revision = table_ro.get_latest_revision_number() + 1;
00823         table_rw.commit(new_revision);
00824         table_ro.open();
00825 
00826         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00827         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00828     }
00829     {
00830         // Reopen and check that the documents are still there.
00831         Btree table_rw(tablename, false);
00832         table_rw.open();
00833         Btree table_ro(tablename, true);
00834         table_ro.open();
00835 
00836         TEST_EQUAL(table_ro.get_entry_count(), 1);
00837         TEST_EQUAL(table_rw.get_entry_count(), 1);
00838 
00839         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
00840         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
00841 
00842         Bcursor * cursor = table_rw.cursor_get();
00843         TEST(!cursor->find_entry("foo"));
00844         TEST_EQUAL(cursor->current_key, "");
00845         cursor->read_tag();
00846         TEST_EQUAL(cursor->current_tag, "");
00847 
00848         cursor->next();
00849         TEST(!cursor->after_end());
00850         TEST_EQUAL(cursor->current_key, "foo3");
00851         cursor->read_tag();
00852         TEST_EQUAL(cursor->current_tag, "bar3");
00853 
00854         cursor->next();
00855         TEST(cursor->after_end());
00856         delete cursor;
00857     }
00858     return true;
00859 }
00860 
00862 static bool test_cursor1()
00863 {
00864     const string tablename = tmpdir + "/test_cursor1_";
00865     unlink_table(tablename);
00866 
00867     // Open table and put stuff in it.
00868     Btree table_rw(tablename, false);
00869     table_rw.create(8192);
00870     table_rw.open();
00871     Btree table_ro(tablename, true);
00872     table_ro.open();
00873 
00874     table_rw.add("foo1", "bar1");
00875     table_rw.add("foo2", "bar2");
00876     table_rw.add("foo3", "bar3");
00877     quartz_revision_number_t new_revision = table_ro.get_latest_revision_number();
00878     new_revision += 1;
00879     table_rw.commit(new_revision);
00880     table_ro.open();
00881 
00882     Btree * table = &table_ro;
00883     int count = 2;
00884 
00885     while (count != 0) {
00886         Bcursor * cursor = table->cursor_get();
00887         TEST(!cursor->find_entry("foo25"));
00888         TEST_EQUAL(cursor->current_key, "foo2");
00889         cursor->read_tag();
00890         TEST_EQUAL(cursor->current_tag, "bar2");
00891 
00892         cursor->next();
00893         TEST(!cursor->after_end());
00894         TEST_EQUAL(cursor->current_key, "foo3");
00895         cursor->read_tag();
00896         TEST_EQUAL(cursor->current_tag, "bar3");
00897 
00898         cursor->next();
00899         TEST(cursor->after_end());
00900 
00901         TEST(!cursor->find_entry("foo"));
00902         TEST_EQUAL(cursor->current_key, "");
00903         cursor->read_tag();
00904         TEST_EQUAL(cursor->current_tag, "");
00905 
00906         cursor->next();
00907         TEST(!cursor->after_end());
00908         TEST_EQUAL(cursor->current_key, "foo1");
00909         cursor->read_tag();
00910         TEST_EQUAL(cursor->current_tag, "bar1");
00911 
00912         TEST(cursor->find_entry("foo2"));
00913         TEST_EQUAL(cursor->current_key, "foo2");
00914         cursor->read_tag();
00915         TEST_EQUAL(cursor->current_tag, "bar2");
00916 
00917         cursor->next();
00918         TEST(!cursor->after_end());
00919         TEST_EQUAL(cursor->current_key, "foo3");
00920         cursor->read_tag();
00921         TEST_EQUAL(cursor->current_tag, "bar3");
00922 
00923         cursor->next();
00924         TEST(cursor->after_end());
00925 
00926         table = &table_rw;
00927         count -= 1;
00928 
00929         delete cursor;
00930     }
00931 
00932     // Test cursors when we have unapplied changes
00933     table_rw.add("foo25", "bar25");
00934 
00935     table_rw.del("foo26");
00936     table_rw.del("foo1");
00937 
00938     Bcursor * cursor = table_ro.cursor_get();
00939     TEST(!cursor->find_entry("foo25"));
00940     TEST_EQUAL(cursor->current_key, "foo2");
00941     cursor->read_tag();
00942     TEST_EQUAL(cursor->current_tag, "bar2");
00943 
00944     cursor->next();
00945     TEST(!cursor->after_end());
00946     TEST_EQUAL(cursor->current_key, "foo3");
00947     cursor->read_tag();
00948     TEST_EQUAL(cursor->current_tag, "bar3");
00949     delete cursor;
00950 
00951     cursor = table_rw.cursor_get();
00952     TEST(cursor->find_entry("foo25"));
00953     TEST_EQUAL(cursor->current_key, "foo25");
00954     cursor->read_tag();
00955     TEST_EQUAL(cursor->current_tag, "bar25");
00956 
00957     cursor->next();
00958     TEST(!cursor->after_end());
00959     TEST_EQUAL(cursor->current_key, "foo3");
00960     cursor->read_tag();
00961     TEST_EQUAL(cursor->current_tag, "bar3");
00962     delete cursor;
00963 
00964     cursor = table_rw.cursor_get();
00965     TEST(!cursor->find_entry("foo26"));
00966     TEST_EQUAL(cursor->current_key, "foo25");
00967     cursor->read_tag();
00968     TEST_EQUAL(cursor->current_tag, "bar25");
00969 
00970     cursor->next();
00971     TEST(!cursor->after_end());
00972     TEST_EQUAL(cursor->current_key, "foo3");
00973     cursor->read_tag();
00974     TEST_EQUAL(cursor->current_tag, "bar3");
00975 
00976     TEST(cursor->find_entry("foo2"));
00977     TEST_EQUAL(cursor->current_key, "foo2");
00978     cursor->read_tag();
00979     TEST_EQUAL(cursor->current_tag, "bar2");
00980 
00981     cursor->next();
00982     TEST(!cursor->after_end());
00983     TEST_EQUAL(cursor->current_key, "foo25");
00984     cursor->read_tag();
00985     TEST_EQUAL(cursor->current_tag, "bar25");
00986 
00987     cursor->next();
00988     TEST(!cursor->after_end());
00989     TEST_EQUAL(cursor->current_key, "foo3");
00990     cursor->read_tag();
00991     TEST_EQUAL(cursor->current_tag, "bar3");
00992 
00993     cursor->next();
00994     TEST(cursor->after_end());
00995 
00996     TEST(!cursor->find_entry("foo1"));
00997     TEST_EQUAL(cursor->current_key, "");
00998     cursor->read_tag();
00999     TEST_EQUAL(cursor->current_tag, "");
01000 
01001     cursor->next();
01002     TEST(!cursor->after_end());
01003     TEST_EQUAL(cursor->current_key, "foo2");
01004     cursor->read_tag();
01005     TEST_EQUAL(cursor->current_tag, "bar2");
01006 
01007     cursor->next();
01008     TEST(!cursor->after_end());
01009     TEST_EQUAL(cursor->current_key, "foo25");
01010     cursor->read_tag();
01011     TEST_EQUAL(cursor->current_tag, "bar25");
01012 
01013     cursor->next();
01014     TEST(!cursor->after_end());
01015     TEST_EQUAL(cursor->current_key, "foo3");
01016     cursor->read_tag();
01017     TEST_EQUAL(cursor->current_tag, "bar3");
01018     delete cursor;
01019 
01020     new_revision += 1;
01021     table_rw.commit(new_revision);
01022     table_ro.open();
01023 
01024     cursor = table_rw.cursor_get();
01025     TEST(cursor->find_entry("foo2"));
01026     TEST_EQUAL(cursor->current_key, "foo2");
01027     cursor->read_tag();
01028     TEST_EQUAL(cursor->current_tag, "bar2");
01029 
01030     TEST(!cursor->find_entry("foo24"));
01031     TEST_EQUAL(cursor->current_key, "foo2");
01032     cursor->read_tag();
01033     TEST_EQUAL(cursor->current_tag, "bar2");
01034 
01035     TEST(cursor->find_entry("foo25"));
01036     TEST_EQUAL(cursor->current_key, "foo25");
01037     cursor->read_tag();
01038     TEST_EQUAL(cursor->current_tag, "bar25");
01039 
01040     TEST(!cursor->find_entry("foo24"));
01041     TEST_EQUAL(cursor->current_key, "foo2");
01042     cursor->read_tag();
01043     TEST_EQUAL(cursor->current_tag, "bar2");
01044 
01045     table_rw.del("foo25");
01046     delete cursor;
01047 
01048     cursor = table_rw.cursor_get();
01049     TEST(!cursor->find_entry("foo25"));
01050     TEST_EQUAL(cursor->current_key, "foo2");
01051     cursor->read_tag();
01052     TEST_EQUAL(cursor->current_tag, "bar2");
01053 
01054     cursor->next();
01055     TEST(!cursor->after_end());
01056     TEST_EQUAL(cursor->current_key, "foo3");
01057     cursor->read_tag();
01058     TEST_EQUAL(cursor->current_tag, "bar3");
01059 
01060     delete cursor;
01061 
01062     return true;
01063 }
01064 
01066 static bool test_cursor2()
01067 {
01068     const string tablename = tmpdir + "/test_cursor2_";
01069     unlink_table(tablename);
01070 
01071     // Open table and put stuff in it.
01072     Btree table_rw(tablename, false);
01073     table_rw.create(8192);
01074     table_rw.open();
01075     Btree table_ro(tablename, true);
01076     table_ro.open();
01077 
01078     table_rw.add("a", string(2036, '\x00'));
01079     table_rw.add("c", "bar2");
01080     quartz_revision_number_t new_revision = table_ro.get_latest_revision_number();
01081     new_revision += 1;
01082     table_rw.commit(new_revision);
01083     table_ro.open();
01084 
01085     Bcursor * cursor = table_ro.cursor_get();
01086 
01087     TEST(!cursor->find_entry("b"));
01088     TEST_EQUAL(cursor->current_key, "a");
01089     cursor->read_tag();
01090     TEST_EQUAL(cursor->current_tag, string(2036, '\x00'));
01091 
01092     delete cursor;
01093 
01094     return true;
01095 }
01096 
01099 static bool test_cursor3()
01100 {
01101     const string tablename = tmpdir + "/test_cursor3_";
01102     unlink_table(tablename);
01103     quartz_revision_number_t new_revision;
01104     {
01105         // Open table and add a couple of documents
01106         Btree table_rw(tablename, false);
01107         table_rw.create(8192);
01108         table_rw.open();
01109         Btree table_ro(tablename, true);
01110         table_ro.open();
01111 
01112         TEST_EQUAL(table_ro.get_entry_count(), 0);
01113         table_rw.add("A", "A");
01114         table_rw.add("B", "B");
01115 
01116         {
01117             Bcursor * cursor = table_rw.cursor_get();
01118             TEST(!cursor->find_entry("AA"));
01119             TEST_EQUAL(cursor->current_key, "A");
01120             cursor->read_tag();
01121             TEST_EQUAL(cursor->current_tag, "A");
01122 
01123             cursor->next();
01124             TEST(!cursor->after_end());
01125             TEST_EQUAL(cursor->current_key, "B");
01126             cursor->read_tag();
01127             TEST_EQUAL(cursor->current_tag, "B");
01128 
01129             delete cursor;
01130         }
01131 
01132         new_revision = table_ro.get_latest_revision_number() + 1;
01133         table_rw.commit(new_revision);
01134         table_ro.open();
01135 
01136         {
01137             Bcursor * cursor = table_rw.cursor_get();
01138             TEST(!cursor->find_entry("AA"));
01139             TEST_EQUAL(cursor->current_key, "A");
01140             cursor->read_tag();
01141             TEST_EQUAL(cursor->current_tag, "A");
01142 
01143             cursor->next();
01144             TEST(!cursor->after_end());
01145             TEST_EQUAL(cursor->current_key, "B");
01146             cursor->read_tag();
01147             TEST_EQUAL(cursor->current_tag, "B");
01148 
01149             delete cursor;
01150         }
01151 
01152         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
01153         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
01154     }
01155     {
01156         // Reopen and check that the documents are still there.
01157         Btree table_ro(tablename, false);
01158         table_ro.open();
01159         TEST_EQUAL(table_ro.get_entry_count(), 2);
01160 
01161         TEST_EQUAL(new_revision, table_ro.get_latest_revision_number());
01162         TEST_EQUAL(new_revision, table_ro.get_open_revision_number());
01163 
01164         {
01165             Bcursor * cursor = table_ro.cursor_get();
01166             TEST(!cursor->find_entry("AA"));
01167             TEST_EQUAL(cursor->current_key, "A");
01168             cursor->read_tag();
01169             TEST_EQUAL(cursor->current_tag, "A");
01170 
01171             cursor->next();
01172             TEST(!cursor->after_end());
01173             TEST_EQUAL(cursor->current_key, "B");
01174             cursor->read_tag();
01175             TEST_EQUAL(cursor->current_tag, "B");
01176 
01177             delete cursor;
01178         }
01179     }
01180     return true;
01181 }
01182 
01184 static bool test_bitmap1()
01185 {
01186     const string tablename = tmpdir + "/test_bitmap1_";
01187     unlink_table(tablename);
01188     /* Use a small block size to make it easier to get a large bitmap */
01189     Btree table_rw(tablename, false);
01190     table_rw.create(2048);
01191     table_rw.open();
01192     Btree table_ro(tablename, true);
01193     table_ro.open();
01194 
01195     quartz_revision_number_t new_revision;
01196 
01197     for (int j = 0; j < 100; ++j) {
01198         for (int i = 1; i <= 1000; ++i) {
01199             string str_i = om_tostring(i);
01200             table_rw.add("foo" + om_tostring(j) + "_" + str_i, "bar" + str_i);
01201         }
01202         new_revision = table_ro.get_latest_revision_number() + 1;
01203         table_rw.commit(new_revision);
01204         table_ro.open();
01205     }
01206     return true;
01207 }
01208 
01210 static bool test_overwrite1()
01211 {
01212     const string tablename = tmpdir + "/test_overwrite1_";
01213     unlink_table(tablename);
01214     Btree bufftable(tablename, false);
01215     bufftable.create(2048);
01216     bufftable.open();
01217     Btree disktable(tablename, true);
01218     disktable.open();
01219 
01220     for (int i = 1; i <= 1000; ++i) {
01221         bufftable.add("foo" + om_tostring(i), "bar" + om_tostring(i));
01222     }
01223 
01224     bufftable.commit(disktable.get_latest_revision_number() + 1);
01225     disktable.open();
01226 
01227     Btree disktable_ro(tablename, true);
01228     disktable_ro.open();
01229     string tag;
01230     TEST(disktable_ro.get_exact_entry("foo1", tag));
01231     TEST_EQUAL(tag, "bar1");
01232 
01233     bufftable.add("foo1", "bar2");
01234     bufftable.commit(disktable.get_latest_revision_number() + 1);
01235     disktable.open();
01236     TEST(disktable_ro.get_exact_entry("foo999", tag));
01237     TEST(disktable_ro.get_exact_entry("foo1", tag));
01238     TEST_EQUAL(tag, "bar1");
01239 
01240     bufftable.add("foo1", "bar3");
01241     bufftable.commit(disktable.get_latest_revision_number() + 1);
01242     disktable.open();
01243     TEST(disktable_ro.get_exact_entry("foo999", tag));
01244     TEST_EXCEPTION(Xapian::DatabaseModifiedError,
01245                    disktable_ro.get_exact_entry("foo1", tag));
01246     //TEST_EQUAL(tag, "bar1");
01247 
01248     return true;
01249 }
01250 
01251 // ================================
01252 // ========= END OF TESTS =========
01253 // ================================
01254 //
01255 // The lists of tests to perform
01256 test_desc tests[] = {
01257     {"simple1",         test_simple1},
01258     {"insertdelete1",   test_insertdelete1},
01259     {"sequent1",        test_sequent1},
01260     {"emptykey1",       test_emptykey1},
01261     {"table1",          test_table1},
01262     {"table2",          test_table2},
01263     {"table3",          test_table3},
01264     {"table4",          test_table4},
01265     {"table5",          test_table5},
01266     {"table6",          test_table6},
01267     {"cursor1",         test_cursor1},
01268     {"cursor2",         test_cursor2},
01269     {"cursor3",         test_cursor3},
01270     {"bitmap1",         test_bitmap1},
01271     {"overwrite1",      test_overwrite1},
01272     {0, 0}
01273 };
01274 
01275 int main(int argc, char **argv)
01276 {
01277     const char * e_tmpdir = getenv("BTREETMP");
01278     if (e_tmpdir) {
01279         tmpdir = e_tmpdir;
01280     } else {
01281         tmpdir = ".btreetmp";
01282     }
01283     rm_rf(tmpdir);
01284     make_dir(tmpdir);
01285     test_driver::parse_command_line(argc, argv);
01286     datadir = test_driver::get_srcdir() + "/testdata/btreetest_";
01287     return test_driver::run(tests);
01288 }

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