1 /* api_replicate.cc: tests of replication functionality
3 * Copyright 2008 Lemur Consulting Ltd
4 * Copyright 2009,2010,2011,2012,2013,2014,2015 Olly Betts
5 * Copyright 2010 Richard Boulton
6 * Copyright 2011 Dan Colish
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.
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.
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
26 #include "api_replicate.h"
29 #include "api/replication.h"
34 #include "filetests.h"
35 #include "safeerrno.h"
36 #include "safefcntl.h"
37 #include "safesysstat.h"
38 #include "safeunistd.h"
39 #include "testsuite.h"
40 #include "testutils.h"
43 #include <sys/types.h>
48 #include <stdlib.h> // For setenv() or putenv()
52 static void rmtmpdir(const string
& path
) {
56 static void mktmpdir(const string
& path
) {
58 if (mkdir(path
.c_str(), 0700) == -1 && errno
!= EEXIST
) {
59 FAIL_TEST("Can't make temporary directory");
63 static off_t
get_file_size(const string
& path
) {
64 off_t size
= file_size(path
);
66 FAIL_TEST("Can't stat '" + path
+ "'");
71 static size_t do_read(int fd
, char * p
, size_t desired
)
75 ssize_t c
= read(fd
, p
, desired
);
76 if (c
== 0) return total
;
78 if (errno
== EINTR
) continue;
79 FAIL_TEST("Error reading from file");
88 static void do_write(int fd
, const char * p
, size_t n
)
91 ssize_t c
= write(fd
, p
, n
);
93 if (errno
== EINTR
) continue;
94 FAIL_TEST("Error writing to file");
101 // Make a truncated copy of a file.
103 truncated_copy(const string
& srcpath
, const string
& destpath
, off_t tocopy
)
105 FD
fdin(open(srcpath
.c_str(), O_RDONLY
| O_BINARY
));
107 FAIL_TEST("Open failed (when opening '" + srcpath
+ "')");
110 FD
fdout(open(destpath
.c_str(), O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0666));
112 FAIL_TEST("Open failed (when creating '" + destpath
+ "')");
115 const int BUFSIZE
= 1024;
117 size_t total_bytes
= 0;
119 size_t thiscopy
= tocopy
> BUFSIZE
? BUFSIZE
: tocopy
;
120 size_t bytes
= do_read(fdin
, buf
, thiscopy
);
121 if (thiscopy
!= bytes
) {
122 FAIL_TEST("Couldn't read desired number of bytes from changeset");
125 total_bytes
+= bytes
;
126 do_write(fdout
, buf
, bytes
);
129 if (close(fdout
) == -1)
130 FAIL_TEST("Error closing file");
136 get_changeset(const string
& changesetpath
,
137 Xapian::DatabaseMaster
& master
,
138 Xapian::DatabaseReplica
& replica
,
139 int expected_changesets
,
140 int expected_fullcopies
,
141 bool expected_changed
,
142 bool full_copy
= false)
144 FD
fd(open(changesetpath
.c_str(), O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0666));
146 FAIL_TEST("Open failed (when creating a new changeset file at '"
147 + changesetpath
+ "')");
149 Xapian::ReplicationInfo info1
;
150 master
.write_changesets_to_fd(fd
,
151 full_copy
? "" : replica
.get_revision_info(),
154 TEST_EQUAL(info1
.changeset_count
, expected_changesets
);
155 TEST_EQUAL(info1
.fullcopy_count
, expected_fullcopies
);
156 TEST_EQUAL(info1
.changed
, expected_changed
);
160 apply_changeset(const string
& changesetpath
,
161 Xapian::DatabaseReplica
& replica
,
162 int expected_changesets
,
163 int expected_fullcopies
,
164 bool expected_changed
)
166 FD
fd(open(changesetpath
.c_str(), O_RDONLY
| O_BINARY
));
168 FAIL_TEST("Open failed (when reading changeset file at '"
169 + changesetpath
+ "')");
173 replica
.set_read_fd(fd
);
174 Xapian::ReplicationInfo info1
;
175 Xapian::ReplicationInfo info2
;
176 bool client_changed
= false;
177 while (replica
.apply_next_changeset(&info2
, 0)) {
179 info1
.changeset_count
+= info2
.changeset_count
;
180 info1
.fullcopy_count
+= info2
.fullcopy_count
;
182 client_changed
= true;
184 info1
.changeset_count
+= info2
.changeset_count
;
185 info1
.fullcopy_count
+= info2
.fullcopy_count
;
187 client_changed
= true;
189 TEST_EQUAL(info1
.changeset_count
, expected_changesets
);
190 TEST_EQUAL(info1
.fullcopy_count
, expected_fullcopies
);
191 TEST_EQUAL(client_changed
, expected_changed
);
195 // Replicate from the master to the replica.
196 // Returns the number of changesets which were applied.
198 replicate(Xapian::DatabaseMaster
& master
,
199 Xapian::DatabaseReplica
& replica
,
200 const string
& tempdir
,
201 int expected_changesets
,
202 int expected_fullcopies
,
203 bool expected_changed
,
204 bool full_copy
= false)
206 string changesetpath
= tempdir
+ "/changeset";
207 get_changeset(changesetpath
, master
, replica
,
212 return apply_changeset(changesetpath
, replica
,
218 // Check that the databases held at the given path are identical.
220 check_equal_dbs(const string
& masterpath
, const string
& replicapath
)
222 Xapian::Database
master(masterpath
);
223 Xapian::Database
replica(replicapath
);
225 TEST_EQUAL(master
.get_uuid(), master
.get_uuid());
226 dbcheck(replica
, master
.get_doccount(), master
.get_lastdocid());
228 for (Xapian::TermIterator t
= master
.allterms_begin();
229 t
!= master
.allterms_end(); ++t
) {
230 TEST_EQUAL(postlist_to_string(master
, *t
),
231 postlist_to_string(replica
, *t
));
235 #if 0 // Dynamic version which we don't currently need.
237 set_max_changesets(int count
) {
238 #ifdef HAVE__PUTENV_S
239 _putenv_s("XAPIAN_MAX_CHANGESETS", str(count
).c_str());
240 #elif defined HAVE_SETENV
241 setenv("XAPIAN_MAX_CHANGESETS", str(count
).c_str(), 1);
243 static char buf
[64] = "XAPIAN_MAX_CHANGESETS=";
244 sprintf(buf
+ CONST_STRLEN("XAPIAN_MAX_CHANGESETS="), "%d", count
);
250 #ifdef HAVE__PUTENV_S
251 # define set_max_changesets(N) _putenv_s("XAPIAN_MAX_CHANGESETS", #N)
252 #elif defined HAVE_SETENV
253 # define set_max_changesets(N) setenv("XAPIAN_MAX_CHANGESETS", #N, 1)
255 # define set_max_changesets(N) putenv(const_cast<char*>("XAPIAN_MAX_CHANGESETS="#N))
258 struct unset_max_changesets_helper_
{
259 unset_max_changesets_helper_() { }
260 ~unset_max_changesets_helper_() { set_max_changesets(0); }
263 // Ensure that we don't leave generation of changesets on for the next
264 // testcase, even if this one exits with an exception.
265 #define UNSET_MAX_CHANGESETS_AFTERWARDS unset_max_changesets_helper_ ezlxq
267 // #######################################################################
268 // # Tests start here
270 // Basic test of replication functionality.
271 DEFINE_TESTCASE(replicate1
, replicas
) {
272 UNSET_MAX_CHANGESETS_AFTERWARDS
;
273 string tempdir
= ".replicatmp";
275 string masterpath
= get_named_writable_database_path("master");
277 set_max_changesets(10);
279 Xapian::WritableDatabase
orig(get_named_writable_database("master"));
280 Xapian::DatabaseMaster
master(masterpath
);
281 string replicapath
= tempdir
+ "/replica";
282 Xapian::DatabaseReplica
replica(replicapath
);
284 // Add a document to the original database.
285 Xapian::Document doc1
;
286 doc1
.set_data(string("doc1"));
287 doc1
.add_posting("doc", 1);
288 doc1
.add_posting("one", 1);
289 orig
.add_document(doc1
);
292 // Apply the replication - we don't have changesets stored, so this should
293 // just do a database copy, and return a count of 1.
294 int count
= replicate(master
, replica
, tempdir
, 0, 1, true);
295 TEST_EQUAL(count
, 1);
297 Xapian::Database
dbcopy(replicapath
);
298 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
301 // Repeating the replication should return a count of 1, since no further
302 // changes should need to be applied.
303 count
= replicate(master
, replica
, tempdir
, 0, 0, false);
304 TEST_EQUAL(count
, 1);
306 Xapian::Database
dbcopy(replicapath
);
307 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
310 // Regression test - if the replica was reopened, a full copy always used
311 // to occur, whether it was needed or not. Fixed in revision #10117.
313 replica
= Xapian::DatabaseReplica(replicapath
);
314 count
= replicate(master
, replica
, tempdir
, 0, 0, false);
315 TEST_EQUAL(count
, 1);
317 Xapian::Database
dbcopy(replicapath
);
318 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
321 orig
.add_document(doc1
);
323 orig
.add_document(doc1
);
326 count
= replicate(master
, replica
, tempdir
, 2, 0, true);
327 TEST_EQUAL(count
, 3);
329 Xapian::Database
dbcopy(replicapath
);
330 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
333 check_equal_dbs(masterpath
, replicapath
);
335 // Need to close the replica before we remove the temporary directory on
342 // Test replication from a replicated copy.
343 DEFINE_TESTCASE(replicate2
, replicas
) {
344 SKIP_TEST_FOR_BACKEND("glass"); // Glass doesn't currently support this.
345 UNSET_MAX_CHANGESETS_AFTERWARDS
;
347 string tempdir
= ".replicatmp";
349 string masterpath
= get_named_writable_database_path("master");
351 set_max_changesets(10);
353 Xapian::WritableDatabase
orig(get_named_writable_database("master"));
354 Xapian::DatabaseMaster
master(masterpath
);
355 string replicapath
= tempdir
+ "/replica";
356 Xapian::DatabaseReplica
replica(replicapath
);
358 Xapian::DatabaseMaster
master2(replicapath
);
359 string replica2path
= tempdir
+ "/replica2";
360 Xapian::DatabaseReplica
replica2(replica2path
);
362 // Add a document to the original database.
363 Xapian::Document doc1
;
364 doc1
.set_data(string("doc1"));
365 doc1
.add_posting("doc", 1);
366 doc1
.add_posting("one", 1);
367 orig
.add_document(doc1
);
370 // Apply the replication - we don't have changesets stored, so this should
371 // just do a database copy, and return a count of 1.
372 TEST_EQUAL(replicate(master
, replica
, tempdir
, 0, 1, true), 1);
373 check_equal_dbs(masterpath
, replicapath
);
375 // Replicate from the replica.
376 TEST_EQUAL(replicate(master2
, replica2
, tempdir
, 0, 1, true), 1);
377 check_equal_dbs(masterpath
, replica2path
);
379 orig
.add_document(doc1
);
381 orig
.add_document(doc1
);
384 // Replicate from the replica - should have no changes.
385 TEST_EQUAL(replicate(master2
, replica2
, tempdir
, 0, 0, false), 1);
386 check_equal_dbs(replicapath
, replica2path
);
388 // Replicate, and replicate from the replica - should have 2 changes.
389 TEST_EQUAL(replicate(master
, replica
, tempdir
, 2, 0, 1), 3);
390 check_equal_dbs(masterpath
, replicapath
);
391 TEST_EQUAL(replicate(master2
, replica2
, tempdir
, 2, 0, 1), 3);
392 check_equal_dbs(masterpath
, replica2path
);
394 // Stop writing changesets, and make a modification
395 set_max_changesets(0);
397 orig
= get_writable_database_again();
398 orig
.add_document(doc1
);
401 // Replication should do a full copy.
402 TEST_EQUAL(replicate(master
, replica
, tempdir
, 0, 1, true), 1);
403 check_equal_dbs(masterpath
, replicapath
);
404 TEST_EQUAL(replicate(master2
, replica2
, tempdir
, 0, 1, true), 1);
405 check_equal_dbs(masterpath
, replica2path
);
407 // Start writing changesets, but only keep 1 in history, and make a
409 set_max_changesets(1);
411 orig
= get_writable_database_again();
412 orig
.add_document(doc1
);
415 // Replicate, and replicate from the replica - should have 1 changes.
416 TEST_EQUAL(replicate(master
, replica
, tempdir
, 1, 0, 1), 2);
417 check_equal_dbs(masterpath
, replicapath
);
418 TEST_EQUAL(replicate(master2
, replica2
, tempdir
, 1, 0, 1), 2);
419 check_equal_dbs(masterpath
, replica2path
);
421 // Make two changes - only one changeset should be preserved.
422 orig
.add_document(doc1
);
425 // Replication should do a full copy, since one of the needed changesets
428 //FIXME - the following tests are commented out because the backends don't currently tidy up old changesets correctly.
429 //TEST_EQUAL(replicate(master, replica, tempdir, 0, 1, true), 1);
430 //check_equal_dbs(masterpath, replicapath);
431 //TEST_EQUAL(replicate(master2, replica2, tempdir, 0, 1, true), 1);
432 //check_equal_dbs(masterpath, replica2path);
434 // Need to close the replicas before we remove the temporary directory on
443 replicate_with_brokenness(Xapian::DatabaseMaster
& master
,
444 Xapian::DatabaseReplica
& replica
,
445 const string
& tempdir
,
446 int expected_changesets
,
447 int expected_fullcopies
,
448 bool expected_changed
)
450 string changesetpath
= tempdir
+ "/changeset";
451 get_changeset(changesetpath
, master
, replica
,
454 // Try applying truncated changesets of various different lengths.
455 string brokenchangesetpath
= tempdir
+ "/changeset_broken";
456 off_t filesize
= get_file_size(changesetpath
);
459 while (len
< filesize
) {
460 copylen
= truncated_copy(changesetpath
, brokenchangesetpath
, len
);
461 TEST_EQUAL(copylen
, len
);
462 tout
<< "Trying replication with a changeset truncated to " << len
<<
463 " bytes, from " << filesize
<< " bytes\n";
464 TEST_EXCEPTION(Xapian::NetworkError
,
465 apply_changeset(brokenchangesetpath
, replica
,
466 expected_changesets
, expected_fullcopies
,
468 if (len
< 30 || len
>= filesize
- 10) {
469 // For lengths near the beginning and end, increment size by 1
472 // Don't bother incrementing by small amounts in the middle of
475 if (len
>= filesize
- 10) {
482 // Test changesets which are truncated (and therefore invalid).
483 DEFINE_TESTCASE(replicate3
, replicas
) {
484 UNSET_MAX_CHANGESETS_AFTERWARDS
;
485 string tempdir
= ".replicatmp";
487 string masterpath
= get_named_writable_database_path("master");
489 set_max_changesets(10);
491 Xapian::WritableDatabase
orig(get_named_writable_database("master"));
492 Xapian::DatabaseMaster
master(masterpath
);
493 string replicapath
= tempdir
+ "/replica";
494 Xapian::DatabaseReplica
replica(replicapath
);
496 // Add a document to the original database.
497 Xapian::Document doc1
;
498 doc1
.set_data(string("doc1"));
499 doc1
.add_posting("doc", 1);
500 doc1
.add_posting("one", 1);
501 orig
.add_document(doc1
);
504 TEST_EQUAL(replicate(master
, replica
, tempdir
, 0, 1, true), 1);
505 check_equal_dbs(masterpath
, replicapath
);
508 orig
.add_document(doc1
);
511 replicate_with_brokenness(master
, replica
, tempdir
, 1, 0, true);
512 // Although it throws an error, the final replication in
513 // replicate_with_brokenness() updates the database, since it's just the
514 // end-of-replication message which is missing its body.
515 check_equal_dbs(masterpath
, replicapath
);
517 // Check that the earlier broken replications didn't cause any problems for the
519 orig
.add_document(doc1
);
521 TEST_EQUAL(replicate(master
, replica
, tempdir
, 1, 0, true), 2);
523 // Need to close the replicas before we remove the temporary directory on
530 // Tests for max_changesets
531 DEFINE_TESTCASE(replicate4
, replicas
) {
532 UNSET_MAX_CHANGESETS_AFTERWARDS
;
533 string tempdir
= ".replicatmp";
535 string masterpath
= get_named_writable_database_path("master");
537 set_max_changesets(1);
539 Xapian::WritableDatabase
orig(get_named_writable_database("master"));
540 Xapian::DatabaseMaster
master(masterpath
);
541 string replicapath
= tempdir
+ "/replica";
542 Xapian::DatabaseReplica
replica(replicapath
);
544 // Add a document with no positions to the original database.
545 Xapian::Document doc1
;
546 doc1
.set_data(string("doc1"));
547 doc1
.add_term("nopos");
548 orig
.add_document(doc1
);
551 // Apply the replication - we don't have changesets stored, so this should
552 // just do a database copy, and return a count of 1.
553 int count
= replicate(master
, replica
, tempdir
, 0, 1, true);
554 TEST_EQUAL(count
, 1);
556 Xapian::Database
dbcopy(replicapath
);
557 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
560 // Add a document with positional information to the original database.
561 doc1
.add_posting("pos", 1);
562 orig
.add_document(doc1
);
565 // Replicate, and check that we have the positional information.
566 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
567 TEST_EQUAL(count
, 2);
569 Xapian::Database
dbcopy(replicapath
);
570 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
572 check_equal_dbs(masterpath
, replicapath
);
574 // Add a document with no positions to the original database.
575 Xapian::Document doc2
;
576 doc2
.set_data(string("doc2"));
577 doc2
.add_term("nopos");
578 orig
.add_document(doc2
);
579 if (get_dbtype() != "chert") {
580 set_max_changesets(0); // FIXME: Needs to be pre-commit for new-glass
584 // Replicate, and check that we have the positional information.
585 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
586 TEST_EQUAL(count
, 2);
588 Xapian::Database
dbcopy(replicapath
);
589 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
591 check_equal_dbs(masterpath
, replicapath
);
592 TEST(!file_exists(masterpath
+ "/changes1"));
594 // Turn off replication, make sure we dont write anything
595 if (get_dbtype() == "chert") {
596 set_max_changesets(0);
599 // Add a document with no positions to the original database.
600 Xapian::Document doc3
;
601 doc3
.set_data(string("doc3"));
602 doc3
.add_term("nonopos");
603 orig
.add_document(doc3
);
606 // Replicate, and check that we have the positional information.
607 count
= replicate(master
, replica
, tempdir
, 0, 1, true);
608 TEST_EQUAL(count
, 1);
610 Xapian::Database
dbcopy(replicapath
);
611 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
613 // Should have pulled a full copy
614 check_equal_dbs(masterpath
, replicapath
);
615 TEST(!file_exists(masterpath
+ "/changes3"));
618 // Need to close the replica before we remove the temporary directory on
626 // Tests for max_changesets
627 DEFINE_TESTCASE(replicate5
, replicas
) {
628 SKIP_TEST_FOR_BACKEND("chert");
629 UNSET_MAX_CHANGESETS_AFTERWARDS
;
630 string tempdir
= ".replicatmp";
632 string masterpath
= get_named_writable_database_path("master");
634 set_max_changesets(2);
636 Xapian::WritableDatabase
orig(get_named_writable_database("master"));
637 Xapian::DatabaseMaster
master(masterpath
);
638 string replicapath
= tempdir
+ "/replica";
639 Xapian::DatabaseReplica
replica(replicapath
);
641 // Add a document with no positions to the original database.
642 Xapian::Document doc1
;
643 doc1
.set_data(string("doc1"));
644 doc1
.add_term("nopos");
645 orig
.add_document(doc1
);
648 // Apply the replication - we don't have changesets stored, so this should
649 // just do a database copy, and return a count of 1.
650 int count
= replicate(master
, replica
, tempdir
, 0, 1, true);
651 TEST_EQUAL(count
, 1);
653 Xapian::Database
dbcopy(replicapath
);
654 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
657 // Add a document with positional information to the original database.
658 doc1
.add_posting("pos", 1);
659 orig
.add_document(doc1
);
662 // Replicate, and check that we have the positional information.
663 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
664 TEST_EQUAL(count
, 2);
666 Xapian::Database
dbcopy(replicapath
);
667 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
669 check_equal_dbs(masterpath
, replicapath
);
671 // Add a document with no positions to the original database.
672 Xapian::Document doc2
;
673 doc2
.set_data(string("doc2"));
674 doc2
.add_term("nopos");
675 orig
.add_document(doc2
);
678 // Replicate, and check that we have the positional information.
679 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
680 TEST_EQUAL(count
, 2);
682 Xapian::Database
dbcopy(replicapath
);
683 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
685 check_equal_dbs(masterpath
, replicapath
);
687 // Add a document with no positions to the original database.
688 Xapian::Document doc3
;
689 doc3
.set_data(string("doc3"));
690 doc3
.add_term("nonopos");
691 orig
.add_document(doc3
);
694 // Replicate, and check that we have the positional information.
695 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
696 TEST_EQUAL(count
, 2);
698 Xapian::Database
dbcopy(replicapath
);
699 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
701 check_equal_dbs(masterpath
, replicapath
);
703 // Ensure that only these changesets exists
704 TEST(!file_exists(masterpath
+ "/changes1"));
705 TEST(file_exists(masterpath
+ "/changes2"));
706 TEST(file_exists(masterpath
+ "/changes3"));
708 set_max_changesets(3);
709 masterpath
= get_named_writable_database_path("master");
711 // Add a document with no positions to the original database.
712 Xapian::Document doc4
;
713 doc4
.set_data(string("doc4"));
714 doc4
.add_term("nononopos");
715 orig
.add_document(doc4
);
718 // Replicate, and check that we have the positional information.
719 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
720 TEST_EQUAL(count
, 2);
722 Xapian::Database
dbcopy(replicapath
);
723 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
725 check_equal_dbs(masterpath
, replicapath
);
727 // Add a document with no positions to the original database.
728 Xapian::Document doc5
;
729 doc5
.set_data(string("doc5"));
730 doc5
.add_term("nonononopos");
731 orig
.add_document(doc5
);
734 // Replicate, and check that we have the positional information.
735 count
= replicate(master
, replica
, tempdir
, 1, 0, true);
736 TEST_EQUAL(count
, 2);
738 Xapian::Database
dbcopy(replicapath
);
739 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
741 check_equal_dbs(masterpath
, replicapath
);
743 TEST(!file_exists(masterpath
+ "/changes2"));
744 TEST(file_exists(masterpath
+ "/changes3"));
745 TEST(file_exists(masterpath
+ "/changes4"));
746 TEST(file_exists(masterpath
+ "/changes5"));
748 // Need to close the replica before we remove the temporary directory on
755 /// Test --full-copy option.
756 DEFINE_TESTCASE(replicate6
, replicas
) {
757 UNSET_MAX_CHANGESETS_AFTERWARDS
;
758 string tempdir
= ".replicatmp";
760 string masterpath
= get_named_writable_database_path("master");
762 set_max_changesets(10);
764 Xapian::WritableDatabase
orig(get_named_writable_database("master"));
765 Xapian::DatabaseMaster
master(masterpath
);
766 string replicapath
= tempdir
+ "/replica";
767 Xapian::DatabaseReplica
replica(replicapath
);
769 // Add a document to the original database.
770 Xapian::Document doc1
;
771 doc1
.set_data(string("doc1"));
772 doc1
.add_posting("doc", 1);
773 doc1
.add_posting("one", 1);
774 orig
.add_document(doc1
);
777 rm_rf(masterpath
+ "1");
778 cp_R(masterpath
, masterpath
+ "1");
780 orig
.add_document(doc1
);
783 // Apply the replication - we don't have changesets stored, so this should
784 // just do a database copy, and return a count of 1.
785 int count
= replicate(master
, replica
, tempdir
, 0, 1, true);
786 TEST_EQUAL(count
, 1);
788 Xapian::Database
dbcopy(replicapath
);
789 TEST_EQUAL(orig
.get_uuid(), dbcopy
.get_uuid());
792 Xapian::DatabaseMaster
master1(masterpath
+ "1");
794 // Try to replicate an older version of the master.
795 count
= replicate(master1
, replica
, tempdir
, 0, 0, false);
796 TEST_EQUAL(count
, 1);
798 // Force a full copy.
799 count
= replicate(master1
, replica
, tempdir
, 0, 1, true, true);
800 TEST_EQUAL(count
, 1);
802 // Test we can still replicate.
803 orig
.add_document(doc1
);
806 count
= replicate(master
, replica
, tempdir
, 2, 0, true);
807 TEST_EQUAL(count
, 3);
809 check_equal_dbs(masterpath
, replicapath
);
811 // Need to close the replica before we remove the temporary directory on