Branch data Line data Source code
1 : : /** @file api_compact.cc
2 : : * @brief Tests of xapian-compact.
3 : : */
4 : : /* Copyright (C) 2009,2010,2011 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_compact.h"
26 : :
27 : : #include "apitest.h"
28 : : #include "dbcheck.h"
29 : : #include "testsuite.h"
30 : : #include "testutils.h"
31 : :
32 : : #include <xapian.h>
33 : :
34 : : #include <cstdlib>
35 : : #include <fstream>
36 : :
37 : : #include "str.h"
38 : : #include "utils.h"
39 : : #include "unixcmds.h"
40 : :
41 : : using namespace std;
42 : :
43 : : static void
44 : 12 : make_sparse_db(Xapian::WritableDatabase &db, const string & s)
45 : : {
46 : : // Need non-const pointer for strtoul(), but data isn't modified.
47 : 12 : char * p = const_cast<char *>(s.c_str());
48 : :
49 [ + - ]: 36 : while (*p) {
50 : 36 : bool del = (*p == '!');
51 [ + + ]: 36 : if (del) ++p;
52 : 36 : Xapian::docid first = strtoul(p, &p, 10);
53 : 36 : Xapian::docid last = first;
54 [ + + ]: 36 : if (*p == '-') {
55 : 12 : last = strtoul(p + 1, &p, 10);
56 : : }
57 [ + + ][ - + ]: 36 : if (*p && *p != ' ') {
58 : 0 : tout << p - s.c_str() << endl;
59 [ # # ]: 0 : FAIL_TEST("Bad sparse db spec (expected space): " << s);
60 : : }
61 [ - + ]: 36 : if (first > last) {
62 [ # # ]: 0 : FAIL_TEST("Bad sparse db spec (first > last): " << s);
63 : : }
64 : :
65 [ + + ]: 99 : do {
66 [ + + ]: 99 : if (del) {
67 : 6 : db.delete_document(first);
68 : : } else {
69 : 93 : Xapian::Document doc;
70 : 93 : string id = str(first);
71 : 93 : doc.set_data(id);
72 : 93 : doc.add_term("Q" + str(first));
73 : 93 : doc.add_term(string(first % 7 + 1, char((first % 26) + 'a')));
74 : 93 : db.replace_document(first, doc);
75 : : }
76 : : } while (first++ < last);
77 : :
78 [ + + ]: 36 : if (*p == '\0') break;
79 : 24 : ++p;
80 : : }
81 : :
82 : 12 : db.commit();
83 : 12 : }
84 : :
85 : : static void
86 : 12 : check_sparse_uid_terms(const string & path)
87 : : {
88 : 12 : Xapian::Database db(path);
89 : 12 : Xapian::TermIterator t;
90 [ + + ]: 267 : for (t = db.allterms_begin("Q"); t != db.allterms_end("Q"); ++t) {
91 : 255 : Xapian::docid did = atoi((*t).c_str() + 1);
92 : 255 : Xapian::PostingIterator p = db.postlist_begin(*t);
93 [ - + # # ]: 255 : TEST_EQUAL(*p, did);
94 : 12 : }
95 : 12 : }
96 : :
97 : 3 : DEFINE_TESTCASE(compactnorenumber1, generated) {
98 : : string a = get_database_path("compactnorenumber1a", make_sparse_db,
99 : 3 : "5-7 24 76 987 1023-1027 9999 !9999");
100 : 3 : string a_uuid;
101 : : {
102 : 3 : Xapian::Database db(a);
103 : 3 : a_uuid = db.get_uuid();
104 : : }
105 : : string b = get_database_path("compactnorenumber1b", make_sparse_db,
106 : 3 : "1027-1030");
107 : : string c = get_database_path("compactnorenumber1c", make_sparse_db,
108 : 3 : "1028-1040");
109 : : string d = get_database_path("compactnorenumber1d", make_sparse_db,
110 : 3 : "3000 999999 !999999");
111 : :
112 : 3 : string out = get_named_writable_database_path("compactnorenumber1out");
113 : :
114 : 3 : rm_rf(out);
115 : : {
116 : 3 : Xapian::Compactor compact;
117 : 3 : compact.set_renumber(false);
118 : 3 : compact.set_destdir(out);
119 : 3 : compact.add_source(a);
120 : 3 : compact.compact();
121 : : }
122 : :
123 : 3 : check_sparse_uid_terms(out);
124 : :
125 : : {
126 [ + + ]: 3 : if (get_dbtype() == "flint") {
127 : : // Flint creates its uuid file if it doesn't already exist, so we
128 : : // need a white box test to ensure xapian-compact created it.
129 [ - + ][ # # ]: 1 : TEST(file_exists(out + "/uuid"));
130 : : }
131 [ - + ][ # # ]: 3 : TEST(!dir_exists(out + "/donor"));
132 : 3 : Xapian::Database db(out);
133 : : // xapian-compact should change the UUID of the database, but didn't
134 : : // prior to 1.0.18/1.1.4.
135 : 3 : string out_uuid = db.get_uuid();
136 [ - + # # ]: 3 : TEST_NOT_EQUAL(a_uuid, out_uuid);
137 [ - + ][ # # ]: 3 : TEST_EQUAL(out_uuid.size(), 36);
138 [ - + ][ # # ]: 3 : TEST_NOT_EQUAL(out_uuid, "00000000-0000-0000-0000-000000000000");
139 : :
140 : : // White box test - ensure that the donor database is removed.
141 [ - + ][ # # ]: 3 : TEST(!dir_exists(out + "/donor"));
142 : : }
143 : :
144 : 3 : rm_rf(out);
145 : : {
146 : 3 : Xapian::Compactor compact;
147 : 3 : compact.set_renumber(false);
148 : 3 : compact.set_destdir(out);
149 : 3 : compact.add_source(a);
150 : 3 : compact.add_source(c);
151 : 3 : compact.compact();
152 : : }
153 : 3 : check_sparse_uid_terms(out);
154 : : {
155 : : // Check that xapian-compact is producing a consistent database. Also,
156 : : // regression test - xapian 1.1.4 set lastdocid to 0 in the output
157 : : // database.
158 : 3 : Xapian::Database outdb(out);
159 : 3 : dbcheck(outdb, 24, 9999);
160 : : }
161 : :
162 : 3 : rm_rf(out);
163 : : {
164 : 3 : Xapian::Compactor compact;
165 : 3 : compact.set_renumber(false);
166 : 3 : compact.set_destdir(out);
167 : 3 : compact.add_source(d);
168 : 3 : compact.add_source(a);
169 : 3 : compact.add_source(c);
170 : 3 : compact.compact();
171 : : }
172 : 3 : check_sparse_uid_terms(out);
173 : :
174 : 3 : rm_rf(out);
175 : : {
176 : 3 : Xapian::Compactor compact;
177 : 3 : compact.set_renumber(false);
178 : 3 : compact.set_destdir(out);
179 : 3 : compact.add_source(c);
180 : 3 : compact.add_source(a);
181 : 3 : compact.add_source(d);
182 : 3 : compact.compact();
183 : : }
184 : 3 : check_sparse_uid_terms(out);
185 : :
186 : : // Should fail.
187 : 3 : rm_rf(out);
188 : : {
189 : 3 : Xapian::Compactor compact;
190 : 3 : compact.set_renumber(false);
191 : 3 : compact.set_destdir(out);
192 : 3 : compact.add_source(a);
193 : 3 : compact.add_source(b);
194 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidOperationError, compact.compact());
[ # # ][ - + ]
195 : : }
196 : :
197 : : // Should fail.
198 : 3 : rm_rf(out);
199 : : {
200 : 3 : Xapian::Compactor compact;
201 : 3 : compact.set_renumber(false);
202 : 3 : compact.set_destdir(out);
203 : 3 : compact.add_source(b);
204 : 3 : compact.add_source(a);
205 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidOperationError, compact.compact());
[ # # ][ - + ]
206 : : }
207 : :
208 : : // Should fail.
209 : 3 : rm_rf(out);
210 : : {
211 : 3 : Xapian::Compactor compact;
212 : 3 : compact.set_renumber(false);
213 : 3 : compact.set_destdir(out);
214 : 3 : compact.add_source(a);
215 : 3 : compact.add_source(b);
216 : 3 : compact.add_source(d);
217 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidOperationError, compact.compact());
[ # # ][ - + ]
218 : : }
219 : :
220 : : // Should fail.
221 : 3 : rm_rf(out);
222 : : {
223 : 3 : Xapian::Compactor compact;
224 : 3 : compact.set_renumber(false);
225 : 3 : compact.set_destdir(out);
226 : 3 : compact.add_source(d);
227 : 3 : compact.add_source(b);
228 : 3 : compact.add_source(a);
229 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidOperationError, compact.compact());
[ # # ][ - + ]
230 : : }
231 : :
232 : : // Should fail.
233 : 3 : rm_rf(out);
234 : : {
235 : 3 : Xapian::Compactor compact;
236 : 3 : compact.set_renumber(false);
237 : 3 : compact.set_destdir(out);
238 : 3 : compact.add_source(b);
239 : 3 : compact.add_source(a);
240 : 3 : compact.add_source(d);
241 [ + - ][ - + ]: 6 : TEST_EXCEPTION(Xapian::InvalidOperationError, compact.compact());
[ # # ][ - + ]
242 : : }
243 : :
244 : 3 : return true;
245 : : }
246 : :
247 : : // Test use of compact to merge two databases.
248 : 3 : DEFINE_TESTCASE(compactmerge1, brass || chert || flint) {
249 : 3 : string indbpath = get_database_path("apitest_simpledata");
250 : 3 : string outdbpath = get_named_writable_database_path("compactmerge1out");
251 : 3 : rm_rf(outdbpath);
252 : :
253 : 3 : Xapian::Compactor compact;
254 : 3 : compact.set_destdir(outdbpath);
255 : 3 : compact.add_source(indbpath);
256 : 3 : compact.add_source(indbpath);
257 : 3 : compact.compact();
258 : :
259 : 3 : Xapian::Database indb(get_database("apitest_simpledata"));
260 : 3 : Xapian::Database outdb(outdbpath);
261 : :
262 [ - + # # ]: 3 : TEST_EQUAL(indb.get_doccount() * 2, outdb.get_doccount());
263 : 3 : dbcheck(outdb, outdb.get_doccount(), outdb.get_doccount());
264 : :
265 : 3 : return true;
266 : : }
267 : :
268 : : static void
269 : 3 : make_multichunk_db(Xapian::WritableDatabase &db, const string &)
270 : : {
271 : 3 : int count = 10000;
272 : :
273 : 3 : Xapian::Document doc;
274 : 3 : doc.add_term("a");
275 [ + + ]: 30003 : while (count) {
276 : 30000 : db.add_document(doc);
277 : 30000 : --count;
278 : : }
279 : :
280 : 3 : db.commit();
281 : 3 : }
282 : :
283 : : // Test use of compact on a database which has multiple chunks for a term.
284 : : // This is a regression test for ticket #427
285 : 3 : DEFINE_TESTCASE(compactmultichunks1, generated) {
286 : : string indbpath = get_database_path("compactmultichunks1in",
287 : 3 : make_multichunk_db, "");
288 : 3 : string outdbpath = get_named_writable_database_path("compactmultichunks1out");
289 : 3 : rm_rf(outdbpath);
290 : :
291 : 3 : Xapian::Compactor compact;
292 : 3 : compact.set_destdir(outdbpath);
293 : 3 : compact.add_source(indbpath);
294 : 3 : compact.compact();
295 : :
296 : 3 : Xapian::Database indb(indbpath);
297 : 3 : Xapian::Database outdb(outdbpath);
298 : :
299 [ - + # # ]: 3 : TEST_EQUAL(indb.get_doccount(), outdb.get_doccount());
300 : 3 : dbcheck(outdb, outdb.get_doccount(), outdb.get_doccount());
301 : :
302 : 3 : return true;
303 : : }
304 : :
305 : : // Test compacting from a stub database directory.
306 : 3 : DEFINE_TESTCASE(compactstub1, brass || chert || flint) {
307 : 3 : const char * stubpath = ".stub/compactstub1";
308 : 3 : const char * stubpathfile = ".stub/compactstub1/XAPIANDB";
309 : 3 : mkdir(".stub", 0755);
310 : 3 : mkdir(stubpath, 0755);
311 : 3 : ofstream stub(stubpathfile);
312 [ - + # # ]: 3 : TEST(stub.is_open());
313 : 3 : stub << "auto ../../" << get_database_path("apitest_simpledata") << endl;
314 : 3 : stub << "auto ../../" << get_database_path("apitest_simpledata2") << endl;
315 : 3 : stub.close();
316 : :
317 : 3 : string outdbpath = get_named_writable_database_path("compactstub1out");
318 : 3 : rm_rf(outdbpath);
319 : :
320 : 3 : Xapian::Compactor compact;
321 : 3 : compact.set_destdir(outdbpath);
322 : 3 : compact.add_source(stubpath);
323 : 3 : compact.compact();
324 : :
325 : 3 : Xapian::Database indb(stubpath);
326 : 3 : Xapian::Database outdb(outdbpath);
327 : :
328 [ - + # # ]: 3 : TEST_EQUAL(indb.get_doccount(), outdb.get_doccount());
329 : 3 : dbcheck(outdb, outdb.get_doccount(), outdb.get_doccount());
330 : :
331 : 3 : return true;
332 : : }
333 : :
334 : : // Test compacting from a stub database file.
335 : 3 : DEFINE_TESTCASE(compactstub2, brass || chert || flint) {
336 : 3 : const char * stubpath = ".stub/compactstub2";
337 : 3 : mkdir(".stub", 0755);
338 : 3 : ofstream stub(stubpath);
339 [ - + # # ]: 3 : TEST(stub.is_open());
340 : 3 : stub << "auto ../" << get_database_path("apitest_simpledata") << endl;
341 : 3 : stub << "auto ../" << get_database_path("apitest_simpledata2") << endl;
342 : 3 : stub.close();
343 : :
344 : 3 : string outdbpath = get_named_writable_database_path("compactstub2out");
345 : 3 : rm_rf(outdbpath);
346 : :
347 : 3 : Xapian::Compactor compact;
348 : 3 : compact.set_destdir(outdbpath);
349 : 3 : compact.add_source(stubpath);
350 : 3 : compact.compact();
351 : :
352 : 3 : Xapian::Database indb(stubpath);
353 : 3 : Xapian::Database outdb(outdbpath);
354 : :
355 [ - + # # ]: 3 : TEST_EQUAL(indb.get_doccount(), outdb.get_doccount());
356 : 3 : dbcheck(outdb, outdb.get_doccount(), outdb.get_doccount());
357 : :
358 : 3 : return true;
359 : : }
360 : :
361 : : // Test compacting a stub database file to itself.
362 : 3 : DEFINE_TESTCASE(compactstub3, brass || chert || flint) {
363 : 3 : const char * stubpath = ".stub/compactstub3";
364 : 3 : mkdir(".stub", 0755);
365 : 3 : ofstream stub(stubpath);
366 [ - + # # ]: 3 : TEST(stub.is_open());
367 : 3 : stub << "auto ../" << get_database_path("apitest_simpledata") << endl;
368 : 3 : stub << "auto ../" << get_database_path("apitest_simpledata2") << endl;
369 : 3 : stub.close();
370 : :
371 : : Xapian::doccount in_docs;
372 : : {
373 : 3 : Xapian::Database indb(stubpath);
374 : 3 : in_docs = indb.get_doccount();
375 : : }
376 : :
377 : 3 : Xapian::Compactor compact;
378 : 3 : compact.set_destdir(stubpath);
379 : 3 : compact.add_source(stubpath);
380 : 3 : compact.compact();
381 : :
382 : 3 : Xapian::Database outdb(stubpath);
383 : :
384 [ - + # # ]: 3 : TEST_EQUAL(in_docs, outdb.get_doccount());
385 : 3 : dbcheck(outdb, outdb.get_doccount(), outdb.get_doccount());
386 : :
387 : 3 : return true;
388 : : }
389 : :
390 : : // Test compacting a stub database directory to itself.
391 : 3 : DEFINE_TESTCASE(compactstub4, brass || chert || flint) {
392 : 3 : const char * stubpath = ".stub/compactstub4";
393 : 3 : const char * stubpathfile = ".stub/compactstub4/XAPIANDB";
394 : 3 : mkdir(".stub", 0755);
395 : 3 : mkdir(stubpath, 0755);
396 : 3 : ofstream stub(stubpathfile);
397 [ - + # # ]: 3 : TEST(stub.is_open());
398 : 3 : stub << "auto ../../" << get_database_path("apitest_simpledata") << endl;
399 : 3 : stub << "auto ../../" << get_database_path("apitest_simpledata2") << endl;
400 : 3 : stub.close();
401 : :
402 : : Xapian::doccount in_docs;
403 : : {
404 : 3 : Xapian::Database indb(stubpath);
405 : 3 : in_docs = indb.get_doccount();
406 : : }
407 : :
408 : 3 : Xapian::Compactor compact;
409 : 3 : compact.set_destdir(stubpath);
410 : 3 : compact.add_source(stubpath);
411 : 3 : compact.compact();
412 : :
413 : 3 : Xapian::Database outdb(stubpath);
414 : :
415 [ - + # # ]: 3 : TEST_EQUAL(in_docs, outdb.get_doccount());
416 : 3 : dbcheck(outdb, outdb.get_doccount(), outdb.get_doccount());
417 : :
418 : 3 : return true;
419 : : }
420 : :
421 : : static void
422 : 3 : make_all_tables(Xapian::WritableDatabase &db, const string &)
423 : : {
424 : 3 : Xapian::Document doc;
425 : 3 : doc.add_term("foo");
426 : 3 : db.add_document(doc);
427 : 3 : db.add_spelling("foo");
428 : 3 : db.add_synonym("foobar", "foo");
429 : :
430 : 3 : db.commit();
431 : 3 : }
432 : :
433 : : static void
434 : 3 : make_missing_tables(Xapian::WritableDatabase &db, const string &)
435 : : {
436 : 3 : Xapian::Document doc;
437 : 3 : doc.add_term("foo");
438 : 3 : db.add_document(doc);
439 : :
440 : 3 : db.commit();
441 : 3 : }
442 : :
443 : 3 : DEFINE_TESTCASE(compactmissingtables1, generated) {
444 : : string a = get_database_path("compactmissingtables1a",
445 : 3 : make_all_tables);
446 : : string b = get_database_path("compactmissingtables1b",
447 : 3 : make_missing_tables);
448 : :
449 : 3 : string out = get_named_writable_database_path("compactmissingtables1out");
450 : 3 : rm_rf(out);
451 : :
452 : 3 : Xapian::Compactor compact;
453 : 3 : compact.set_destdir(out);
454 : 3 : compact.add_source(a);
455 : 3 : compact.add_source(b);
456 : 3 : compact.compact();
457 : :
458 : : {
459 : 3 : Xapian::Database db(out);
460 [ - + ][ # # ]: 3 : TEST_NOT_EQUAL(db.spellings_begin(), db.spellings_end());
461 [ - + ][ # # ]: 3 : TEST_NOT_EQUAL(db.synonym_keys_begin(), db.synonym_keys_end());
462 : : // FIXME: arrange for input b to not have a termlist table.
463 : : // TEST_EXCEPTION(Xapian::FeatureUnavailableError, db.termlist_begin(1));
464 : : }
465 : :
466 : 3 : return true;
467 : : }
468 : :
|