Branch data Line data Source code
1 : : /** @file api_matchspy.cc
2 : : * @brief tests of MatchSpy usage
3 : : */
4 : : /* Copyright 2007,2009 Lemur Consulting Ltd
5 : : * Copyright 2009 Olly Betts
6 : : * Copyright 2010 Richard Boulton
7 : : *
8 : : * This program is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU General Public License as
10 : : * published by the Free Software Foundation; either version 2 of the
11 : : * License, or (at your option) any later version.
12 : : *
13 : : * This program is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program; if not, write to the Free Software
20 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 : : * USA
22 : : */
23 : :
24 : : #include <config.h>
25 : :
26 : : #include "api_matchspy.h"
27 : :
28 : : #include <xapian.h>
29 : :
30 : : #include "str.h"
31 : : #include <cmath>
32 : : #include <map>
33 : : #include <vector>
34 : :
35 : : #include "backendmanager.h"
36 : : #include "testsuite.h"
37 : : #include "testutils.h"
38 : : #include "apitest.h"
39 : :
40 : : using namespace std;
41 : :
42 : : // #######################################################################
43 : : // # Tests start here
44 : :
45 [ # # ][ - + ]: 14 : class SimpleMatchSpy : public Xapian::MatchSpy {
46 : : public:
47 : : // Vector which will be filled with all the document contents seen.
48 : : std::vector<std::string> seen;
49 : :
50 : 42 : void operator()(const Xapian::Document &doc,
51 : : Xapian::weight) {
52 : : // Note that this is not recommended usage of get_data() - you
53 : : // generally shouldn't call get_data() from inside a MatchSpy, because
54 : : // it is (likely to be) a slow operation resulting in considerable IO.
55 : 42 : seen.push_back(doc.get_data());
56 : 42 : }
57 : : };
58 : :
59 : : // Basic test of a matchspy.
60 : 7 : DEFINE_TESTCASE(matchspy1, backend && !remote) {
61 : 7 : Xapian::Database db(get_database("apitest_simpledata"));
62 : 7 : Xapian::Enquire enquire(db);
63 : 7 : enquire.set_query(Xapian::Query("this"));
64 : :
65 : 7 : SimpleMatchSpy myspy;
66 : :
67 : 7 : Xapian::MSet nospymset = enquire.get_mset(0, 100);
68 : 7 : enquire.add_matchspy(&myspy);
69 : 7 : Xapian::MSet spymset = enquire.get_mset(0, 100);
70 : :
71 : : // Check that the match estimates aren't affected by the matchspy.
72 [ - + # # ]: 7 : TEST_EQUAL(nospymset, spymset);
73 : :
74 : 7 : vector<bool> docid_checked(db.get_lastdocid());
75 : :
76 : : // Check that we get the expected number of matches, and that the stored
77 : : // document contents are right.
78 : 7 : Xapian::MSetIterator i = spymset.begin();
79 [ - + ][ # # ]: 7 : TEST(i != spymset.end());
80 [ - + ][ # # ]: 7 : TEST_EQUAL(spymset.size(), 6);
81 [ - + ][ # # ]: 7 : TEST_EQUAL(myspy.seen.size(), spymset.size());
82 : :
83 : 7 : std::sort(myspy.seen.begin(), myspy.seen.end());
84 : :
85 : 7 : std::vector<std::string> seen2;
86 [ + + ]: 49 : for ( ; i != spymset.end(); ++i) {
87 : 42 : const Xapian::Document doc(i.get_document());
88 : 42 : seen2.push_back(doc.get_data());
89 : : }
90 : 7 : std::sort(seen2.begin(), seen2.end());
91 : :
92 [ - + # # ]: 7 : TEST_EQUAL(myspy.seen.size(), seen2.size());
93 : 7 : std::vector<std::string>::const_iterator j = myspy.seen.begin();
94 : 7 : std::vector<std::string>::const_iterator j2 = seen2.begin();
95 [ + + ]: 49 : for (; j != myspy.seen.end(); ++j, ++j2) {
96 [ - + ][ # # ]: 42 : TEST_EQUAL(*j, *j2);
97 : : }
98 : :
99 : 7 : return true;
100 : : }
101 : :
102 : 30 : static string values_to_repr(const Xapian::ValueCountMatchSpy & spy) {
103 : 30 : string resultrepr("|");
104 [ + + ]: 220 : for (Xapian::TermIterator i = spy.values_begin();
105 : : i != spy.values_end();
106 : : ++i) {
107 : 190 : resultrepr += *i;
108 : 190 : resultrepr += ':';
109 : 190 : resultrepr += str(i.get_termfreq());
110 : 190 : resultrepr += '|';
111 : 30 : }
112 : 0 : return resultrepr;
113 : : }
114 : :
115 : 10 : DEFINE_TESTCASE(matchspy2, writable)
116 : : {
117 [ + - ][ - + ]: 10 : if (get_dbtype() == "remotetcp" || get_dbtype() == "remoteprog") {
[ + - ][ # # ]
[ # # ][ + - ]
[ - + ]
118 [ # # ]: 0 : SKIP_TEST("Test not supported for remote backend");
119 : : }
120 : :
121 : 10 : Xapian::WritableDatabase db = get_writable_database();
122 [ + + ]: 260 : for (int c = 1; c <= 25; ++c) {
123 : 250 : Xapian::Document doc;
124 : 250 : doc.set_data("Document " + str(c));
125 : 250 : int factors = 0;
126 [ + + ]: 3500 : for (int factor = 1; factor <= c; ++factor) {
127 : 3250 : doc.add_term("all");
128 [ + + ]: 3250 : if (c % factor == 0) {
129 : 870 : doc.add_term("XFACT" + str(factor));
130 : 870 : ++factors;
131 : : }
132 : : }
133 : :
134 : : // Number of factors.
135 : 250 : doc.add_value(0, str(factors));
136 : : // Units digits.
137 : 250 : doc.add_value(1, str(c % 10));
138 : : // Constant.
139 : 250 : doc.add_value(2, "fish");
140 : : // Number of digits.
141 : 250 : doc.add_value(3, str(str(c).size()));
142 : :
143 : 250 : db.add_document(doc);
144 : : }
145 : :
146 : 10 : Xapian::ValueCountMatchSpy spy0(0);
147 : 10 : Xapian::ValueCountMatchSpy spy1(1);
148 : 10 : Xapian::ValueCountMatchSpy spy3(3);
149 : :
150 : 10 : Xapian::Enquire enq(db);
151 : :
152 : 10 : enq.set_query(Xapian::Query("all"));
153 : :
154 : 10 : enq.add_matchspy(&spy0);
155 : 10 : enq.add_matchspy(&spy1);
156 : 10 : enq.add_matchspy(&spy3);
157 : 10 : Xapian::MSet mset = enq.get_mset(0, 10);
158 : :
159 [ - + # # ]: 10 : TEST_EQUAL(spy0.get_total(), 25);
160 [ - + ][ # # ]: 10 : TEST_EQUAL(spy1.get_total(), 25);
161 [ - + ][ # # ]: 10 : TEST_EQUAL(spy3.get_total(), 25);
162 : :
163 : : static const char * results[] = {
164 : : "|1:1|2:9|3:3|4:7|5:1|6:3|8:1|",
165 : : "|0:2|1:3|2:3|3:3|4:3|5:3|6:2|7:2|8:2|9:2|",
166 : : "|1:9|2:16|",
167 : : };
168 [ - + ][ # # ]: 10 : TEST_STRINGS_EQUAL(values_to_repr(spy0), results[0]);
169 [ - + ][ # # ]: 10 : TEST_STRINGS_EQUAL(values_to_repr(spy1), results[1]);
170 [ - + ][ # # ]: 10 : TEST_STRINGS_EQUAL(values_to_repr(spy3), results[2]);
171 : :
172 : 10 : return true;
173 : : }
174 : :
175 : 10 : DEFINE_TESTCASE(matchspy4, writable)
176 : : {
177 [ + - ][ - + ]: 10 : if (get_dbtype() == "remotetcp" || get_dbtype() == "remoteprog") {
[ + - ][ # # ]
[ # # ][ + - ]
[ - + ]
178 [ # # ]: 0 : SKIP_TEST("Test not supported for remote backend");
179 : : }
180 : :
181 : 10 : Xapian::WritableDatabase db = get_writable_database();
182 [ + + ]: 260 : for (int c = 1; c <= 25; ++c) {
183 : 250 : Xapian::Document doc;
184 : 250 : doc.set_data("Document " + str(c));
185 : 250 : int factors = 0;
186 [ + + ]: 3500 : for (int factor = 1; factor <= c; ++factor) {
187 : 3250 : doc.add_term("all");
188 [ + + ]: 3250 : if (c % factor == 0) {
189 : 870 : doc.add_term("XFACT" + str(factor));
190 : 870 : ++factors;
191 : : }
192 : : }
193 : :
194 : : // Number of factors.
195 : 250 : doc.add_value(0, str(factors));
196 : : // Units digits.
197 : 250 : doc.add_value(1, str(c % 10));
198 : : // Constant.
199 : 250 : doc.add_value(2, "fish");
200 : : // Number of digits.
201 : 250 : doc.add_value(3, str(str(c).size()));
202 : :
203 : 250 : db.add_document(doc);
204 : : }
205 : :
206 : : // We're going to run the match twice - once sorted by relevance, and once
207 : : // sorted by a value. This is a regression test - the matcher used to fail
208 : : // to show some documents to the spy when sorting by non-pure-relevance.
209 : 10 : Xapian::ValueCountMatchSpy spya0(0);
210 : 10 : Xapian::ValueCountMatchSpy spya1(1);
211 : 10 : Xapian::ValueCountMatchSpy spya3(3);
212 : 10 : Xapian::ValueCountMatchSpy spyb0(0);
213 : 10 : Xapian::ValueCountMatchSpy spyb1(1);
214 : 10 : Xapian::ValueCountMatchSpy spyb3(3);
215 : :
216 : 10 : Xapian::Enquire enqa(db);
217 : 10 : Xapian::Enquire enqb(db);
218 : :
219 : 10 : enqa.set_query(Xapian::Query("all"));
220 : 10 : enqb.set_query(Xapian::Query("all"));
221 : :
222 : 10 : enqa.add_matchspy(&spya0);
223 : 10 : enqa.add_matchspy(&spya1);
224 : 10 : enqa.add_matchspy(&spya3);
225 : 10 : enqb.add_matchspy(&spyb0);
226 : 10 : enqb.add_matchspy(&spyb1);
227 : 10 : enqb.add_matchspy(&spyb3);
228 : :
229 : 10 : Xapian::MSet mseta = enqa.get_mset(0, 10);
230 : 10 : enqb.set_sort_by_value(0, false);
231 : 10 : Xapian::MSet msetb = enqb.get_mset(0, 10, 100);
232 : :
233 [ - + # # ]: 10 : TEST_EQUAL(spya0.get_total(), 25);
234 [ - + ][ # # ]: 10 : TEST_EQUAL(spya1.get_total(), 25);
235 [ - + ][ # # ]: 10 : TEST_EQUAL(spya3.get_total(), 25);
236 [ - + ][ # # ]: 10 : TEST_EQUAL(spyb0.get_total(), 25);
237 [ - + ][ # # ]: 10 : TEST_EQUAL(spyb1.get_total(), 25);
238 [ - + ][ # # ]: 10 : TEST_EQUAL(spyb3.get_total(), 25);
239 : :
240 : : static const char * results[] = {
241 : : "|2:9|4:7|3:3|6:3|1:1|5:1|8:1|",
242 : : "|1:3|2:3|3:3|4:3|5:3|0:2|6:2|7:2|8:2|9:2|",
243 : : "|",
244 : : "|2:16|1:9|",
245 : : "|2:9|4:7|3:3|6:3|1:1|5:1|8:1|",
246 : : "|1:3|2:3|3:3|4:3|5:3|0:2|6:2|7:2|8:2|9:2|",
247 : : "|",
248 : : "|2:16|1:9|",
249 : : NULL
250 : : };
251 : 10 : std::vector<Xapian::ValueCountMatchSpy *> spies;
252 : 10 : spies.push_back(&spya0);
253 : 10 : spies.push_back(&spya1);
254 : 10 : spies.push_back(NULL);
255 : 10 : spies.push_back(&spya3);
256 : 10 : spies.push_back(&spyb0);
257 : 10 : spies.push_back(&spyb1);
258 : 10 : spies.push_back(NULL);
259 : 10 : spies.push_back(&spyb3);
260 [ + + ]: 90 : for (Xapian::valueno v = 0; results[v]; ++v) {
261 : 80 : tout << "value " << v << endl;
262 : 80 : Xapian::ValueCountMatchSpy * spy = spies[v];
263 : 80 : string allvals_str("|");
264 [ + + ]: 80 : if (spy != NULL) {
265 : 60 : size_t allvals_size = 0;
266 [ + + ]: 440 : for (Xapian::TermIterator i = spy->top_values_begin(100);
267 : : i != spy->top_values_end(100);
268 : : ++i, ++allvals_size) {
269 : 380 : allvals_str += *i;
270 : 380 : allvals_str += ':';
271 : 380 : allvals_str += str(i.get_termfreq());
272 : 380 : allvals_str += '|';
273 : 60 : }
274 : 60 : tout << allvals_str << endl;
275 [ - + # # ]: 60 : TEST_STRINGS_EQUAL(allvals_str, results[v]);
276 : :
277 [ + + ]: 440 : for (size_t count = 0; count < allvals_size; ++count) {
278 : 380 : tout << "count " << count << endl;
279 [ + - ][ + + ]: 2100 : for (Xapian::TermIterator i = spy->top_values_begin(100),
[ + - ][ # # ]
[ # # ][ + - ]
[ + + ]
280 : 380 : j = spy->top_values_begin(count);
281 : : i != spy->top_values_end(100) &&
282 : : j != spy->top_values_end(count);
283 : : ++i, ++j) {
284 : 1340 : tout << "j " << j << endl;
285 [ - + ][ # # ]: 1340 : TEST_EQUAL(*i, *j);
286 [ - + ][ # # ]: 1340 : TEST_EQUAL(i.get_termfreq(), j.get_termfreq());
287 : 380 : }
288 : : }
289 : : }
290 : : }
291 : :
292 : 10 : return true;
293 : : }
294 : :
295 : : // Test builtin match spies
296 : 13 : DEFINE_TESTCASE(matchspy5, backend)
297 : : {
298 : 13 : Xapian::Database db(get_database("apitest_simpledata"));
299 : 13 : Xapian::Enquire enquire(db);
300 : 13 : enquire.set_query(Xapian::Query("this"));
301 : :
302 : 13 : Xapian::ValueCountMatchSpy myspy1(1);
303 : 13 : Xapian::ValueCountMatchSpy myspy2(1);
304 : :
305 : 13 : enquire.add_matchspy(&myspy1);
306 : 13 : enquire.add_matchspy(&myspy2);
307 : 13 : Xapian::MSet mymset = enquire.get_mset(0, 100);
308 [ - + # # ]: 13 : TEST_EQUAL(mymset.size(), 6);
309 : :
310 : 13 : Xapian::TermIterator i = myspy1.values_begin();
311 [ - + ][ # # ]: 13 : TEST(i != myspy1.values_end());
312 [ - + ][ # # ]: 13 : TEST(*i == "h");
313 [ - + ][ # # ]: 13 : TEST_EQUAL(i.get_termfreq(), 5);
314 : 13 : ++i;
315 [ - + ][ # # ]: 13 : TEST(i != myspy1.values_end());
316 [ - + ][ # # ]: 13 : TEST(*i == "n");
317 [ - + ][ # # ]: 13 : TEST_EQUAL(i.get_termfreq(), 1);
318 : 13 : ++i;
319 [ - + ][ # # ]: 13 : TEST(i == myspy1.values_end());
320 : :
321 : 13 : i = myspy2.values_begin();
322 [ - + ][ # # ]: 13 : TEST(i != myspy2.values_end());
323 [ - + ][ # # ]: 13 : TEST(*i == "h");
324 [ - + ][ # # ]: 13 : TEST_EQUAL(i.get_termfreq(), 5);
325 : 13 : ++i;
326 [ - + ][ # # ]: 13 : TEST(i != myspy2.values_end());
327 [ - + ][ # # ]: 13 : TEST(*i == "n");
328 [ - + ][ # # ]: 13 : TEST_EQUAL(i.get_termfreq(), 1);
329 : 13 : ++i;
330 [ - + ][ # # ]: 13 : TEST(i == myspy2.values_end());
331 : :
332 : 13 : return true;
333 : : }
334 : :
335 [ # # ][ - + ]: 2 : class MySpy : public Xapian::MatchSpy {
336 : 0 : void operator()(const Xapian::Document &, Xapian::weight) {
337 : 0 : }
338 : : };
339 : :
340 : : // Test exceptions from matchspy base class, and get_description method.
341 : 1 : DEFINE_TESTCASE(matchspy6, !backend)
342 : : {
343 : 1 : MySpy spy;
344 : :
345 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::UnimplementedError, spy.clone());
[ # # ][ - + ]
346 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::UnimplementedError, spy.name());
[ # # ][ - + ]
347 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::UnimplementedError, spy.serialise());
[ # # ][ - + ]
348 [ + - ][ - + ]: 4 : TEST_EXCEPTION(Xapian::UnimplementedError,
[ # # ][ - + ]
349 : : spy.unserialise(std::string(), Xapian::Registry()));
350 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::UnimplementedError, spy.serialise_results());
[ # # ][ - + ]
351 [ + - ][ - + ]: 3 : TEST_EXCEPTION(Xapian::UnimplementedError,
[ # # ][ - + ]
352 : : spy.merge_results(std::string()));
353 [ - + ][ # # ]: 1 : TEST_EQUAL(spy.get_description(), "Xapian::MatchSpy()");
354 : :
355 : 1 : return true;
356 : : }
|