1 /////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2009-2014 Alan Wright. All rights reserved.
3 // Distributable under the terms of either the Apache License (Version 2.0)
4 // or the GNU Lesser General Public License.
5 /////////////////////////////////////////////////////////////////////////////
6 
7 #include "TestInc.h"
8 #include "LuceneTestFixture.h"
9 #include "TestUtils.h"
10 #include "IndexReader.h"
11 #include "MockRAMDirectory.h"
12 #include "IndexWriter.h"
13 #include "WhitespaceAnalyzer.h"
14 #include "SimpleAnalyzer.h"
15 #include "LogDocMergePolicy.h"
16 #include "Document.h"
17 #include "Field.h"
18 #include "ReadOnlySegmentReader.h"
19 #include "ReadOnlyDirectoryReader.h"
20 #include "ParallelReader.h"
21 #include "SegmentReader.h"
22 #include "_SegmentReader.h"
23 #include "Similarity.h"
24 #include "Term.h"
25 #include "MultiReader.h"
26 #include "MiscUtils.h"
27 
28 using namespace Lucene;
29 
30 /// Tests cloning multiple types of readers, modifying the deletedDocs and norms and verifies copy on write semantics
31 /// of the deletedDocs and norms is implemented properly
32 typedef LuceneTestFixture IndexReaderCloneTest;
33 
createDocument(int32_t n,int32_t numFields)34 static DocumentPtr createDocument(int32_t n, int32_t numFields) {
35     StringStream sb;
36     DocumentPtr doc = newLucene<Document>();
37     sb << L"a" << n;
38     doc->add(newLucene<Field>(L"field1", sb.str(), Field::STORE_YES, Field::INDEX_ANALYZED));
39     doc->add(newLucene<Field>(L"fielda", sb.str(), Field::STORE_YES, Field::INDEX_NOT_ANALYZED_NO_NORMS));
40     doc->add(newLucene<Field>(L"fieldb", sb.str(), Field::STORE_YES, Field::INDEX_NO));
41     sb << L" b" << n;
42     for (int32_t i = 1; i < numFields; ++i) {
43         doc->add(newLucene<Field>(L"field" + StringUtils::toString(i + 1), sb.str(), Field::STORE_YES, Field::INDEX_ANALYZED));
44     }
45     return doc;
46 }
47 
createIndex(const DirectoryPtr & dir,bool multiSegment)48 static void createIndex(const DirectoryPtr& dir, bool multiSegment) {
49     IndexWriter::unlock(dir);
50     IndexWriterPtr w = newLucene<IndexWriter>(dir, newLucene<WhitespaceAnalyzer>(), IndexWriter::MaxFieldLengthLIMITED);
51 
52     w->setMergePolicy(newLucene<LogDocMergePolicy>(w));
53 
54     for (int32_t i = 0; i < 100; ++i) {
55         w->addDocument(createDocument(i, 4));
56         if (multiSegment && (i % 10) == 0) {
57             w->commit();
58         }
59     }
60 
61     if (!multiSegment) {
62         w->optimize();
63     }
64 
65     w->close();
66 
67     IndexReaderPtr r = IndexReader::open(dir, false);
68     if (multiSegment) {
69         EXPECT_TRUE(r->getSequentialSubReaders().size() > 1);
70     } else {
71         EXPECT_EQ(r->getSequentialSubReaders().size(), 1);
72     }
73     r->close();
74 }
75 
isReadOnly(const IndexReaderPtr & r)76 static bool isReadOnly(const IndexReaderPtr& r) {
77     return (MiscUtils::typeOf<ReadOnlySegmentReader>(r) || MiscUtils::typeOf<ReadOnlyDirectoryReader>(r));
78 }
79 
deleteWorked(int32_t doc,const IndexReaderPtr & r)80 static bool deleteWorked(int32_t doc, const IndexReaderPtr& r) {
81     bool exception = false;
82     try {
83         // trying to delete from the original reader should throw an exception
84         r->deleteDocument(doc);
85     } catch (...) {
86         exception = true;
87     }
88     return !exception;
89 }
90 
91 /// 1. Get a norm from the original reader
92 /// 2. Clone the original reader
93 /// 3. Delete a document and set the norm of the cloned reader
94 /// 4. Verify the norms are not the same on each reader
95 /// 5. Verify the doc deleted is only in the cloned reader
96 /// 6. Try to delete a document in the original reader, an exception should be thrown
performDefaultTests(const IndexReaderPtr & r1)97 static void performDefaultTests(const IndexReaderPtr& r1) {
98     double norm1 = Similarity::decodeNorm(r1->norms(L"field1")[4]);
99 
100     IndexReaderPtr pr1Clone = boost::dynamic_pointer_cast<IndexReader>(r1->clone());
101     pr1Clone->deleteDocument(10);
102     pr1Clone->setNorm(4, L"field1", 0.5);
103     EXPECT_TRUE(Similarity::decodeNorm(r1->norms(L"field1")[4]) == norm1);
104     EXPECT_NE(Similarity::decodeNorm(pr1Clone->norms(L"field1")[4]), norm1);
105 
106     EXPECT_TRUE(!r1->isDeleted(10));
107     EXPECT_TRUE(pr1Clone->isDeleted(10));
108 
109     // try to update the original reader, which should throw an exception
110     try {
111         r1->deleteDocument(11);
112     } catch (LuceneException& e) {
113         EXPECT_TRUE(check_exception(LuceneException::Null)(e));
114     }
115     pr1Clone->close();
116 }
117 
modifyIndex(int32_t i,const DirectoryPtr & dir)118 static void modifyIndex(int32_t i, const DirectoryPtr& dir) {
119     switch (i) {
120     case 0: {
121         IndexWriterPtr w = newLucene<IndexWriter>(dir, newLucene<WhitespaceAnalyzer>(), IndexWriter::MaxFieldLengthLIMITED);
122         w->deleteDocuments(newLucene<Term>(L"field2", L"a11"));
123         w->deleteDocuments(newLucene<Term>(L"field2", L"b30"));
124         w->close();
125         break;
126     }
127     case 1: {
128         IndexReaderPtr reader = IndexReader::open(dir, false);
129         reader->setNorm(4, L"field1", (uint8_t)123);
130         reader->setNorm(44, L"field2", (uint8_t)222);
131         reader->setNorm(44, L"field4", (uint8_t)22);
132         reader->close();
133         break;
134     }
135     case 2: {
136         IndexWriterPtr w = newLucene<IndexWriter>(dir, newLucene<WhitespaceAnalyzer>(), IndexWriter::MaxFieldLengthLIMITED);
137         w->optimize();
138         w->close();
139         break;
140     }
141     case 3: {
142         IndexWriterPtr w = newLucene<IndexWriter>(dir, newLucene<WhitespaceAnalyzer>(), IndexWriter::MaxFieldLengthLIMITED);
143         w->addDocument(createDocument(101, 4));
144         w->optimize();
145         w->addDocument(createDocument(102, 4));
146         w->addDocument(createDocument(103, 4));
147         w->close();
148         break;
149     }
150     case 4: {
151         IndexReaderPtr reader = IndexReader::open(dir, false);
152         reader->setNorm(5, L"field1", (uint8_t)123);
153         reader->setNorm(55, L"field2", (uint8_t)222);
154         reader->close();
155         break;
156     }
157     case 5: {
158         IndexWriterPtr w = newLucene<IndexWriter>(dir, newLucene<WhitespaceAnalyzer>(), IndexWriter::MaxFieldLengthLIMITED);
159         w->addDocument(createDocument(101, 4));
160         w->close();
161         break;
162     }
163     }
164 }
165 
checkDelDocsRefCountEquals(int32_t refCount,const SegmentReaderPtr & reader)166 static void checkDelDocsRefCountEquals(int32_t refCount, const SegmentReaderPtr& reader) {
167     EXPECT_EQ(refCount, reader->deletedDocsRef->refCount());
168 }
169 
checkDocDeleted(const SegmentReaderPtr & reader,const SegmentReaderPtr & reader2,int32_t doc)170 static void checkDocDeleted(const SegmentReaderPtr& reader, const SegmentReaderPtr& reader2, int32_t doc) {
171     EXPECT_EQ(reader->isDeleted(doc), reader2->isDeleted(doc));
172 }
173 
TEST_F(IndexReaderCloneTest,testCloneReadOnlySegmentReader)174 TEST_F(IndexReaderCloneTest, testCloneReadOnlySegmentReader) {
175     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
176     createIndex(dir1, false);
177     IndexReaderPtr reader = IndexReader::open(dir1, false);
178     IndexReaderPtr readOnlyReader = boost::dynamic_pointer_cast<IndexReader>(reader->clone(true));
179     EXPECT_TRUE(isReadOnly(readOnlyReader));
180     EXPECT_TRUE(!deleteWorked(1, readOnlyReader));
181     reader->close();
182     readOnlyReader->close();
183     dir1->close();
184 }
185 
186 /// Open non-readOnly reader1, clone to non-readOnly reader2, make sure we can change reader2
TEST_F(IndexReaderCloneTest,testCloneNoChangesStillReadOnly)187 TEST_F(IndexReaderCloneTest, testCloneNoChangesStillReadOnly) {
188     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
189     createIndex(dir1, true);
190     IndexReaderPtr r1 = IndexReader::open(dir1, false);
191     IndexReaderPtr r2 = boost::dynamic_pointer_cast<IndexReader>(r1->clone(false));
192     EXPECT_TRUE(deleteWorked(1, r2));
193     r1->close();
194     r2->close();
195     dir1->close();
196 }
197 
198 /// Open non-readOnly reader1, clone to non-readOnly reader2, make sure we can change reader1
TEST_F(IndexReaderCloneTest,testCloneWriteToOrig)199 TEST_F(IndexReaderCloneTest, testCloneWriteToOrig) {
200     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
201     createIndex(dir1, true);
202     IndexReaderPtr r1 = IndexReader::open(dir1, false);
203     IndexReaderPtr r2 = boost::dynamic_pointer_cast<IndexReader>(r1->clone(false));
204     EXPECT_TRUE(deleteWorked(1, r1));
205     r1->close();
206     r2->close();
207     dir1->close();
208 }
209 
210 /// Open non-readOnly reader1, clone to non-readOnly reader2, make sure we can change reader2
TEST_F(IndexReaderCloneTest,testCloneWriteToClone)211 TEST_F(IndexReaderCloneTest, testCloneWriteToClone) {
212     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
213     createIndex(dir1, true);
214     IndexReaderPtr r1 = IndexReader::open(dir1, false);
215     IndexReaderPtr r2 = boost::dynamic_pointer_cast<IndexReader>(r1->clone(false));
216     EXPECT_TRUE(deleteWorked(1, r2));
217     // should fail because reader1 holds the write lock
218     EXPECT_TRUE(!deleteWorked(1, r1));
219     r2->close();
220     // should fail because we are now stale (reader1 committed changes)
221     EXPECT_TRUE(!deleteWorked(1, r1));
222     r1->close();
223 
224     dir1->close();
225 }
226 
227 /// Create single-segment index, open non-readOnly SegmentReader, add docs, reopen to multireader, then do delete
TEST_F(IndexReaderCloneTest,testReopenSegmentReaderToMultiReader)228 TEST_F(IndexReaderCloneTest, testReopenSegmentReaderToMultiReader) {
229     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
230     createIndex(dir1, false);
231     IndexReaderPtr reader1 = IndexReader::open(dir1, false);
232 
233     modifyIndex(5, dir1);
234 
235     IndexReaderPtr reader2 = reader1->reopen();
236     EXPECT_NE(reader1, reader2);
237 
238     EXPECT_TRUE(deleteWorked(1, reader2));
239     reader1->close();
240     reader2->close();
241     dir1->close();
242 }
243 
244 /// Open non-readOnly reader1, clone to readOnly reader2
TEST_F(IndexReaderCloneTest,testCloneWriteableToReadOnly)245 TEST_F(IndexReaderCloneTest, testCloneWriteableToReadOnly) {
246     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
247     createIndex(dir1, true);
248     IndexReaderPtr reader = IndexReader::open(dir1, false);
249     IndexReaderPtr readOnlyReader = boost::dynamic_pointer_cast<IndexReader>(reader->clone(true));
250 
251     EXPECT_TRUE(isReadOnly(readOnlyReader));
252     EXPECT_TRUE(!deleteWorked(1, readOnlyReader));
253     EXPECT_TRUE(!readOnlyReader->hasChanges());
254 
255     reader->close();
256     readOnlyReader->close();
257     dir1->close();
258 }
259 
260 /// Open non-readOnly reader1, reopen to readOnly reader2
TEST_F(IndexReaderCloneTest,testReopenWriteableToReadOnly)261 TEST_F(IndexReaderCloneTest, testReopenWriteableToReadOnly) {
262     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
263     createIndex(dir1, true);
264     IndexReaderPtr reader = IndexReader::open(dir1, false);
265     int32_t docCount = reader->numDocs();
266     EXPECT_TRUE(deleteWorked(1, reader));
267     EXPECT_EQ(docCount - 1, reader->numDocs());
268 
269     IndexReaderPtr readOnlyReader = reader->reopen(true);
270     EXPECT_TRUE(isReadOnly(readOnlyReader));
271     EXPECT_TRUE(!deleteWorked(1, readOnlyReader));
272     EXPECT_EQ(docCount - 1, readOnlyReader->numDocs());
273     reader->close();
274     readOnlyReader->close();
275     dir1->close();
276 }
277 
278 /// Open readOnly reader1, clone to non-readOnly reader2
TEST_F(IndexReaderCloneTest,testCloneReadOnlyToWriteable)279 TEST_F(IndexReaderCloneTest, testCloneReadOnlyToWriteable) {
280     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
281     createIndex(dir1, true);
282     IndexReaderPtr reader1 = IndexReader::open(dir1, true);
283     IndexReaderPtr reader2 = boost::dynamic_pointer_cast<IndexReader>(reader1->clone(false));
284 
285     EXPECT_TRUE(!isReadOnly(reader2));
286     EXPECT_TRUE(!deleteWorked(1, reader1));
287     // this readonly reader shouldn't yet have a write lock
288     EXPECT_TRUE(!reader2->hasChanges());
289     EXPECT_TRUE(deleteWorked(1, reader2));
290     reader1->close();
291     reader2->close();
292     dir1->close();
293 }
294 
295 /// Open non-readOnly reader1 on multi-segment index, then optimize the index, then clone to readOnly reader2
TEST_F(IndexReaderCloneTest,testReadOnlyCloneAfterOptimize)296 TEST_F(IndexReaderCloneTest, testReadOnlyCloneAfterOptimize) {
297     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
298     createIndex(dir1, true);
299     IndexReaderPtr reader1 = IndexReader::open(dir1, false);
300     IndexWriterPtr w = newLucene<IndexWriter>(dir1, newLucene<SimpleAnalyzer>(), IndexWriter::MaxFieldLengthLIMITED);
301     w->optimize();
302     w->close();
303     IndexReaderPtr reader2 = boost::dynamic_pointer_cast<IndexReader>(reader1->clone(true));
304     EXPECT_TRUE(isReadOnly(reader2));
305     reader1->close();
306     reader2->close();
307     dir1->close();
308 }
309 
TEST_F(IndexReaderCloneTest,testCloneReadOnlyDirectoryReader)310 TEST_F(IndexReaderCloneTest, testCloneReadOnlyDirectoryReader) {
311     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
312     createIndex(dir1, true);
313     IndexReaderPtr reader = IndexReader::open(dir1, false);
314     IndexReaderPtr readOnlyReader = boost::dynamic_pointer_cast<IndexReader>(reader->clone(true));
315     EXPECT_TRUE(isReadOnly(readOnlyReader));
316     reader->close();
317     readOnlyReader->close();
318     dir1->close();
319 }
320 
TEST_F(IndexReaderCloneTest,testParallelReader)321 TEST_F(IndexReaderCloneTest, testParallelReader) {
322     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
323     createIndex(dir1, true);
324     DirectoryPtr dir2 = newLucene<MockRAMDirectory>();
325     createIndex(dir2, true);
326 
327     IndexReaderPtr r1 = IndexReader::open(dir1, false);
328     IndexReaderPtr r2 = IndexReader::open(dir2, false);
329 
330     ParallelReaderPtr pr1 = newLucene<ParallelReader>();
331     pr1->add(r1);
332     pr1->add(r2);
333 
334     performDefaultTests(pr1);
335     pr1->close();
336     dir1->close();
337     dir2->close();
338 }
339 
TEST_F(IndexReaderCloneTest,testMixedReaders)340 TEST_F(IndexReaderCloneTest, testMixedReaders) {
341     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
342     createIndex(dir1, true);
343     DirectoryPtr dir2 = newLucene<MockRAMDirectory>();
344     createIndex(dir2, true);
345 
346     IndexReaderPtr r1 = IndexReader::open(dir1, false);
347     IndexReaderPtr r2 = IndexReader::open(dir2, false);
348 
349     Collection<IndexReaderPtr> multiReaders = newCollection<IndexReaderPtr>(r1, r2);
350     MultiReaderPtr multiReader = newLucene<MultiReader>(multiReaders);
351     performDefaultTests(multiReader);
352     multiReader->close();
353     dir1->close();
354     dir2->close();
355 }
356 
TEST_F(IndexReaderCloneTest,testSegmentReaderUndeleteall)357 TEST_F(IndexReaderCloneTest, testSegmentReaderUndeleteall) {
358     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
359     createIndex(dir1, false);
360     SegmentReaderPtr origSegmentReader = SegmentReader::getOnlySegmentReader(dir1);
361     origSegmentReader->deleteDocument(10);
362     checkDelDocsRefCountEquals(1, origSegmentReader);
363     origSegmentReader->undeleteAll();
364     EXPECT_TRUE(!origSegmentReader->deletedDocsRef);
365     origSegmentReader->close();
366     // need to test norms?
367     dir1->close();
368 }
369 
TEST_F(IndexReaderCloneTest,testSegmentReaderCloseReferencing)370 TEST_F(IndexReaderCloneTest, testSegmentReaderCloseReferencing) {
371     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
372     createIndex(dir1, false);
373     SegmentReaderPtr origSegmentReader = SegmentReader::getOnlySegmentReader(dir1);
374     origSegmentReader->deleteDocument(1);
375     origSegmentReader->setNorm(4, L"field1", 0.5);
376 
377     SegmentReaderPtr clonedSegmentReader = boost::dynamic_pointer_cast<SegmentReader>(origSegmentReader->clone());
378     checkDelDocsRefCountEquals(2, origSegmentReader);
379     origSegmentReader->close();
380     checkDelDocsRefCountEquals(1, origSegmentReader);
381     // check the norm refs
382     NormPtr norm = clonedSegmentReader->_norms.get(L"field1");
383     EXPECT_EQ(1, norm->bytesRef()->refCount());
384     clonedSegmentReader->close();
385     dir1->close();
386 }
387 
TEST_F(IndexReaderCloneTest,testSegmentReaderDelDocsReferenceCounting)388 TEST_F(IndexReaderCloneTest, testSegmentReaderDelDocsReferenceCounting) {
389     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
390     createIndex(dir1, false);
391     IndexReaderPtr origReader = IndexReader::open(dir1, false);
392     SegmentReaderPtr origSegmentReader = SegmentReader::getOnlySegmentReader(origReader);
393     // deletedDocsRef should be null because nothing has updated yet
394     EXPECT_TRUE(!origSegmentReader->deletedDocsRef);
395 
396     // we deleted a document, so there is now a deletedDocs bitvector and a reference to it
397     origReader->deleteDocument(1);
398     checkDelDocsRefCountEquals(1, origSegmentReader);
399 
400     // the cloned segmentreader should have 2 references, 1 to itself, and 1 to the original segmentreader
401     IndexReaderPtr clonedReader = boost::dynamic_pointer_cast<IndexReader>(origReader->clone());
402     SegmentReaderPtr clonedSegmentReader = SegmentReader::getOnlySegmentReader(clonedReader);
403     checkDelDocsRefCountEquals(2, origSegmentReader);
404     // deleting a document creates a new deletedDocs bitvector, the refs goes to 1
405     clonedReader->deleteDocument(2);
406     checkDelDocsRefCountEquals(1, origSegmentReader);
407     checkDelDocsRefCountEquals(1, clonedSegmentReader);
408 
409     // make sure the deletedocs objects are different (copy on write)
410     EXPECT_NE(origSegmentReader->deletedDocs, clonedSegmentReader->deletedDocs);
411 
412     checkDocDeleted(origSegmentReader, clonedSegmentReader, 1);
413     EXPECT_TRUE(!origSegmentReader->isDeleted(2)); // doc 2 should not be deleted in original segmentreader
414     EXPECT_TRUE(clonedSegmentReader->isDeleted(2)); // doc 2 should be deleted in cloned segmentreader
415 
416     try {
417         origReader->deleteDocument(4);
418     } catch (LockObtainFailedException& e) {
419         EXPECT_TRUE(check_exception(LuceneException::LockObtainFailed)(e));
420     }
421 
422     origReader->close();
423     // try closing the original segment reader to see if it affects the clonedSegmentReader
424     clonedReader->deleteDocument(3);
425     clonedReader->flush();
426     checkDelDocsRefCountEquals(1, clonedSegmentReader);
427 
428     // test a reopened reader
429     IndexReaderPtr reopenedReader = clonedReader->reopen();
430     IndexReaderPtr cloneReader2 = boost::dynamic_pointer_cast<IndexReader>(reopenedReader->clone());
431     SegmentReaderPtr cloneSegmentReader2 = SegmentReader::getOnlySegmentReader(cloneReader2);
432     checkDelDocsRefCountEquals(2, cloneSegmentReader2);
433     clonedReader->close();
434     reopenedReader->close();
435     cloneReader2->close();
436 
437     dir1->close();
438 }
439 
TEST_F(IndexReaderCloneTest,testCloneWithDeletes)440 TEST_F(IndexReaderCloneTest, testCloneWithDeletes) {
441     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
442     createIndex(dir1, false);
443     IndexReaderPtr origReader = IndexReader::open(dir1, false);
444     origReader->deleteDocument(1);
445 
446     IndexReaderPtr clonedReader = boost::dynamic_pointer_cast<IndexReader>(origReader->clone());
447     origReader->close();
448     clonedReader->close();
449 
450     IndexReaderPtr r = IndexReader::open(dir1, false);
451     EXPECT_TRUE(r->isDeleted(1));
452     r->close();
453     dir1->close();
454 }
455 
TEST_F(IndexReaderCloneTest,testCloneWithSetNorm)456 TEST_F(IndexReaderCloneTest, testCloneWithSetNorm) {
457     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
458     createIndex(dir1, false);
459     IndexReaderPtr orig = IndexReader::open(dir1, false);
460     orig->setNorm(1, L"field1", 17.0);
461     uint8_t encoded = Similarity::encodeNorm(17.0);
462     EXPECT_EQ(encoded, orig->norms(L"field1")[1]);
463 
464     // the cloned segmentreader should have 2 references, 1 to itself, and 1 to the original segmentreader
465     IndexReaderPtr clonedReader = boost::dynamic_pointer_cast<IndexReader>(orig->clone());
466     orig->close();
467     clonedReader->close();
468 
469     IndexReaderPtr r = IndexReader::open(dir1, false);
470     EXPECT_EQ(encoded, r->norms(L"field1")[1]);
471     r->close();
472     dir1->close();
473 }
474 
TEST_F(IndexReaderCloneTest,testCloneSubreaders)475 TEST_F(IndexReaderCloneTest, testCloneSubreaders) {
476     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
477     createIndex(dir1, true);
478 
479     IndexReaderPtr reader = IndexReader::open(dir1, false);
480     reader->deleteDocument(1); // acquire write lock
481     Collection<IndexReaderPtr> subs = reader->getSequentialSubReaders();
482     EXPECT_TRUE(subs.size() > 1);
483 
484     Collection<IndexReaderPtr> clones = Collection<IndexReaderPtr>::newInstance(subs.size());
485     for (int32_t x = 0; x < subs.size(); ++x) {
486         clones[x] = boost::dynamic_pointer_cast<IndexReader>(subs[x]->clone());
487     }
488     reader->close();
489     for (int32_t x = 0; x < subs.size(); ++x) {
490         clones[x]->close();
491     }
492     dir1->close();
493 }
494 
TEST_F(IndexReaderCloneTest,testIncDecRef)495 TEST_F(IndexReaderCloneTest, testIncDecRef) {
496     DirectoryPtr dir1 = newLucene<MockRAMDirectory>();
497     createIndex(dir1, false);
498     IndexReaderPtr r1 = IndexReader::open(dir1, false);
499     r1->incRef();
500     IndexReaderPtr r2 = boost::dynamic_pointer_cast<IndexReader>(r1->clone(false));
501     r1->deleteDocument(5);
502     r1->decRef();
503 
504     r1->incRef();
505 
506     r2->close();
507     r1->decRef();
508     r1->close();
509     dir1->close();
510 }
511 
TEST_F(IndexReaderCloneTest,testCloseStoredFields)512 TEST_F(IndexReaderCloneTest, testCloseStoredFields) {
513     DirectoryPtr dir = newLucene<MockRAMDirectory>();
514     IndexWriterPtr w = newLucene<IndexWriter>(dir, newLucene<SimpleAnalyzer>(), IndexWriter::MaxFieldLengthUNLIMITED);
515     w->setUseCompoundFile(false);
516     DocumentPtr doc = newLucene<Document>();
517     doc->add(newLucene<Field>(L"field", L"yes it's stored", Field::STORE_YES, Field::INDEX_ANALYZED));
518     w->addDocument(doc);
519     w->close();
520     IndexReaderPtr r1 = IndexReader::open(dir, false);
521     IndexReaderPtr r2 = boost::dynamic_pointer_cast<IndexReader>(r1->clone(false));
522     r1->close();
523     r2->close();
524     dir->close();
525 }
526