"Fossies" - the Fresh Open Source Software Archive

Member "xapian-core-1.4.14/tests/api_none.cc" (23 Nov 2019, 28293 Bytes) of package /linux/www/xapian-core-1.4.14.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "api_none.cc": 1.4.12_vs_1.4.13.

    1 /** @file api_none.cc
    2  * @brief tests which don't need a backend
    3  */
    4 /* Copyright (C) 2009 Richard Boulton
    5  * Copyright (C) 2009,2010,2011,2013,2014,2015,2016,2017,2018 Olly Betts
    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_none.h"
   26 
   27 #define XAPIAN_DEPRECATED(D) D
   28 #include <xapian.h>
   29 
   30 #include "apitest.h"
   31 #include "str.h"
   32 #include "testsuite.h"
   33 #include "testutils.h"
   34 
   35 using namespace std;
   36 
   37 // Check the version functions give consistent results.
   38 DEFINE_TESTCASE(version1, !backend) {
   39     string version = str(Xapian::major_version());
   40     version += '.';
   41     version += str(Xapian::minor_version());
   42     version += '.';
   43     version += str(Xapian::revision());
   44     TEST_EQUAL(Xapian::version_string(), version);
   45     return true;
   46 }
   47 
   48 // Regression test: various methods on Database() used to segfault or cause
   49 // division by 0.  Fixed in 1.1.4 and 1.0.18.  Ticket#415.
   50 DEFINE_TESTCASE(nosubdatabases1, !backend) {
   51     Xapian::Database db;
   52     TEST(db.get_metadata("foo").empty());
   53     TEST_EQUAL(db.metadata_keys_begin(), db.metadata_keys_end());
   54     TEST_EXCEPTION(Xapian::InvalidOperationError, db.termlist_begin(1));
   55     TEST_EQUAL(db.allterms_begin(), db.allterms_end());
   56     TEST_EQUAL(db.allterms_begin("foo"), db.allterms_end("foo"));
   57     TEST_EXCEPTION(Xapian::InvalidOperationError, db.positionlist_begin(1, "foo"));
   58     TEST_EQUAL(db.get_lastdocid(), 0);
   59     TEST_EQUAL(db.valuestream_begin(7), db.valuestream_end(7));
   60     TEST_EXCEPTION(Xapian::InvalidOperationError, db.get_doclength(1));
   61     TEST_EXCEPTION(Xapian::InvalidOperationError, db.get_unique_terms(1));
   62     TEST_EXCEPTION(Xapian::InvalidOperationError, db.get_document(1));
   63     return true;
   64 }
   65 
   66 /// Feature test for Document::add_boolean_term(), new in 1.0.18/1.1.4.
   67 DEFINE_TESTCASE(document1, !backend) {
   68     Xapian::Document doc;
   69     doc.add_boolean_term("Hxapian.org");
   70     TEST_EQUAL(doc.termlist_count(), 1);
   71     Xapian::TermIterator t = doc.termlist_begin();
   72     TEST(t != doc.termlist_end());
   73     TEST_EQUAL(*t, "Hxapian.org");
   74     TEST_EQUAL(t.get_wdf(), 0);
   75     TEST(++t == doc.termlist_end());
   76     doc.remove_term("Hxapian.org");
   77     TEST_EQUAL(doc.termlist_count(), 0);
   78     TEST(doc.termlist_begin() == doc.termlist_end());
   79     return true;
   80 }
   81 
   82 /// Regression test - the docid wasn't initialised prior to 1.0.22/1.2.4.
   83 DEFINE_TESTCASE(document2, !backend) {
   84     Xapian::Document doc;
   85     // The return value is uninitialised, so running under valgrind this
   86     // will fail reliably prior to the fix.
   87     TEST_EQUAL(doc.get_docid(), 0);
   88     return true;
   89 }
   90 
   91 /// Feature tests for Document::clear_terms().
   92 DEFINE_TESTCASE(documentclearterms1, !backend) {
   93     {
   94     Xapian::Document doc;
   95     doc.add_boolean_term("Hlocalhost");
   96     doc.add_term("hello");
   97     doc.add_term("there", 2);
   98     doc.add_posting("positional", 1);
   99     doc.add_posting("information", 2, 3);
  100     TEST_EQUAL(doc.termlist_count(), 5);
  101     TEST(doc.termlist_begin() != doc.termlist_end());
  102     doc.clear_terms();
  103     TEST_EQUAL(doc.termlist_count(), 0);
  104     TEST(doc.termlist_begin() == doc.termlist_end());
  105     // Test clear_terms() when there are no terms.
  106     doc.clear_terms();
  107     TEST_EQUAL(doc.termlist_count(), 0);
  108     TEST(doc.termlist_begin() == doc.termlist_end());
  109     }
  110 
  111     {
  112     // Test clear_terms() when there have never been any terms.
  113     Xapian::Document doc;
  114     doc.clear_terms();
  115     TEST_EQUAL(doc.termlist_count(), 0);
  116     TEST(doc.termlist_begin() == doc.termlist_end());
  117     }
  118 
  119     return true;
  120 }
  121 
  122 /// Feature tests for Document::clear_values().
  123 DEFINE_TESTCASE(documentclearvalues1, !backend) {
  124     {
  125     Xapian::Document doc;
  126     doc.add_value(37, "hello");
  127     doc.add_value(42, "world");
  128     TEST_EQUAL(doc.values_count(), 2);
  129     TEST(doc.values_begin() != doc.values_end());
  130     doc.clear_values();
  131     TEST_EQUAL(doc.values_count(), 0);
  132     TEST(doc.values_begin() == doc.values_end());
  133     // Test clear_values() when there are no values.
  134     doc.clear_values();
  135     TEST_EQUAL(doc.values_count(), 0);
  136     TEST(doc.values_begin() == doc.values_end());
  137     }
  138 
  139     {
  140     // Test clear_values() when there have never been any values.
  141     Xapian::Document doc;
  142     doc.clear_values();
  143     TEST_EQUAL(doc.values_count(), 0);
  144     TEST(doc.termlist_begin() == doc.termlist_end());
  145     }
  146 
  147     return true;
  148 }
  149 
  150 /// Feature tests for errors for empty terms.
  151 DEFINE_TESTCASE(documentemptyterm1, !backend) {
  152     Xapian::Document doc;
  153     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  154         doc.add_boolean_term(string()));
  155     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  156         doc.add_term(string()));
  157     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  158         doc.add_posting(string(), 1));
  159     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  160         doc.add_posting(string(), 2, 3));
  161     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  162         doc.remove_term(string()));
  163     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  164         doc.remove_posting(string(), 1));
  165     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  166         doc.remove_posting(string(), 2, 3));
  167     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  168         doc.remove_postings(string(), 2, 3));
  169     TEST_EXCEPTION(Xapian::InvalidArgumentError,
  170         doc.remove_postings(string(), 2, 3, 4));
  171     return true;
  172 }
  173 
  174 DEFINE_TESTCASE(emptyquery4, !backend) {
  175     // Test we get an empty query from applying any of the following ops to
  176     // an empty list of subqueries.
  177     Xapian::Query q;
  178     TEST(Xapian::Query(q.OP_AND, &q, &q).empty());
  179     TEST(Xapian::Query(q.OP_OR, &q, &q).empty());
  180     TEST(Xapian::Query(q.OP_AND_NOT, &q, &q).empty());
  181     TEST(Xapian::Query(q.OP_XOR, &q, &q).empty());
  182     TEST(Xapian::Query(q.OP_AND_MAYBE, &q, &q).empty());
  183     TEST(Xapian::Query(q.OP_FILTER, &q, &q).empty());
  184     TEST(Xapian::Query(q.OP_NEAR, &q, &q).empty());
  185     TEST(Xapian::Query(q.OP_PHRASE, &q, &q).empty());
  186     TEST(Xapian::Query(q.OP_ELITE_SET, &q, &q).empty());
  187     TEST(Xapian::Query(q.OP_SYNONYM, &q, &q).empty());
  188     TEST(Xapian::Query(q.OP_MAX, &q, &q).empty());
  189     return true;
  190 }
  191 
  192 DEFINE_TESTCASE(singlesubquery1, !backend) {
  193     // Test that we get just the subquery if we apply any of the following
  194     // ops to just that subquery.
  195 #define singlesubquery1_(OP) \
  196     TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
  197     "Query(test)")
  198     Xapian::Query q[1] = { Xapian::Query("test") };
  199     singlesubquery1_(OP_AND);
  200     singlesubquery1_(OP_OR);
  201     singlesubquery1_(OP_AND_NOT);
  202     singlesubquery1_(OP_XOR);
  203     singlesubquery1_(OP_AND_MAYBE);
  204     singlesubquery1_(OP_FILTER);
  205     singlesubquery1_(OP_NEAR);
  206     singlesubquery1_(OP_PHRASE);
  207     singlesubquery1_(OP_ELITE_SET);
  208     singlesubquery1_(OP_SYNONYM);
  209     singlesubquery1_(OP_MAX);
  210     return true;
  211 }
  212 
  213 DEFINE_TESTCASE(singlesubquery2, !backend) {
  214     // Like the previous test, but using MatchNothing as the subquery.
  215 #define singlesubquery2_(OP) \
  216     TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
  217     "Query()")
  218     Xapian::Query q[1] = { Xapian::Query::MatchNothing };
  219     singlesubquery2_(OP_AND);
  220     singlesubquery2_(OP_OR);
  221     singlesubquery2_(OP_AND_NOT);
  222     singlesubquery2_(OP_XOR);
  223     singlesubquery2_(OP_AND_MAYBE);
  224     singlesubquery2_(OP_FILTER);
  225     singlesubquery2_(OP_NEAR);
  226     singlesubquery2_(OP_PHRASE);
  227     singlesubquery2_(OP_ELITE_SET);
  228     singlesubquery2_(OP_SYNONYM);
  229     singlesubquery2_(OP_MAX);
  230     return true;
  231 }
  232 
  233 DEFINE_TESTCASE(singlesubquery3, !backend) {
  234     // Like the previous test, but using MatchAll as the subquery.
  235 #define singlesubquery3_(OP) \
  236     TEST_STRINGS_EQUAL(Xapian::Query(q->OP, q, q + 1).get_description(),\
  237     "Query(<alldocuments>)")
  238     Xapian::Query q[1] = { Xapian::Query::MatchAll };
  239     singlesubquery3_(OP_AND);
  240     singlesubquery3_(OP_OR);
  241     singlesubquery3_(OP_AND_NOT);
  242     singlesubquery3_(OP_XOR);
  243     singlesubquery3_(OP_AND_MAYBE);
  244     singlesubquery3_(OP_FILTER);
  245     // OP_NEAR and OP_PHRASE over MatchAll doesn't really make sense.
  246     singlesubquery3_(OP_ELITE_SET);
  247     singlesubquery3_(OP_SYNONYM);
  248     singlesubquery3_(OP_MAX);
  249     return true;
  250 }
  251 
  252 /// Check we no longer combine wqf for same term at the same position.
  253 DEFINE_TESTCASE(combinewqfnomore1, !backend) {
  254     Xapian::Query q(Xapian::Query::OP_OR,
  255             Xapian::Query("beer", 1, 1),
  256             Xapian::Query("beer", 1, 1));
  257     // Prior to 1.3.0, we would have given beer@2, but we decided that wasn't
  258     // really useful or helpful.
  259     TEST_EQUAL(q.get_description(), "Query((beer@1 OR beer@1))");
  260     return true;
  261 }
  262 
  263 class DestroyedFlag {
  264     bool & destroyed;
  265 
  266   public:
  267     DestroyedFlag(bool & destroyed_) : destroyed(destroyed_) {
  268     destroyed = false;
  269     }
  270 
  271     ~DestroyedFlag() {
  272     destroyed = true;
  273     }
  274 };
  275 
  276 class TestRangeProcessor : public Xapian::RangeProcessor {
  277     DestroyedFlag destroyed;
  278 
  279   public:
  280     TestRangeProcessor(bool & destroyed_)
  281     : Xapian::RangeProcessor(0), destroyed(destroyed_) { }
  282 
  283     Xapian::Query operator()(const std::string&, const std::string&)
  284     {
  285     return Xapian::Query::MatchAll;
  286     }
  287 };
  288 
  289 /// Check reference counting of user-subclassable classes.
  290 DEFINE_TESTCASE(subclassablerefcount1, !backend) {
  291     bool gone_auto, gone;
  292 
  293     // Simple test of release().
  294     {
  295     Xapian::RangeProcessor * rp = new TestRangeProcessor(gone);
  296     TEST(!gone);
  297     Xapian::QueryParser qp;
  298     qp.add_rangeprocessor(rp->release());
  299     TEST(!gone);
  300     }
  301     TEST(gone);
  302 
  303     // Check a second call to release() has no effect.
  304     {
  305     Xapian::RangeProcessor * rp = new TestRangeProcessor(gone);
  306     TEST(!gone);
  307     Xapian::QueryParser qp;
  308     qp.add_rangeprocessor(rp->release());
  309     rp->release();
  310     TEST(!gone);
  311     }
  312     TEST(gone);
  313 
  314     // Test reference counting works, and that a RangeProcessor with automatic
  315     // storage works OK.
  316     {
  317     TestRangeProcessor rp_auto(gone_auto);
  318     TEST(!gone_auto);
  319     {
  320         Xapian::QueryParser qp1;
  321         {
  322         Xapian::QueryParser qp2;
  323         Xapian::RangeProcessor * rp;
  324         rp = new TestRangeProcessor(gone);
  325         TEST(!gone);
  326         qp1.add_rangeprocessor(rp->release());
  327         TEST(!gone);
  328         qp2.add_rangeprocessor(rp);
  329         TEST(!gone);
  330         qp2.add_rangeprocessor(&rp_auto);
  331         TEST(!gone);
  332         TEST(!gone_auto);
  333         }
  334         TEST(!gone);
  335     }
  336     TEST(gone);
  337     TEST(!gone_auto);
  338     }
  339     TEST(gone_auto);
  340 
  341     // Regression test for initial implementation, where ~opt_intrusive_ptr()
  342     // checked the reference of the object, which may have already been deleted
  343     // if it wasn't been reference counted.
  344     {
  345     Xapian::QueryParser qp;
  346     {
  347         Xapian::RangeProcessor * rp = new TestRangeProcessor(gone);
  348         TEST(!gone);
  349         qp.add_rangeprocessor(rp);
  350         delete rp;
  351         TEST(gone);
  352     }
  353     // At the end of this block, qp is destroyed, but mustn't dereference
  354     // the pointer it has to rp.  If it does, that should get caught
  355     // when tests are run under valgrind.
  356     }
  357 
  358     return true;
  359 }
  360 
  361 class TestFieldProcessor : public Xapian::FieldProcessor {
  362     DestroyedFlag destroyed;
  363 
  364   public:
  365     TestFieldProcessor(bool & destroyed_) : destroyed(destroyed_) { }
  366 
  367     Xapian::Query operator()(const string &str) {
  368     return Xapian::Query(str);
  369     }
  370 };
  371 
  372 /// Check reference counting of user-subclassable classes.
  373 DEFINE_TESTCASE(subclassablerefcount2, !backend) {
  374     bool gone_auto, gone;
  375 
  376     // Simple test of release().
  377     {
  378     Xapian::FieldProcessor * proc = new TestFieldProcessor(gone);
  379     TEST(!gone);
  380     Xapian::QueryParser qp;
  381     qp.add_prefix("foo", proc->release());
  382     TEST(!gone);
  383     }
  384     TEST(gone);
  385 
  386     // Check a second call to release() has no effect.
  387     {
  388     Xapian::FieldProcessor * proc = new TestFieldProcessor(gone);
  389     TEST(!gone);
  390     Xapian::QueryParser qp;
  391     qp.add_prefix("foo", proc->release());
  392     proc->release();
  393     TEST(!gone);
  394     }
  395     TEST(gone);
  396 
  397     // Test reference counting works, and that a FieldProcessor with automatic
  398     // storage works OK.
  399     {
  400     TestFieldProcessor proc_auto(gone_auto);
  401     TEST(!gone_auto);
  402     {
  403         Xapian::QueryParser qp1;
  404         {
  405         Xapian::QueryParser qp2;
  406         Xapian::FieldProcessor * proc;
  407         proc = new TestFieldProcessor(gone);
  408         TEST(!gone);
  409         qp1.add_prefix("foo", proc->release());
  410         TEST(!gone);
  411         qp2.add_prefix("foo", proc);
  412         TEST(!gone);
  413         qp2.add_prefix("bar", &proc_auto);
  414         TEST(!gone);
  415         TEST(!gone_auto);
  416         }
  417         TEST(!gone);
  418     }
  419     TEST(gone);
  420     TEST(!gone_auto);
  421     }
  422     TEST(gone_auto);
  423 
  424     return true;
  425 }
  426 
  427 class TestMatchSpy : public Xapian::MatchSpy {
  428     DestroyedFlag destroyed;
  429 
  430   public:
  431     TestMatchSpy(bool & destroyed_) : destroyed(destroyed_) { }
  432 
  433     void operator()(const Xapian::Document &, double) { }
  434 };
  435 
  436 /// Check reference counting of MatchSpy.
  437 DEFINE_TESTCASE(subclassablerefcount3, backend) {
  438     Xapian::Database db = get_database("apitest_simpledata");
  439 
  440     bool gone_auto, gone;
  441 
  442     // Simple test of release().
  443     {
  444     Xapian::MatchSpy * spy = new TestMatchSpy(gone);
  445     TEST(!gone);
  446     Xapian::Enquire enquire(db);
  447     enquire.add_matchspy(spy->release());
  448     TEST(!gone);
  449     }
  450     TEST(gone);
  451 
  452     // Check a second call to release() has no effect.
  453     {
  454     Xapian::MatchSpy * spy = new TestMatchSpy(gone);
  455     TEST(!gone);
  456     Xapian::Enquire enquire(db);
  457     enquire.add_matchspy(spy->release());
  458     spy->release();
  459     TEST(!gone);
  460     }
  461     TEST(gone);
  462 
  463     // Test reference counting works, and that a MatchSpy with automatic
  464     // storage works OK.
  465     {
  466     TestMatchSpy spy_auto(gone_auto);
  467     TEST(!gone_auto);
  468     {
  469         Xapian::Enquire enq1(db);
  470         {
  471         Xapian::Enquire enq2(db);
  472         Xapian::MatchSpy * spy;
  473         spy = new TestMatchSpy(gone);
  474         TEST(!gone);
  475         enq1.add_matchspy(spy->release());
  476         TEST(!gone);
  477         enq2.add_matchspy(spy);
  478         TEST(!gone);
  479         enq2.add_matchspy(&spy_auto);
  480         TEST(!gone);
  481         TEST(!gone_auto);
  482         }
  483         TEST(!gone);
  484     }
  485     TEST(gone);
  486     TEST(!gone_auto);
  487     }
  488     TEST(gone_auto);
  489 
  490     return true;
  491 }
  492 
  493 class TestStopper : public Xapian::Stopper {
  494     DestroyedFlag destroyed;
  495 
  496   public:
  497     TestStopper(bool & destroyed_) : destroyed(destroyed_) { }
  498 
  499     bool operator()(const std::string&) const { return true; }
  500 };
  501 
  502 /// Check reference counting of Stopper with QueryParser.
  503 DEFINE_TESTCASE(subclassablerefcount4, !backend) {
  504     bool gone_auto, gone;
  505 
  506     // Simple test of release().
  507     {
  508     Xapian::Stopper * stopper = new TestStopper(gone);
  509     TEST(!gone);
  510     Xapian::QueryParser qp;
  511     qp.set_stopper(stopper->release());
  512     TEST(!gone);
  513     }
  514     TEST(gone);
  515 
  516     // Test that setting a new stopper causes the previous one to be released.
  517     {
  518     bool gone0;
  519     Xapian::Stopper * stopper0 = new TestStopper(gone0);
  520     TEST(!gone0);
  521     Xapian::QueryParser qp;
  522     qp.set_stopper(stopper0->release());
  523     TEST(!gone0);
  524 
  525     Xapian::Stopper * stopper = new TestStopper(gone);
  526     TEST(!gone);
  527     qp.set_stopper(stopper->release());
  528     TEST(gone0);
  529     TEST(!gone);
  530     }
  531     TEST(gone);
  532 
  533     // Check a second call to release() has no effect.
  534     {
  535     Xapian::Stopper * stopper = new TestStopper(gone);
  536     TEST(!gone);
  537     Xapian::QueryParser qp;
  538     qp.set_stopper(stopper->release());
  539     stopper->release();
  540     TEST(!gone);
  541     }
  542     TEST(gone);
  543 
  544     // Test reference counting works, and that a Stopper with automatic
  545     // storage works OK.
  546     {
  547     TestStopper stopper_auto(gone_auto);
  548     TEST(!gone_auto);
  549     {
  550         Xapian::QueryParser qp1;
  551         {
  552         Xapian::QueryParser qp2;
  553         Xapian::Stopper * stopper;
  554         stopper = new TestStopper(gone);
  555         TEST(!gone);
  556         qp1.set_stopper(stopper->release());
  557         TEST(!gone);
  558         qp2.set_stopper(stopper);
  559         TEST(!gone);
  560         qp2.set_stopper(&stopper_auto);
  561         TEST(!gone);
  562         TEST(!gone_auto);
  563         }
  564         TEST(!gone);
  565     }
  566     TEST(gone);
  567     TEST(!gone_auto);
  568     }
  569     TEST(gone_auto);
  570 
  571     return true;
  572 }
  573 
  574 /// Check reference counting of Stopper with TermGenerator.
  575 DEFINE_TESTCASE(subclassablerefcount5, !backend) {
  576     bool gone_auto, gone;
  577 
  578     // Simple test of release().
  579     {
  580     Xapian::Stopper * stopper = new TestStopper(gone);
  581     TEST(!gone);
  582     Xapian::TermGenerator indexer;
  583     indexer.set_stopper(stopper->release());
  584     TEST(!gone);
  585     }
  586     TEST(gone);
  587 
  588     // Test that setting a new stopper causes the previous one to be released.
  589     {
  590     bool gone0;
  591     Xapian::Stopper * stopper0 = new TestStopper(gone0);
  592     TEST(!gone0);
  593     Xapian::TermGenerator indexer;
  594     indexer.set_stopper(stopper0->release());
  595     TEST(!gone0);
  596 
  597     Xapian::Stopper * stopper = new TestStopper(gone);
  598     TEST(!gone);
  599     indexer.set_stopper(stopper->release());
  600     TEST(gone0);
  601     TEST(!gone);
  602     }
  603     TEST(gone);
  604 
  605     // Check a second call to release() has no effect.
  606     {
  607     Xapian::Stopper * stopper = new TestStopper(gone);
  608     TEST(!gone);
  609     Xapian::TermGenerator indexer;
  610     indexer.set_stopper(stopper->release());
  611     stopper->release();
  612     TEST(!gone);
  613     }
  614     TEST(gone);
  615 
  616     // Test reference counting works, and that a Stopper with automatic
  617     // storage works OK.
  618     {
  619     TestStopper stopper_auto(gone_auto);
  620     TEST(!gone_auto);
  621     {
  622         Xapian::TermGenerator indexer1;
  623         {
  624         Xapian::TermGenerator indexer2;
  625         Xapian::Stopper * stopper;
  626         stopper = new TestStopper(gone);
  627         TEST(!gone);
  628         indexer1.set_stopper(stopper->release());
  629         TEST(!gone);
  630         indexer2.set_stopper(stopper);
  631         TEST(!gone);
  632         indexer2.set_stopper(&stopper_auto);
  633         TEST(!gone);
  634         TEST(!gone_auto);
  635         }
  636         TEST(!gone);
  637     }
  638     TEST(gone);
  639     TEST(!gone_auto);
  640     }
  641     TEST(gone_auto);
  642 
  643     return true;
  644 }
  645 
  646 class TestKeyMaker : public Xapian::KeyMaker {
  647     DestroyedFlag destroyed;
  648 
  649   public:
  650     TestKeyMaker(bool & destroyed_) : destroyed(destroyed_) { }
  651 
  652     string operator()(const Xapian::Document&) const { return string(); }
  653 };
  654 
  655 /// Check reference counting of KeyMaker.
  656 DEFINE_TESTCASE(subclassablerefcount6, backend) {
  657     Xapian::Database db = get_database("apitest_simpledata");
  658 
  659     bool gone_auto, gone;
  660 
  661     // Simple test of release().
  662     {
  663     Xapian::KeyMaker * keymaker = new TestKeyMaker(gone);
  664     TEST(!gone);
  665     Xapian::Enquire enq(db);
  666     enq.set_sort_by_key(keymaker->release(), false);
  667     TEST(!gone);
  668     }
  669     TEST(gone);
  670 
  671     // Test that setting a new keymaker causes the previous one to be released.
  672     {
  673     bool gone0;
  674     Xapian::KeyMaker * keymaker0 = new TestKeyMaker(gone0);
  675     TEST(!gone0);
  676     Xapian::Enquire enq(db);
  677     enq.set_sort_by_key(keymaker0->release(), false);
  678     TEST(!gone0);
  679 
  680     Xapian::KeyMaker * keymaker = new TestKeyMaker(gone);
  681     TEST(!gone);
  682     enq.set_sort_by_key_then_relevance(keymaker->release(), false);
  683     TEST(gone0);
  684     TEST(!gone);
  685     }
  686     TEST(gone);
  687 
  688     // Check a second call to release() has no effect.
  689     {
  690     Xapian::KeyMaker * keymaker = new TestKeyMaker(gone);
  691     TEST(!gone);
  692     Xapian::Enquire enq(db);
  693     enq.set_sort_by_key(keymaker->release(), false);
  694     keymaker->release();
  695     TEST(!gone);
  696     }
  697     TEST(gone);
  698 
  699     // Test reference counting works, and that a KeyMaker with automatic
  700     // storage works OK.
  701     {
  702     TestKeyMaker keymaker_auto(gone_auto);
  703     TEST(!gone_auto);
  704     {
  705         Xapian::Enquire enq1(db);
  706         {
  707         Xapian::Enquire enq2(db);
  708         Xapian::KeyMaker * keymaker;
  709         keymaker = new TestKeyMaker(gone);
  710         TEST(!gone);
  711         enq1.set_sort_by_key(keymaker->release(), false);
  712         TEST(!gone);
  713         enq2.set_sort_by_relevance_then_key(keymaker, false);
  714         TEST(!gone);
  715         enq2.set_sort_by_key_then_relevance(&keymaker_auto, false);
  716         TEST(!gone);
  717         TEST(!gone_auto);
  718         }
  719         TEST(!gone);
  720     }
  721     TEST(gone);
  722     TEST(!gone_auto);
  723     }
  724     TEST(gone_auto);
  725 
  726     return true;
  727 }
  728 
  729 class TestExpandDecider : public Xapian::ExpandDecider {
  730     DestroyedFlag destroyed;
  731 
  732   public:
  733     TestExpandDecider(bool & destroyed_) : destroyed(destroyed_) { }
  734 
  735     bool operator()(const string&) const { return true; }
  736 };
  737 
  738 /// Check reference counting of ExpandDecider.
  739 DEFINE_TESTCASE(subclassablerefcount7, backend) {
  740     Xapian::Database db = get_database("apitest_simpledata");
  741     Xapian::Enquire enq(db);
  742     Xapian::RSet rset;
  743     rset.add_document(1);
  744 
  745     bool gone_auto, gone;
  746 
  747     for (int flags = 0;
  748      flags <= Xapian::Enquire::INCLUDE_QUERY_TERMS;
  749      flags += Xapian::Enquire::INCLUDE_QUERY_TERMS) {
  750     // Test of auto lifetime ExpandDecider.
  751     {
  752         TestExpandDecider edecider_auto(gone_auto);
  753         TEST(!gone_auto);
  754         (void)enq.get_eset(5, rset, 0, &edecider_auto);
  755         TEST(!gone_auto);
  756     }
  757     TEST(gone_auto);
  758 
  759     // Simple test of release().
  760     {
  761         Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
  762         TEST(!gone);
  763         (void)enq.get_eset(5, rset, 0, edecider);
  764         TEST(!gone);
  765         delete edecider;
  766         TEST(gone);
  767     }
  768 
  769     // Test that a released ExpandDecider gets cleaned up by get_eset().
  770     {
  771         Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
  772         TEST(!gone);
  773         (void)enq.get_eset(5, rset, 0, edecider->release());
  774         TEST(gone);
  775     }
  776 
  777     // Check a second call to release() has no effect.
  778     {
  779         Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
  780         TEST(!gone);
  781         edecider->release();
  782         TEST(!gone);
  783         (void)enq.get_eset(5, rset, 0, edecider->release());
  784         TEST(gone);
  785     }
  786     }
  787 
  788     // Test combinations of released/non-released with ExpandDeciderAnd.
  789     {
  790     TestExpandDecider edecider_auto(gone_auto);
  791     TEST(!gone_auto);
  792     Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
  793     TEST(!gone);
  794     (void)enq.get_eset(5, rset, 0,
  795         (new Xapian::ExpandDeciderAnd(
  796             &edecider_auto,
  797             edecider->release()))->release());
  798     TEST(!gone_auto);
  799     TEST(gone);
  800     }
  801     TEST(gone_auto);
  802     {
  803     TestExpandDecider edecider_auto(gone_auto);
  804     TEST(!gone_auto);
  805     Xapian::ExpandDecider * edecider = new TestExpandDecider(gone);
  806     TEST(!gone);
  807     (void)enq.get_eset(5, rset, 0,
  808         (new Xapian::ExpandDeciderAnd(
  809             edecider->release(),
  810             &edecider_auto))->release());
  811     TEST(!gone_auto);
  812     TEST(gone);
  813     }
  814     TEST(gone_auto);
  815 
  816     return true;
  817 }
  818 
  819 class TestValueRangeProcessor : public Xapian::ValueRangeProcessor {
  820     DestroyedFlag destroyed;
  821 
  822   public:
  823     TestValueRangeProcessor(bool & destroyed_) : destroyed(destroyed_) { }
  824 
  825     Xapian::valueno operator()(std::string &, std::string &) {
  826     return 42;
  827     }
  828 };
  829 
  830 /// Check reference counting of user-subclassable classes.
  831 DEFINE_TESTCASE(subclassablerefcount8, !backend) {
  832     bool gone_auto, gone;
  833 
  834     // Simple test of release().
  835     {
  836     Xapian::ValueRangeProcessor * vrp = new TestValueRangeProcessor(gone);
  837     TEST(!gone);
  838     Xapian::QueryParser qp;
  839     qp.add_valuerangeprocessor(vrp->release());
  840     TEST(!gone);
  841     }
  842     TEST(gone);
  843 
  844     // Check a second call to release() has no effect.
  845     {
  846     Xapian::ValueRangeProcessor * vrp = new TestValueRangeProcessor(gone);
  847     TEST(!gone);
  848     Xapian::QueryParser qp;
  849     qp.add_valuerangeprocessor(vrp->release());
  850     vrp->release();
  851     TEST(!gone);
  852     }
  853     TEST(gone);
  854 
  855     // Test reference counting works, and that a VRP with automatic storage
  856     // works OK.
  857     {
  858     TestValueRangeProcessor vrp_auto(gone_auto);
  859     TEST(!gone_auto);
  860     {
  861         Xapian::QueryParser qp1;
  862         {
  863         Xapian::QueryParser qp2;
  864         Xapian::ValueRangeProcessor * vrp;
  865         vrp = new TestValueRangeProcessor(gone);
  866         TEST(!gone);
  867         qp1.add_valuerangeprocessor(vrp->release());
  868         TEST(!gone);
  869         qp2.add_valuerangeprocessor(vrp);
  870         TEST(!gone);
  871         qp2.add_valuerangeprocessor(&vrp_auto);
  872         TEST(!gone);
  873         TEST(!gone_auto);
  874         }
  875         TEST(!gone);
  876     }
  877     TEST(gone);
  878     TEST(!gone_auto);
  879     }
  880     TEST(gone_auto);
  881 
  882     // Regression test for initial implementation, where ~opt_intrusive_ptr()
  883     // checked the reference of the object, which may have already been deleted
  884     // if it wasn't been reference counted.
  885     {
  886     Xapian::QueryParser qp;
  887     {
  888         Xapian::ValueRangeProcessor * vrp =
  889         new TestValueRangeProcessor(gone);
  890         TEST(!gone);
  891         qp.add_valuerangeprocessor(vrp);
  892         delete vrp;
  893         TEST(gone);
  894     }
  895     // At the end of this block, qp is destroyed, but mustn't dereference
  896     // the pointer it has to vrp.  If it does, that should get caught
  897     // when tests are run under valgrind.
  898     }
  899 
  900     return true;
  901 }
  902 
  903 /// Check encoding of non-UTF8 document data.
  904 DEFINE_TESTCASE(nonutf8docdesc1, !backend) {
  905     Xapian::Document doc;
  906     doc.set_data("\xc0\x80\xf5\x80\x80\x80\xfe\xff");
  907     TEST_EQUAL(doc.get_description(),
  908           "Document(data='\\xc0\\x80\\xf5\\x80\\x80\\x80\\xfe\\xff')");
  909     doc.set_data(string("\x00\x1f", 2));
  910     TEST_EQUAL(doc.get_description(),
  911           "Document(data='\\x00\\x1f')");
  912     // Check that backslashes are encoded so output isn't ambiguous.
  913     doc.set_data("back\\slash");
  914     TEST_EQUAL(doc.get_description(),
  915           "Document(data='back\\x5cslash')");
  916     return true;
  917 }
  918 
  919 /** Test removal of terms from a document while iterating over them.
  920  *
  921  *  Prior to 1.5.0 and 1.4.6 the underlying iterator was invalidated when
  922  *  preinc == false, leading to undefined behaviour (typically a segmentation
  923  *  fault).
  924  */
  925 DEFINE_TESTCASE(deletewhileiterating1, !backend) {
  926     for (bool preinc : { false, true }) {
  927     Xapian::Document doc;
  928     Xapian::TermGenerator indexer;
  929     indexer.set_document(doc);
  930     indexer.index_text("Pull the rug out from under ourselves", 1, "S");
  931     Xapian::TermIterator term_iterator = doc.termlist_begin();
  932     term_iterator.skip_to("S");
  933     while (term_iterator != doc.termlist_end()) {
  934         const string& term = *term_iterator;
  935         if (!startswith(term, "S")) {
  936         break;
  937         }
  938         if (preinc) ++term_iterator;
  939         doc.remove_term(term);
  940         if (!preinc) ++term_iterator;
  941     }
  942     TEST_EQUAL(doc.termlist_count(), 0);
  943     TEST(doc.termlist_begin() == doc.termlist_end());
  944     }
  945     return true;
  946 }
  947 
  948 /// Feature test for Document::remove_postings().
  949 DEFINE_TESTCASE(removepostings, !backend) {
  950     Xapian::Document doc;
  951     // Add Fibonacci sequence as positions.
  952     Xapian::termpos prev_pos = 1;
  953     Xapian::termpos pos = 1;
  954     while (pos < 1000) {
  955     doc.add_posting("foo", pos);
  956     auto new_pos = prev_pos + pos;
  957     prev_pos = pos;
  958     pos = new_pos;
  959     }
  960 
  961     // Check we added exactly one term.
  962     TEST_EQUAL(doc.termlist_count(), 1);
  963 
  964     Xapian::TermIterator t = doc.termlist_begin();
  965     auto num_pos = t.positionlist_count();
  966     TEST_EQUAL(t.get_wdf(), num_pos);
  967 
  968     // Out of order is a no-op.
  969     TEST_EQUAL(doc.remove_postings("foo", 2, 1), 0);
  970     t = doc.termlist_begin();
  971     TEST_EQUAL(t.positionlist_count(), num_pos);
  972     TEST_EQUAL(t.get_wdf(), num_pos);
  973 
  974     // 6 and 7 aren't in the sequence.
  975     TEST_EQUAL(doc.remove_postings("foo", 6, 7), 0);
  976     t = doc.termlist_begin();
  977     TEST_EQUAL(t.positionlist_count(), num_pos);
  978     TEST_EQUAL(t.get_wdf(), num_pos);
  979 
  980     // Beyond the end of the positions.
  981     TEST_EQUAL(doc.remove_postings("foo", 1000, 2000), 0);
  982     t = doc.termlist_begin();
  983     TEST_EQUAL(t.positionlist_count(), num_pos);
  984     TEST_EQUAL(t.get_wdf(), num_pos);
  985 
  986     // 1, 2, 3 are in the sequence, 4 isn't.
  987     TEST_EQUAL(doc.remove_postings("foo", 1, 4), 3);
  988     t = doc.termlist_begin();
  989     TEST_EQUAL(t.positionlist_count(), num_pos - 3);
  990     TEST_EQUAL(t.get_wdf(), num_pos - 3);
  991 
  992     // Remove the end position.
  993     TEST_EQUAL(doc.remove_postings("foo", 876, 987), 1);
  994     t = doc.termlist_begin();
  995     TEST_EQUAL(t.positionlist_count(), num_pos - 4);
  996     TEST_EQUAL(t.get_wdf(), num_pos - 4);
  997 
  998     // Remove a range in the middle.
  999     TEST_EQUAL(doc.remove_postings("foo", 33, 233), 5);
 1000     t = doc.termlist_begin();
 1001     TEST_EQUAL(t.positionlist_count(), num_pos - 9);
 1002     TEST_EQUAL(t.get_wdf(), num_pos - 9);
 1003 
 1004     // Check the expected positions are left.
 1005     t = doc.termlist_begin();
 1006     static const Xapian::termpos expected[] = { 5, 8, 13, 21, 377, 610, 9999 };
 1007     const Xapian::termpos* expect = expected;
 1008     for (auto p = t.positionlist_begin(); p != t.positionlist_end(); ++p) {
 1009     TEST_EQUAL(*p, *expect);
 1010     ++expect;
 1011     }
 1012 
 1013     return true;
 1014 }
 1015 
 1016 static void
 1017 errorcopyctor_helper(Xapian::Error& error)
 1018 {
 1019     // GCC 9 was giving a warning on the next line with -Wdeprecated-copy
 1020     // (which is enabled by -Wextra).
 1021     throw error;
 1022 }
 1023 
 1024 /// Regression test for warning with GCC 9.
 1025 DEFINE_TESTCASE(errorcopyctor, !backend) {
 1026     Xapian::RangeError e("test");
 1027     try {
 1028     errorcopyctor_helper(e);
 1029     } catch (Xapian::Error&) {
 1030     return true;
 1031     }
 1032     return false;
 1033 }