LCOV - code coverage report
Current view: top level - tests - api_backend.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 956 1026 93.2 %
Date: 2019-06-30 05:20:33 Functions: 68 69 98.6 %
Branches: 1394 6535 21.3 %

           Branch data     Line data    Source code
       1                 :            : /** @file api_backend.cc
       2                 :            :  * @brief Backend-related tests.
       3                 :            :  */
       4                 :            : /* Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019 Olly Betts
       5                 :            :  * Copyright (C) 2010 Richard Boulton
       6                 :            :  *
       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.
      11                 :            :  *
      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.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU General Public License
      18                 :            :  * along with this program; if not, write to the Free Software
      19                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
      20                 :            :  * USA
      21                 :            :  */
      22                 :            : 
      23                 :            : #include <config.h>
      24                 :            : 
      25                 :            : #include "api_backend.h"
      26                 :            : 
      27                 :            : #include <xapian.h>
      28                 :            : 
      29                 :            : #include "backendmanager.h"
      30                 :            : #include "errno_to_string.h"
      31                 :            : #include "filetests.h"
      32                 :            : #include "str.h"
      33                 :            : #include "testrunner.h"
      34                 :            : #include "testsuite.h"
      35                 :            : #include "testutils.h"
      36                 :            : #include "unixcmds.h"
      37                 :            : 
      38                 :            : #include "apitest.h"
      39                 :            : 
      40                 :            : #include "safefcntl.h"
      41                 :            : #include "safesysstat.h"
      42                 :            : #include "safeunistd.h"
      43                 :            : #ifdef HAVE_SOCKETPAIR
      44                 :            : # include "safesyssocket.h"
      45                 :            : # include <signal.h>
      46                 :            : # include "safesyswait.h"
      47                 :            : #endif
      48                 :            : 
      49                 :            : #include <cerrno>
      50                 :            : #include <fstream>
      51                 :            : #include <iterator>
      52                 :            : 
      53                 :            : using namespace std;
      54                 :            : 
      55                 :            : /// Regression test - lockfile should honour umask, was only user-readable.
      56                 :          1 : DEFINE_TESTCASE(lockfileumask1, glass) {
      57                 :            : #if !defined __WIN32__ && !defined __CYGWIN__ && !defined __OS2__
      58                 :          1 :     mode_t old_umask = umask(022);
      59                 :            :     try {
      60 [ +  - ][ +  - ]:          1 :         Xapian::WritableDatabase db = get_named_writable_database("lockfileumask1");
                 [ +  - ]
      61                 :            : 
      62 [ +  - ][ +  - ]:          2 :         string path = get_named_writable_database_path("lockfileumask1");
      63         [ +  - ]:          1 :         path += "/flintlock";
      64                 :            : 
      65                 :            :         struct stat statbuf;
      66 [ -  + ][ #  # ]:          1 :         TEST(stat(path.c_str(), &statbuf) == 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
      67 [ -  + ][ #  # ]:          2 :         TEST_EQUAL(statbuf.st_mode & 0777, 0644);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      68                 :          0 :     } catch (...) {
      69                 :          0 :         umask(old_umask);
      70                 :          0 :         throw;
      71                 :            :     }
      72                 :            : 
      73                 :          1 :     umask(old_umask);
      74                 :            : #endif
      75                 :            : 
      76                 :          1 :     return true;
      77                 :            : }
      78                 :            : 
      79                 :            : /// Check that the backend handles total document length > 0xffffffff.
      80                 :          5 : DEFINE_TESTCASE(totaldoclen1, writable) {
      81 [ +  - ][ +  - ]:          5 :     Xapian::WritableDatabase db = get_writable_database();
      82         [ +  - ]:         10 :     Xapian::Document doc;
      83 [ +  - ][ +  - ]:          5 :     doc.add_posting("foo", 1, 2000000000);
      84         [ +  - ]:          5 :     db.add_document(doc);
      85         [ +  - ]:         10 :     Xapian::Document doc2;
      86 [ +  - ][ +  - ]:          5 :     doc2.add_posting("bar", 1, 2000000000);
      87         [ +  - ]:          5 :     db.add_document(doc2);
      88 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_avlength(), 2000000000);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      89 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_total_length(), 4000000000ull);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      90         [ +  - ]:          5 :     db.commit();
      91 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_avlength(), 2000000000);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      92 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_total_length(), 4000000000ull);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      93         [ +  + ]:        105 :     for (int i = 0; i != 20; ++i) {
      94         [ +  - ]:        100 :         Xapian::Document doc3;
      95 [ +  - ][ +  - ]:        100 :         doc3.add_posting("count" + str(i), 1, 2000000000);
                 [ +  - ]
      96         [ +  - ]:        100 :         db.add_document(doc3);
      97                 :        100 :     }
      98 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_avlength(), 2000000000);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      99 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_total_length(), 44000000000ull);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     100         [ +  - ]:          5 :     db.commit();
     101 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_avlength(), 2000000000);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     102 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(db.get_total_length(), 44000000000ull);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     103 [ +  - ][ +  - ]:          5 :     if (get_dbtype() != "inmemory") {
                 [ +  + ]
     104                 :            :         // InMemory doesn't support get_writable_database_as_database().
     105         [ +  - ]:          4 :         Xapian::Database dbr = get_writable_database_as_database();
     106 [ +  - ][ -  + ]:          4 :         TEST_EQUAL(dbr.get_avlength(), 2000000000);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     107 [ +  - ][ -  + ]:          4 :         TEST_EQUAL(dbr.get_total_length(), 44000000000ull);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     108                 :            :     }
     109                 :          5 :     return true;
     110                 :            : }
     111                 :            : 
     112                 :            : // Check that exceeding 32bit in combined database doesn't cause a problem
     113                 :            : // when using 64bit docids.
     114                 :         10 : DEFINE_TESTCASE(exceed32bitcombineddb1, writable) {
     115                 :            :     // Test case is for 64-bit Xapian::docid.
     116                 :            :     // FIXME: Though we should check that the overflow is handled gracefully
     117                 :            :     // for 32-bit...
     118                 :          5 :     if (sizeof(Xapian::docid) == 4) return true;
     119                 :            : 
     120                 :            :     // The InMemory backend uses a vector for the documents, so trying to add
     121                 :            :     // a document with the maximum docid is likely to fail because we can't
     122                 :            :     // allocate enough memory!
     123                 :            :     SKIP_TEST_FOR_BACKEND("inmemory");
     124                 :            : 
     125                 :            :     Xapian::WritableDatabase db1 = get_writable_database();
     126                 :            :     Xapian::WritableDatabase db2 = get_writable_database();
     127                 :            :     Xapian::Document doc;
     128                 :            :     doc.set_data("prose");
     129                 :            :     doc.add_term("word");
     130                 :            : 
     131                 :            :     Xapian::docid max_id = 0xffffffff;
     132                 :            : 
     133                 :            :     db1.replace_document(max_id, doc);
     134                 :            :     db2.replace_document(max_id, doc);
     135                 :            : 
     136                 :            :     Xapian::Database db;
     137                 :            :     db.add_database(db1);
     138                 :            :     db.add_database(db2);
     139                 :            : 
     140                 :            :     Xapian::Enquire enquire(db);
     141                 :            :     enquire.set_query(Xapian::Query::MatchAll);
     142                 :            :     Xapian::MSet mymset = enquire.get_mset(0, 10);
     143                 :            : 
     144                 :            :     TEST_EQUAL(2, mymset.size());
     145                 :            : 
     146                 :            :     for (Xapian::MSetIterator i = mymset.begin(); i != mymset.end(); ++i) {
     147                 :            :         TEST_EQUAL("prose", i.get_document().get_data());
     148                 :            :     }
     149                 :            : 
     150                 :            :     return true;
     151                 :            : }
     152                 :            : 
     153                 :          7 : DEFINE_TESTCASE(dbstats1, backend) {
     154 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("etext");
     155                 :            : 
     156                 :            :     // Use precalculated values to avoid expending CPU cycles to calculate
     157                 :            :     // these every time without improving test coverage.
     158                 :          7 :     const Xapian::termcount min_len = 2;
     159                 :          7 :     const Xapian::termcount max_len = 532;
     160                 :          7 :     const Xapian::termcount max_wdf = 22;
     161                 :            : 
     162 [ +  - ][ +  - ]:          7 :     if (get_dbtype() != "inmemory") {
                 [ +  + ]
     163                 :            :         // Should be exact as no deletions have happened.
     164 [ +  - ][ -  + ]:          6 :         TEST_EQUAL(db.get_doclength_upper_bound(), max_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     165 [ +  - ][ -  + ]:          6 :         TEST_EQUAL(db.get_doclength_lower_bound(), min_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     166                 :            :     } else {
     167                 :            :         // For inmemory, we usually give rather loose bounds.
     168 [ +  - ][ -  + ]:          1 :         TEST_REL(db.get_doclength_upper_bound(),>=,max_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     169 [ +  - ][ -  + ]:          1 :         TEST_REL(db.get_doclength_lower_bound(),<=,min_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     170                 :            :     }
     171                 :            : 
     172 [ +  - ][ +  - ]:          7 :     if (get_dbtype() != "inmemory" && !startswith(get_dbtype(), "remote")) {
         [ +  + ][ +  - ]
         [ +  + ][ +  + ]
         [ +  - ][ +  +  
             #  #  #  # ]
     173 [ +  - ][ +  - ]:          4 :         TEST_EQUAL(db.get_wdf_upper_bound("the"), max_wdf);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     174                 :            :     } else {
     175                 :            :         // For inmemory and remote backends, we usually give rather loose
     176                 :            :         // bounds (remote matches use tighter bounds, but querying the
     177                 :            :         // wdf bound gives a looser one).
     178 [ +  - ][ +  - ]:          3 :         TEST_REL(db.get_wdf_upper_bound("the"),>=,max_wdf);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     179                 :            :     }
     180                 :            : 
     181                 :            :     // This failed with an assertion during development between 1.3.1 and
     182                 :            :     // 1.3.2.
     183 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.get_wdf_upper_bound(""), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     184                 :            : 
     185 [ +  - ][ +  - ]:          7 :     if (get_dbtype() == "honey") {
                 [ +  + ]
     186                 :          1 :         const Xapian::termcount min_unique_len = 2;
     187                 :          1 :         const Xapian::termcount max_unique_len = 272;
     188 [ +  - ][ -  + ]:          1 :         TEST_EQUAL(db.get_unique_terms_lower_bound(), min_unique_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     189 [ +  - ][ -  + ]:          1 :         TEST_EQUAL(db.get_unique_terms_upper_bound(), max_unique_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     190                 :            :     }
     191                 :            : 
     192                 :          7 :     return true;
     193                 :            : }
     194                 :            : 
     195                 :            : // Check stats with a single document.  In a multi-database situation, this
     196                 :            : // gave 0 for get-_doclength_lower_bound() in 1.3.2.
     197                 :          7 : DEFINE_TESTCASE(dbstats2, backend) {
     198 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_onedoc");
     199                 :            : 
     200                 :            :     // Use precalculated values to avoid expending CPU cycles to calculate
     201                 :            :     // these every time without improving test coverage.
     202                 :          7 :     const Xapian::termcount min_len = 15;
     203                 :          7 :     const Xapian::termcount max_len = 15;
     204                 :          7 :     const Xapian::termcount max_wdf = 7;
     205                 :            : 
     206 [ +  - ][ +  - ]:          7 :     if (get_dbtype() != "inmemory") {
                 [ +  + ]
     207                 :            :         // Should be exact as no deletions have happened.
     208 [ +  - ][ -  + ]:          6 :         TEST_EQUAL(db.get_doclength_upper_bound(), max_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     209 [ +  - ][ -  + ]:          6 :         TEST_EQUAL(db.get_doclength_lower_bound(), min_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     210                 :            :     } else {
     211                 :            :         // For inmemory, we usually give rather loose bounds.
     212 [ +  - ][ -  + ]:          1 :         TEST_REL(db.get_doclength_upper_bound(),>=,max_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     213 [ +  - ][ -  + ]:          1 :         TEST_REL(db.get_doclength_lower_bound(),<=,min_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     214                 :            :     }
     215                 :            : 
     216 [ +  - ][ +  - ]:          7 :     if (get_dbtype() != "inmemory" && !startswith(get_dbtype(), "remote")) {
         [ +  + ][ +  - ]
         [ +  + ][ +  + ]
         [ +  - ][ +  +  
             #  #  #  # ]
     217 [ +  - ][ +  - ]:          4 :         TEST_EQUAL(db.get_wdf_upper_bound("word"), max_wdf);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     218                 :            :     } else {
     219                 :            :         // For inmemory and remote backends, we usually give rather loose
     220                 :            :         // bounds (remote matches use tighter bounds, but querying the
     221                 :            :         // wdf bound gives a looser one).
     222 [ +  - ][ +  - ]:          3 :         TEST_REL(db.get_wdf_upper_bound("word"),>=,max_wdf);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     223                 :            :     }
     224                 :            : 
     225 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.get_wdf_upper_bound(""), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     226                 :            : 
     227 [ +  - ][ +  - ]:          7 :     if (get_dbtype() == "honey") {
                 [ +  + ]
     228                 :          1 :         const Xapian::termcount min_unique_len = 9;
     229                 :          1 :         const Xapian::termcount max_unique_len = 9;
     230 [ +  - ][ -  + ]:          1 :         TEST_EQUAL(db.get_unique_terms_lower_bound(), min_unique_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     231 [ +  - ][ -  + ]:          1 :         TEST_EQUAL(db.get_unique_terms_upper_bound(), max_unique_len);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     232                 :            :     }
     233                 :            : 
     234                 :          7 :     return true;
     235                 :            : }
     236                 :            : 
     237                 :            : /// Check handling of alldocs on an empty database.
     238                 :          7 : DEFINE_TESTCASE(alldocspl3, backend) {
     239 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database(string());
     240                 :            : 
     241 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.get_termfreq(string()), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     242 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.get_collection_freq(string()), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     243 [ +  - ][ +  - ]:          7 :     TEST(db.postlist_begin(string()) == db.postlist_end(string()));
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     244                 :            : 
     245                 :          7 :     return true;
     246                 :            : }
     247                 :            : 
     248                 :            : /// Regression test for bug#392 in ModifiedPostList iteration, fixed in 1.0.15.
     249                 :          5 : DEFINE_TESTCASE(modifiedpostlist1, writable) {
     250 [ +  - ][ +  - ]:          5 :     Xapian::WritableDatabase db = get_writable_database();
     251 [ +  - ][ +  - ]:         10 :     Xapian::Document a, b;
     252         [ +  - ]:         10 :     Xapian::Enquire enq(db);
     253                 :            : 
     254 [ +  - ][ +  - ]:          5 :     a.add_term("T");
     255 [ +  - ][ +  - ]:          5 :     enq.set_query(Xapian::Query("T"));
                 [ +  - ]
     256                 :            : 
     257         [ +  - ]:          5 :     db.replace_document(2, a);
     258         [ +  - ]:          5 :     db.commit();
     259         [ +  - ]:          5 :     db.replace_document(1, a);
     260         [ +  - ]:          5 :     db.replace_document(1, b);
     261                 :            : 
     262 [ +  - ][ +  - ]:          5 :     mset_expect_order(enq.get_mset(0, 2), 2);
     263                 :            : 
     264                 :          5 :     return true;
     265                 :            : }
     266                 :            : 
     267                 :            : /// Regression test for chert bug fixed in 1.1.3 (ticket#397).
     268                 :          5 : DEFINE_TESTCASE(doclenaftercommit1, writable) {
     269 [ +  - ][ +  - ]:          5 :     Xapian::WritableDatabase db = get_writable_database();
     270 [ +  - ][ -  + ]:          5 :     TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_doclength(1));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
     271 [ +  - ][ -  +  :          5 :     TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_unique_terms(1));
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ -  + ]
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ +  - ]
     272 [ +  - ][ +  - ]:          5 :     db.replace_document(1, Xapian::Document());
     273         [ +  - ]:          5 :     db.commit();
     274 [ +  - ][ -  +  :          5 :     TEST_EQUAL(db.get_doclength(1), 0);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     275 [ +  - ][ -  +  :          5 :     TEST_EQUAL(db.get_unique_terms(1), 0);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     276                 :          5 :     return true;
     277                 :            : }
     278                 :            : 
     279                 :          5 : DEFINE_TESTCASE(valuesaftercommit1, writable) {
     280 [ +  - ][ +  - ]:          5 :     Xapian::WritableDatabase db = get_writable_database();
     281         [ +  - ]:         10 :     Xapian::Document doc;
     282 [ +  - ][ +  - ]:          5 :     doc.add_value(0, "value");
     283         [ +  - ]:          5 :     db.replace_document(2, doc);
     284         [ +  - ]:          5 :     db.commit();
     285         [ +  - ]:          5 :     db.replace_document(1, doc);
     286         [ +  - ]:          5 :     db.replace_document(3, doc);
     287 [ +  - ][ +  - ]:          5 :     TEST_EQUAL(db.get_document(3).get_value(0), "value");
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     288         [ +  - ]:          5 :     db.commit();
     289 [ +  - ][ +  - ]:          5 :     TEST_EQUAL(db.get_document(3).get_value(0), "value");
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     290                 :          5 :     return true;
     291                 :            : }
     292                 :            : 
     293                 :          1 : DEFINE_TESTCASE(lockfilefd0or1, glass) {
     294                 :            : #if !defined __WIN32__ && !defined __CYGWIN__ && !defined __OS2__
     295                 :          1 :     int old_stdin = dup(0);
     296                 :          1 :     int old_stdout = dup(1);
     297                 :            :     try {
     298                 :            :         // With fd 0 available.
     299         [ +  - ]:          1 :         close(0);
     300                 :            :         {
     301 [ +  - ][ +  - ]:          1 :             Xapian::WritableDatabase db = get_writable_database();
     302 [ +  - ][ -  + ]:          1 :             TEST_EXCEPTION(Xapian::DatabaseLockError,
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
     303                 :          1 :                            (void)get_writable_database_again());
     304                 :            :         }
     305                 :            :         // With fd 0 and fd 1 available.
     306         [ +  - ]:          1 :         close(1);
     307                 :            :         {
     308 [ +  - ][ +  - ]:          1 :             Xapian::WritableDatabase db = get_writable_database();
     309 [ +  - ][ -  +  :          1 :             TEST_EXCEPTION(Xapian::DatabaseLockError,
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ -  + ]
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ +  - ]
     310                 :          1 :                            (void)get_writable_database_again());
     311                 :            :         }
     312                 :            :         // With fd 1 available.
     313                 :          1 :         dup2(old_stdin, 0);
     314                 :            :         {
     315 [ +  - ][ +  - ]:          1 :             Xapian::WritableDatabase db = get_writable_database();
     316 [ +  - ][ -  +  :          1 :             TEST_EXCEPTION(Xapian::DatabaseLockError,
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ -  + ]
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ +  - ]
     317                 :          1 :                            (void)get_writable_database_again());
     318                 :            :         }
     319                 :          0 :     } catch (...) {
     320                 :          0 :         dup2(old_stdin, 0);
     321                 :          0 :         dup2(old_stdout, 1);
     322         [ #  # ]:          0 :         close(old_stdin);
     323         [ #  # ]:          0 :         close(old_stdout);
     324                 :          0 :         throw;
     325                 :            :     }
     326                 :            : 
     327                 :          1 :     dup2(old_stdout, 1);
     328                 :          1 :     close(old_stdin);
     329                 :          1 :     close(old_stdout);
     330                 :            : #endif
     331                 :            : 
     332                 :          1 :     return true;
     333                 :            : }
     334                 :            : 
     335                 :            : /// Regression test for bug fixed in 1.2.13 and 1.3.1.
     336                 :          1 : DEFINE_TESTCASE(lockfilealreadyopen1, glass) {
     337                 :            :     // Ensure database has been created.
     338 [ +  - ][ +  - ]:          1 :     (void)get_named_writable_database("lockfilealreadyopen1");
                 [ +  - ]
     339 [ +  - ][ +  - ]:          1 :     string path = get_named_writable_database_path("lockfilealreadyopen1");
     340 [ +  - ][ +  - ]:          1 :     int fd = ::open((path + "/flintlock").c_str(), O_RDONLY);
     341 [ -  + ][ #  # ]:          1 :     TEST(fd != -1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     342                 :            :     try {
     343         [ +  - ]:          1 :         Xapian::WritableDatabase db(path, Xapian::DB_CREATE_OR_OPEN);
     344 [ +  - ][ -  + ]:          1 :         TEST_EXCEPTION(Xapian::DatabaseLockError,
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
     345                 :            :             Xapian::WritableDatabase db2(path, Xapian::DB_CREATE_OR_OPEN)
     346                 :          1 :         );
     347                 :          0 :     } catch (...) {
     348         [ #  # ]:          0 :         close(fd);
     349                 :          0 :         throw;
     350                 :            :     }
     351         [ +  - ]:          1 :     close(fd);
     352                 :            : 
     353                 :          1 :     return true;
     354                 :            : }
     355                 :            : 
     356                 :            : /// Feature tests for Database::locked().
     357                 :          1 : DEFINE_TESTCASE(testlock1, glass) {
     358         [ +  - ]:          1 :     Xapian::Database rdb;
     359 [ +  - ][ -  + ]:          1 :     TEST(!rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     360                 :            :     {
     361 [ +  - ][ +  - ]:          1 :         Xapian::WritableDatabase db = get_named_writable_database("testlock1");
                 [ +  - ]
     362 [ -  + ][ #  # ]:          1 :         TEST(db.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     363         [ +  - ]:          2 :         Xapian::Database db_as_database = db;
     364 [ +  - ][ -  + ]:          1 :         TEST(db_as_database.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     365 [ +  - ][ -  + ]:          1 :         TEST(!rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     366 [ +  - ][ +  - ]:          1 :         rdb = get_writable_database_as_database();
     367 [ -  + ][ #  # ]:          1 :         TEST(db.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     368 [ +  - ][ -  + ]:          1 :         TEST(db_as_database.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     369                 :            :         try {
     370 [ +  - ][ -  + ]:          1 :             TEST(rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     371         [ #  # ]:          0 :         } catch (const Xapian::FeatureUnavailableError&) {
     372   [ #  #  #  #  :          0 :             SKIP_TEST("Database::locked() not supported on this platform");
          #  #  #  #  #  
                      # ]
     373                 :            :         }
     374         [ +  - ]:          1 :         db_as_database = rdb;
     375 [ -  + ][ #  # ]:          1 :         TEST(db.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     376 [ +  - ][ -  + ]:          1 :         TEST(db_as_database.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     377 [ +  - ][ -  + ]:          1 :         TEST(rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     378         [ +  - ]:          1 :         db_as_database.close();
     379 [ -  + ][ #  # ]:          1 :         TEST(db.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     380 [ +  - ][ -  + ]:          1 :         TEST(rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     381                 :            :         // After close(), locked() should either work as if close() hadn't been
     382                 :            :         // called or throw Xapian::DatabaseClosedError.
     383                 :            :         try {
     384 [ +  - ][ -  + ]:          1 :             TEST(db_as_database.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     385   [ #  #  #  # ]:          0 :         } catch (const Xapian::DatabaseClosedError&) {
     386                 :            :         }
     387         [ +  - ]:          1 :         db.close();
     388 [ +  - ][ -  + ]:          1 :         TEST(!rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     389                 :            :         try {
     390 [ +  - ][ -  + ]:          1 :             TEST(!db_as_database.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     391         [ #  # ]:          0 :         } catch (const Xapian::DatabaseClosedError&) {
     392                 :          1 :         }
     393                 :            :     }
     394 [ +  - ][ -  + ]:          1 :     TEST(!rdb.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     395                 :          1 :     return true;
     396                 :            : }
     397                 :            : 
     398                 :            : /// Test that locked() returns false for backends which don't support update.
     399                 :            : /// Regression test for bug fixed in 1.4.6.
     400                 :          2 : DEFINE_TESTCASE(testlock2, backend && !writable && !multi) {
     401 [ +  - ][ +  - ]:          2 :     Xapian::Database db = get_database("apitest_simpledata");
     402 [ +  - ][ -  + ]:          2 :     TEST(!db.locked());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     403                 :          2 :     return true;
     404                 :            : }
     405                 :            : 
     406         [ -  + ]:          4 : class CheckMatchDecider : public Xapian::MatchDecider {
     407                 :            :     mutable bool called;
     408                 :            : 
     409                 :            :   public:
     410                 :          4 :     CheckMatchDecider() : called(false) { }
     411                 :            : 
     412                 :          0 :     bool operator()(const Xapian::Document &) const {
     413                 :          0 :         called = true;
     414                 :          0 :         return true;
     415                 :            :     }
     416                 :            : 
     417                 :          4 :     bool was_called() const { return called; }
     418                 :            : };
     419                 :            : 
     420                 :            : /// Test Xapian::MatchDecider with remote backend fails.
     421                 :          2 : DEFINE_TESTCASE(matchdecider4, remote) {
     422 [ +  - ][ +  - ]:          2 :     Xapian::Database db(get_database("apitest_simpledata"));
     423         [ +  - ]:          4 :     Xapian::Enquire enquire(db);
     424 [ +  - ][ +  - ]:          2 :     enquire.set_query(Xapian::Query("paragraph"));
                 [ +  - ]
     425                 :            : 
     426                 :          4 :     CheckMatchDecider mdecider;
     427         [ +  - ]:          4 :     Xapian::MSet mset;
     428                 :            : 
     429 [ +  - ][ -  + ]:          2 :     TEST_EXCEPTION(Xapian::UnimplementedError,
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ -  + ]
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
           #  # ][ +  - ]
     430                 :            :         mset = enquire.get_mset(0, 10, NULL, &mdecider));
     431   [ -  +  #  #  :          2 :     TEST(!mdecider.was_called());
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     432                 :            : 
     433                 :          2 :     return true;
     434                 :            : }
     435                 :            : 
     436                 :            : /** Check that replacing an unmodified document doesn't increase the automatic
     437                 :            :  *  commit counter.  Regression test for bug fixed in 1.1.4/1.0.18.
     438                 :            :  */
     439                 :          2 : DEFINE_TESTCASE(replacedoc7, writable && !inmemory && !remote) {
     440                 :            :     // The inmemory backend doesn't batch changes, so there's nothing to
     441                 :            :     // check there.
     442                 :            :     //
     443                 :            :     // The remote backend doesn't implement the lazy replacement of documents
     444                 :            :     // optimisation currently.
     445 [ +  - ][ +  - ]:          2 :     Xapian::WritableDatabase db(get_writable_database());
     446         [ +  - ]:          4 :     Xapian::Document doc;
     447 [ +  - ][ +  - ]:          2 :     doc.set_data("fish");
     448 [ +  - ][ +  - ]:          2 :     doc.add_term("Hlocalhost");
     449 [ +  - ][ +  - ]:          2 :     doc.add_posting("hello", 1);
     450 [ +  - ][ +  - ]:          2 :     doc.add_posting("world", 2);
     451 [ +  - ][ +  - ]:          2 :     doc.add_value(1, "myvalue");
     452         [ +  - ]:          2 :     db.add_document(doc);
     453         [ +  - ]:          2 :     db.commit();
     454                 :            : 
     455                 :            :     // We add a second document, and then replace the first document with
     456                 :            :     // itself 10000 times.  If the document count for the database reopened
     457                 :            :     // read-only is 2, then we triggered an automatic commit.
     458                 :            : 
     459 [ +  - ][ +  - ]:          2 :     doc.add_term("XREV2");
     460         [ +  - ]:          2 :     db.add_document(doc);
     461                 :            : 
     462         [ +  + ]:      20002 :     for (int i = 0; i < 10000; ++i) {
     463 [ +  - ][ +  - ]:      20000 :         doc = db.get_document(1);
     464         [ +  - ]:      20000 :         db.replace_document(1, doc);
     465                 :            :     }
     466                 :            : 
     467         [ +  - ]:          4 :     Xapian::Database rodb(get_writable_database_as_database());
     468 [ +  - ][ -  + ]:          2 :     TEST_EQUAL(rodb.get_doccount(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     469                 :            : 
     470         [ +  - ]:          2 :     db.commit();
     471 [ +  - ][ -  + ]:          2 :     TEST(rodb.reopen());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     472                 :            : 
     473 [ +  - ][ -  + ]:          2 :     TEST_EQUAL(rodb.get_doccount(), 2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     474                 :          2 :     return true;
     475                 :            : }
     476                 :            : 
     477                 :            : /** Check that replacing a document deleted since the last commit works.
     478                 :            :  *  Prior to 1.1.4/1.0.18, this failed to update the collection frequency and
     479                 :            :  *  wdf, and caused an assertion failure when assertions were enabled.
     480                 :            :  */
     481                 :          5 : DEFINE_TESTCASE(replacedoc8, writable) {
     482 [ +  - ][ +  - ]:          5 :     Xapian::WritableDatabase db(get_writable_database());
     483                 :            :     {
     484         [ +  - ]:          5 :         Xapian::Document doc;
     485 [ +  - ][ +  - ]:          5 :         doc.set_data("fish");
     486 [ +  - ][ +  - ]:          5 :         doc.add_term("takeaway");
     487         [ +  - ]:          5 :         db.add_document(doc);
     488                 :            :     }
     489         [ +  - ]:          5 :     db.delete_document(1);
     490                 :            :     {
     491         [ +  - ]:          5 :         Xapian::Document doc;
     492 [ +  - ][ +  - ]:          5 :         doc.set_data("chips");
     493 [ +  - ][ +  - ]:          5 :         doc.add_term("takeaway", 2);
     494         [ +  - ]:          5 :         db.replace_document(1, doc);
     495                 :            :     }
     496         [ +  - ]:          5 :     db.commit();
     497 [ +  - ][ +  - ]:          5 :     TEST_EQUAL(db.get_collection_freq("takeaway"), 2);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     498 [ +  - ][ +  - ]:         10 :     Xapian::PostingIterator p = db.postlist_begin("takeaway");
     499 [ +  - ][ -  + ]:          5 :     TEST(p != db.postlist_end("takeaway"));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     500 [ +  - ][ -  + ]:          5 :     TEST_EQUAL(p.get_wdf(), 2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     501                 :          5 :     return true;
     502                 :            : }
     503                 :            : 
     504                 :            : /// Test coverage for DatabaseModifiedError.
     505                 :          1 : DEFINE_TESTCASE(databasemodified1, writable && !inmemory && !remote && !multi) {
     506                 :            :     // The inmemory backend doesn't support revisions.
     507                 :            :     //
     508                 :            :     // The remote backend doesn't work as expected here, I think due to
     509                 :            :     // test harness issues.
     510                 :            :     //
     511                 :            :     // With multi, DatabaseModifiedError doesn't trigger as easily.
     512 [ +  - ][ +  - ]:          1 :     Xapian::WritableDatabase db(get_writable_database());
     513         [ +  - ]:          2 :     Xapian::Document doc;
     514 [ +  - ][ +  - ]:          1 :     doc.set_data("cargo");
     515 [ +  - ][ +  - ]:          1 :     doc.add_term("abc");
     516 [ +  - ][ +  - ]:          1 :     doc.add_term("def");
     517 [ +  - ][ +  - ]:          1 :     doc.add_term("ghi");
     518                 :          1 :     const int N = 500;
     519         [ +  + ]:        501 :     for (int i = 0; i < N; ++i) {
     520         [ +  - ]:        500 :         db.add_document(doc);
     521                 :            :     }
     522         [ +  - ]:          1 :     db.commit();
     523                 :            : 
     524         [ +  - ]:          2 :     Xapian::Database rodb(get_writable_database_as_database());
     525         [ +  - ]:          1 :     db.add_document(doc);
     526         [ +  - ]:          1 :     db.commit();
     527                 :            : 
     528         [ +  - ]:          1 :     db.add_document(doc);
     529         [ +  - ]:          1 :     db.commit();
     530                 :            : 
     531         [ +  - ]:          1 :     db.add_document(doc);
     532                 :            :     try {
     533 [ -  + ][ #  # ]:          1 :         TEST_EQUAL(*rodb.termlist_begin(N - 1), "abc");
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ -  + ]
     534                 :          0 :         return false;
     535         [ +  - ]:          1 :     } catch (const Xapian::DatabaseModifiedError &) {
     536                 :            :     }
     537                 :            : 
     538                 :            :     try {
     539         [ +  - ]:          1 :         Xapian::Enquire enq(rodb);
     540 [ +  - ][ +  - ]:          1 :         enq.set_query(Xapian::Query("abc"));
                 [ +  - ]
     541         [ -  + ]:          1 :         Xapian::MSet mset = enq.get_mset(0, 10);
     542         [ -  + ]:          1 :         return false;
     543         [ +  - ]:          1 :     } catch (const Xapian::DatabaseModifiedError &) {
     544                 :            :     }
     545                 :            : 
     546                 :          2 :     return true;
     547                 :            : }
     548                 :            : 
     549                 :            : /// Regression test for bug#462 fixed in 1.0.19 and 1.1.5.
     550                 :          4 : DEFINE_TESTCASE(qpmemoryleak1, writable && !inmemory) {
     551                 :            :     // Inmemory never throws DatabaseModifiedError.
     552 [ +  - ][ +  - ]:          4 :     Xapian::WritableDatabase wdb(get_writable_database());
     553         [ +  - ]:          8 :     Xapian::Document doc;
     554                 :            : 
     555 [ +  - ][ +  - ]:          4 :     doc.add_term("foo");
     556         [ +  + ]:         84 :     for (int i = 100; i < 120; ++i) {
     557 [ +  - ][ +  - ]:         80 :         doc.add_term(str(i));
     558                 :            :     }
     559                 :            : 
     560         [ +  + ]:        204 :     for (int j = 0; j < 50; ++j) {
     561         [ +  - ]:        200 :         wdb.add_document(doc);
     562                 :            :     }
     563         [ +  - ]:          4 :     wdb.commit();
     564                 :            : 
     565         [ +  - ]:          8 :     Xapian::Database database(get_writable_database_as_database());
     566         [ +  - ]:          8 :     Xapian::QueryParser queryparser;
     567         [ +  - ]:          4 :     queryparser.set_database(database);
     568 [ +  - ][ +  - ]:       4004 :     TEST_EXCEPTION(Xapian::DatabaseModifiedError,
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  + ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
           [ #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     569                 :            :         for (int k = 0; k < 1000; ++k) {
     570                 :            :             wdb.add_document(doc);
     571                 :            :             wdb.commit();
     572                 :            :             (void)queryparser.parse_query("1", queryparser.FLAG_PARTIAL);
     573                 :            :         }
     574                 :            :         SKIP_TEST("didn't manage to trigger DatabaseModifiedError");
     575                 :            :     );
     576                 :            : 
     577                 :          4 :     return true;
     578                 :            : }
     579                 :            : 
     580                 :            : static void
     581                 :          4 : make_msize1_db(Xapian::WritableDatabase &db, const string &)
     582                 :            : {
     583                 :            :     const char * value0 =
     584                 :          4 :         "ABBCDEFGHIJKLMMNOPQQRSTTUUVVWXYZZaabcdefghhijjkllmnopqrsttuvwxyz";
     585                 :            :     const char * value1 =
     586                 :          4 :         "EMLEMMMMMMMNMMLMELEDNLEDMLMLDMLMLMLMEDGFHPOPBAHJIQJNGRKCGF";
     587         [ +  + ]:        260 :     while (*value0) {
     588         [ +  - ]:        256 :         Xapian::Document doc;
     589 [ +  - ][ +  - ]:        256 :         doc.add_value(0, string(1, *value0++));
     590         [ +  + ]:        256 :         if (*value1) {
     591 [ +  - ][ +  - ]:        232 :             doc.add_value(1, string(1, *value1++));
     592 [ +  - ][ +  - ]:        232 :             doc.add_term("K1");
     593                 :            :         }
     594         [ +  - ]:        256 :         db.add_document(doc);
     595                 :        256 :     }
     596                 :          4 : }
     597                 :            : 
     598                 :            : /// Regression test for ticket#464, fixed in 1.1.6 and 1.0.20.
     599                 :          6 : DEFINE_TESTCASE(msize1, generated) {
     600 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("msize1", make_msize1_db);
                 [ +  - ]
     601         [ +  - ]:         12 :     Xapian::Enquire enq(db);
     602         [ +  - ]:          6 :     enq.set_sort_by_value(1, false);
     603         [ +  - ]:          6 :     enq.set_collapse_key(0);
     604 [ +  - ][ +  - ]:          6 :     enq.set_query(Xapian::Query("K1"));
                 [ +  - ]
     605                 :            : 
     606         [ +  - ]:         12 :     Xapian::MSet mset = enq.get_mset(0, 60);
     607         [ +  - ]:          6 :     Xapian::doccount lb = mset.get_matches_lower_bound();
     608         [ +  - ]:          6 :     Xapian::doccount ub = mset.get_matches_upper_bound();
     609         [ +  - ]:          6 :     Xapian::doccount est = mset.get_matches_estimated();
     610 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb, ub);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     611 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb, est);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     612                 :            : 
     613         [ +  - ]:         12 :     Xapian::MSet mset2 = enq.get_mset(50, 10, 1000);
     614         [ +  - ]:          6 :     Xapian::doccount lb2 = mset2.get_matches_lower_bound();
     615         [ +  - ]:          6 :     Xapian::doccount ub2 = mset2.get_matches_upper_bound();
     616         [ +  - ]:          6 :     Xapian::doccount est2 = mset2.get_matches_estimated();
     617 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb2, ub2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     618 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb2, est2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     619 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(est, est2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     620                 :            : 
     621         [ +  - ]:         12 :     Xapian::MSet mset3 = enq.get_mset(0, 10, 1000);
     622         [ +  - ]:          6 :     Xapian::doccount lb3 = mset3.get_matches_lower_bound();
     623         [ +  - ]:          6 :     Xapian::doccount ub3 = mset3.get_matches_upper_bound();
     624         [ +  - ]:          6 :     Xapian::doccount est3 = mset3.get_matches_estimated();
     625 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb3, ub3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     626 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb3, est3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     627 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(est, est3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     628                 :            : 
     629                 :          6 :     return true;
     630                 :            : }
     631                 :            : 
     632                 :            : static void
     633                 :          4 : make_msize2_db(Xapian::WritableDatabase &db, const string &)
     634                 :            : {
     635                 :          4 :     const char * value0 = "AAABCDEEFGHIIJJKLLMNNOOPPQQRSTTUVWXYZ";
     636                 :          4 :     const char * value1 = "MLEMNMLMLMEDEDEMLEMLMLMLPOAHGF";
     637         [ +  + ]:        152 :     while (*value0) {
     638         [ +  - ]:        148 :         Xapian::Document doc;
     639 [ +  - ][ +  - ]:        148 :         doc.add_value(0, string(1, *value0++));
     640         [ +  + ]:        148 :         if (*value1) {
     641 [ +  - ][ +  - ]:        120 :             doc.add_value(1, string(1, *value1++));
     642 [ +  - ][ +  - ]:        120 :             doc.add_term("K1");
     643                 :            :         }
     644         [ +  - ]:        148 :         db.add_document(doc);
     645                 :        148 :     }
     646                 :          4 : }
     647                 :            : 
     648                 :            : /// Regression test for bug related to ticket#464, fixed in 1.1.6 and 1.0.20.
     649                 :          6 : DEFINE_TESTCASE(msize2, generated) {
     650 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("msize2", make_msize2_db);
                 [ +  - ]
     651         [ +  - ]:         12 :     Xapian::Enquire enq(db);
     652         [ +  - ]:          6 :     enq.set_sort_by_value(1, false);
     653         [ +  - ]:          6 :     enq.set_collapse_key(0);
     654 [ +  - ][ +  - ]:          6 :     enq.set_query(Xapian::Query("K1"));
                 [ +  - ]
     655                 :            : 
     656         [ +  - ]:         12 :     Xapian::MSet mset = enq.get_mset(0, 60);
     657         [ +  - ]:          6 :     Xapian::doccount lb = mset.get_matches_lower_bound();
     658         [ +  - ]:          6 :     Xapian::doccount ub = mset.get_matches_upper_bound();
     659         [ +  - ]:          6 :     Xapian::doccount est = mset.get_matches_estimated();
     660 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb, ub);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     661 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb, est);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     662                 :            : 
     663         [ +  - ]:         12 :     Xapian::MSet mset2 = enq.get_mset(50, 10, 1000);
     664         [ +  - ]:          6 :     Xapian::doccount lb2 = mset2.get_matches_lower_bound();
     665         [ +  - ]:          6 :     Xapian::doccount ub2 = mset2.get_matches_upper_bound();
     666         [ +  - ]:          6 :     Xapian::doccount est2 = mset2.get_matches_estimated();
     667 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb2, ub2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     668 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb2, est2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     669 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(est, est2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     670                 :            : 
     671         [ +  - ]:         12 :     Xapian::MSet mset3 = enq.get_mset(0, 10, 1000);
     672         [ +  - ]:          6 :     Xapian::doccount lb3 = mset3.get_matches_lower_bound();
     673         [ +  - ]:          6 :     Xapian::doccount ub3 = mset3.get_matches_upper_bound();
     674         [ +  - ]:          6 :     Xapian::doccount est3 = mset3.get_matches_estimated();
     675 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb3, ub3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     676 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(lb3, est3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     677 [ -  + ][ #  # ]:          6 :     TEST_EQUAL(est, est3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     678                 :            : 
     679                 :          6 :     return true;
     680                 :            : }
     681                 :            : 
     682                 :            : static void
     683                 :          4 : make_xordecay1_db(Xapian::WritableDatabase &db, const string &)
     684                 :            : {
     685         [ +  + ]:        200 :     for (int n = 1; n != 50; ++n) {
     686         [ +  - ]:        196 :         Xapian::Document doc;
     687         [ +  + ]:       9800 :         for (int i = 1; i != 50; ++i) {
     688         [ +  + ]:       9604 :             if (n % i == 0)
     689 [ +  - ][ +  - ]:        804 :                 doc.add_term("N" + str(i));
                 [ +  - ]
     690                 :            :         }
     691         [ +  - ]:        196 :         db.add_document(doc);
     692                 :        196 :     }
     693                 :          4 : }
     694                 :            : 
     695                 :            : /// Regression test for bug in decay of XOR, fixed in 1.2.1 and 1.0.21.
     696                 :          6 : DEFINE_TESTCASE(xordecay1, generated) {
     697 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("xordecay1", make_xordecay1_db);
                 [ +  - ]
     698         [ +  - ]:         12 :     Xapian::Enquire enq(db);
     699                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_XOR,
     700                 :            :                                 Xapian::Query("N10"),
     701                 :            :                                 Xapian::Query(Xapian::Query::OP_OR,
     702                 :            :                                               Xapian::Query("N2"),
     703 [ +  - ][ +  - ]:          6 :                                               Xapian::Query("N3"))));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     704         [ +  - ]:         12 :     Xapian::MSet mset1 = enq.get_mset(0, 1);
     705 [ +  - ][ +  - ]:         12 :     Xapian::MSet msetall = enq.get_mset(0, db.get_doccount());
     706                 :            : 
     707 [ +  - ][ +  - ]:          6 :     TEST(mset_range_is_same(mset1, 0, msetall, 0, mset1.size()));
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     708                 :          6 :     return true;
     709                 :            : }
     710                 :            : 
     711                 :            : static void
     712                 :          5 : make_ordecay_db(Xapian::WritableDatabase &db, const string &)
     713                 :            : {
     714                 :          5 :     const char * p = "VJ=QC]LUNTaARLI;715RR^];A4O=P4ZG<2CS4EM^^VS[A6QENR";
     715         [ +  + ]:        255 :     for (int d = 0; p[d]; ++d) {
     716                 :        250 :         int l = int(p[d] - '0');
     717         [ +  - ]:        250 :         Xapian::Document doc;
     718         [ +  + ]:       6445 :         for (int n = 1; n < l; ++n) {
     719 [ +  - ][ +  - ]:       6195 :             doc.add_term("N" + str(n));
                 [ +  - ]
     720         [ +  + ]:       6195 :             if (n % (d + 1) == 0) {
     721 [ +  - ][ +  - ]:        515 :                 doc.add_term("M" + str(n));
                 [ +  - ]
     722                 :            :             }
     723                 :            :         }
     724         [ +  - ]:        250 :         db.add_document(doc);
     725                 :        250 :     }
     726                 :          5 : }
     727                 :            : 
     728                 :            : /// Regression test for bug in decay of OR to AND, fixed in 1.2.1 and 1.0.21.
     729                 :          6 : DEFINE_TESTCASE(ordecay1, generated) {
     730 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("ordecay", make_ordecay_db);
                 [ +  - ]
     731         [ +  - ]:         12 :     Xapian::Enquire enq(db);
     732                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_OR,
     733                 :            :                                 Xapian::Query("N20"),
     734 [ +  - ][ +  - ]:          6 :                                 Xapian::Query("N21")));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     735                 :            : 
     736 [ +  - ][ +  - ]:         12 :     Xapian::MSet msetall = enq.get_mset(0, db.get_doccount());
     737 [ +  - ][ +  + ]:        192 :     for (unsigned int i = 1; i < msetall.size(); ++i) {
     738         [ +  - ]:        186 :         Xapian::MSet submset = enq.get_mset(0, i);
     739 [ +  - ][ +  - ]:        186 :         TEST(mset_range_is_same(submset, 0, msetall, 0, submset.size()));
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     740                 :        186 :     }
     741                 :          6 :     return true;
     742                 :            : }
     743                 :            : 
     744                 :            : /** Regression test for bug in decay of OR to AND_MAYBE, fixed in 1.2.1 and
     745                 :            :  *  1.0.21.
     746                 :            :  */
     747                 :          6 : DEFINE_TESTCASE(ordecay2, generated) {
     748 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("ordecay", make_ordecay_db);
                 [ +  - ]
     749         [ +  - ]:         12 :     Xapian::Enquire enq(db);
     750                 :         12 :     std::vector<Xapian::Query> q;
     751 [ +  - ][ +  - ]:          6 :     q.push_back(Xapian::Query("M20"));
                 [ +  - ]
     752 [ +  - ][ +  - ]:          6 :     q.push_back(Xapian::Query("N21"));
                 [ +  - ]
     753 [ +  - ][ +  - ]:          6 :     q.push_back(Xapian::Query("N22"));
                 [ +  - ]
     754                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_OR,
     755                 :            :                                 Xapian::Query("N25"),
     756                 :            :                                 Xapian::Query(Xapian::Query::OP_AND,
     757                 :            :                                               q.begin(),
     758 [ +  - ][ +  - ]:          6 :                                               q.end())));
         [ +  - ][ +  - ]
                 [ +  - ]
     759                 :            : 
     760 [ +  - ][ +  - ]:         12 :     Xapian::MSet msetall = enq.get_mset(0, db.get_doccount());
     761 [ +  - ][ +  + ]:        168 :     for (unsigned int i = 1; i < msetall.size(); ++i) {
     762         [ +  - ]:        162 :         Xapian::MSet submset = enq.get_mset(0, i);
     763 [ +  - ][ +  - ]:        162 :         TEST(mset_range_is_same(submset, 0, msetall, 0, submset.size()));
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     764                 :        162 :     }
     765                 :          6 :     return true;
     766                 :            : }
     767                 :            : 
     768                 :            : static void
     769                 :          4 : make_orcheck_db(Xapian::WritableDatabase &db, const string &)
     770                 :            : {
     771                 :            :     static const unsigned t1[] = {2, 4, 6, 8, 10};
     772                 :            :     static const unsigned t2[] = {6, 7, 8, 11, 12, 13, 14, 15, 16, 17};
     773                 :            :     static const unsigned t3[] = {3, 7, 8, 11, 12, 13, 14, 15, 16, 17};
     774                 :            : 
     775         [ +  + ]:         72 :     for (unsigned i = 1; i <= 17; ++i) {
     776         [ +  - ]:         68 :         Xapian::Document doc;
     777         [ +  - ]:         68 :         db.replace_document(i, doc);
     778                 :         68 :     }
     779         [ +  + ]:         24 :     for (unsigned i : t1) {
     780         [ +  - ]:         20 :         Xapian::Document doc(db.get_document(i));
     781 [ +  - ][ +  - ]:         20 :         doc.add_term("T1");
     782         [ +  - ]:         20 :         db.replace_document(i, doc);
     783                 :         20 :     }
     784         [ +  + ]:         44 :     for (unsigned i : t2) {
     785         [ +  - ]:         40 :         Xapian::Document doc(db.get_document(i));
     786 [ +  - ][ +  - ]:         40 :         doc.add_term("T2");
     787         [ +  + ]:         40 :         if (i < 17) {
     788 [ +  - ][ +  - ]:         36 :             doc.add_term("T2_lowfreq");
     789                 :            :         }
     790 [ +  - ][ +  - ]:         40 :         doc.add_value(2, "1");
     791         [ +  - ]:         40 :         db.replace_document(i, doc);
     792                 :         40 :     }
     793         [ +  + ]:         44 :     for (unsigned i : t3) {
     794         [ +  - ]:         40 :         Xapian::Document doc(db.get_document(i));
     795 [ +  - ][ +  - ]:         40 :         doc.add_term("T3");
     796         [ +  + ]:         40 :         if (i < 17) {
     797 [ +  - ][ +  - ]:         36 :             doc.add_term("T3_lowfreq");
     798                 :            :         }
     799 [ +  - ][ +  - ]:         40 :         doc.add_value(3, "1");
     800         [ +  - ]:         40 :         db.replace_document(i, doc);
     801                 :         40 :     }
     802                 :          4 : }
     803                 :            : 
     804                 :            : /** Regression test for bugs in the check() method of OrPostList. (ticket #485)
     805                 :            :  *  Bugs introduced and fixed between 1.2.0 and 1.2.1 (never in a release).
     806                 :            :  */
     807                 :          6 : DEFINE_TESTCASE(orcheck1, generated) {
     808 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("orcheck1", make_orcheck_db);
                 [ +  - ]
     809         [ +  - ]:         12 :     Xapian::Enquire enq(db);
     810 [ +  - ][ +  - ]:         12 :     Xapian::Query q1("T1");
     811 [ +  - ][ +  - ]:         12 :     Xapian::Query q2("T2");
     812 [ +  - ][ +  - ]:         12 :     Xapian::Query q2l("T2_lowfreq");
     813 [ +  - ][ +  - ]:         12 :     Xapian::Query q3("T3");
     814 [ +  - ][ +  - ]:         12 :     Xapian::Query q3l("T3_lowfreq");
     815 [ +  - ][ +  - ]:         12 :     Xapian::Query v2(Xapian::Query::OP_VALUE_RANGE, 2, "0", "2");
                 [ +  - ]
     816 [ +  - ][ +  - ]:         12 :     Xapian::Query v3(Xapian::Query::OP_VALUE_RANGE, 3, "0", "2");
                 [ +  - ]
     817                 :            : 
     818         [ +  - ]:          6 :     tout << "Checking q2 OR q3\n";
     819                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
     820 [ +  - ][ +  - ]:          6 :                                 Xapian::Query(Xapian::Query::OP_OR, q2, q3)));
                 [ +  - ]
     821 [ +  - ][ +  - ]:          6 :     mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
                 [ +  - ]
     822                 :            : 
     823         [ +  - ]:          6 :     tout << "Checking q2l OR q3\n";
     824                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
     825 [ +  - ][ +  - ]:          6 :                                 Xapian::Query(Xapian::Query::OP_OR, q2l, q3)));
                 [ +  - ]
     826 [ +  - ][ +  - ]:          6 :     mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
                 [ +  - ]
     827                 :            : 
     828         [ +  - ]:          6 :     tout << "Checking q2 OR q3l\n";
     829                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
     830 [ +  - ][ +  - ]:          6 :                                 Xapian::Query(Xapian::Query::OP_OR, q2, q3l)));
                 [ +  - ]
     831 [ +  - ][ +  - ]:          6 :     mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
                 [ +  - ]
     832                 :            : 
     833         [ +  - ]:          6 :     tout << "Checking v2 OR q3\n";
     834                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
     835 [ +  - ][ +  - ]:          6 :                                 Xapian::Query(Xapian::Query::OP_OR, v2, q3)));
                 [ +  - ]
     836 [ +  - ][ +  - ]:          6 :     mset_expect_order(enq.get_mset(0, db.get_doccount()), 8, 6);
                 [ +  - ]
     837                 :            : 
     838         [ +  - ]:          6 :     tout << "Checking q2 OR v3\n";
     839                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_AND, q1,
     840 [ +  - ][ +  - ]:          6 :                                 Xapian::Query(Xapian::Query::OP_OR, q2, v3)));
                 [ +  - ]
     841                 :            :     // Order of results in this one is different, because v3 gives no weight,
     842                 :            :     // both documents are in q2, and document 8 has a higher length.
     843 [ +  - ][ +  - ]:          6 :     mset_expect_order(enq.get_mset(0, db.get_doccount()), 6, 8);
                 [ +  - ]
     844                 :            : 
     845                 :          6 :     return true;
     846                 :            : }
     847                 :            : 
     848                 :            : /** Regression test for bug fixed in 1.2.1 and 1.0.21.
     849                 :            :  *
     850                 :            :  *  We failed to mark the Btree as unmodified after cancel().
     851                 :            :  */
     852                 :          1 : DEFINE_TESTCASE(failedreplace1, glass) {
     853 [ +  - ][ +  - ]:          1 :     Xapian::WritableDatabase db(get_writable_database());
     854         [ +  - ]:          2 :     Xapian::Document doc;
     855 [ +  - ][ +  - ]:          1 :     doc.add_term("foo");
     856         [ +  - ]:          1 :     db.add_document(doc);
     857         [ +  - ]:          1 :     Xapian::docid did = db.add_document(doc);
     858 [ +  - ][ +  - ]:          1 :     doc.add_term("abc");
     859 [ +  - ][ +  - ]:          1 :     doc.add_term(string(1000, 'm'));
     860 [ +  - ][ +  - ]:          1 :     doc.add_term("xyz");
     861 [ +  - ][ -  + ]:          1 :     TEST_EXCEPTION(Xapian::InvalidArgumentError, db.replace_document(did, doc));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
     862         [ +  - ]:          1 :     db.commit();
     863 [ +  - ][ -  +  :          1 :     TEST_EQUAL(db.get_doccount(), 0);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     864 [ +  - ][ +  - ]:          1 :     TEST_EQUAL(db.get_termfreq("foo"), 0);
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     865                 :          1 :     return true;
     866                 :            : }
     867                 :            : 
     868                 :          1 : DEFINE_TESTCASE(failedreplace2, glass) {
     869 [ +  - ][ +  - ]:          1 :     Xapian::WritableDatabase db(get_writable_database("apitest_simpledata"));
     870         [ +  - ]:          1 :     db.commit();
     871         [ +  - ]:          1 :     Xapian::doccount db_size = db.get_doccount();
     872         [ +  - ]:          2 :     Xapian::Document doc;
     873 [ +  - ][ +  - ]:          1 :     doc.set_data("wibble");
     874 [ +  - ][ +  - ]:          1 :     doc.add_term("foo");
     875 [ +  - ][ +  - ]:          1 :     doc.add_value(0, "seven");
     876         [ +  - ]:          1 :     db.add_document(doc);
     877         [ +  - ]:          1 :     Xapian::docid did = db.add_document(doc);
     878 [ +  - ][ +  - ]:          1 :     doc.add_term("abc");
     879 [ +  - ][ +  - ]:          1 :     doc.add_term(string(1000, 'm'));
     880 [ +  - ][ +  - ]:          1 :     doc.add_term("xyz");
     881 [ +  - ][ +  - ]:          1 :     doc.add_value(0, "six");
     882 [ +  - ][ -  + ]:          1 :     TEST_EXCEPTION(Xapian::InvalidArgumentError, db.replace_document(did, doc));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
     883         [ +  - ]:          1 :     db.commit();
     884 [ +  - ][ -  +  :          1 :     TEST_EQUAL(db.get_doccount(), db_size);
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     885 [ +  - ][ +  - ]:          1 :     TEST_EQUAL(db.get_termfreq("foo"), 0);
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     886                 :          1 :     return true;
     887                 :            : }
     888                 :            : 
     889                 :            : /// Coverage for SelectPostList::skip_to().
     890                 :          7 : DEFINE_TESTCASE(phrase3, positional) {
     891 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_phrase");
     892                 :            : 
     893                 :            :     static const char * const phrase_words[] = { "phrase", "near" };
     894         [ +  - ]:         14 :     Xapian::Query q(Xapian::Query::OP_NEAR, phrase_words, phrase_words + 2, 12);
     895 [ +  - ][ +  - ]:          7 :     q = Xapian::Query(Xapian::Query::OP_AND_MAYBE, Xapian::Query("pad"), q);
         [ +  - ][ +  - ]
     896                 :            : 
     897         [ +  - ]:         14 :     Xapian::Enquire enquire(db);
     898         [ +  - ]:          7 :     enquire.set_query(q);
     899         [ +  - ]:          7 :     Xapian::MSet mset = enquire.get_mset(0, 5);
     900                 :            : 
     901                 :          7 :     return true;
     902                 :            : }
     903                 :            : 
     904                 :            : /// Check that get_mset(<large number>, 10) doesn't exhaust memory needlessly.
     905                 :            : // Regression test for fix in 1.2.4.
     906                 :          7 : DEFINE_TESTCASE(msetfirst2, backend) {
     907 [ +  - ][ +  - ]:          7 :     Xapian::Database db(get_database("apitest_simpledata"));
     908         [ +  - ]:         14 :     Xapian::Enquire enquire(db);
     909 [ +  - ][ +  - ]:          7 :     enquire.set_query(Xapian::Query("paragraph"));
                 [ +  - ]
     910         [ +  - ]:         14 :     Xapian::MSet mset;
     911                 :            :     // Before the fix, this tried to allocate too much memory.
     912 [ +  - ][ +  - ]:          7 :     mset = enquire.get_mset(0xfffffff0, 1);
     913 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_firstitem(), 0xfffffff0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     914                 :            :     // Check that the number of documents gets clamped too.
     915 [ +  - ][ +  - ]:          7 :     mset = enquire.get_mset(1, 0xfffffff0);
     916 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_firstitem(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     917                 :            :     // Another regression test - MatchNothing used to give an MSet with
     918                 :            :     // get_firstitem() returning 0.
     919         [ +  - ]:          7 :     enquire.set_query(Xapian::Query::MatchNothing);
     920 [ +  - ][ +  - ]:          7 :     mset = enquire.get_mset(1, 1);
     921 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_firstitem(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     922                 :          7 :     return true;
     923                 :            : }
     924                 :            : 
     925                 :          7 : DEFINE_TESTCASE(bm25weight2, backend) {
     926 [ +  - ][ +  - ]:          7 :     Xapian::Database db(get_database("etext"));
     927         [ +  - ]:         14 :     Xapian::Enquire enquire(db);
     928 [ +  - ][ +  - ]:          7 :     enquire.set_query(Xapian::Query("the"));
                 [ +  - ]
     929         [ +  - ]:          7 :     enquire.set_weighting_scheme(Xapian::BM25Weight(0, 0, 0, 0, 1));
     930         [ +  - ]:         14 :     Xapian::MSet mset = enquire.get_mset(0, 100);
     931 [ +  - ][ -  + ]:          7 :     TEST_REL(mset.size(),>=,2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     932 [ +  - ][ +  - ]:          7 :     double weight0 = mset[0].get_weight();
     933 [ +  - ][ +  + ]:        700 :     for (size_t i = 1; i != mset.size(); ++i) {
     934 [ +  - ][ +  - ]:        693 :         TEST_EQUAL(weight0, mset[i].get_weight());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     935                 :            :     }
     936                 :          7 :     return true;
     937                 :            : }
     938                 :            : 
     939                 :          7 : DEFINE_TESTCASE(unigramlmweight2, backend) {
     940 [ +  - ][ +  - ]:          7 :     Xapian::Database db(get_database("etext"));
     941         [ +  - ]:         14 :     Xapian::Enquire enquire(db);
     942 [ +  - ][ +  - ]:          7 :     enquire.set_query(Xapian::Query("the"));
                 [ +  - ]
     943         [ +  - ]:          7 :     enquire.set_weighting_scheme(Xapian::LMWeight());
     944         [ +  - ]:         14 :     Xapian::MSet mset = enquire.get_mset(0, 100);
     945 [ +  - ][ -  + ]:          7 :     TEST_REL(mset.size(),>=,2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     946                 :          7 :     return true;
     947                 :            : }
     948                 :            : 
     949                 :          7 : DEFINE_TESTCASE(tradweight2, backend) {
     950 [ +  - ][ +  - ]:          7 :     Xapian::Database db(get_database("etext"));
     951         [ +  - ]:         14 :     Xapian::Enquire enquire(db);
     952 [ +  - ][ +  - ]:          7 :     enquire.set_query(Xapian::Query("the"));
                 [ +  - ]
     953         [ +  - ]:          7 :     enquire.set_weighting_scheme(Xapian::TradWeight(0));
     954         [ +  - ]:         14 :     Xapian::MSet mset = enquire.get_mset(0, 100);
     955 [ +  - ][ -  + ]:          7 :     TEST_REL(mset.size(),>=,2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     956 [ +  - ][ +  - ]:          7 :     double weight0 = mset[0].get_weight();
     957 [ +  - ][ +  + ]:        700 :     for (size_t i = 1; i != mset.size(); ++i) {
     958 [ +  - ][ +  - ]:        693 :         TEST_EQUAL(weight0, mset[i].get_weight());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     959                 :            :     }
     960                 :          7 :     return true;
     961                 :            : }
     962                 :            : 
     963                 :            : // Regression test for bug fix in 1.2.9.
     964                 :          7 : DEFINE_TESTCASE(emptydb1, backend) {
     965 [ +  - ][ +  - ]:          7 :     Xapian::Database db(get_database(string()));
     966                 :            :     static const Xapian::Query::op ops[] = {
     967                 :            :         Xapian::Query::OP_AND,
     968                 :            :         Xapian::Query::OP_OR,
     969                 :            :         Xapian::Query::OP_AND_NOT,
     970                 :            :         Xapian::Query::OP_XOR,
     971                 :            :         Xapian::Query::OP_AND_MAYBE,
     972                 :            :         Xapian::Query::OP_FILTER,
     973                 :            :         Xapian::Query::OP_NEAR,
     974                 :            :         Xapian::Query::OP_PHRASE,
     975                 :            :         Xapian::Query::OP_ELITE_SET
     976                 :            :     };
     977                 :            :     const Xapian::Query::op * p;
     978         [ +  + ]:         70 :     for (p = ops; p - ops != sizeof(ops) / sizeof(*ops); ++p) {
     979 [ +  - ][ +  - ]:         63 :         tout << *p << endl;
     980         [ +  - ]:         63 :         Xapian::Enquire enquire(db);
     981 [ +  - ][ +  - ]:        126 :         Xapian::Query query(*p, Xapian::Query("a"), Xapian::Query("b"));
         [ +  - ][ +  - ]
                 [ +  - ]
     982         [ +  - ]:         63 :         enquire.set_query(query);
     983         [ +  - ]:        126 :         Xapian::MSet mset = enquire.get_mset(0, 10);
     984 [ +  - ][ -  + ]:         63 :         TEST_EQUAL(mset.get_matches_estimated(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     985 [ +  - ][ -  + ]:         63 :         TEST_EQUAL(mset.get_matches_upper_bound(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     986 [ +  - ][ -  + ]:         63 :         TEST_EQUAL(mset.get_matches_lower_bound(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     987                 :         63 :     }
     988                 :          7 :     return true;
     989                 :            : }
     990                 :            : 
     991                 :            : /// Test error opening non-existent stub databases.
     992                 :            : // Regression test for bug fixed in 1.3.1 and 1.2.11.
     993                 :          1 : DEFINE_TESTCASE(stubdb7, !backend) {
     994 [ +  - ][ +  - ]:          1 :     TEST_EXCEPTION(Xapian::DatabaseNotFoundError,
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ -  + ]
           [ -  +  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     995                 :            :             Xapian::Database("nosuchdirectory", Xapian::DB_BACKEND_STUB));
     996 [ +  - ][ -  +  :          1 :     TEST_EXCEPTION(Xapian::DatabaseNotFoundError,
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  +  - ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     997                 :            :             Xapian::WritableDatabase("nosuchdirectory",
     998                 :            :                 Xapian::DB_OPEN|Xapian::DB_BACKEND_STUB));
     999                 :          1 :     return true;
    1000                 :            : }
    1001                 :            : 
    1002                 :            : /// Test which checks the weights are as expected.
    1003                 :            : //  This runs for multi_* too, so serves to check that we get the same weights
    1004                 :            : //  with multiple databases as without.
    1005                 :          7 : DEFINE_TESTCASE(msetweights1, backend) {
    1006 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1007         [ +  - ]:         14 :     Xapian::Enquire enq(db);
    1008                 :            :     Xapian::Query q(Xapian::Query::OP_OR,
    1009                 :            :                     Xapian::Query("paragraph"),
    1010 [ +  - ][ +  - ]:         14 :                     Xapian::Query("word"));
         [ +  - ][ +  - ]
                 [ +  - ]
    1011         [ +  - ]:          7 :     enq.set_query(q);
    1012                 :            :     // 5 documents match, and the 4th and 5th have the same weight, so ask for
    1013                 :            :     // 4 as that's a good test that we get the right one in this case.
    1014         [ +  - ]:         14 :     Xapian::MSet mset = enq.get_mset(0, 4);
    1015                 :            : 
    1016                 :            :     static const struct { Xapian::docid did; double wt; } expected[] = {
    1017                 :            :         { 2, 1.2058248004573934864 },
    1018                 :            :         { 4, 0.81127876655507624726 },
    1019                 :            :         { 1, 0.17309550762546158098 },
    1020                 :            :         { 3, 0.14609528172558261527 }
    1021                 :            :     };
    1022                 :            : 
    1023 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.size(), sizeof(expected) / sizeof(expected[0]));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1024 [ +  - ][ +  + ]:         35 :     for (size_t i = 0; i < mset.size(); ++i) {
    1025 [ +  - ][ +  - ]:         28 :         TEST_EQUAL(*mset[i], expected[i].did);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1026 [ +  - ][ +  - ]:         28 :         TEST_EQUAL_DOUBLE(mset[i].get_weight(), expected[i].wt);
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1027                 :            :     }
    1028                 :            : 
    1029                 :            :     // Now test a query which matches only even docids, so in the multi case
    1030                 :            :     // one subdatabase doesn't match.
    1031 [ +  - ][ +  - ]:          7 :     enq.set_query(Xapian::Query("one"));
                 [ +  - ]
    1032 [ +  - ][ +  - ]:          7 :     mset = enq.get_mset(0, 3);
    1033                 :            : 
    1034                 :            :     static const struct { Xapian::docid did; double wt; } expected2[] = {
    1035                 :            :         { 6, 0.73354729848273669823 },
    1036                 :            :         { 2, 0.45626501034348893038 }
    1037                 :            :     };
    1038                 :            : 
    1039 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.size(), sizeof(expected2) / sizeof(expected2[0]));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1040 [ +  - ][ +  + ]:         21 :     for (size_t i = 0; i < mset.size(); ++i) {
    1041 [ +  - ][ +  - ]:         14 :         TEST_EQUAL(*mset[i], expected2[i].did);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1042 [ +  - ][ +  - ]:         14 :         TEST_EQUAL_DOUBLE(mset[i].get_weight(), expected2[i].wt);
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1043                 :            :     }
    1044                 :            : 
    1045                 :          7 :     return true;
    1046                 :            : }
    1047                 :            : 
    1048                 :          7 : DEFINE_TESTCASE(itorskiptofromend1, backend) {
    1049 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1050                 :            : 
    1051         [ +  - ]:         14 :     Xapian::TermIterator t = db.termlist_begin(1);
    1052 [ +  - ][ +  - ]:          7 :     t.skip_to("zzzzz");
    1053 [ -  + ][ #  # ]:          7 :     TEST(t == db.termlist_end(1));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1054                 :            :     // This worked in 1.2.x but segfaulted in 1.3.1.
    1055 [ +  - ][ +  - ]:          7 :     t.skip_to("zzzzzz");
    1056                 :            : 
    1057 [ +  - ][ +  - ]:         14 :     Xapian::PostingIterator p = db.postlist_begin("one");
    1058         [ +  - ]:          7 :     p.skip_to(99999);
    1059 [ +  - ][ -  + ]:          7 :     TEST(p == db.postlist_end("one"));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1060                 :            :     // This segfaulted prior to 1.3.2.
    1061         [ +  - ]:          7 :     p.skip_to(999999);
    1062                 :            : 
    1063 [ +  - ][ +  - ]:         14 :     Xapian::PositionIterator i = db.positionlist_begin(6, "one");
    1064         [ +  - ]:          7 :     i.skip_to(99999);
    1065 [ +  - ][ -  + ]:          7 :     TEST(i == db.positionlist_end(6, "one"));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1066                 :            :     // This segfaulted prior to 1.3.2.
    1067         [ +  - ]:          7 :     i.skip_to(999999);
    1068                 :            : 
    1069         [ +  - ]:         14 :     Xapian::ValueIterator v = db.valuestream_begin(1);
    1070         [ +  - ]:          7 :     v.skip_to(99999);
    1071 [ -  + ][ #  # ]:          7 :     TEST(v == db.valuestream_end(1));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1072                 :            :     // These segfaulted prior to 1.3.2.
    1073         [ +  - ]:          7 :     v.skip_to(999999);
    1074         [ +  - ]:          7 :     v.check(9999999);
    1075                 :            : 
    1076                 :          7 :     return true;
    1077                 :            : }
    1078                 :            : 
    1079                 :            : /// Check handling of invalid block sizes.
    1080                 :            : // Regression test for bug fixed in 1.2.17 and 1.3.2 - the size gets fixed
    1081                 :            : // but the uncorrected size was passed to the base file.  Also, abort() was
    1082                 :            : // called on 0.
    1083                 :          1 : DEFINE_TESTCASE(blocksize1, glass) {
    1084 [ +  - ][ +  - ]:          1 :     string db_dir = "." + get_dbtype();
    1085                 :          1 :     mkdir(db_dir.c_str(), 0755);
    1086         [ +  - ]:          1 :     db_dir += "/db__blocksize1";
    1087                 :            :     int flags;
    1088 [ +  - ][ +  - ]:          1 :     if (get_dbtype() == "glass") {
                 [ +  - ]
    1089                 :          1 :         flags = Xapian::DB_CREATE|Xapian::DB_BACKEND_GLASS;
    1090                 :            :     } else {
    1091 [ #  # ][ #  # ]:          0 :         FAIL_TEST("Unhandled backend type");
         [ #  # ][ #  # ]
    1092                 :            :     }
    1093                 :            :     static const unsigned bad_sizes[] = {
    1094                 :            :         65537, 8000, 2000, 1024, 16, 7, 3, 1, 0
    1095                 :            :     };
    1096         [ +  + ]:         10 :     for (size_t i = 0; i < sizeof(bad_sizes) / sizeof(bad_sizes[0]); ++i) {
    1097                 :          9 :         size_t block_size = bad_sizes[i];
    1098         [ +  - ]:          9 :         rm_rf(db_dir);
    1099         [ +  - ]:          9 :         Xapian::WritableDatabase db(db_dir, flags, block_size);
    1100         [ +  - ]:         18 :         Xapian::Document doc;
    1101 [ +  - ][ +  - ]:          9 :         doc.add_term("XYZ");
    1102 [ +  - ][ +  - ]:          9 :         doc.set_data("foo");
    1103         [ +  - ]:          9 :         db.add_document(doc);
    1104         [ +  - ]:          9 :         db.commit();
    1105                 :          9 :     }
    1106                 :          1 :     return true;
    1107                 :            : }
    1108                 :            : 
    1109                 :            : /// Feature test for Xapian::DB_NO_TERMLIST.
    1110                 :          1 : DEFINE_TESTCASE(notermlist1, glass) {
    1111 [ +  - ][ +  - ]:          1 :     string db_dir = "." + get_dbtype();
    1112                 :          1 :     mkdir(db_dir.c_str(), 0755);
    1113         [ +  - ]:          1 :     db_dir += "/db__notermlist1";
    1114                 :          1 :     int flags = Xapian::DB_CREATE|Xapian::DB_NO_TERMLIST;
    1115 [ +  - ][ +  - ]:          1 :     if (get_dbtype() == "glass") {
                 [ +  - ]
    1116                 :          1 :         flags |= Xapian::DB_BACKEND_GLASS;
    1117                 :            :     }
    1118         [ +  - ]:          1 :     rm_rf(db_dir);
    1119         [ +  - ]:          2 :     Xapian::WritableDatabase db(db_dir, flags);
    1120         [ +  - ]:          2 :     Xapian::Document doc;
    1121 [ +  - ][ +  - ]:          1 :     doc.add_term("hello");
    1122 [ +  - ][ +  - ]:          1 :     doc.add_value(42, "answer");
    1123         [ +  - ]:          1 :     db.add_document(doc);
    1124         [ +  - ]:          1 :     db.commit();
    1125 [ +  - ][ -  + ]:          1 :     TEST(!file_exists(db_dir + "/termlist.glass"));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1126 [ +  - ][ -  + ]:          1 :     TEST_EXCEPTION(Xapian::FeatureUnavailableError, db.termlist_begin(1));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
    1127                 :          1 :     return true;
    1128                 :            : }
    1129                 :            : 
    1130                 :            : /// Regression test for bug starting a new glass freelist block.
    1131                 :          5 : DEFINE_TESTCASE(newfreelistblock1, writable) {
    1132         [ +  - ]:          5 :     Xapian::Document doc;
    1133 [ +  - ][ +  - ]:          5 :     doc.add_term("foo");
    1134         [ +  + ]:        105 :     for (int i = 100; i < 120; ++i) {
    1135 [ +  - ][ +  - ]:        100 :         doc.add_term(str(i));
    1136                 :            :     }
    1137                 :            : 
    1138 [ +  - ][ +  - ]:         10 :     Xapian::WritableDatabase wdb(get_writable_database());
    1139         [ +  + ]:        255 :     for (int j = 0; j < 50; ++j) {
    1140         [ +  - ]:        250 :         wdb.add_document(doc);
    1141                 :            :     }
    1142         [ +  - ]:          5 :     wdb.commit();
    1143                 :            : 
    1144         [ +  + ]:       5005 :     for (int k = 0; k < 1000; ++k) {
    1145         [ +  - ]:       5000 :         wdb.add_document(doc);
    1146         [ +  - ]:       5000 :         wdb.commit();
    1147                 :            :     }
    1148                 :            : 
    1149                 :          5 :     return true;
    1150                 :            : }
    1151                 :            : 
    1152                 :            : /** Check that the parent directory for the database doesn't need to be
    1153                 :            :  *  writable.  Regression test for early versions on the glass new btree
    1154                 :            :  *  branch which failed to append a "/" when generating a temporary filename
    1155                 :            :  *  from the database directory.
    1156                 :            :  */
    1157                 :          1 : DEFINE_TESTCASE(readonlyparentdir1, glass) {
    1158                 :            : #if !defined __WIN32__ && !defined __CYGWIN__ && !defined __OS2__
    1159 [ +  - ][ +  - ]:          1 :     string path = get_named_writable_database_path("readonlyparentdir1");
    1160                 :            :     // Fix permissions if the previous test was killed.
    1161                 :          1 :     (void)chmod(path.c_str(), 0700);
    1162                 :          1 :     mkdir(path.c_str(), 0777);
    1163         [ +  - ]:          1 :     mkdir((path + "/sub").c_str(), 0777);
    1164 [ +  - ][ +  - ]:          2 :     Xapian::WritableDatabase db = get_named_writable_database("readonlyparentdir1/sub");
                 [ +  - ]
    1165 [ -  + ][ #  # ]:          1 :     TEST(chmod(path.c_str(), 0500) == 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1166                 :            :     try {
    1167         [ +  - ]:          1 :         Xapian::Document doc;
    1168 [ +  - ][ +  - ]:          1 :         doc.add_term("hello");
    1169 [ +  - ][ +  - ]:          1 :         doc.set_data("some text");
    1170         [ +  - ]:          1 :         db.add_document(doc);
    1171         [ +  - ]:          1 :         db.commit();
    1172                 :          0 :     } catch (...) {
    1173                 :            :         // Attempt to fix the permissions, otherwise things like "rm -rf" on
    1174                 :            :         // the source tree will fail.
    1175                 :          0 :         (void)chmod(path.c_str(), 0700);
    1176                 :          0 :         throw;
    1177                 :            :     }
    1178 [ -  + ][ #  # ]:          1 :     TEST(chmod(path.c_str(), 0700) == 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1179                 :            : #endif
    1180                 :          1 :     return true;
    1181                 :            : }
    1182                 :            : 
    1183                 :            : static void
    1184                 :          4 : make_phrasebug1_db(Xapian::WritableDatabase &db, const string &)
    1185                 :            : {
    1186         [ +  - ]:          4 :     Xapian::Document doc;
    1187 [ +  - ][ +  - ]:          4 :     doc.add_posting("hurricane", 199881);
    1188 [ +  - ][ +  - ]:          4 :     doc.add_posting("hurricane", 203084);
    1189 [ +  - ][ +  - ]:          4 :     doc.add_posting("katrina", 199882);
    1190 [ +  - ][ +  - ]:          4 :     doc.add_posting("katrina", 202473);
    1191 [ +  - ][ +  - ]:          4 :     doc.add_posting("katrina", 203085);
    1192         [ +  - ]:          4 :     db.add_document(doc);
    1193                 :          4 : }
    1194                 :            : 
    1195                 :            : /// Regression test for ticket#653, fixed in 1.3.2 and 1.2.19.
    1196                 :          6 : DEFINE_TESTCASE(phrasebug1, generated && positional) {
    1197 [ +  - ][ +  - ]:          6 :     Xapian::Database db = get_database("phrasebug1", make_phrasebug1_db);
                 [ +  - ]
    1198                 :            :     static const char * const qterms[] = { "katrina", "hurricane" };
    1199         [ +  - ]:         12 :     Xapian::Enquire e(db);
    1200         [ +  - ]:         12 :     Xapian::Query q(Xapian::Query::OP_PHRASE, qterms, qterms + 2, 5);
    1201         [ +  - ]:          6 :     e.set_query(q);
    1202         [ +  - ]:         12 :     Xapian::MSet mset = e.get_mset(0, 100);
    1203 [ +  - ][ -  + ]:          6 :     TEST_EQUAL(mset.size(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1204                 :            :     static const char * const qterms2[] = { "hurricane", "katrina" };
    1205         [ +  - ]:         12 :     Xapian::Query q2(Xapian::Query::OP_PHRASE, qterms2, qterms2 + 2, 5);
    1206         [ +  - ]:          6 :     e.set_query(q2);
    1207 [ +  - ][ +  - ]:          6 :     mset = e.get_mset(0, 100);
    1208 [ +  - ][ -  + ]:          6 :     TEST_EQUAL(mset.size(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1209                 :          6 :     return true;
    1210                 :            : }
    1211                 :            : 
    1212                 :            : /// Feature test for Xapian::DB_RETRY_LOCK
    1213                 :          2 : DEFINE_TESTCASE(retrylock1, writable && path) {
    1214                 :            :     // FIXME: Can't see an easy way to test this for remote databases - the
    1215                 :            :     // harness doesn't seem to provide a suitable way to reopen a remote.
    1216                 :            : #if defined HAVE_FORK && defined HAVE_SOCKETPAIR
    1217                 :            :     int fds[2];
    1218         [ -  + ]:          2 :     if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, PF_UNSPEC, fds) < 0) {
    1219 [ #  # ][ #  # ]:          0 :         FAIL_TEST("socketpair() failed");
         [ #  # ][ #  # ]
    1220                 :            :     }
    1221         [ -  + ]:          2 :     if (fds[1] >= FD_SETSIZE)
    1222 [ #  # ][ #  # ]:          0 :         SKIP_TEST("socketpair() gave fd >= FD_SETSIZE");
         [ #  # ][ #  # ]
    1223 [ +  - ][ -  + ]:          2 :     if (fcntl(fds[1], F_SETFL, O_NONBLOCK) < 0)
    1224 [ #  # ][ #  # ]:          0 :         FAIL_TEST("fcntl() failed to set O_NONBLOCK");
         [ #  # ][ #  # ]
    1225                 :          2 :     pid_t child = fork();
    1226         [ -  + ]:          2 :     if (child == -1)
    1227 [ #  # ][ #  # ]:          0 :         FAIL_TEST("fork() failed");
         [ #  # ][ #  # ]
    1228         [ -  + ]:          2 :     if (child == 0) {
    1229                 :            :         // Wait for signal that parent has opened the database.
    1230                 :            :         char ch;
    1231 [ #  # ][ #  # ]:          0 :         while (read(fds[0], &ch, 1) < 0) { }
    1232                 :            : 
    1233                 :            :         try {
    1234                 :            :             Xapian::WritableDatabase db2(get_named_writable_database_path("retrylock1"),
    1235 [ #  # ][ #  # ]:          0 :                                          Xapian::DB_OPEN|Xapian::DB_RETRY_LOCK);
                 [ #  # ]
    1236         [ #  # ]:          0 :             if (write(fds[0], "y", 1)) { }
    1237         [ #  # ]:          0 :         } catch (const Xapian::DatabaseLockError &) {
    1238         [ #  # ]:          0 :             if (write(fds[0], "l", 1)) { }
    1239                 :          0 :         } catch (const Xapian::Error &e) {
    1240         [ #  # ]:          0 :             const string & m = e.get_description();
    1241         [ #  # ]:          0 :             if (write(fds[0], m.data(), m.size())) { }
    1242   [ #  #  #  #  :          0 :         } catch (...) {
                      # ]
    1243         [ #  # ]:          0 :             if (write(fds[0], "o", 1)) { }
    1244                 :            :         }
    1245                 :          0 :         _exit(0);
    1246                 :            :     }
    1247                 :            : 
    1248         [ +  - ]:          2 :     close(fds[0]);
    1249                 :            : 
    1250 [ +  - ][ +  - ]:          2 :     Xapian::WritableDatabase db = get_named_writable_database("retrylock1");
                 [ +  - ]
    1251 [ +  - ][ -  + ]:          2 :     if (write(fds[1], "", 1) != 1)
    1252 [ #  # ][ #  # ]:          0 :         FAIL_TEST("Failed to signal to child process");
         [ #  # ][ #  # ]
    1253                 :            : 
    1254                 :            :     char result[256];
    1255         [ +  - ]:          2 :     int r = read(fds[1], result, sizeof(result));
    1256         [ +  - ]:          2 :     if (r == -1) {
    1257         [ +  - ]:          2 :         if (errno == EAGAIN) {
    1258                 :            :             // Good.
    1259                 :          2 :             result[0] = 'y';
    1260                 :            :         } else {
    1261                 :            :             // Error.
    1262 [ #  # ][ #  # ]:          0 :             tout << "errno=" << errno << ": " << errno_to_string(errno) << endl;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1263                 :          0 :             result[0] = 'e';
    1264                 :            :         }
    1265                 :          2 :         r = 1;
    1266         [ #  # ]:          0 :     } else if (r >= 1) {
    1267         [ #  # ]:          0 :         if (result[0] == 'y') {
    1268                 :            :             // Child process managed to also get write lock!
    1269                 :          0 :             result[0] = '!';
    1270                 :            :         }
    1271                 :            :     } else {
    1272                 :            :         // EOF.
    1273                 :          0 :         result[0] = 'z';
    1274                 :          0 :         r = 1;
    1275                 :            :     }
    1276                 :            : 
    1277                 :            :     try {
    1278         [ +  - ]:          2 :         db.close();
    1279                 :          0 :     } catch (...) {
    1280                 :          0 :         kill(child, SIGKILL);
    1281                 :            :         int status;
    1282   [ #  #  #  # ]:          0 :         while (waitpid(child, &status, 0) < 0) {
    1283         [ #  # ]:          0 :             if (errno != EINTR) break;
    1284                 :            :         }
    1285                 :          0 :         throw;
    1286                 :            :     }
    1287                 :            : 
    1288         [ +  - ]:          2 :     if (result[0] == 'y') {
    1289                 :            : retry:
    1290                 :            :         struct timeval tv;
    1291                 :          2 :         tv.tv_sec = 3;
    1292                 :          2 :         tv.tv_usec = 0;
    1293                 :            :         fd_set fdset;
    1294                 :          2 :         FD_ZERO(&fdset);
    1295                 :          2 :         FD_SET(fds[1], &fdset);
    1296         [ +  - ]:          2 :         int sr = select(fds[1] + 1, &fdset, NULL, NULL, &tv);
    1297         [ -  + ]:          2 :         if (sr == 0) {
    1298                 :            :             // Timed out.
    1299                 :          0 :             result[0] = 'T';
    1300                 :          0 :             r = 1;
    1301         [ -  + ]:          2 :         } else if (sr == -1) {
    1302 [ #  # ][ #  # ]:          0 :             if (errno == EINTR || errno == EAGAIN)
    1303                 :            :                 goto retry;
    1304 [ #  # ][ #  # ]:          0 :             tout << "select() failed with errno=" << errno << ": "
                 [ #  # ]
    1305 [ #  # ][ #  # ]:          0 :                  << errno_to_string(errno) << endl;
                 [ #  # ]
    1306                 :          0 :             result[0] = 'S';
    1307                 :          0 :             r = 1;
    1308                 :            :         } else {
    1309         [ +  - ]:          2 :             r = read(fds[1], result, sizeof(result));
    1310         [ -  + ]:          2 :             if (r == -1) {
    1311                 :            :                 // Error.
    1312 [ #  # ][ #  # ]:          0 :                 tout << "read() failed with errno=" << errno << ": "
                 [ #  # ]
    1313 [ #  # ][ #  # ]:          0 :                      << errno_to_string(errno) << endl;
                 [ #  # ]
    1314                 :          0 :                 result[0] = 'R';
    1315                 :          0 :                 r = 1;
    1316         [ -  + ]:          2 :             } else if (r == 0) {
    1317                 :            :                 // EOF.
    1318                 :          0 :                 result[0] = 'Z';
    1319                 :          2 :                 r = 1;
    1320                 :            :             }
    1321                 :            :         }
    1322                 :            :     }
    1323                 :            : 
    1324         [ +  - ]:          2 :     close(fds[1]);
    1325                 :            : 
    1326                 :          2 :     kill(child, SIGKILL);
    1327                 :            :     int status;
    1328 [ +  - ][ -  + ]:          2 :     while (waitpid(child, &status, 0) < 0) {
    1329         [ #  # ]:          0 :         if (errno != EINTR) break;
    1330                 :            :     }
    1331                 :            : 
    1332 [ +  - ][ +  - ]:          2 :     tout << string(result, r) << endl;
                 [ +  - ]
    1333 [ -  + ][ #  # ]:          2 :     TEST_EQUAL(result[0], 'y');
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1334                 :            : #endif
    1335                 :            : 
    1336                 :          2 :     return true;
    1337                 :            : }
    1338                 :            : 
    1339                 :            : // Opening a WritableDatabase with low fds available - it should avoid them.
    1340                 :          1 : DEFINE_TESTCASE(dbfilefd012, glass) {
    1341                 :            : #if !defined __WIN32__ && !defined __CYGWIN__ && !defined __OS2__
    1342                 :            :     int oldfds[3];
    1343         [ +  + ]:          4 :     for (int i = 0; i < 3; ++i) {
    1344                 :          3 :         oldfds[i] = dup(i);
    1345                 :            :     }
    1346                 :            :     try {
    1347         [ +  + ]:          4 :         for (int j = 0; j < 3; ++j) {
    1348         [ +  - ]:          3 :             close(j);
    1349 [ -  + ][ #  # ]:          3 :             TEST_REL(lseek(j, 0, SEEK_CUR), <, 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1350 [ -  + ][ #  # ]:          3 :             TEST_EQUAL(errno, EBADF);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1351                 :            :         }
    1352                 :            : 
    1353 [ +  - ][ +  - ]:          1 :         Xapian::WritableDatabase db = get_writable_database();
    1354                 :            : 
    1355                 :            :         // Check we didn't use any of those low fds for tables, as that risks
    1356                 :            :         // data corruption if some other code in the same process tries to
    1357                 :            :         // write to them (see #651).
    1358         [ +  + ]:          4 :         for (int fd = 0; fd < 3; ++fd) {
    1359                 :            :             // Check that the fd is still closed, or isn't open O_RDWR (the
    1360                 :            :             // lock file gets opened O_WRONLY), or it's a pipe (if we're using
    1361                 :            :             // a child process to hold a non-OFD fcntl lock).
    1362         [ +  - ]:          3 :             int flags = fcntl(fd, F_GETFL);
    1363         [ +  + ]:          3 :             if (flags == -1) {
    1364 [ -  + ][ #  # ]:          2 :                 TEST_EQUAL(errno, EBADF);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1365         [ +  - ]:          1 :             } else if ((flags & O_ACCMODE) != O_RDWR) {
    1366                 :            :                 // OK.
    1367                 :            :             } else {
    1368                 :            :                 struct stat sb;
    1369 [ -  + ][ #  # ]:          1 :                 TEST_NOT_EQUAL(fstat(fd, &sb), -1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1370                 :            : #ifdef S_ISSOCK
    1371 [ -  + ][ #  # ]:          1 :                 TEST(S_ISSOCK(sb.st_mode));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1372                 :            : #else
    1373                 :            :                 // If we can't check it is a socket, at least check it is not a
    1374                 :            :                 // regular file.
    1375                 :            :                 TEST(!S_ISREG(sb.st_mode));
    1376                 :            : #endif
    1377                 :            :             }
    1378                 :          1 :         }
    1379                 :          0 :     } catch (...) {
    1380         [ #  # ]:          0 :         for (int j = 0; j < 3; ++j) {
    1381                 :          0 :             dup2(oldfds[j], j);
    1382         [ #  # ]:          0 :             close(oldfds[j]);
    1383                 :            :         }
    1384                 :          0 :         throw;
    1385                 :            :     }
    1386                 :            : 
    1387         [ +  + ]:          4 :     for (int j = 0; j < 3; ++j) {
    1388                 :          3 :         dup2(oldfds[j], j);
    1389         [ +  - ]:          3 :         close(oldfds[j]);
    1390                 :            :     }
    1391                 :            : #endif
    1392                 :            : 
    1393                 :          1 :     return true;
    1394                 :            : }
    1395                 :            : 
    1396                 :            : /// Regression test for #675, fixed in 1.3.3 and 1.2.21.
    1397                 :          1 : DEFINE_TESTCASE(cursorbug1, glass) {
    1398 [ +  - ][ +  - ]:          1 :     Xapian::WritableDatabase wdb = get_writable_database();
    1399         [ +  - ]:          2 :     Xapian::Database db = get_writable_database_as_database();
    1400         [ +  - ]:          2 :     Xapian::Enquire enq(db);
    1401         [ +  - ]:          1 :     enq.set_query(Xapian::Query::MatchAll);
    1402         [ +  - ]:          2 :     Xapian::MSet mset;
    1403                 :            :     // The original problem triggered for chert and glass on repeat==7.
    1404         [ +  + ]:         11 :     for (int repeat = 0; repeat < 10; ++repeat) {
    1405 [ +  - ][ +  - ]:         10 :         tout.str(string());
    1406 [ +  - ][ +  - ]:         10 :         tout << "iteration #" << repeat << endl;
                 [ +  - ]
    1407                 :            : 
    1408                 :         10 :         const int ITEMS = 10;
    1409         [ +  - ]:         10 :         int free_id = db.get_doccount();
    1410                 :         10 :         int offset = max(free_id, ITEMS * 2) - (ITEMS * 2);
    1411                 :         10 :         int limit = offset + (ITEMS * 2);
    1412                 :            : 
    1413 [ +  - ][ +  - ]:         10 :         mset = enq.get_mset(offset, limit);
    1414 [ +  - ][ +  - ]:        181 :         for (Xapian::MSetIterator m1 = mset.begin(); m1 != mset.end(); ++m1) {
                 [ +  + ]
    1415 [ +  - ][ +  - ]:        171 :             (void)m1.get_document().get_value(0);
    1416                 :         10 :         }
    1417                 :            : 
    1418         [ +  + ]:        120 :         for (int i = free_id; i <= free_id + ITEMS; ++i) {
    1419         [ +  - ]:        110 :             Xapian::Document doc;
    1420         [ +  - ]:        220 :             const string & id = str(i);
    1421         [ +  - ]:        220 :             string qterm = "Q" + id;
    1422         [ +  - ]:        110 :             doc.add_value(0, id);
    1423         [ +  - ]:        110 :             doc.add_boolean_term(qterm);
    1424         [ +  - ]:        110 :             wdb.replace_document(qterm, doc);
    1425                 :        110 :         }
    1426         [ +  - ]:         10 :         wdb.commit();
    1427                 :            : 
    1428         [ +  - ]:         10 :         db.reopen();
    1429 [ +  - ][ +  - ]:         10 :         mset = enq.get_mset(offset, limit);
    1430 [ +  - ][ +  - ]:        280 :         for (Xapian::MSetIterator m2 = mset.begin(); m2 != mset.end(); ++m2) {
                 [ +  + ]
    1431 [ +  - ][ +  - ]:        270 :             (void)m2.get_document().get_value(0);
    1432                 :         10 :         }
    1433                 :            :     }
    1434                 :            : 
    1435                 :          1 :     return true;
    1436                 :            : }
    1437                 :            : 
    1438                 :            : // Regression test for #674, fixed in 1.2.21 and 1.3.3.
    1439                 :          7 : DEFINE_TESTCASE(sortvalue2, backend) {
    1440 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1441 [ +  - ][ +  - ]:          7 :     db.add_database(get_database("apitest_simpledata2"));
                 [ +  - ]
    1442         [ +  - ]:         14 :     Xapian::Enquire enq(db);
    1443         [ +  - ]:          7 :     enq.set_query(Xapian::Query::MatchAll);
    1444         [ +  - ]:          7 :     enq.set_sort_by_value(0, false);
    1445         [ +  - ]:         14 :     Xapian::MSet mset = enq.get_mset(0, 50);
    1446                 :            : 
    1447                 :            :     // Check all results are in key order - the bug was that they were sorted
    1448                 :            :     // by docid instead with multiple remote databases.
    1449         [ +  - ]:         14 :     string old_key;
    1450 [ +  - ][ +  - ]:         63 :     for (Xapian::MSetIterator i = mset.begin(); i != mset.end(); ++i) {
                 [ +  + ]
    1451 [ +  - ][ +  - ]:         56 :         string key = db.get_document(*i).get_value(0);
                 [ +  - ]
    1452 [ +  - ][ -  + ]:         56 :         TEST(old_key <= key);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1453         [ +  - ]:         56 :         swap(old_key, key);
    1454                 :         63 :     }
    1455                 :          7 :     return true;
    1456                 :            : }
    1457                 :            : 
    1458                 :            : /// Check behaviour of Enquire::get_query().
    1459                 :          7 : DEFINE_TESTCASE(enquiregetquery1, backend) {
    1460 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1461         [ +  - ]:         14 :     Xapian::Enquire enq(db);
    1462 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(enq.get_query().get_description(), "Query()");
         [ +  - ][ -  + ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1463                 :          7 :     return true;
    1464                 :            : }
    1465                 :            : 
    1466                 :          1 : DEFINE_TESTCASE(embedded1, singlefile) {
    1467                 :            :     // In reality you should align the embedded database to a multiple of
    1468                 :            :     // database block size, but any offset is meant to work.
    1469                 :          1 :     off_t offset = 1234;
    1470                 :            : 
    1471 [ +  - ][ +  - ]:          1 :     Xapian::Database db = get_database("apitest_simpledata");
    1472 [ +  - ][ +  - ]:          2 :     const string & db_path = get_database_path("apitest_simpledata");
    1473         [ +  - ]:          2 :     const string & tmp_path = db_path + "-embedded";
    1474         [ +  - ]:          2 :     ofstream out(tmp_path, fstream::trunc|fstream::binary);
    1475         [ +  - ]:          1 :     out.seekp(offset);
    1476 [ +  - ][ +  - ]:          1 :     out << ifstream(db_path, fstream::binary).rdbuf();
                 [ +  - ]
    1477         [ +  - ]:          1 :     out.close();
    1478                 :            : 
    1479                 :            :     {
    1480         [ +  - ]:          1 :         int fd = open(tmp_path.c_str(), O_RDONLY|O_BINARY);
    1481                 :          1 :         lseek(fd, offset, SEEK_SET);
    1482         [ +  - ]:          1 :         Xapian::Database db_embedded(fd);
    1483 [ +  - ][ +  - ]:          1 :         TEST_EQUAL(db.get_doccount(), db_embedded.get_doccount());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1484                 :            :     }
    1485                 :            : 
    1486                 :            :     {
    1487         [ +  - ]:          1 :         int fd = open(tmp_path.c_str(), O_RDONLY|O_BINARY);
    1488                 :          1 :         lseek(fd, offset, SEEK_SET);
    1489                 :            :         size_t check_errors =
    1490         [ +  - ]:          1 :             Xapian::Database::check(fd, Xapian::DBCHECK_SHOW_STATS, &tout);
    1491 [ -  + ][ #  # ]:          1 :         TEST_EQUAL(check_errors, 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1492                 :            :     }
    1493                 :            : 
    1494                 :          1 :     return true;
    1495                 :            : }
    1496                 :            : 
    1497                 :            : /// Regression test for bug fixed in 1.3.7.
    1498                 :          7 : DEFINE_TESTCASE(exactxor1, backend) {
    1499 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1500         [ +  - ]:         14 :     Xapian::Enquire enq(db);
    1501                 :            : 
    1502                 :            :     static const char * const words[4] = {
    1503                 :            :         "blank", "test", "paragraph", "banana"
    1504                 :            :     };
    1505         [ +  - ]:         14 :     Xapian::Query q(Xapian::Query::OP_XOR, words, words + 4);
    1506         [ +  - ]:          7 :     enq.set_query(q);
    1507         [ +  - ]:          7 :     enq.set_weighting_scheme(Xapian::BoolWeight());
    1508         [ +  - ]:         14 :     Xapian::MSet mset = enq.get_mset(0, 0);
    1509                 :            :     // A reversed conditional gave us 5 in this case.
    1510 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_matches_upper_bound(), 6);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1511                 :            :     // Test improved lower bound in 1.3.7 (earlier versions gave 0).
    1512 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_matches_lower_bound(), 2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1513                 :            : 
    1514                 :            :     static const char * const words2[4] = {
    1515                 :            :         "queri", "test", "paragraph", "word"
    1516                 :            :     };
    1517         [ +  - ]:         14 :     Xapian::Query q2(Xapian::Query::OP_XOR, words2, words2 + 4);
    1518         [ +  - ]:          7 :     enq.set_query(q2);
    1519         [ +  - ]:          7 :     enq.set_weighting_scheme(Xapian::BoolWeight());
    1520 [ +  - ][ +  - ]:          7 :     mset = enq.get_mset(0, 0);
    1521                 :            :     // A reversed conditional gave us 6 in this case.
    1522 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_matches_upper_bound(), 5);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1523                 :            :     // Test improved lower bound in 1.3.7 (earlier versions gave 0).
    1524 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_matches_lower_bound(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1525                 :            : 
    1526                 :          7 :     return true;
    1527                 :            : }
    1528                 :            : 
    1529                 :            : /// Feature test for Database::get_revision().
    1530                 :          1 : DEFINE_TESTCASE(getrevision1, glass) {
    1531 [ +  - ][ +  - ]:          1 :     Xapian::WritableDatabase db = get_writable_database();
    1532 [ +  - ][ -  + ]:          1 :     TEST_EQUAL(db.get_revision(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1533         [ +  - ]:          1 :     db.commit();
    1534 [ +  - ][ -  + ]:          1 :     TEST_EQUAL(db.get_revision(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1535         [ +  - ]:          2 :     Xapian::Document doc;
    1536 [ +  - ][ +  - ]:          1 :     doc.add_term("hello");
    1537         [ +  - ]:          1 :     db.add_document(doc);
    1538 [ +  - ][ -  + ]:          1 :     TEST_EQUAL(db.get_revision(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1539         [ +  - ]:          1 :     db.commit();
    1540 [ +  - ][ -  + ]:          1 :     TEST_EQUAL(db.get_revision(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1541         [ +  - ]:          1 :     db.commit();
    1542 [ +  - ][ -  + ]:          1 :     TEST_EQUAL(db.get_revision(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1543         [ +  - ]:          1 :     db.add_document(doc);
    1544         [ +  - ]:          1 :     db.commit();
    1545 [ +  - ][ -  + ]:          1 :     TEST_EQUAL(db.get_revision(), 2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1546                 :          1 :     return true;
    1547                 :            : }
    1548                 :            : 
    1549                 :            : /// Feature test for DOC_ASSUME_VALID.
    1550                 :          7 : DEFINE_TESTCASE(getdocumentlazy1, backend) {
    1551 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1552         [ +  - ]:         14 :     Xapian::Document doc_lazy = db.get_document(2, Xapian::DOC_ASSUME_VALID);
    1553         [ +  - ]:         14 :     Xapian::Document doc = db.get_document(2);
    1554 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(doc.get_data(), doc_lazy.get_data());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1555 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(doc.get_value(0), doc_lazy.get_value(0));
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1556                 :          7 :     return true;
    1557                 :            : }
    1558                 :            : 
    1559                 :            : /// Feature test for DOC_ASSUME_VALID for a docid that doesn't actually exist.
    1560                 :          7 : DEFINE_TESTCASE(getdocumentlazy2, backend) {
    1561 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1562         [ +  - ]:         14 :     Xapian::Document doc;
    1563                 :            :     try {
    1564 [ +  - ][ +  + ]:          7 :         doc = db.get_document(db.get_lastdocid() + 1, Xapian::DOC_ASSUME_VALID);
         [ +  - ][ -  + ]
    1565         [ +  - ]:          2 :     } catch (const Xapian::DocNotFoundError&) {
    1566                 :            :         // DOC_ASSUME_VALID is really just a hint, so ignoring is OK (the
    1567                 :            :         // remote backend currently does).
    1568                 :            :     }
    1569 [ +  - ][ -  + ]:          7 :     TEST(doc.get_data().empty());
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1570 [ +  - ][ +  - ]:          7 :     TEST_EXCEPTION(Xapian::DocNotFoundError,
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ -  + ][ -  +  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
                 [ +  - ]
    1571                 :            :         doc = db.get_document(db.get_lastdocid() + 1);
    1572                 :            :     );
    1573                 :          7 :     return true;
    1574                 :            : }
    1575                 :            : 
    1576                 :            : static void
    1577                 :          5 : gen_uniqterms_gt_doclen_db(Xapian::WritableDatabase& db, const string&)
    1578                 :            : {
    1579         [ +  - ]:          5 :     Xapian::Document doc;
    1580 [ +  - ][ +  - ]:          5 :     doc.add_term("foo");
    1581 [ +  - ][ +  - ]:          5 :     doc.add_boolean_term("bar");
    1582         [ +  - ]:          5 :     db.add_document(doc);
    1583         [ +  - ]:         10 :     Xapian::Document doc2;
    1584 [ +  - ][ +  - ]:          5 :     doc2.add_posting("foo", 0, 2);
    1585 [ +  - ][ +  - ]:          5 :     doc2.add_term("foo2");
    1586 [ +  - ][ +  - ]:          5 :     doc2.add_boolean_term("baz");
    1587 [ +  - ][ +  - ]:          5 :     doc2.add_boolean_term("baz2");
    1588         [ +  - ]:         10 :     db.add_document(doc2);
    1589                 :          5 : }
    1590                 :            : 
    1591                 :          6 : DEFINE_TESTCASE(getuniqueterms1, generated) {
    1592                 :            :     Xapian::Database db =
    1593 [ +  - ][ +  - ]:          6 :         get_database("uniqterms_gt_doclen", gen_uniqterms_gt_doclen_db);
                 [ +  - ]
    1594                 :            : 
    1595         [ +  - ]:          6 :     auto unique1 = db.get_unique_terms(1);
    1596 [ +  - ][ -  + ]:          6 :     TEST_REL(unique1, <=, db.get_doclength(1));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1597 [ +  - ][ +  - ]:          6 :     TEST_REL(unique1, <, db.get_document(1).termlist_count());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1598                 :            :     // Ideally it'd be equal to 1, and in this case it is, but the current
    1599                 :            :     // backends can't always efficiently ensure an exact answer.
    1600 [ -  + ][ #  # ]:          6 :     TEST_REL(unique1, >=, 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1601                 :            : 
    1602         [ +  - ]:          6 :     auto unique2 = db.get_unique_terms(2);
    1603 [ +  - ][ -  + ]:          6 :     TEST_REL(unique2, <=, db.get_doclength(2));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1604 [ +  - ][ +  - ]:          6 :     TEST_REL(unique2, <, db.get_document(2).termlist_count());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1605                 :            :     // Ideally it'd be equal to 2, but the current backends can't always
    1606                 :            :     // efficiently ensure an exact answer and here it is actually 3.
    1607 [ -  + ][ #  # ]:          6 :     TEST_REL(unique2, >=, 2);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1608                 :            : 
    1609                 :          6 :     return true;
    1610                 :            : }
    1611                 :            : 
    1612                 :            : /** Regression test for bug fixed in 1.4.6.
    1613                 :            :  *
    1614                 :            :  *  OP_NEAR would think a term without positional information occurred at
    1615                 :            :  *  position 1 if it had the lowest term frequency amongst the OP_NEAR's
    1616                 :            :  *  subqueries.
    1617                 :            :  */
    1618                 :          6 : DEFINE_TESTCASE(nopositionbug1, generated) {
    1619                 :            :     Xapian::Database db =
    1620 [ +  - ][ +  - ]:          6 :         get_database("uniqterms_gt_doclen", gen_uniqterms_gt_doclen_db);
                 [ +  - ]
    1621                 :            : 
    1622                 :            :     // Test both orders.
    1623                 :            :     static const char* const terms1[] = { "foo", "baz" };
    1624                 :            :     static const char* const terms2[] = { "baz", "foo" };
    1625                 :            : 
    1626         [ +  - ]:         12 :     Xapian::Enquire enq(db);
    1627                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_NEAR,
    1628 [ +  - ][ +  - ]:          6 :                                 begin(terms1), end(terms1), 10));
    1629 [ +  - ][ +  - ]:          6 :     TEST_EQUAL(enq.get_mset(0, 5).size(), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1630                 :            : 
    1631                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_NEAR,
    1632 [ +  - ][ +  - ]:          6 :                                 begin(terms2), end(terms2), 10));
    1633 [ +  - ][ +  - ]:          6 :     TEST_EQUAL(enq.get_mset(0, 5).size(), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1634                 :            : 
    1635                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_PHRASE,
    1636 [ +  - ][ +  - ]:          6 :                                 begin(terms1), end(terms1), 10));
    1637 [ +  - ][ +  - ]:          6 :     TEST_EQUAL(enq.get_mset(0, 5).size(), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1638                 :            : 
    1639                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_PHRASE,
    1640 [ +  - ][ +  - ]:          6 :                                 begin(terms2), end(terms2), 10));
    1641 [ +  - ][ +  - ]:          6 :     TEST_EQUAL(enq.get_mset(0, 5).size(), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1642                 :            : 
    1643                 :            :     // Exercise exact phrase case too.
    1644                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_PHRASE,
    1645 [ +  - ][ +  - ]:          6 :                                 begin(terms1), end(terms1), 2));
    1646 [ +  - ][ +  - ]:          6 :     TEST_EQUAL(enq.get_mset(0, 5).size(), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1647                 :            : 
    1648                 :            :     enq.set_query(Xapian::Query(Xapian::Query::OP_PHRASE,
    1649 [ +  - ][ +  - ]:          6 :                                 begin(terms2), end(terms2), 2));
    1650 [ +  - ][ +  - ]:          6 :     TEST_EQUAL(enq.get_mset(0, 5).size(), 0);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1651                 :            : 
    1652                 :          6 :     return true;
    1653                 :            : }
    1654                 :            : 
    1655                 :            : /// Check estimate is rounded to suitable number of S.F. - new in 1.4.3.
    1656                 :          7 : DEFINE_TESTCASE(estimaterounding1, backend) {
    1657 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("etext");
    1658         [ +  - ]:         14 :     Xapian::Enquire enquire(db);
    1659 [ +  - ][ +  - ]:          7 :     enquire.set_query(Xapian::Query("the") | Xapian::Query("road"));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1660         [ +  - ]:         14 :     Xapian::MSet mset = enquire.get_mset(0, 10);
    1661                 :            :     // MSet::get_description() includes bounds and raw estimate.
    1662 [ +  - ][ +  - ]:          7 :     tout << mset.get_description() << endl;
                 [ +  - ]
    1663                 :            :     // Bounds are 411-439, raw estimate is 419.
    1664 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_matches_estimated() % 10, 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1665 [ +  - ][ +  - ]:          7 :     enquire.set_query(Xapian::Query("king") | Xapian::Query("prussia"));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1666 [ +  - ][ +  - ]:          7 :     mset = enquire.get_mset(0, 10);
    1667 [ +  - ][ +  - ]:          7 :     tout << mset.get_description() << endl;
                 [ +  - ]
    1668                 :            :     // Bounds are 111-138, raw estimate is 133.
    1669 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.get_matches_estimated() % 10, 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1670                 :          7 :     return true;
    1671                 :            : }
    1672                 :            : 
    1673                 :            : /** Check that a TermIterator returns the correct termfreqs.
    1674                 :            :  *
    1675                 :            :  *  Prior to 1.5.0, the termfreq was approximated in the multidatabase case.
    1676                 :            :  */
    1677                 :          7 : DEFINE_TESTCASE(termitertf1, backend) {
    1678 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1679         [ +  - ]:         14 :     Xapian::TermIterator t = db.termlist_begin(2);
    1680                 :            : 
    1681 [ +  - ][ +  - ]:          7 :     t.skip_to("mset");
    1682 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(*t, "mset");
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1683 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(t.get_termfreq(), 1);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1684                 :            : 
    1685 [ +  - ][ +  - ]:          7 :     t.skip_to("paragraph");
    1686 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(*t, "paragraph");
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1687 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(t.get_termfreq(), 5);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1688                 :            : 
    1689 [ +  - ][ +  - ]:          7 :     t.skip_to("queri");
    1690 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(*t, "queri");
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1691 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(t.get_termfreq(), 3);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1692                 :            : 
    1693                 :          7 :     return true;
    1694                 :            : }
    1695                 :            : 
    1696                 :            : /** Regression test for bug with get_mset(0, 0, N) (N > 0).
    1697                 :            :  *
    1698                 :            :  *  Fixed in 1.5.0 and 1.4.6.
    1699                 :            :  */
    1700                 :          7 : DEFINE_TESTCASE(checkatleast4, backend) {
    1701 [ +  - ][ +  - ]:          7 :     Xapian::Database db = get_database("apitest_simpledata");
    1702         [ +  - ]:         14 :     Xapian::Enquire enq(db);
    1703 [ +  - ][ +  - ]:          7 :     enq.set_query(Xapian::Query("paragraph"));
                 [ +  - ]
    1704                 :            :     // This used to cause access to an element in an empty vector.
    1705         [ +  - ]:         14 :     Xapian::MSet mset = enq.get_mset(0, 0, 4);
    1706 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(mset.size(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1707                 :          7 :     return true;
    1708                 :            : }
    1709                 :            : 
    1710                 :            : /// Regression test for glass bug fixed in 1.4.6 and 1.5.0.
    1711                 :          1 : DEFINE_TESTCASE(nodocs1, transactions && !remote) {
    1712                 :            :     {
    1713 [ +  - ][ +  - ]:          1 :         Xapian::WritableDatabase db = get_named_writable_database("nodocs1");
                 [ +  - ]
    1714 [ +  - ][ +  - ]:          1 :         db.set_metadata("foo", "bar");
                 [ +  - ]
    1715         [ +  - ]:          1 :         db.commit();
    1716         [ +  - ]:          2 :         Xapian::Document doc;
    1717 [ +  - ][ +  - ]:          1 :         doc.add_term("baz");
    1718         [ +  - ]:          1 :         db.add_document(doc);
    1719         [ +  - ]:          2 :         db.commit();
    1720                 :            :     }
    1721                 :            : 
    1722                 :            :     size_t check_errors =
    1723                 :            :         Xapian::Database::check(get_named_writable_database_path("nodocs1"),
    1724 [ +  - ][ +  - ]:          1 :                                 Xapian::DBCHECK_SHOW_STATS, &tout);
                 [ +  - ]
    1725 [ -  + ][ #  # ]:          1 :     TEST_EQUAL(check_errors, 0);
    1726                 :            : 
    1727                 :          1 :     return true;
    1728                 :            : }
    1729                 :            : 
    1730                 :            : /// Regression test for split position handling - broken in 1.4.8.
    1731                 :          5 : DEFINE_TESTCASE(splitpostings1, writable) {
    1732 [ +  - ][ +  - ]:          5 :     Xapian::WritableDatabase db = get_writable_database();
    1733         [ +  - ]:         10 :     Xapian::Document doc;
    1734                 :            :     // Add postings to create a split internally.
    1735         [ +  + ]:         60 :     for (Xapian::termpos pos = 0; pos <= 100; pos += 10) {
    1736 [ +  - ][ +  - ]:         55 :         doc.add_posting("foo", pos);
    1737                 :            :     }
    1738         [ +  + ]:         30 :     for (Xapian::termpos pos = 5; pos <= 100; pos += 20) {
    1739 [ +  - ][ +  - ]:         25 :         doc.add_posting("foo", pos);
    1740                 :            :     }
    1741         [ +  - ]:          5 :     db.add_document(doc);
    1742         [ +  - ]:          5 :     db.commit();
    1743                 :            : 
    1744                 :          5 :     Xapian::termpos expect = 0;
    1745                 :          5 :     Xapian::termpos pos = 0;
    1746 [ +  - ][ +  - ]:        255 :     for (auto p = db.positionlist_begin(1, "foo");
           [ +  -  +  + ]
    1747         [ +  - ]:        170 :          p != db.positionlist_end(1, "foo"); ++p) {
    1748 [ -  + ][ #  # ]:         80 :         TEST_REL(expect, <=, 100);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1749         [ +  - ]:         80 :         pos = *p;
    1750 [ -  + ][ #  # ]:         80 :         TEST_EQUAL(pos, expect);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1751                 :         80 :         expect += 5;
    1752         [ +  + ]:         80 :         if (expect % 20 == 15) expect += 5;
    1753                 :          5 :     }
    1754 [ -  + ][ #  # ]:          5 :     TEST_EQUAL(pos, 100);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1755                 :            : 
    1756                 :          5 :     return true;
    1757                 :            : }
    1758                 :            : 
    1759                 :            : /// Feature tests for Database::size().
    1760                 :          7 : DEFINE_TESTCASE(multidb1, backend) {
    1761         [ +  - ]:          7 :     Xapian::Database db;
    1762 [ +  - ][ -  + ]:          7 :     TEST_EQUAL(db.size(), 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1763 [ +  - ][ +  - ]:         14 :     Xapian::Database db2 = get_database("apitest_simpledata");
    1764 [ +  - ][ -  + ]:          7 :     TEST(db2.size() != 0);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1765         [ +  - ]:          7 :     db.add_database(db2);
    1766 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.size(), db2.size());
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1767         [ +  - ]:          7 :     db.add_database(db2);
    1768                 :            :     // Regression test for bug introduced and fixed in git master before 1.5.0.
    1769                 :            :     // Adding a multi database to an empty database incorrectly worked just
    1770                 :            :     // like assigning the database object.  The list of shards is now copied
    1771                 :            :     // instead.
    1772 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.size(), db2.size() * 2);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1773 [ +  - ][ +  - ]:          7 :     db.add_database(Xapian::Database());
    1774 [ +  - ][ +  - ]:          7 :     TEST_EQUAL(db.size(), db2.size() * 2);
         [ -  + ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1775                 :          7 :     return true;
    1776                 :            : }

Generated by: LCOV version 1.11