Branch data Line data Source code
1 : : /** @file api_serialise.cc
2 : : * @brief Tests of serialisation functionality.
3 : : */
4 : : /* Copyright 2009 Lemur Consulting Ltd
5 : : * Copyright 2009 Olly Betts
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (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 USA
20 : : */
21 : :
22 : : #include <config.h>
23 : :
24 : : #include "api_serialise.h"
25 : :
26 : : #include <xapian.h>
27 : :
28 : : #include <exception>
29 : : #include <stdexcept>
30 : :
31 : : #include "apitest.h"
32 : : #include "testutils.h"
33 : :
34 : : using namespace std;
35 : :
36 : : // Test for serialising a document
37 : 1 : DEFINE_TESTCASE(serialise_document1, !backend) {
38 : 1 : Xapian::Document doc;
39 : 1 : doc.add_term("foo", 2);
40 : 1 : doc.add_posting("foo", 10);
41 : 1 : doc.add_value(1, "bar");
42 : 1 : doc.set_data("baz");
43 : :
44 : 1 : Xapian::Document doc2 = Xapian::Document::unserialise(doc.serialise());
45 : :
46 [ - + # # ]: 1 : TEST_EQUAL(doc.termlist_count(), doc2.termlist_count());
47 [ - + ][ # # ]: 1 : TEST_EQUAL(doc.termlist_count(), 1);
48 : 1 : Xapian::TermIterator i;
49 : 1 : Xapian::PositionIterator j;
50 : 1 : Xapian::ValueIterator k;
51 : :
52 : 1 : i = doc.termlist_begin();
53 [ - + ][ # # ]: 1 : TEST_NOT_EQUAL(i, doc.termlist_end());
54 [ - + ][ # # ]: 1 : TEST_EQUAL(i.get_wdf(), 3);
55 [ - + ][ # # ]: 1 : TEST_EQUAL(*i, "foo");
56 [ - + ][ # # ]: 1 : TEST_EQUAL(i.positionlist_count(), 1);
57 : 1 : j = i.positionlist_begin();
58 [ - + ][ # # ]: 1 : TEST_NOT_EQUAL(j, i.positionlist_end());
59 [ - + ][ # # ]: 1 : TEST_EQUAL(*j, 10);
60 : 1 : ++j;
61 [ - + ][ # # ]: 1 : TEST_EQUAL(j, i.positionlist_end());
62 : 1 : ++i;
63 [ - + ][ # # ]: 1 : TEST_EQUAL(i, doc.termlist_end());
64 : :
65 [ - + ][ # # ]: 1 : TEST_EQUAL(doc.values_count(), 1);
66 : 1 : k = doc.values_begin();
67 [ - + # # ]: 1 : TEST_NOT_EQUAL(k, doc.values_end());
68 [ - + ][ # # ]: 1 : TEST_EQUAL(k.get_valueno(), 1);
69 [ - + ][ # # ]: 1 : TEST_EQUAL(*k, "bar");
70 : 1 : ++k;
71 [ - + # # ]: 1 : TEST_EQUAL(k, doc.values_end());
72 : :
73 [ - + ][ # # ]: 1 : TEST_EQUAL(doc.get_data(), "baz");
74 : :
75 : 1 : i = doc2.termlist_begin();
76 [ - + ][ # # ]: 1 : TEST_NOT_EQUAL(i, doc2.termlist_end());
77 [ - + ][ # # ]: 1 : TEST_EQUAL(i.get_wdf(), 3);
78 [ - + ][ # # ]: 1 : TEST_EQUAL(*i, "foo");
79 [ - + ][ # # ]: 1 : TEST_EQUAL(i.positionlist_count(), 1);
80 : 1 : j = i.positionlist_begin();
81 [ - + ][ # # ]: 1 : TEST_NOT_EQUAL(j, i.positionlist_end());
82 [ - + ][ # # ]: 1 : TEST_EQUAL(*j, 10);
83 : 1 : ++j;
84 [ - + ][ # # ]: 1 : TEST_EQUAL(j, i.positionlist_end());
85 : 1 : ++i;
86 [ - + ][ # # ]: 1 : TEST_EQUAL(i, doc2.termlist_end());
87 : :
88 [ - + ][ # # ]: 1 : TEST_EQUAL(doc2.values_count(), 1);
89 : 1 : k = doc2.values_begin();
90 [ - + # # ]: 1 : TEST_NOT_EQUAL(k, doc2.values_end());
91 [ - + ][ # # ]: 1 : TEST_EQUAL(k.get_valueno(), 1);
92 [ - + ][ # # ]: 1 : TEST_EQUAL(*k, "bar");
93 : 1 : ++k;
94 [ - + # # ]: 1 : TEST_EQUAL(k, doc2.values_end());
95 : :
96 [ - + ][ # # ]: 1 : TEST_EQUAL(doc2.get_data(), "baz");
97 : :
98 : 1 : return true;
99 : : }
100 : :
101 : : // Test for serialising a document obtained from a database.
102 : 10 : DEFINE_TESTCASE(serialise_document2, writable) {
103 : 10 : Xapian::Document origdoc;
104 : 10 : origdoc.add_term("foo", 2);
105 : 10 : origdoc.add_posting("foo", 10);
106 : 10 : origdoc.add_value(1, "bar");
107 : 10 : origdoc.set_data("baz");
108 : 10 : Xapian::WritableDatabase db = get_writable_database();
109 : 10 : db.add_document(origdoc);
110 : :
111 : 10 : Xapian::Document doc = db.get_document(1);
112 : :
113 : 10 : Xapian::Document doc2 = Xapian::Document::unserialise(doc.serialise());
114 : :
115 [ - + # # ]: 10 : TEST_EQUAL(doc.termlist_count(), doc2.termlist_count());
116 [ - + ][ # # ]: 10 : TEST_EQUAL(doc.termlist_count(), 1);
117 : 10 : Xapian::TermIterator i;
118 : 10 : Xapian::PositionIterator j;
119 : 10 : Xapian::ValueIterator k;
120 : :
121 : 10 : i = doc.termlist_begin();
122 [ - + ][ # # ]: 10 : TEST_NOT_EQUAL(i, doc.termlist_end());
123 [ - + ][ # # ]: 10 : TEST_EQUAL(i.get_wdf(), 3);
124 [ - + ][ # # ]: 10 : TEST_EQUAL(*i, "foo");
125 [ - + ][ # # ]: 10 : TEST_EQUAL(i.positionlist_count(), 1);
126 : 10 : j = i.positionlist_begin();
127 [ - + ][ # # ]: 10 : TEST_NOT_EQUAL(j, i.positionlist_end());
128 [ - + ][ # # ]: 10 : TEST_EQUAL(*j, 10);
129 : 10 : ++j;
130 [ - + ][ # # ]: 10 : TEST_EQUAL(j, i.positionlist_end());
131 : 10 : ++i;
132 [ - + ][ # # ]: 10 : TEST_EQUAL(i, doc.termlist_end());
133 : :
134 [ - + ][ # # ]: 10 : TEST_EQUAL(doc.values_count(), 1);
135 : 10 : k = doc.values_begin();
136 [ - + # # ]: 10 : TEST_NOT_EQUAL(k, doc.values_end());
137 [ - + ][ # # ]: 10 : TEST_EQUAL(k.get_valueno(), 1);
138 [ - + ][ # # ]: 10 : TEST_EQUAL(*k, "bar");
139 : 10 : ++k;
140 [ - + # # ]: 10 : TEST_EQUAL(k, doc.values_end());
141 : :
142 [ - + ][ # # ]: 10 : TEST_EQUAL(doc.get_data(), "baz");
143 : :
144 : 10 : i = doc2.termlist_begin();
145 [ - + ][ # # ]: 10 : TEST_NOT_EQUAL(i, doc2.termlist_end());
146 [ - + ][ # # ]: 10 : TEST_EQUAL(i.get_wdf(), 3);
147 [ - + ][ # # ]: 10 : TEST_EQUAL(*i, "foo");
148 [ - + ][ # # ]: 10 : TEST_EQUAL(i.positionlist_count(), 1);
149 : 10 : j = i.positionlist_begin();
150 [ - + ][ # # ]: 10 : TEST_NOT_EQUAL(j, i.positionlist_end());
151 [ - + ][ # # ]: 10 : TEST_EQUAL(*j, 10);
152 : 10 : ++j;
153 [ - + ][ # # ]: 10 : TEST_EQUAL(j, i.positionlist_end());
154 : 10 : ++i;
155 [ - + ][ # # ]: 10 : TEST_EQUAL(i, doc2.termlist_end());
156 : :
157 [ - + ][ # # ]: 10 : TEST_EQUAL(doc2.values_count(), 1);
158 : 10 : k = doc2.values_begin();
159 [ - + # # ]: 10 : TEST_NOT_EQUAL(k, doc2.values_end());
160 [ - + ][ # # ]: 10 : TEST_EQUAL(k.get_valueno(), 1);
161 [ - + ][ # # ]: 10 : TEST_EQUAL(*k, "bar");
162 : 10 : ++k;
163 [ - + # # ]: 10 : TEST_EQUAL(k, doc2.values_end());
164 : :
165 [ - + ][ # # ]: 10 : TEST_EQUAL(doc2.get_data(), "baz");
166 : :
167 : 10 : return true;
168 : : }
169 : :
170 : : // Test for serialising a query
171 : 1 : DEFINE_TESTCASE(serialise_query1, !backend) {
172 : 1 : Xapian::Query q;
173 : 1 : Xapian::Query q2 = Xapian::Query::unserialise(q.serialise());
174 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
175 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query()");
176 : :
177 : 1 : q = Xapian::Query("hello");
178 : 1 : q2 = Xapian::Query::unserialise(q.serialise());
179 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
180 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(hello)");
181 : :
182 : 1 : q = Xapian::Query(Xapian::Query::OP_OR, Xapian::Query("hello"), Xapian::Query("world"));
183 : 1 : q2 = Xapian::Query::unserialise(q.serialise());
184 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
185 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query((hello OR world))");
186 : :
187 : 1 : return true;
188 : : }
189 : :
190 : : // Test for serialising a query which contains a PostingSource.
191 : 1 : DEFINE_TESTCASE(serialise_query2, !backend) {
192 : 1 : Xapian::ValueWeightPostingSource s1(10);
193 : 1 : Xapian::Query q(&s1);
194 : 1 : Xapian::Query q2 = Xapian::Query::unserialise(q.serialise());
195 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
196 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(Xapian::ValueWeightPostingSource(slot=10)))");
197 : :
198 : 1 : Xapian::ValueMapPostingSource s2(11);
199 : 1 : s2.set_default_weight(5.0);
200 : 1 : q = Xapian::Query(&s2);
201 : 1 : q2 = Xapian::Query::unserialise(q.serialise());
202 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
203 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(Xapian::ValueMapPostingSource(slot=11)))");
204 : :
205 : 1 : Xapian::FixedWeightPostingSource s3(5.5);
206 : 1 : q = Xapian::Query(&s3);
207 : 1 : q2 = Xapian::Query::unserialise(q.serialise());
208 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
209 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(Xapian::FixedWeightPostingSource(wt=5.5)))");
210 : :
211 : 1 : return true;
212 : : }
213 : :
214 : : // Test for unserialising a query using the default registry.
215 : 1 : DEFINE_TESTCASE(serialise_query3, !backend) {
216 : 1 : Xapian::ValueWeightPostingSource s1(10);
217 : 1 : Xapian::Query q(&s1);
218 : 1 : Xapian::Registry reg;
219 : 1 : Xapian::Query q2 = Xapian::Query::unserialise(q.serialise(), reg);
220 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
221 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(Xapian::ValueWeightPostingSource(slot=10)))");
222 : :
223 : 1 : Xapian::ValueMapPostingSource s2(11);
224 : 1 : s2.set_default_weight(5.0);
225 : 1 : q = Xapian::Query(&s2);
226 : 1 : q2 = Xapian::Query::unserialise(q.serialise(), reg);
227 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
228 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(Xapian::ValueMapPostingSource(slot=11)))");
229 : :
230 : 1 : Xapian::FixedWeightPostingSource s3(5.5);
231 : 1 : q = Xapian::Query(&s3);
232 : 1 : q2 = Xapian::Query::unserialise(q.serialise(), reg);
233 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
234 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(Xapian::FixedWeightPostingSource(wt=5.5)))");
235 : :
236 : 1 : return true;
237 : : }
238 : :
239 [ + - ][ - + ]: 8 : class MyPostingSource2 : public Xapian::ValuePostingSource {
240 : : std::string desc;
241 : : public:
242 : 8 : MyPostingSource2(const std::string & desc_)
243 : 8 : : Xapian::ValuePostingSource(0), desc(desc_)
244 : : {
245 : 8 : }
246 : :
247 : 5 : MyPostingSource2 * clone() const
248 : : {
249 : 5 : return new MyPostingSource2(desc);
250 : : }
251 : :
252 : 5 : std::string name() const {
253 : 5 : return "MyPostingSource2";
254 : : }
255 : :
256 : 1 : std::string serialise() const {
257 : 1 : return desc;
258 : : }
259 : :
260 : 1 : MyPostingSource2 * unserialise(const std::string & s) const {
261 : 1 : return new MyPostingSource2(s);
262 : : }
263 : :
264 : 0 : Xapian::weight get_weight() const { return 1.0; }
265 : :
266 : 3 : std::string get_description() const {
267 : 3 : return "MyPostingSource2(" + desc + ")";
268 : : }
269 : : };
270 : :
271 : : // Test for unserialising a query which contains a custom PostingSource.
272 : 1 : DEFINE_TESTCASE(serialise_query4, !backend) {
273 : 1 : MyPostingSource2 s1("foo");
274 : 1 : Xapian::Query q(&s1);
275 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), "Xapian::Query(PostingSource(MyPostingSource2(foo)))");
276 : 1 : std::string serialised = q.serialise();
277 : :
278 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::InvalidArgumentError, Xapian::Query::unserialise(serialised));
[ # # ][ - + ]
279 : 1 : Xapian::Registry reg;
280 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::InvalidArgumentError, Xapian::Query::unserialise(serialised, reg));
[ # # ][ - + ]
281 : :
282 : 1 : reg.register_posting_source(s1);
283 : 1 : Xapian::Query q2 = Xapian::Query::unserialise(serialised, reg);
284 [ - + ][ # # ]: 1 : TEST_EQUAL(q.get_description(), q2.get_description());
285 : :
286 : 1 : return true;
287 : : }
288 : :
289 : : /// Test for memory leaks when registering posting sources or weights twice.
290 : 1 : DEFINE_TESTCASE(double_register_leak, !backend) {
291 : 1 : MyPostingSource2 s1("foo");
292 : 1 : Xapian::BM25Weight w1;
293 : :
294 : 1 : Xapian::Registry reg;
295 : 1 : reg.register_posting_source(s1);
296 : 1 : reg.register_posting_source(s1);
297 : 1 : reg.register_posting_source(s1);
298 : :
299 : 1 : reg.register_weighting_scheme(w1);
300 : 1 : reg.register_weighting_scheme(w1);
301 : 1 : reg.register_weighting_scheme(w1);
302 : :
303 : 1 : return true;
304 : : }
305 : :
306 : : class ExceptionalPostingSource : public Xapian::PostingSource {
307 : : public:
308 : : typedef enum { NONE, CLONE, DTOR } failmode;
309 : :
310 : : failmode fail;
311 : :
312 : : PostingSource * & allocated;
313 : :
314 : 6 : ExceptionalPostingSource(failmode fail_, PostingSource * & allocated_)
315 : 6 : : fail(fail_), allocated(allocated_) { }
316 : :
317 : 6 : ~ExceptionalPostingSource() {
318 [ + + ][ - + ]: 6 : if (fail == DTOR) {
319 : 1 : throw runtime_error(string("arfle barfle gloop?"));
320 : : }
321 [ + - ][ - + ]: 6 : }
322 : :
323 : 4 : string name() const {
324 : 4 : return "ExceptionalPostingSource";
325 : : }
326 : :
327 : 3 : PostingSource * clone() const {
328 [ + + ]: 3 : if (fail == CLONE)
329 : 1 : throw bad_alloc();
330 : 2 : allocated = new ExceptionalPostingSource(fail, allocated);
331 : 2 : return allocated;
332 : : }
333 : :
334 : 0 : void init(const Xapian::Database &) { }
335 : :
336 : 0 : Xapian::doccount get_termfreq_min() const { return 0; }
337 : 0 : Xapian::doccount get_termfreq_est() const { return 1; }
338 : 0 : Xapian::doccount get_termfreq_max() const { return 2; }
339 : :
340 : 0 : void next(Xapian::weight) { }
341 : :
342 : 0 : void skip_to(Xapian::docid, Xapian::weight) { }
343 : :
344 : 0 : bool at_end() const { return true; }
345 : 0 : Xapian::docid get_docid() const { return 0; }
346 : : };
347 : :
348 : : /// Check that exceptions when registering a postingsource are handled well.
349 : 1 : DEFINE_TESTCASE(registry1, !backend) {
350 : : // Test that a replacement object throwing bad_alloc is handled.
351 : : {
352 : 1 : Xapian::Registry reg;
353 : :
354 : 1 : Xapian::PostingSource * ptr = NULL;
355 : 1 : ExceptionalPostingSource eps(ExceptionalPostingSource::NONE, ptr);
356 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::UnimplementedError, eps.serialise());
[ # # ][ - + ]
357 [ + - ][ - + ]: 3 : TEST_EXCEPTION(Xapian::UnimplementedError, eps.unserialise(string()));
[ # # ][ - + ]
358 : 1 : reg.register_posting_source(eps);
359 : : try {
360 : 1 : Xapian::PostingSource * ptr_clone = NULL;
361 : : ExceptionalPostingSource eps_clone(ExceptionalPostingSource::CLONE,
362 : 1 : ptr_clone);
363 : 1 : reg.register_posting_source(eps_clone);
364 : 1 : return false;
365 : 1 : } catch (const bad_alloc &) {
366 : : }
367 : :
368 : : // Either the old entry should be removed, or it should work.
369 : : const Xapian::PostingSource * p;
370 : 1 : p = reg.get_posting_source("ExceptionalPostingSource");
371 [ - + ]: 1 : if (p) {
372 [ # # ][ # # ]: 1 : TEST_EQUAL(p->name(), "ExceptionalPostingSource");
373 [ - + ][ + - ]: 1 : }
374 : : }
375 : :
376 : : // Test that the replaced object throwing runtime_error is handled.
377 : : {
378 : 1 : Xapian::Registry reg;
379 : :
380 : 1 : Xapian::PostingSource * ptr_dtor = NULL;
381 : : ExceptionalPostingSource eps_dtor(ExceptionalPostingSource::DTOR,
382 : 1 : ptr_dtor);
383 : 1 : reg.register_posting_source(eps_dtor);
384 : : // Prevent eps_dtor's dtor from throwing an exception.
385 : 1 : eps_dtor.fail = ExceptionalPostingSource::NONE;
386 : :
387 : : try {
388 : 1 : Xapian::PostingSource * ptr = NULL;
389 : 1 : ExceptionalPostingSource eps(ExceptionalPostingSource::NONE, ptr);
390 : 1 : reg.register_posting_source(eps);
391 : 1 : return false;
392 : 1 : } catch (const runtime_error &) {
393 : : }
394 : :
395 : : // Either the old entry should be removed, or it should work.
396 : : const Xapian::PostingSource * p;
397 : 1 : p = reg.get_posting_source("ExceptionalPostingSource");
398 [ - + ]: 1 : if (p) {
399 [ # # ][ # # ]: 0 : TEST_EQUAL(p->name(), "ExceptionalPostingSource");
400 : : }
401 : :
402 : : // Because the destructor threw an exception, the memory allocated for
403 : : // the object didn't get released.
404 [ - + ][ + - ]: 1 : operator delete(ptr_dtor);
405 : : }
406 : :
407 : 1 : return true;
408 : : }
409 : :
410 : : class ExceptionalWeight : public Xapian::Weight {
411 : : public:
412 : : typedef enum { NONE, CLONE, DTOR } failmode;
413 : :
414 : : failmode fail;
415 : :
416 : : Weight * & allocated;
417 : :
418 : 6 : ExceptionalWeight(failmode fail_, Weight * & allocated_)
419 : 6 : : fail(fail_), allocated(allocated_) { }
420 : :
421 : 6 : ~ExceptionalWeight() {
422 [ + + ][ - + ]: 6 : if (fail == DTOR) {
423 : 1 : throw runtime_error(string("arfle barfle gloop?"));
424 : : }
425 [ + - ][ - + ]: 6 : }
426 : :
427 : 4 : string name() const {
428 : 4 : return "ExceptionalWeight";
429 : : }
430 : :
431 : 3 : Weight * clone() const {
432 [ + + ]: 3 : if (fail == CLONE)
433 : 1 : throw bad_alloc();
434 : 2 : allocated = new ExceptionalWeight(fail, allocated);
435 : 2 : return allocated;
436 : : }
437 : :
438 : 0 : void init(double) { }
439 : :
440 : 0 : Xapian::weight get_sumpart(Xapian::termcount, Xapian::termcount) const {
441 : 0 : return 0;
442 : : }
443 : 0 : Xapian::weight get_maxpart() const { return 0; }
444 : :
445 : 0 : Xapian::weight get_sumextra(Xapian::termcount) const { return 0; }
446 : 0 : Xapian::weight get_maxextra() const { return 0; }
447 : : };
448 : :
449 : : /// Check that exceptions when registering are handled well.
450 : 1 : DEFINE_TESTCASE(registry2, !backend) {
451 : : // Test that a replacement object throwing bad_alloc is handled.
452 : : {
453 : 1 : Xapian::Registry reg;
454 : :
455 : 1 : Xapian::Weight * ptr = NULL;
456 : 1 : ExceptionalWeight ewt(ExceptionalWeight::NONE, ptr);
457 : 1 : reg.register_weighting_scheme(ewt);
458 : : try {
459 : 1 : Xapian::Weight * ptr_clone = NULL;
460 : : ExceptionalWeight ewt_clone(ExceptionalWeight::CLONE,
461 : 1 : ptr_clone);
462 : 1 : reg.register_weighting_scheme(ewt_clone);
463 : 1 : return false;
464 : 1 : } catch (const bad_alloc &) {
465 : : }
466 : :
467 : : // Either the old entry should be removed, or it should work.
468 : : const Xapian::Weight * p;
469 : 1 : p = reg.get_weighting_scheme("ExceptionalWeight");
470 [ - + ]: 1 : if (p) {
471 [ # # ][ # # ]: 1 : TEST_EQUAL(p->name(), "ExceptionalWeight");
472 [ - + ][ + - ]: 1 : }
473 : : }
474 : :
475 : : // Test that the replaced object throwing runtime_error is handled.
476 : : {
477 : 1 : Xapian::Registry reg;
478 : :
479 : 1 : Xapian::Weight * ptr_dtor = NULL;
480 : : ExceptionalWeight ewt_dtor(ExceptionalWeight::DTOR,
481 : 1 : ptr_dtor);
482 : 1 : reg.register_weighting_scheme(ewt_dtor);
483 : : // Prevent ewt_dtor's dtor from throwing an exception.
484 : 1 : ewt_dtor.fail = ExceptionalWeight::NONE;
485 : :
486 : : try {
487 : 1 : Xapian::Weight * ptr = NULL;
488 : 1 : ExceptionalWeight ewt(ExceptionalWeight::NONE, ptr);
489 : 1 : reg.register_weighting_scheme(ewt);
490 : 1 : return false;
491 : 1 : } catch (const runtime_error &) {
492 : : }
493 : :
494 : : // Either the old entry should be removed, or it should work.
495 : : const Xapian::Weight * p;
496 : 1 : p = reg.get_weighting_scheme("ExceptionalWeight");
497 [ - + ]: 1 : if (p) {
498 [ # # ][ # # ]: 0 : TEST_EQUAL(p->name(), "ExceptionalWeight");
499 : : }
500 : :
501 : : // Because the destructor threw an exception, the memory allocated for
502 : : // the object didn't get released.
503 [ - + ][ + - ]: 1 : operator delete(ptr_dtor);
504 : : }
505 : :
506 : 1 : return true;
507 : : }
508 : :
509 : : class ExceptionalMatchSpy : public Xapian::MatchSpy {
510 : : public:
511 : : typedef enum { NONE, CLONE, DTOR } failmode;
512 : :
513 : : failmode fail;
514 : :
515 : : MatchSpy * & allocated;
516 : :
517 : 6 : ExceptionalMatchSpy(failmode fail_, MatchSpy * & allocated_)
518 : 6 : : fail(fail_), allocated(allocated_) { }
519 : :
520 : 6 : ~ExceptionalMatchSpy() {
521 [ + + ][ - + ]: 6 : if (fail == DTOR) {
522 : 1 : throw runtime_error(string("arfle barfle gloop?"));
523 : : }
524 [ + - ][ - + ]: 6 : }
525 : :
526 : 4 : string name() const {
527 : 4 : return "ExceptionalMatchSpy";
528 : : }
529 : :
530 : 3 : MatchSpy * clone() const {
531 [ + + ]: 3 : if (fail == CLONE)
532 : 1 : throw bad_alloc();
533 : 2 : allocated = new ExceptionalMatchSpy(fail, allocated);
534 : 2 : return allocated;
535 : : }
536 : :
537 : 0 : void operator()(const Xapian::Document &, Xapian::weight) {
538 : 0 : }
539 : : };
540 : :
541 : : /// Check that exceptions when registering are handled well.
542 : 1 : DEFINE_TESTCASE(registry3, !backend) {
543 : : // Test that a replacement object throwing bad_alloc is handled.
544 : : {
545 : 1 : Xapian::Registry reg;
546 : :
547 : 1 : Xapian::MatchSpy * ptr = NULL;
548 : 1 : ExceptionalMatchSpy ems(ExceptionalMatchSpy::NONE, ptr);
549 : 1 : reg.register_match_spy(ems);
550 : : try {
551 : 1 : Xapian::MatchSpy * ptr_clone = NULL;
552 : : ExceptionalMatchSpy ems_clone(ExceptionalMatchSpy::CLONE,
553 : 1 : ptr_clone);
554 : 1 : reg.register_match_spy(ems_clone);
555 : 1 : return false;
556 : 1 : } catch (const bad_alloc &) {
557 : : }
558 : :
559 : : // Either the old entry should be removed, or it should work.
560 : : const Xapian::MatchSpy * p;
561 : 1 : p = reg.get_match_spy("ExceptionalMatchSpy");
562 [ - + ]: 1 : if (p) {
563 [ # # ][ # # ]: 1 : TEST_EQUAL(p->name(), "ExceptionalMatchSpy");
564 [ - + ][ + - ]: 1 : }
565 : : }
566 : :
567 : : // Test that the replaced object throwing runtime_error is handled.
568 : : {
569 : 1 : Xapian::Registry reg;
570 : :
571 : 1 : Xapian::MatchSpy * ptr_dtor = NULL;
572 : 1 : ExceptionalMatchSpy ems_dtor(ExceptionalMatchSpy::DTOR, ptr_dtor);
573 : 1 : reg.register_match_spy(ems_dtor);
574 : : // Prevent ems_dtor's dtor from throwing an exception.
575 : 1 : ems_dtor.fail = ExceptionalMatchSpy::NONE;
576 : :
577 : : try {
578 : 1 : Xapian::MatchSpy * ptr = NULL;
579 : 1 : ExceptionalMatchSpy ems(ExceptionalMatchSpy::NONE, ptr);
580 : 1 : reg.register_match_spy(ems);
581 : 1 : return false;
582 : 1 : } catch (const runtime_error &) {
583 : : }
584 : :
585 : : // Either the old entry should be removed, or it should work.
586 : : const Xapian::MatchSpy * p;
587 : 1 : p = reg.get_match_spy("ExceptionalMatchSpy");
588 [ - + ]: 1 : if (p) {
589 [ # # ][ # # ]: 0 : TEST_EQUAL(p->name(), "ExceptionalMatchSpy");
590 : : }
591 : :
592 : : // Because the destructor threw an exception, the memory allocated for
593 : : // the object didn't get released.
594 [ - + ][ + - ]: 1 : operator delete(ptr_dtor);
595 : : }
596 : :
597 : 1 : return true;
598 : : }
|