You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2017/02/26 23:37:25 UTC

[37/72] [abbrv] [partial] lucenenet git commit: Lucene.Net.Tests: Removed \core directory and put its contents in root directory

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Index/TestDirectoryReader.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Index/TestDirectoryReader.cs b/src/Lucene.Net.Tests/Index/TestDirectoryReader.cs
new file mode 100644
index 0000000..14fea69
--- /dev/null
+++ b/src/Lucene.Net.Tests/Index/TestDirectoryReader.cs
@@ -0,0 +1,1341 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using Lucene.Net.Documents;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Index
+{
+    using Attributes;
+    using Lucene.Net.Support;
+    using NUnit.Framework;
+    using IBits = Lucene.Net.Util.IBits;
+    using BytesRef = Lucene.Net.Util.BytesRef;
+    using Directory = Lucene.Net.Store.Directory;
+    using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
+    using Document = Documents.Document;
+    using Field = Field;
+    using FieldType = FieldType;
+    using Lucene41PostingsFormat = Lucene.Net.Codecs.Lucene41.Lucene41PostingsFormat;
+    using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+
+    /*
+         * Licensed to the Apache Software Foundation (ASF) under one or more
+         * contributor license agreements.  See the NOTICE file distributed with
+         * this work for additional information regarding copyright ownership.
+         * The ASF licenses this file to You under the Apache License, Version 2.0
+         * (the "License"); you may not use this file except in compliance with
+         * the License.  You may obtain a copy of the License at
+         *
+         *     http://www.apache.org/licenses/LICENSE-2.0
+         *
+         * Unless required by applicable law or agreed to in writing, software
+         * distributed under the License is distributed on an "AS IS" BASIS,
+         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         * See the License for the specific language governing permissions and
+         * limitations under the License.
+         */
+
+    using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+    using NoSuchDirectoryException = Lucene.Net.Store.NoSuchDirectoryException;
+    using StoredField = StoredField;
+    using StringField = StringField;
+    using TestUtil = Lucene.Net.Util.TestUtil;
+    using TextField = TextField;
+
+    [TestFixture]
+    public class TestDirectoryReader : LuceneTestCase
+    {
+        [Test]
+        public virtual void TestDocument()
+        {
+            SegmentReader[] readers = new SegmentReader[2];
+            Directory dir = NewDirectory();
+            Document doc1 = new Document();
+            Document doc2 = new Document();
+            DocHelper.SetupDoc(doc1);
+            DocHelper.SetupDoc(doc2);
+            DocHelper.WriteDoc(Random(), dir, doc1);
+            DocHelper.WriteDoc(Random(), dir, doc2);
+            DirectoryReader reader = DirectoryReader.Open(dir);
+            Assert.IsTrue(reader != null);
+            Assert.IsTrue(reader is StandardDirectoryReader);
+
+            Document newDoc1 = reader.Document(0);
+            Assert.IsTrue(newDoc1 != null);
+            Assert.IsTrue(DocHelper.NumFields(newDoc1) == DocHelper.NumFields(doc1) - DocHelper.Unstored.Count);
+            Document newDoc2 = reader.Document(1);
+            Assert.IsTrue(newDoc2 != null);
+            Assert.IsTrue(DocHelper.NumFields(newDoc2) == DocHelper.NumFields(doc2) - DocHelper.Unstored.Count);
+            Terms vector = reader.GetTermVectors(0).GetTerms(DocHelper.TEXT_FIELD_2_KEY);
+            Assert.IsNotNull(vector);
+
+            reader.Dispose();
+            if (readers[0] != null)
+            {
+                readers[0].Dispose();
+            }
+            if (readers[1] != null)
+            {
+                readers[1].Dispose();
+            }
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestMultiTermDocs()
+        {
+            Directory ramDir1 = NewDirectory();
+            AddDoc(Random(), ramDir1, "test foo", true);
+            Directory ramDir2 = NewDirectory();
+            AddDoc(Random(), ramDir2, "test blah", true);
+            Directory ramDir3 = NewDirectory();
+            AddDoc(Random(), ramDir3, "test wow", true);
+
+            IndexReader[] readers1 = new IndexReader[] { DirectoryReader.Open(ramDir1), DirectoryReader.Open(ramDir3) };
+            IndexReader[] readers2 = new IndexReader[] { DirectoryReader.Open(ramDir1), DirectoryReader.Open(ramDir2), DirectoryReader.Open(ramDir3) };
+            MultiReader mr2 = new MultiReader(readers1);
+            MultiReader mr3 = new MultiReader(readers2);
+
+            // test mixing up TermDocs and TermEnums from different readers.
+            TermsEnum te2 = MultiFields.GetTerms(mr2, "body").GetIterator(null);
+            te2.SeekCeil(new BytesRef("wow"));
+            DocsEnum td = TestUtil.Docs(Random(), mr2, "body", te2.Term, MultiFields.GetLiveDocs(mr2), null, 0);
+
+            TermsEnum te3 = MultiFields.GetTerms(mr3, "body").GetIterator(null);
+            te3.SeekCeil(new BytesRef("wow"));
+            td = TestUtil.Docs(Random(), te3, MultiFields.GetLiveDocs(mr3), td, 0);
+
+            int ret = 0;
+
+            // this should blow up if we forget to check that the TermEnum is from the same
+            // reader as the TermDocs.
+            while (td.NextDoc() != DocIdSetIterator.NO_MORE_DOCS)
+            {
+                ret += td.DocID;
+            }
+
+            // really a dummy assert to ensure that we got some docs and to ensure that
+            // nothing is eliminated by hotspot
+            Assert.IsTrue(ret > 0);
+            readers1[0].Dispose();
+            readers1[1].Dispose();
+            readers2[0].Dispose();
+            readers2[1].Dispose();
+            readers2[2].Dispose();
+            ramDir1.Dispose();
+            ramDir2.Dispose();
+            ramDir3.Dispose();
+        }
+
+        private void AddDoc(Random random, Directory ramDir1, string s, bool create)
+        {
+            IndexWriter iw = new IndexWriter(ramDir1, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).SetOpenMode(create ? OpenMode.CREATE : OpenMode.APPEND));
+            Document doc = new Document();
+            doc.Add(NewTextField("body", s, Field.Store.NO));
+            iw.AddDocument(doc);
+            iw.Dispose();
+        }
+
+        [Test]
+        public virtual void TestIsCurrent()
+        {
+            Directory d = NewDirectory();
+            IndexWriter writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            AddDocumentWithFields(writer);
+            writer.Dispose();
+            // set up reader:
+            DirectoryReader reader = DirectoryReader.Open(d);
+            Assert.IsTrue(reader.IsCurrent);
+            // modify index by adding another document:
+            writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.APPEND));
+            AddDocumentWithFields(writer);
+            writer.Dispose();
+            Assert.IsFalse(reader.IsCurrent);
+            // re-create index:
+            writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.CREATE));
+            AddDocumentWithFields(writer);
+            writer.Dispose();
+            Assert.IsFalse(reader.IsCurrent);
+            reader.Dispose();
+            d.Dispose();
+        }
+
+        /// <summary>
+        /// Tests the IndexReader.getFieldNames implementation </summary>
+        /// <exception cref="Exception"> on error </exception>
+        [Test]
+        public virtual void TestGetFieldNames()
+        {
+            Directory d = NewDirectory();
+            // set up writer
+            IndexWriter writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+
+            Document doc = new Document();
+
+            FieldType customType3 = new FieldType();
+            customType3.IsStored = true;
+
+            doc.Add(new StringField("keyword", "test1", Field.Store.YES));
+            doc.Add(new TextField("text", "test1", Field.Store.YES));
+            doc.Add(new Field("unindexed", "test1", customType3));
+            doc.Add(new TextField("unstored", "test1", Field.Store.NO));
+            writer.AddDocument(doc);
+
+            writer.Dispose();
+            // set up reader
+            DirectoryReader reader = DirectoryReader.Open(d);
+            FieldInfos fieldInfos = MultiFields.GetMergedFieldInfos(reader);
+            Assert.IsNotNull(fieldInfos.FieldInfo("keyword"));
+            Assert.IsNotNull(fieldInfos.FieldInfo("text"));
+            Assert.IsNotNull(fieldInfos.FieldInfo("unindexed"));
+            Assert.IsNotNull(fieldInfos.FieldInfo("unstored"));
+            reader.Dispose();
+            // add more documents
+            writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.APPEND).SetMergePolicy(NewLogMergePolicy()));
+            // want to get some more segments here
+            int mergeFactor = ((LogMergePolicy)writer.Config.MergePolicy).MergeFactor;
+            for (int i = 0; i < 5 * mergeFactor; i++)
+            {
+                doc = new Document();
+                doc.Add(new StringField("keyword", "test1", Field.Store.YES));
+                doc.Add(new TextField("text", "test1", Field.Store.YES));
+                doc.Add(new Field("unindexed", "test1", customType3));
+                doc.Add(new TextField("unstored", "test1", Field.Store.NO));
+                writer.AddDocument(doc);
+            }
+            // new fields are in some different segments (we hope)
+            for (int i = 0; i < 5 * mergeFactor; i++)
+            {
+                doc = new Document();
+                doc.Add(new StringField("keyword2", "test1", Field.Store.YES));
+                doc.Add(new TextField("text2", "test1", Field.Store.YES));
+                doc.Add(new Field("unindexed2", "test1", customType3));
+                doc.Add(new TextField("unstored2", "test1", Field.Store.NO));
+                writer.AddDocument(doc);
+            }
+            // new termvector fields
+
+            FieldType customType5 = new FieldType(TextField.TYPE_STORED);
+            customType5.StoreTermVectors = true;
+            FieldType customType6 = new FieldType(TextField.TYPE_STORED);
+            customType6.StoreTermVectors = true;
+            customType6.StoreTermVectorOffsets = true;
+            FieldType customType7 = new FieldType(TextField.TYPE_STORED);
+            customType7.StoreTermVectors = true;
+            customType7.StoreTermVectorPositions = true;
+            FieldType customType8 = new FieldType(TextField.TYPE_STORED);
+            customType8.StoreTermVectors = true;
+            customType8.StoreTermVectorOffsets = true;
+            customType8.StoreTermVectorPositions = true;
+
+            for (int i = 0; i < 5 * mergeFactor; i++)
+            {
+                doc = new Document();
+                doc.Add(new TextField("tvnot", "tvnot", Field.Store.YES));
+                doc.Add(new Field("termvector", "termvector", customType5));
+                doc.Add(new Field("tvoffset", "tvoffset", customType6));
+                doc.Add(new Field("tvposition", "tvposition", customType7));
+                doc.Add(new Field("tvpositionoffset", "tvpositionoffset", customType8));
+                writer.AddDocument(doc);
+            }
+
+            writer.Dispose();
+
+            // verify fields again
+            reader = DirectoryReader.Open(d);
+            fieldInfos = MultiFields.GetMergedFieldInfos(reader);
+
+            ICollection<string> allFieldNames = new HashSet<string>();
+            ICollection<string> indexedFieldNames = new HashSet<string>();
+            ICollection<string> notIndexedFieldNames = new HashSet<string>();
+            ICollection<string> tvFieldNames = new HashSet<string>();
+
+            foreach (FieldInfo fieldInfo in fieldInfos)
+            {
+                string name = fieldInfo.Name;
+                allFieldNames.Add(name);
+                if (fieldInfo.IsIndexed)
+                {
+                    indexedFieldNames.Add(name);
+                }
+                else
+                {
+                    notIndexedFieldNames.Add(name);
+                }
+                if (fieldInfo.HasVectors)
+                {
+                    tvFieldNames.Add(name);
+                }
+            }
+
+            Assert.IsTrue(allFieldNames.Contains("keyword"));
+            Assert.IsTrue(allFieldNames.Contains("text"));
+            Assert.IsTrue(allFieldNames.Contains("unindexed"));
+            Assert.IsTrue(allFieldNames.Contains("unstored"));
+            Assert.IsTrue(allFieldNames.Contains("keyword2"));
+            Assert.IsTrue(allFieldNames.Contains("text2"));
+            Assert.IsTrue(allFieldNames.Contains("unindexed2"));
+            Assert.IsTrue(allFieldNames.Contains("unstored2"));
+            Assert.IsTrue(allFieldNames.Contains("tvnot"));
+            Assert.IsTrue(allFieldNames.Contains("termvector"));
+            Assert.IsTrue(allFieldNames.Contains("tvposition"));
+            Assert.IsTrue(allFieldNames.Contains("tvoffset"));
+            Assert.IsTrue(allFieldNames.Contains("tvpositionoffset"));
+
+            // verify that only indexed fields were returned
+            Assert.AreEqual(11, indexedFieldNames.Count); // 6 original + the 5 termvector fields
+            Assert.IsTrue(indexedFieldNames.Contains("keyword"));
+            Assert.IsTrue(indexedFieldNames.Contains("text"));
+            Assert.IsTrue(indexedFieldNames.Contains("unstored"));
+            Assert.IsTrue(indexedFieldNames.Contains("keyword2"));
+            Assert.IsTrue(indexedFieldNames.Contains("text2"));
+            Assert.IsTrue(indexedFieldNames.Contains("unstored2"));
+            Assert.IsTrue(indexedFieldNames.Contains("tvnot"));
+            Assert.IsTrue(indexedFieldNames.Contains("termvector"));
+            Assert.IsTrue(indexedFieldNames.Contains("tvposition"));
+            Assert.IsTrue(indexedFieldNames.Contains("tvoffset"));
+            Assert.IsTrue(indexedFieldNames.Contains("tvpositionoffset"));
+
+            // verify that only unindexed fields were returned
+            Assert.AreEqual(2, notIndexedFieldNames.Count); // the following fields
+            Assert.IsTrue(notIndexedFieldNames.Contains("unindexed"));
+            Assert.IsTrue(notIndexedFieldNames.Contains("unindexed2"));
+
+            // verify index term vector fields
+            Assert.AreEqual(4, tvFieldNames.Count, tvFieldNames.ToString()); // 4 field has term vector only
+            Assert.IsTrue(tvFieldNames.Contains("termvector"));
+
+            reader.Dispose();
+            d.Dispose();
+        }
+
+#if !NETSTANDARD
+        // LUCENENET: There is no Timeout on NUnit for .NET Core.
+        [Timeout(40000)]
+#endif
+        [Test, HasTimeout]
+        public virtual void TestTermVectors()
+        {
+            Directory d = NewDirectory();
+            // set up writer
+            IndexWriter writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+            // want to get some more segments here
+            // new termvector fields
+            int mergeFactor = ((LogMergePolicy)writer.Config.MergePolicy).MergeFactor;
+            FieldType customType5 = new FieldType(TextField.TYPE_STORED);
+            customType5.StoreTermVectors = true;
+            FieldType customType6 = new FieldType(TextField.TYPE_STORED);
+            customType6.StoreTermVectors = true;
+            customType6.StoreTermVectorOffsets = true;
+            FieldType customType7 = new FieldType(TextField.TYPE_STORED);
+            customType7.StoreTermVectors = true;
+            customType7.StoreTermVectorPositions = true;
+            FieldType customType8 = new FieldType(TextField.TYPE_STORED);
+            customType8.StoreTermVectors = true;
+            customType8.StoreTermVectorOffsets = true;
+            customType8.StoreTermVectorPositions = true;
+            for (int i = 0; i < 5 * mergeFactor; i++)
+            {
+                Document doc = new Document();
+                doc.Add(new TextField("tvnot", "one two two three three three", Field.Store.YES));
+                doc.Add(new Field("termvector", "one two two three three three", customType5));
+                doc.Add(new Field("tvoffset", "one two two three three three", customType6));
+                doc.Add(new Field("tvposition", "one two two three three three", customType7));
+                doc.Add(new Field("tvpositionoffset", "one two two three three three", customType8));
+
+                writer.AddDocument(doc);
+            }
+            writer.Dispose();
+            d.Dispose();
+        }
+
+        internal virtual void AssertTermDocsCount(string msg, IndexReader reader, Term term, int expected)
+        {
+            DocsEnum tdocs = TestUtil.Docs(Random(), reader, term.Field, new BytesRef(term.Text()), MultiFields.GetLiveDocs(reader), null, 0);
+            int count = 0;
+            if (tdocs != null)
+            {
+                while (tdocs.NextDoc() != DocIdSetIterator.NO_MORE_DOCS)
+                {
+                    count++;
+                }
+            }
+            Assert.AreEqual(expected, count, msg + ", count mismatch");
+        }
+
+        [Test]
+        public virtual void TestBinaryFields()
+        {
+            Directory dir = NewDirectory();
+            byte[] bin = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+
+            for (int i = 0; i < 10; i++)
+            {
+                AddDoc(writer, "document number " + (i + 1));
+                AddDocumentWithFields(writer);
+                AddDocumentWithDifferentFields(writer);
+                AddDocumentWithTermVectorFields(writer);
+            }
+            writer.Dispose();
+            writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.APPEND).SetMergePolicy(NewLogMergePolicy()));
+            Document doc = new Document();
+            doc.Add(new StoredField("bin1", bin));
+            doc.Add(new TextField("junk", "junk text", Field.Store.NO));
+            writer.AddDocument(doc);
+            writer.Dispose();
+            DirectoryReader reader = DirectoryReader.Open(dir);
+            Document doc2 = reader.Document(reader.MaxDoc - 1);
+            IIndexableField[] fields = doc2.GetFields("bin1");
+            Assert.IsNotNull(fields);
+            Assert.AreEqual(1, fields.Length);
+            IIndexableField b1 = fields[0];
+            Assert.IsTrue(b1.GetBinaryValue() != null);
+            BytesRef bytesRef = b1.GetBinaryValue();
+            Assert.AreEqual(bin.Length, bytesRef.Length);
+            for (int i = 0; i < bin.Length; i++)
+            {
+                Assert.AreEqual(bin[i], bytesRef.Bytes[i + bytesRef.Offset]);
+            }
+            reader.Dispose();
+            // force merge
+
+            writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.APPEND).SetMergePolicy(NewLogMergePolicy()));
+            writer.ForceMerge(1);
+            writer.Dispose();
+            reader = DirectoryReader.Open(dir);
+            doc2 = reader.Document(reader.MaxDoc - 1);
+            fields = doc2.GetFields("bin1");
+            Assert.IsNotNull(fields);
+            Assert.AreEqual(1, fields.Length);
+            b1 = fields[0];
+            Assert.IsTrue(b1.GetBinaryValue() != null);
+            bytesRef = b1.GetBinaryValue();
+            Assert.AreEqual(bin.Length, bytesRef.Length);
+            for (int i = 0; i < bin.Length; i++)
+            {
+                Assert.AreEqual(bin[i], bytesRef.Bytes[i + bytesRef.Offset]);
+            }
+            reader.Dispose();
+            dir.Dispose();
+        }
+
+        /* ??? public void testOpenEmptyDirectory() throws IOException{
+          String dirName = "test.empty";
+          File fileDirName = new File(dirName);
+          if (!fileDirName.exists()) {
+            fileDirName.mkdir();
+          }
+          try {
+            DirectoryReader.Open(fileDirName);
+            Assert.Fail("opening DirectoryReader on empty directory failed to produce FileNotFoundException/NoSuchFileException");
+          } catch (FileNotFoundException | NoSuchFileException e) {
+            // GOOD
+          }
+          rmDir(fileDirName);
+        }*/
+
+        [Test]
+        public virtual void TestFilesOpenClose()
+        {
+            // Create initial data set
+            DirectoryInfo dirFile = CreateTempDir("TestIndexReader.testFilesOpenClose");
+            Directory dir = NewFSDirectory(dirFile);
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            AddDoc(writer, "test");
+            writer.Dispose();
+            dir.Dispose();
+
+            // Try to erase the data - this ensures that the writer closed all files
+            System.IO.Directory.Delete(dirFile.FullName, true);
+            dir = NewFSDirectory(dirFile);
+
+            // Now create the data set again, just as before
+            writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.CREATE));
+            AddDoc(writer, "test");
+            writer.Dispose();
+            dir.Dispose();
+
+            // Now open existing directory and test that reader closes all files
+            dir = NewFSDirectory(dirFile);
+            DirectoryReader reader1 = DirectoryReader.Open(dir);
+            reader1.Dispose();
+            dir.Dispose();
+
+            // The following will fail if reader did not close
+            // all files
+            System.IO.Directory.Delete(dirFile.FullName, true);
+        }
+
+        [Test]
+        public virtual void TestOpenReaderAfterDelete()
+        {
+            DirectoryInfo dirFile = CreateTempDir("deletetest");
+            Directory dir = NewFSDirectory(dirFile);
+            try
+            {
+                DirectoryReader.Open(dir);
+                Assert.Fail("expected FileNotFoundException/NoSuchFileException");
+            }
+#pragma warning disable 168
+            catch (System.IO.FileNotFoundException /*| NoSuchFileException*/ e)
+#pragma warning restore 168
+            {
+                // expected
+            }
+
+            dirFile.Delete();
+
+            // Make sure we still get a CorruptIndexException (not NPE):
+            try
+            {
+                DirectoryReader.Open(dir);
+                Assert.Fail("expected FileNotFoundException/NoSuchFileException");
+            }
+#pragma warning disable 168
+            catch (System.IO.FileNotFoundException /*| NoSuchFileException*/ e)
+#pragma warning restore 168
+            {
+                // expected
+            }
+
+            dir.Dispose();
+        }
+
+        /// <summary>
+        /// LUCENENET specific
+        /// Is non-static because NewStringField, NewTextField, NewField methods
+        /// are no longer static.
+        /// </summary>
+        internal void AddDocumentWithFields(IndexWriter writer)
+        {
+            Document doc = new Document();
+
+            FieldType customType3 = new FieldType();
+            customType3.IsStored = true;
+            doc.Add(NewStringField("keyword", "test1", Field.Store.YES));
+            doc.Add(NewTextField("text", "test1", Field.Store.YES));
+            doc.Add(NewField("unindexed", "test1", customType3));
+            doc.Add(new TextField("unstored", "test1", Field.Store.NO));
+            writer.AddDocument(doc);
+        }
+
+        /// <summary>
+        /// LUCENENET specific
+        /// Is non-static because NewStringField, NewTextField, NewField methods
+        /// are no longer static.
+        /// </summary>
+        internal void AddDocumentWithDifferentFields(IndexWriter writer)
+        {
+            Document doc = new Document();
+
+            FieldType customType3 = new FieldType();
+            customType3.IsStored = true;
+            doc.Add(NewStringField("keyword2", "test1", Field.Store.YES));
+            doc.Add(NewTextField("text2", "test1", Field.Store.YES));
+            doc.Add(NewField("unindexed2", "test1", customType3));
+            doc.Add(new TextField("unstored2", "test1", Field.Store.NO));
+            writer.AddDocument(doc);
+        }
+
+        /// <summary>
+        /// LUCENENET specific
+        /// Is non-static because NewTextField, NewField methods are no longer
+        /// static.
+        /// </summary>
+        internal void AddDocumentWithTermVectorFields(IndexWriter writer)
+        {
+            Document doc = new Document();
+            FieldType customType5 = new FieldType(TextField.TYPE_STORED);
+            customType5.StoreTermVectors = true;
+            FieldType customType6 = new FieldType(TextField.TYPE_STORED);
+            customType6.StoreTermVectors = true;
+            customType6.StoreTermVectorOffsets = true;
+            FieldType customType7 = new FieldType(TextField.TYPE_STORED);
+            customType7.StoreTermVectors = true;
+            customType7.StoreTermVectorPositions = true;
+            FieldType customType8 = new FieldType(TextField.TYPE_STORED);
+            customType8.StoreTermVectors = true;
+            customType8.StoreTermVectorOffsets = true;
+            customType8.StoreTermVectorPositions = true;
+            doc.Add(NewTextField("tvnot", "tvnot", Field.Store.YES));
+            doc.Add(NewField("termvector", "termvector", customType5));
+            doc.Add(NewField("tvoffset", "tvoffset", customType6));
+            doc.Add(NewField("tvposition", "tvposition", customType7));
+            doc.Add(NewField("tvpositionoffset", "tvpositionoffset", customType8));
+
+            writer.AddDocument(doc);
+        }
+
+        /// <summary>
+        /// LUCENENET specific
+        /// Is non-static because NewTextField is no longer static.
+        /// </summary>
+        internal void AddDoc(IndexWriter writer, string value)
+        {
+            Document doc = new Document();
+            doc.Add(NewTextField("content", value, Field.Store.NO));
+            writer.AddDocument(doc);
+        }
+
+        // TODO: maybe this can reuse the logic of test dueling codecs?
+        public static void AssertIndexEquals(DirectoryReader index1, DirectoryReader index2)
+        {
+            Assert.AreEqual(index1.NumDocs, index2.NumDocs, "IndexReaders have different values for numDocs.");
+            Assert.AreEqual(index1.MaxDoc, index2.MaxDoc, "IndexReaders have different values for maxDoc.");
+            Assert.AreEqual(index1.HasDeletions, index2.HasDeletions, "Only one IndexReader has deletions.");
+            Assert.AreEqual(index1.Leaves.Count == 1, index2.Leaves.Count == 1, "Single segment test differs.");
+
+            // check field names
+            FieldInfos fieldInfos1 = MultiFields.GetMergedFieldInfos(index1);
+            FieldInfos fieldInfos2 = MultiFields.GetMergedFieldInfos(index2);
+            Assert.AreEqual(fieldInfos1.Count, fieldInfos2.Count, "IndexReaders have different numbers of fields.");
+            int numFields = fieldInfos1.Count;
+            for (int fieldID = 0; fieldID < numFields; fieldID++)
+            {
+                FieldInfo fieldInfo1 = fieldInfos1.FieldInfo(fieldID);
+                FieldInfo fieldInfo2 = fieldInfos2.FieldInfo(fieldID);
+                Assert.AreEqual(fieldInfo1.Name, fieldInfo2.Name, "Different field names.");
+            }
+
+            // check norms
+            foreach (FieldInfo fieldInfo in fieldInfos1)
+            {
+                string curField = fieldInfo.Name;
+                NumericDocValues norms1 = MultiDocValues.GetNormValues(index1, curField);
+                NumericDocValues norms2 = MultiDocValues.GetNormValues(index2, curField);
+                if (norms1 != null && norms2 != null)
+                {
+                    // todo: generalize this (like TestDuelingCodecs assert)
+                    for (int i = 0; i < index1.MaxDoc; i++)
+                    {
+                        Assert.AreEqual(norms1.Get(i), norms2.Get(i), "Norm different for doc " + i + " and field '" + curField + "'.");
+                    }
+                }
+                else
+                {
+                    Assert.IsNull(norms1);
+                    Assert.IsNull(norms2);
+                }
+            }
+
+            // check deletions
+            IBits liveDocs1 = MultiFields.GetLiveDocs(index1);
+            IBits liveDocs2 = MultiFields.GetLiveDocs(index2);
+            for (int i = 0; i < index1.MaxDoc; i++)
+            {
+                Assert.AreEqual(liveDocs1 == null || !liveDocs1.Get(i), liveDocs2 == null || !liveDocs2.Get(i), "Doc " + i + " only deleted in one index.");
+            }
+
+            // check stored fields
+            for (int i = 0; i < index1.MaxDoc; i++)
+            {
+                if (liveDocs1 == null || liveDocs1.Get(i))
+                {
+                    Document doc1 = index1.Document(i);
+                    Document doc2 = index2.Document(i);
+                    IList<IIndexableField> field1 = doc1.Fields;
+                    IList<IIndexableField> field2 = doc2.Fields;
+                    Assert.AreEqual(field1.Count, field2.Count, "Different numbers of fields for doc " + i + ".");
+                    IEnumerator<IIndexableField> itField1 = field1.GetEnumerator();
+                    IEnumerator<IIndexableField> itField2 = field2.GetEnumerator();
+                    while (itField1.MoveNext())
+                    {
+                        Field curField1 = (Field)itField1.Current;
+                        itField2.MoveNext();
+                        Field curField2 = (Field)itField2.Current;
+                        Assert.AreEqual(curField1.Name, curField2.Name, "Different fields names for doc " + i + ".");
+                        Assert.AreEqual(curField1.GetStringValue(), curField2.GetStringValue(), "Different field values for doc " + i + ".");
+                    }
+                }
+            }
+
+            // check dictionary and posting lists
+            Fields fields1 = MultiFields.GetFields(index1);
+            Fields fields2 = MultiFields.GetFields(index2);
+            IEnumerator<string> fenum2 = fields2.GetEnumerator();
+            IBits liveDocs = MultiFields.GetLiveDocs(index1);
+            foreach (string field1 in fields1)
+            {
+                fenum2.MoveNext();
+                Assert.AreEqual(field1, fenum2.Current, "Different fields");
+                Terms terms1 = fields1.GetTerms(field1);
+                if (terms1 == null)
+                {
+                    Assert.IsNull(fields2.GetTerms(field1));
+                    continue;
+                }
+                TermsEnum enum1 = terms1.GetIterator(null);
+
+                Terms terms2 = fields2.GetTerms(field1);
+                Assert.IsNotNull(terms2);
+                TermsEnum enum2 = terms2.GetIterator(null);
+
+                while (enum1.Next() != null)
+                {
+                    Assert.AreEqual(enum1.Term, enum2.Next(), "Different terms");
+                    DocsAndPositionsEnum tp1 = enum1.DocsAndPositions(liveDocs, null);
+                    DocsAndPositionsEnum tp2 = enum2.DocsAndPositions(liveDocs, null);
+
+                    while (tp1.NextDoc() != DocIdSetIterator.NO_MORE_DOCS)
+                    {
+                        Assert.IsTrue(tp2.NextDoc() != DocIdSetIterator.NO_MORE_DOCS);
+                        Assert.AreEqual(tp1.DocID, tp2.DocID, "Different doc id in postinglist of term " + enum1.Term + ".");
+                        Assert.AreEqual(tp1.Freq, tp2.Freq, "Different term frequence in postinglist of term " + enum1.Term + ".");
+                        for (int i = 0; i < tp1.Freq; i++)
+                        {
+                            Assert.AreEqual(tp1.NextPosition(), tp2.NextPosition(), "Different positions in postinglist of term " + enum1.Term + ".");
+                        }
+                    }
+                }
+            }
+            Assert.IsFalse(fenum2.MoveNext());
+        }
+
+        [Test]
+        public virtual void TestGetIndexCommit()
+        {
+            Directory d = NewDirectory();
+
+            // set up writer
+            IndexWriter writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMaxBufferedDocs(2).SetMergePolicy(NewLogMergePolicy(10)));
+            for (int i = 0; i < 27; i++)
+            {
+                AddDocumentWithFields(writer);
+            }
+            writer.Dispose();
+
+            SegmentInfos sis = new SegmentInfos();
+            sis.Read(d);
+            DirectoryReader r = DirectoryReader.Open(d);
+            IndexCommit c = r.IndexCommit;
+
+            Assert.AreEqual(sis.GetSegmentsFileName(), c.SegmentsFileName);
+
+            Assert.IsTrue(c.Equals(r.IndexCommit));
+
+            // Change the index
+            writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.APPEND).SetMaxBufferedDocs(2).SetMergePolicy(NewLogMergePolicy(10)));
+            for (int i = 0; i < 7; i++)
+            {
+                AddDocumentWithFields(writer);
+            }
+            writer.Dispose();
+
+            DirectoryReader r2 = DirectoryReader.OpenIfChanged(r);
+            Assert.IsNotNull(r2);
+            Assert.IsFalse(c.Equals(r2.IndexCommit));
+            Assert.IsFalse(r2.IndexCommit.SegmentCount == 1);
+            r2.Dispose();
+
+            writer = new IndexWriter(d, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetOpenMode(OpenMode.APPEND));
+            writer.ForceMerge(1);
+            writer.Dispose();
+
+            r2 = DirectoryReader.OpenIfChanged(r);
+            Assert.IsNotNull(r2);
+            Assert.IsNull(DirectoryReader.OpenIfChanged(r2));
+            Assert.AreEqual(1, r2.IndexCommit.SegmentCount);
+
+            r.Dispose();
+            r2.Dispose();
+            d.Dispose();
+        }
+
+        internal Document CreateDocument(string id)
+        {
+            Document doc = new Document();
+            FieldType customType = new FieldType(TextField.TYPE_STORED);
+            customType.IsTokenized = false;
+            customType.OmitNorms = true;
+
+            doc.Add(NewField("id", id, customType));
+            return doc;
+        }
+
+        // LUCENE-1468 -- make sure on attempting to open an
+        // DirectoryReader on a non-existent directory, you get a
+        // good exception
+        [Test]
+        public virtual void TestNoDir()
+        {
+            DirectoryInfo tempDir = CreateTempDir("doesnotexist");
+            System.IO.Directory.Delete(tempDir.FullName, true);
+            Directory dir = NewFSDirectory(tempDir);
+            try
+            {
+                DirectoryReader.Open(dir);
+                Assert.Fail("did not hit expected exception");
+            }
+#pragma warning disable 168
+            catch (NoSuchDirectoryException nsde)
+#pragma warning restore 168
+            {
+                // expected
+            }
+            dir.Dispose();
+        }
+
+        // LUCENE-1509
+        [Test]
+        public virtual void TestNoDupCommitFileNames()
+        {
+            Directory dir = NewDirectory();
+
+            IndexWriter writer = new IndexWriter(dir, (IndexWriterConfig)NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMaxBufferedDocs(2));
+            writer.AddDocument(CreateDocument("a"));
+            writer.AddDocument(CreateDocument("a"));
+            writer.AddDocument(CreateDocument("a"));
+            writer.Dispose();
+
+            ICollection<IndexCommit> commits = DirectoryReader.ListCommits(dir);
+            foreach (IndexCommit commit in commits)
+            {
+                ICollection<string> files = commit.FileNames;
+                HashSet<string> seen = new HashSet<string>();
+                foreach (String fileName in files)
+                {
+                    Assert.IsTrue(!seen.Contains(fileName), "file " + fileName + " was duplicated");
+                    seen.Add(fileName);
+                }
+            }
+
+            dir.Dispose();
+        }
+
+        // LUCENE-1579: Ensure that on a reopened reader, that any
+        // shared segments reuse the doc values arrays in
+        // FieldCache
+        [Test]
+        public virtual void TestFieldCacheReuseAfterReopen()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy(10)));
+            Document doc = new Document();
+            doc.Add(NewStringField("number", "17", Field.Store.NO));
+            writer.AddDocument(doc);
+            writer.Commit();
+
+            // Open reader1
+            DirectoryReader r = DirectoryReader.Open(dir);
+            AtomicReader r1 = GetOnlySegmentReader(r);
+            FieldCache.Int32s ints = FieldCache.DEFAULT.GetInt32s(r1, "number", false);
+            Assert.AreEqual(17, ints.Get(0));
+
+            // Add new segment
+            writer.AddDocument(doc);
+            writer.Commit();
+
+            // Reopen reader1 --> reader2
+            DirectoryReader r2 = DirectoryReader.OpenIfChanged(r);
+            Assert.IsNotNull(r2);
+            r.Dispose();
+            AtomicReader sub0 = (AtomicReader)r2.Leaves[0].Reader;
+            FieldCache.Int32s ints2 = FieldCache.DEFAULT.GetInt32s(sub0, "number", false);
+            r2.Dispose();
+            Assert.IsTrue(ints == ints2);
+
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        // LUCENE-1586: getUniqueTermCount
+        [Test]
+        public virtual void TestUniqueTermCount()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            Document doc = new Document();
+            doc.Add(NewTextField("field", "a b c d e f g h i j k l m n o p q r s t u v w x y z", Field.Store.NO));
+            doc.Add(NewTextField("number", "0 1 2 3 4 5 6 7 8 9", Field.Store.NO));
+            writer.AddDocument(doc);
+            writer.AddDocument(doc);
+            writer.Commit();
+
+            DirectoryReader r = DirectoryReader.Open(dir);
+            AtomicReader r1 = GetOnlySegmentReader(r);
+#pragma warning disable 612, 618
+            Assert.AreEqual(36, r1.Fields.UniqueTermCount);
+#pragma warning restore 612, 618
+            writer.AddDocument(doc);
+            writer.Commit();
+            DirectoryReader r2 = DirectoryReader.OpenIfChanged(r);
+            Assert.IsNotNull(r2);
+            r.Dispose();
+
+            foreach (AtomicReaderContext s in r2.Leaves)
+            {
+#pragma warning disable 612, 618
+                Assert.AreEqual(36, ((AtomicReader)s.Reader).Fields.UniqueTermCount);
+#pragma warning restore 612, 618
+            }
+            r2.Dispose();
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        // LUCENE-1609: don't load terms index
+        [Test]
+        public virtual void TestNoTermsIndex()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetCodec(TestUtil.AlwaysPostingsFormat(new Lucene41PostingsFormat())));
+            Document doc = new Document();
+            doc.Add(NewTextField("field", "a b c d e f g h i j k l m n o p q r s t u v w x y z", Field.Store.NO));
+            doc.Add(NewTextField("number", "0 1 2 3 4 5 6 7 8 9", Field.Store.NO));
+            writer.AddDocument(doc);
+            writer.AddDocument(doc);
+            writer.Dispose();
+
+            DirectoryReader r = DirectoryReader.Open(dir, -1);
+            try
+            {
+                r.DocFreq(new Term("field", "f"));
+                Assert.Fail("did not hit expected exception");
+            }
+#pragma warning disable 168
+            catch (InvalidOperationException ise)
+#pragma warning restore 168
+            {
+                // expected
+            }
+
+            Assert.AreEqual(-1, ((SegmentReader)r.Leaves[0].Reader).TermInfosIndexDivisor);
+            writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetCodec(TestUtil.AlwaysPostingsFormat(new Lucene41PostingsFormat())).SetMergePolicy(NewLogMergePolicy(10)));
+            writer.AddDocument(doc);
+            writer.Dispose();
+
+            // LUCENE-1718: ensure re-open carries over no terms index:
+            DirectoryReader r2 = DirectoryReader.OpenIfChanged(r);
+            Assert.IsNotNull(r2);
+            Assert.IsNull(DirectoryReader.OpenIfChanged(r2));
+            r.Dispose();
+            IList<AtomicReaderContext> leaves = r2.Leaves;
+            Assert.AreEqual(2, leaves.Count);
+            foreach (AtomicReaderContext ctx in leaves)
+            {
+                try
+                {
+                    ctx.Reader.DocFreq(new Term("field", "f"));
+                    Assert.Fail("did not hit expected exception");
+                }
+#pragma warning disable 168
+                catch (InvalidOperationException ise)
+#pragma warning restore 168
+                {
+                    // expected
+                }
+            }
+            r2.Dispose();
+            dir.Dispose();
+        }
+
+        // LUCENE-2046
+        [Test]
+        public virtual void TestPrepareCommitIsCurrent()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            writer.Commit();
+            Document doc = new Document();
+            writer.AddDocument(doc);
+            DirectoryReader r = DirectoryReader.Open(dir);
+            Assert.IsTrue(r.IsCurrent);
+            writer.AddDocument(doc);
+            writer.PrepareCommit();
+            Assert.IsTrue(r.IsCurrent);
+            DirectoryReader r2 = DirectoryReader.OpenIfChanged(r);
+            Assert.IsNull(r2);
+            writer.Commit();
+            Assert.IsFalse(r.IsCurrent);
+            writer.Dispose();
+            r.Dispose();
+            dir.Dispose();
+        }
+
+        // LUCENE-2753
+        [Test]
+        public virtual void TestListCommits()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, null).SetIndexDeletionPolicy(new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy())));
+            SnapshotDeletionPolicy sdp = (SnapshotDeletionPolicy)writer.Config.IndexDeletionPolicy;
+            writer.AddDocument(new Document());
+            writer.Commit();
+            sdp.Snapshot();
+            writer.AddDocument(new Document());
+            writer.Commit();
+            sdp.Snapshot();
+            writer.AddDocument(new Document());
+            writer.Commit();
+            sdp.Snapshot();
+            writer.Dispose();
+            long currentGen = 0;
+            foreach (IndexCommit ic in DirectoryReader.ListCommits(dir))
+            {
+                Assert.IsTrue(currentGen < ic.Generation, "currentGen=" + currentGen + " commitGen=" + ic.Generation);
+                currentGen = ic.Generation;
+            }
+            dir.Dispose();
+        }
+
+        // Make sure totalTermFreq works correctly in the terms
+        // dict cache
+        [Test]
+        public virtual void TestTotalTermFreqCached()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            Document d = new Document();
+            d.Add(NewTextField("f", "a a b", Field.Store.NO));
+            writer.AddDocument(d);
+            DirectoryReader r = writer.Reader;
+            writer.Dispose();
+            try
+            {
+                // Make sure codec impls totalTermFreq (eg PreFlex doesn't)
+                Assume.That(r.TotalTermFreq(new Term("f", new BytesRef("b"))) != -1);
+                Assert.AreEqual(1, r.TotalTermFreq(new Term("f", new BytesRef("b"))));
+                Assert.AreEqual(2, r.TotalTermFreq(new Term("f", new BytesRef("a"))));
+                Assert.AreEqual(1, r.TotalTermFreq(new Term("f", new BytesRef("b"))));
+            }
+            finally
+            {
+                r.Dispose();
+                dir.Dispose();
+            }
+        }
+
+        [Test]
+        public virtual void TestGetSumDocFreq()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            Document d = new Document();
+            d.Add(NewTextField("f", "a", Field.Store.NO));
+            writer.AddDocument(d);
+            d = new Document();
+            d.Add(NewTextField("f", "b", Field.Store.NO));
+            writer.AddDocument(d);
+            DirectoryReader r = writer.Reader;
+            writer.Dispose();
+            try
+            {
+                // Make sure codec impls getSumDocFreq (eg PreFlex doesn't)
+                Assume.That(r.GetSumDocFreq("f") != -1);
+                Assert.AreEqual(2, r.GetSumDocFreq("f"));
+            }
+            finally
+            {
+                r.Dispose();
+                dir.Dispose();
+            }
+        }
+
+        [Test]
+        public virtual void TestGetDocCount()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            Document d = new Document();
+            d.Add(NewTextField("f", "a", Field.Store.NO));
+            writer.AddDocument(d);
+            d = new Document();
+            d.Add(NewTextField("f", "a", Field.Store.NO));
+            writer.AddDocument(d);
+            DirectoryReader r = writer.Reader;
+            writer.Dispose();
+            try
+            {
+                // Make sure codec impls getSumDocFreq (eg PreFlex doesn't)
+                Assume.That(r.GetDocCount("f") != -1);
+                Assert.AreEqual(2, r.GetDocCount("f"));
+            }
+            finally
+            {
+                r.Dispose();
+                dir.Dispose();
+            }
+        }
+
+        [Test]
+        public virtual void TestGetSumTotalTermFreq()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            Document d = new Document();
+            d.Add(NewTextField("f", "a b b", Field.Store.NO));
+            writer.AddDocument(d);
+            d = new Document();
+            d.Add(NewTextField("f", "a a b", Field.Store.NO));
+            writer.AddDocument(d);
+            DirectoryReader r = writer.Reader;
+            writer.Dispose();
+            try
+            {
+                // Make sure codec impls getSumDocFreq (eg PreFlex doesn't)
+                Assume.That(r.GetSumTotalTermFreq("f") != -1);
+                Assert.AreEqual(6, r.GetSumTotalTermFreq("f"));
+            }
+            finally
+            {
+                r.Dispose();
+                dir.Dispose();
+            }
+        }
+
+        // LUCENE-2474
+        [Test]
+        public virtual void TestReaderFinishedListener()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(NewLogMergePolicy()));
+            ((LogMergePolicy)writer.Config.MergePolicy).MergeFactor = 3;
+            writer.AddDocument(new Document());
+            writer.Commit();
+            writer.AddDocument(new Document());
+            writer.Commit();
+            DirectoryReader reader = writer.Reader;
+            int[] closeCount = new int[1];
+            IndexReader.IReaderClosedListener listener = new ReaderClosedListenerAnonymousInnerClassHelper(this, reader, closeCount);
+
+            reader.AddReaderClosedListener(listener);
+
+            reader.Dispose();
+
+            // Close the top reader, its the only one that should be closed
+            Assert.AreEqual(1, closeCount[0]);
+            writer.Dispose();
+
+            DirectoryReader reader2 = DirectoryReader.Open(dir);
+            reader2.AddReaderClosedListener(listener);
+
+            closeCount[0] = 0;
+            reader2.Dispose();
+            Assert.AreEqual(1, closeCount[0]);
+            dir.Dispose();
+        }
+
+        private class ReaderClosedListenerAnonymousInnerClassHelper : IndexReader.IReaderClosedListener
+        {
+            private readonly TestDirectoryReader OuterInstance;
+
+            private DirectoryReader Reader;
+            private int[] CloseCount;
+
+            public ReaderClosedListenerAnonymousInnerClassHelper(TestDirectoryReader outerInstance, DirectoryReader reader, int[] closeCount)
+            {
+                this.OuterInstance = outerInstance;
+                this.Reader = reader;
+                this.CloseCount = closeCount;
+            }
+
+            public void OnClose(IndexReader reader)
+            {
+                CloseCount[0]++;
+            }
+        }
+
+        [Test]
+        public virtual void TestOOBDocID()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            writer.AddDocument(new Document());
+            DirectoryReader r = writer.Reader;
+            writer.Dispose();
+            r.Document(0);
+            try
+            {
+                r.Document(1);
+                Assert.Fail("did not hit exception");
+            }
+#pragma warning disable 168
+            catch (System.ArgumentException iae)
+#pragma warning restore 168
+            {
+                // expected
+            }
+            r.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestTryIncRef()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            writer.AddDocument(new Document());
+            writer.Commit();
+            DirectoryReader r = DirectoryReader.Open(dir);
+            Assert.IsTrue(r.TryIncRef());
+            r.DecRef();
+            r.Dispose();
+            Assert.IsFalse(r.TryIncRef());
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestStressTryIncRef()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            writer.AddDocument(new Document());
+            writer.Commit();
+            DirectoryReader r = DirectoryReader.Open(dir);
+            int numThreads = AtLeast(2);
+
+            IncThread[] threads = new IncThread[numThreads];
+            for (int i = 0; i < threads.Length; i++)
+            {
+                threads[i] = new IncThread(r, Random());
+                threads[i].Start();
+            }
+            Thread.Sleep(100);
+
+            Assert.IsTrue(r.TryIncRef());
+            r.DecRef();
+            r.Dispose();
+
+            for (int i = 0; i < threads.Length; i++)
+            {
+                threads[i].Join();
+                Assert.IsNull(threads[i].Failed);
+            }
+            Assert.IsFalse(r.TryIncRef());
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        internal class IncThread : ThreadClass
+        {
+            internal readonly IndexReader ToInc;
+            internal readonly Random Random;
+            internal Exception Failed;
+
+            internal IncThread(IndexReader toInc, Random random)
+            {
+                this.ToInc = toInc;
+                this.Random = random;
+            }
+
+            public override void Run()
+            {
+                try
+                {
+                    while (ToInc.TryIncRef())
+                    {
+                        Assert.IsFalse(ToInc.HasDeletions);
+                        ToInc.DecRef();
+                    }
+                    Assert.IsFalse(ToInc.TryIncRef());
+                }
+                catch (Exception e)
+                {
+                    Failed = e;
+                }
+            }
+        }
+
+        [Test]
+        public virtual void TestLoadCertainFields()
+        {
+            Directory dir = NewDirectory();
+            RandomIndexWriter writer = new RandomIndexWriter(Random(), dir, Similarity, TimeZone);
+            Document doc = new Document();
+            doc.Add(NewStringField("field1", "foobar", Field.Store.YES));
+            doc.Add(NewStringField("field2", "foobaz", Field.Store.YES));
+            writer.AddDocument(doc);
+            DirectoryReader r = writer.Reader;
+            writer.Dispose();
+            HashSet<string> fieldsToLoad = new HashSet<string>();
+            Assert.AreEqual(0, r.Document(0, fieldsToLoad).Fields.Count);
+            fieldsToLoad.Add("field1");
+            Document doc2 = r.Document(0, fieldsToLoad);
+            Assert.AreEqual(1, doc2.Fields.Count);
+            Assert.AreEqual("foobar", doc2.Get("field1"));
+            r.Dispose();
+            dir.Dispose();
+        }
+
+        /// @deprecated just to ensure IndexReader static methods work
+        [Obsolete("just to ensure IndexReader static methods work")]
+        [Test]
+        public virtual void TestBackwards()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetCodec(TestUtil.AlwaysPostingsFormat(new Lucene41PostingsFormat())));
+            Document doc = new Document();
+            doc.Add(NewTextField("field", "a b c d e f g h i j k l m n o p q r s t u v w x y z", Field.Store.NO));
+            doc.Add(NewTextField("number", "0 1 2 3 4 5 6 7 8 9", Field.Store.NO));
+            writer.AddDocument(doc);
+
+            // open(IndexWriter, boolean)
+            DirectoryReader r = IndexReader.Open(writer, true);
+            Assert.AreEqual(1, r.DocFreq(new Term("field", "f")));
+            r.Dispose();
+            writer.AddDocument(doc);
+            writer.Dispose();
+
+            // open(Directory)
+            r = IndexReader.Open(dir);
+            Assert.AreEqual(2, r.DocFreq(new Term("field", "f")));
+            r.Dispose();
+
+            // open(IndexCommit)
+            IList<IndexCommit> commits = DirectoryReader.ListCommits(dir);
+            Assert.AreEqual(1, commits.Count);
+            r = IndexReader.Open(commits[0]);
+            Assert.AreEqual(2, r.DocFreq(new Term("field", "f")));
+            r.Dispose();
+
+            // open(Directory, int)
+            r = IndexReader.Open(dir, -1);
+            try
+            {
+                r.DocFreq(new Term("field", "f"));
+                Assert.Fail("did not hit expected exception");
+            }
+#pragma warning disable 168
+            catch (InvalidOperationException ise)
+#pragma warning restore 168
+            {
+                // expected
+            }
+            Assert.AreEqual(-1, ((SegmentReader)r.Leaves[0].Reader).TermInfosIndexDivisor);
+            r.Dispose();
+
+            // open(IndexCommit, int)
+            r = IndexReader.Open(commits[0], -1);
+            try
+            {
+                r.DocFreq(new Term("field", "f"));
+                Assert.Fail("did not hit expected exception");
+            }
+#pragma warning disable 168
+            catch (InvalidOperationException ise)
+#pragma warning restore 168
+            {
+                // expected
+            }
+            Assert.AreEqual(-1, ((SegmentReader)r.Leaves[0].Reader).TermInfosIndexDivisor);
+            r.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestIndexExistsOnNonExistentDirectory()
+        {
+            DirectoryInfo tempDir = CreateTempDir("testIndexExistsOnNonExistentDirectory");
+            tempDir.Delete();
+            Directory dir = NewFSDirectory(tempDir);
+            Console.WriteLine("dir=" + dir);
+            Assert.IsFalse(DirectoryReader.IndexExists(dir));
+            dir.Dispose();
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/96822396/src/Lucene.Net.Tests/Index/TestDirectoryReaderReopen.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/Index/TestDirectoryReaderReopen.cs b/src/Lucene.Net.Tests/Index/TestDirectoryReaderReopen.cs
new file mode 100644
index 0000000..bb52f58
--- /dev/null
+++ b/src/Lucene.Net.Tests/Index/TestDirectoryReaderReopen.cs
@@ -0,0 +1,785 @@
+using Lucene.Net.Documents;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Lucene.Net.Index
+{
+    using Lucene.Net.Support;
+    using NUnit.Framework;
+    using Directory = Lucene.Net.Store.Directory;
+    using Document = Documents.Document;
+    using Field = Field;
+    using FieldType = FieldType;
+    using IndexSearcher = Lucene.Net.Search.IndexSearcher;
+    using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
+
+    /*
+         * Licensed to the Apache Software Foundation (ASF) under one or more
+         * contributor license agreements.  See the NOTICE file distributed with
+         * this work for additional information regarding copyright ownership.
+         * The ASF licenses this file to You under the Apache License, Version 2.0
+         * (the "License"); you may not use this file except in compliance with
+         * the License.  You may obtain a copy of the License at
+         *
+         *     http://www.apache.org/licenses/LICENSE-2.0
+         *
+         * Unless required by applicable law or agreed to in writing, software
+         * distributed under the License is distributed on an "AS IS" BASIS,
+         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         * See the License for the specific language governing permissions and
+         * limitations under the License.
+         */
+
+    using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+    using ScoreDoc = Lucene.Net.Search.ScoreDoc;
+    using TermQuery = Lucene.Net.Search.TermQuery;
+    using TestUtil = Lucene.Net.Util.TestUtil;
+    using TextField = TextField;
+
+    [TestFixture]
+    public class TestDirectoryReaderReopen : LuceneTestCase
+    {
+        [Test]
+        public virtual void TestReopen_Mem()
+        {
+            Directory dir1 = NewDirectory();
+
+            CreateIndex(Random(), dir1, false);
+            PerformDefaultTests(new TestReopenAnonymousInnerClassHelper(this, dir1));
+            dir1.Dispose();
+
+            Directory dir2 = NewDirectory();
+
+            CreateIndex(Random(), dir2, true);
+            PerformDefaultTests(new TestReopenAnonymousInnerClassHelper2(this, dir2));
+            dir2.Dispose();
+        }
+
+        private class TestReopenAnonymousInnerClassHelper : TestReopen
+        {
+            private readonly TestDirectoryReaderReopen OuterInstance;
+
+            private Directory Dir1;
+
+            public TestReopenAnonymousInnerClassHelper(TestDirectoryReaderReopen outerInstance, Directory dir1)
+            {
+                this.OuterInstance = outerInstance;
+                this.Dir1 = dir1;
+            }
+
+            protected internal override void ModifyIndex(int i)
+            {
+                TestDirectoryReaderReopen.ModifyIndex(i, Dir1);
+            }
+
+            protected internal override DirectoryReader OpenReader()
+            {
+                return DirectoryReader.Open(Dir1);
+            }
+        }
+
+        private class TestReopenAnonymousInnerClassHelper2 : TestReopen
+        {
+            private readonly TestDirectoryReaderReopen OuterInstance;
+
+            private Directory Dir2;
+
+            public TestReopenAnonymousInnerClassHelper2(TestDirectoryReaderReopen outerInstance, Directory dir2)
+            {
+                this.OuterInstance = outerInstance;
+                this.Dir2 = dir2;
+            }
+
+            protected internal override void ModifyIndex(int i)
+            {
+                TestDirectoryReaderReopen.ModifyIndex(i, Dir2);
+            }
+
+            protected internal override DirectoryReader OpenReader()
+            {
+                return DirectoryReader.Open(Dir2);
+            }
+        }
+
+        // LUCENE-1228: IndexWriter.Commit() does not update the index version
+        // populate an index in iterations.
+        // at the end of every iteration, commit the index and reopen/recreate the reader.
+        // in each iteration verify the work of previous iteration.
+        // try this once with reopen once recreate, on both RAMDir and FSDir.
+        [Test]
+        public virtual void TestCommitReopen()
+        {
+            Directory dir = NewDirectory();
+            DoTestReopenWithCommit(Random(), dir, true);
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestCommitRecreate()
+        {
+            Directory dir = NewDirectory();
+            DoTestReopenWithCommit(Random(), dir, false);
+            dir.Dispose();
+        }
+
+        private void DoTestReopenWithCommit(Random random, Directory dir, bool withReopen)
+        {
+            IndexWriter iwriter = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).SetOpenMode(OpenMode.CREATE).SetMergeScheduler(new SerialMergeScheduler()).SetMergePolicy(NewLogMergePolicy()));
+            iwriter.Commit();
+            DirectoryReader reader = DirectoryReader.Open(dir);
+            try
+            {
+                int M = 3;
+                FieldType customType = new FieldType(TextField.TYPE_STORED);
+                customType.IsTokenized = false;
+                FieldType customType2 = new FieldType(TextField.TYPE_STORED);
+                customType2.IsTokenized = false;
+                customType2.OmitNorms = true;
+                FieldType customType3 = new FieldType();
+                customType3.IsStored = true;
+                for (int i = 0; i < 4; i++)
+                {
+                    for (int j = 0; j < M; j++)
+                    {
+                        Document doc = new Document();
+                        doc.Add(NewField("id", i + "_" + j, customType));
+                        doc.Add(NewField("id2", i + "_" + j, customType2));
+                        doc.Add(NewField("id3", i + "_" + j, customType3));
+                        iwriter.AddDocument(doc);
+                        if (i > 0)
+                        {
+                            int k = i - 1;
+                            int n = j + k * M;
+                            Document prevItereationDoc = reader.Document(n);
+                            Assert.IsNotNull(prevItereationDoc);
+                            string id = prevItereationDoc.Get("id");
+                            Assert.AreEqual(k + "_" + j, id);
+                        }
+                    }
+                    iwriter.Commit();
+                    if (withReopen)
+                    {
+                        // reopen
+                        DirectoryReader r2 = DirectoryReader.OpenIfChanged(reader);
+                        if (r2 != null)
+                        {
+                            reader.Dispose();
+                            reader = r2;
+                        }
+                    }
+                    else
+                    {
+                        // recreate
+                        reader.Dispose();
+                        reader = DirectoryReader.Open(dir);
+                    }
+                }
+            }
+            finally
+            {
+                iwriter.Dispose();
+                reader.Dispose();
+            }
+        }
+
+        private void PerformDefaultTests(TestReopen test)
+        {
+            DirectoryReader index1 = test.OpenReader();
+            DirectoryReader index2 = test.OpenReader();
+
+            TestDirectoryReader.AssertIndexEquals(index1, index2);
+
+            // verify that reopen() does not return a new reader instance
+            // in case the index has no changes
+            ReaderCouple couple = RefreshReader(index2, false);
+            Assert.IsTrue(couple.RefreshedReader == index2);
+
+            couple = RefreshReader(index2, test, 0, true);
+            index1.Dispose();
+            index1 = couple.NewReader;
+
+            DirectoryReader index2_refreshed = couple.RefreshedReader;
+            index2.Dispose();
+
+            // test if refreshed reader and newly opened reader return equal results
+            TestDirectoryReader.AssertIndexEquals(index1, index2_refreshed);
+
+            index2_refreshed.Dispose();
+            AssertReaderClosed(index2, true);
+            AssertReaderClosed(index2_refreshed, true);
+
+            index2 = test.OpenReader();
+
+            for (int i = 1; i < 4; i++)
+            {
+                index1.Dispose();
+                couple = RefreshReader(index2, test, i, true);
+                // refresh DirectoryReader
+                index2.Dispose();
+
+                index2 = couple.RefreshedReader;
+                index1 = couple.NewReader;
+                TestDirectoryReader.AssertIndexEquals(index1, index2);
+            }
+
+            index1.Dispose();
+            index2.Dispose();
+            AssertReaderClosed(index1, true);
+            AssertReaderClosed(index2, true);
+        }
+
+        [Test]
+        public virtual void TestThreadSafety()
+        {
+            Directory dir = NewDirectory();
+            // NOTE: this also controls the number of threads!
+            int n = TestUtil.NextInt(Random(), 20, 40);
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            for (int i = 0; i < n; i++)
+            {
+                writer.AddDocument(CreateDocument(i, 3));
+            }
+            writer.ForceMerge(1);
+            writer.Dispose();
+
+            TestReopen test = new TestReopenAnonymousInnerClassHelper3(this, dir, n);
+
+            IList<ReaderCouple> readers = new SynchronizedList<ReaderCouple>();
+            DirectoryReader firstReader = DirectoryReader.Open(dir);
+            DirectoryReader reader = firstReader;
+
+            ReaderThread[] threads = new ReaderThread[n];
+            ISet<DirectoryReader> readersToClose = new ConcurrentHashSet<DirectoryReader>(new HashSet<DirectoryReader>());
+
+            for (int i = 0; i < n; i++)
+            {
+                if (i % 2 == 0)
+                {
+                    DirectoryReader refreshed = DirectoryReader.OpenIfChanged(reader);
+                    if (refreshed != null)
+                    {
+                        readersToClose.Add(reader);
+                        reader = refreshed;
+                    }
+                }
+                DirectoryReader r = reader;
+
+                int index = i;
+
+                ReaderThreadTask task;
+
+                if (i < 4 || (i >= 10 && i < 14) || i > 18)
+                {
+                    task = new ReaderThreadTaskAnonymousInnerClassHelper(this, test, readers, readersToClose, r, index);
+                }
+                else
+                {
+                    task = new ReaderThreadTaskAnonymousInnerClassHelper2(this, readers);
+                }
+
+                threads[i] = new ReaderThread(task);
+                threads[i].Start();
+            }
+
+            lock (this)
+            {
+                Monitor.Wait(this, TimeSpan.FromMilliseconds(1000));
+            }
+
+            for (int i = 0; i < n; i++)
+            {
+                if (threads[i] != null)
+                {
+                    threads[i].StopThread();
+                }
+            }
+
+            for (int i = 0; i < n; i++)
+            {
+                if (threads[i] != null)
+                {
+                    threads[i].Join();
+                    if (threads[i].Error != null)
+                    {
+                        string msg = "Error occurred in thread " + threads[i].Name + ":\n" + threads[i].Error.Message;
+                        Assert.Fail(msg);
+                    }
+                }
+            }
+
+            foreach (DirectoryReader readerToClose in readersToClose)
+            {
+                readerToClose.Dispose();
+            }
+
+            firstReader.Dispose();
+            reader.Dispose();
+
+            foreach (DirectoryReader readerToClose in readersToClose)
+            {
+                AssertReaderClosed(readerToClose, true);
+            }
+
+            AssertReaderClosed(reader, true);
+            AssertReaderClosed(firstReader, true);
+
+            dir.Dispose();
+        }
+
+        private class TestReopenAnonymousInnerClassHelper3 : TestReopen
+        {
+            private readonly TestDirectoryReaderReopen OuterInstance;
+
+            private Directory Dir;
+            private int n;
+
+            public TestReopenAnonymousInnerClassHelper3(TestDirectoryReaderReopen outerInstance, Directory dir, int n)
+            {
+                this.OuterInstance = outerInstance;
+                this.Dir = dir;
+                this.n = n;
+            }
+
+            protected internal override void ModifyIndex(int i)
+            {
+                IndexWriter modifier = new IndexWriter(Dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+                modifier.AddDocument(CreateDocument(n + i, 6));
+                modifier.Dispose();
+            }
+
+            protected internal override DirectoryReader OpenReader()
+            {
+                return DirectoryReader.Open(Dir);
+            }
+        }
+
+        private class ReaderThreadTaskAnonymousInnerClassHelper : ReaderThreadTask
+        {
+            private readonly TestDirectoryReaderReopen OuterInstance;
+
+            private Lucene.Net.Index.TestDirectoryReaderReopen.TestReopen Test;
+            private IList<ReaderCouple> Readers;
+            private ISet<DirectoryReader> ReadersToClose;
+            private DirectoryReader r;
+            private int Index;
+
+            public ReaderThreadTaskAnonymousInnerClassHelper(TestDirectoryReaderReopen outerInstance, Lucene.Net.Index.TestDirectoryReaderReopen.TestReopen test, IList<ReaderCouple> readers, ISet<DirectoryReader> readersToClose, DirectoryReader r, int index)
+            {
+                this.OuterInstance = outerInstance;
+                this.Test = test;
+                this.Readers = readers;
+                this.ReadersToClose = readersToClose;
+                this.r = r;
+                this.Index = index;
+            }
+
+            public override void Run()
+            {
+                Random rnd = LuceneTestCase.Random();
+                while (!Stopped)
+                {
+                    if (Index % 2 == 0)
+                    {
+                        // refresh reader synchronized
+                        ReaderCouple c = (OuterInstance.RefreshReader(r, Test, Index, true));
+                        ReadersToClose.Add(c.NewReader);
+                        ReadersToClose.Add(c.RefreshedReader);
+                        Readers.Add(c);
+                        // prevent too many readers
+                        break;
+                    }
+                    else
+                    {
+                        // not synchronized
+                        DirectoryReader refreshed = DirectoryReader.OpenIfChanged(r);
+                        if (refreshed == null)
+                        {
+                            refreshed = r;
+                        }
+
+                        IndexSearcher searcher = OuterInstance.NewSearcher(refreshed);
+                        ScoreDoc[] hits = searcher.Search(new TermQuery(new Term("field1", "a" + rnd.Next(refreshed.MaxDoc))), null, 1000).ScoreDocs;
+                        if (hits.Length > 0)
+                        {
+                            searcher.Doc(hits[0].Doc);
+                        }
+                        if (refreshed != r)
+                        {
+                            refreshed.Dispose();
+                        }
+                    }
+                    lock (this)
+                    {
+                        Monitor.Wait(this, TimeSpan.FromMilliseconds(TestUtil.NextInt(Random(), 1, 100)));
+                    }
+                }
+            }
+        }
+
+        private class ReaderThreadTaskAnonymousInnerClassHelper2 : ReaderThreadTask
+        {
+            private readonly TestDirectoryReaderReopen OuterInstance;
+
+            private IList<ReaderCouple> Readers;
+
+            public ReaderThreadTaskAnonymousInnerClassHelper2(TestDirectoryReaderReopen outerInstance, IList<ReaderCouple> readers)
+            {
+                this.OuterInstance = outerInstance;
+                this.Readers = readers;
+            }
+
+            public override void Run()
+            {
+                Random rnd = LuceneTestCase.Random();
+                while (!Stopped)
+                {
+                    int numReaders = Readers.Count;
+                    if (numReaders > 0)
+                    {
+                        ReaderCouple c = Readers[rnd.Next(numReaders)];
+                        TestDirectoryReader.AssertIndexEquals(c.NewReader, c.RefreshedReader);
+                    }
+
+                    lock (this)
+                    {
+                        Monitor.Wait(this, TimeSpan.FromMilliseconds(TestUtil.NextInt(Random(), 1, 100)));
+                    }
+                }
+            }
+        }
+
+        internal class ReaderCouple
+        {
+            internal ReaderCouple(DirectoryReader r1, DirectoryReader r2)
+            {
+                NewReader = r1;
+                RefreshedReader = r2;
+            }
+
+            internal DirectoryReader NewReader;
+            internal DirectoryReader RefreshedReader;
+        }
+
+        internal abstract class ReaderThreadTask
+        {
+            protected internal volatile bool Stopped;
+
+            public virtual void Stop()
+            {
+                this.Stopped = true;
+            }
+
+            public abstract void Run();
+        }
+
+        private class ReaderThread : ThreadClass
+        {
+            internal ReaderThreadTask Task;
+            internal Exception Error;
+
+            internal ReaderThread(ReaderThreadTask task)
+            {
+                this.Task = task;
+            }
+
+            public virtual void StopThread()
+            {
+                this.Task.Stop();
+            }
+
+            public override void Run()
+            {
+                try
+                {
+                    this.Task.Run();
+                }
+                catch (Exception r)
+                {
+                    Console.WriteLine(r.StackTrace);
+                    this.Error = r;
+                }
+            }
+        }
+
+        private object CreateReaderMutex = new object();
+
+        private ReaderCouple RefreshReader(DirectoryReader reader, bool hasChanges)
+        {
+            return RefreshReader(reader, null, -1, hasChanges);
+        }
+
+        internal virtual ReaderCouple RefreshReader(DirectoryReader reader, TestReopen test, int modify, bool hasChanges)
+        {
+            lock (CreateReaderMutex)
+            {
+                DirectoryReader r = null;
+                if (test != null)
+                {
+                    test.ModifyIndex(modify);
+                    r = test.OpenReader();
+                }
+
+                DirectoryReader refreshed = null;
+                try
+                {
+                    refreshed = DirectoryReader.OpenIfChanged(reader);
+                    if (refreshed == null)
+                    {
+                        refreshed = reader;
+                    }
+                }
+                finally
+                {
+                    if (refreshed == null && r != null)
+                    {
+                        // Hit exception -- close opened reader
+                        r.Dispose();
+                    }
+                }
+
+                if (hasChanges)
+                {
+                    if (refreshed == reader)
+                    {
+                        Assert.Fail("No new DirectoryReader instance created during refresh.");
+                    }
+                }
+                else
+                {
+                    if (refreshed != reader)
+                    {
+                        Assert.Fail("New DirectoryReader instance created during refresh even though index had no changes.");
+                    }
+                }
+
+                return new ReaderCouple(r, refreshed);
+            }
+        }
+
+        /// <summary>
+        /// LUCENENET specific
+        /// Is non-static because NewIndexWriterConfig is no longer static.
+        /// </summary>
+        public void CreateIndex(Random random, Directory dir, bool multiSegment)
+        {
+            IndexWriter.Unlock(dir);
+            IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(random, TEST_VERSION_CURRENT, new MockAnalyzer(random)).SetMergePolicy(new LogDocMergePolicy()));
+
+            for (int i = 0; i < 100; i++)
+            {
+                w.AddDocument(CreateDocument(i, 4));
+                if (multiSegment && (i % 10) == 0)
+                {
+                    w.Commit();
+                }
+            }
+
+            if (!multiSegment)
+            {
+                w.ForceMerge(1);
+            }
+
+            w.Dispose();
+
+            DirectoryReader r = DirectoryReader.Open(dir);
+            if (multiSegment)
+            {
+                Assert.IsTrue(r.Leaves.Count > 1);
+            }
+            else
+            {
+                Assert.IsTrue(r.Leaves.Count == 1);
+            }
+            r.Dispose();
+        }
+
+        public static Document CreateDocument(int n, int numFields)
+        {
+            StringBuilder sb = new StringBuilder();
+            Document doc = new Document();
+            sb.Append("a");
+            sb.Append(n);
+            FieldType customType2 = new FieldType(TextField.TYPE_STORED);
+            customType2.IsTokenized = false;
+            customType2.OmitNorms = true;
+            FieldType customType3 = new FieldType();
+            customType3.IsStored = true;
+            doc.Add(new TextField("field1", sb.ToString(), Field.Store.YES));
+            doc.Add(new Field("fielda", sb.ToString(), customType2));
+            doc.Add(new Field("fieldb", sb.ToString(), customType3));
+            sb.Append(" b");
+            sb.Append(n);
+            for (int i = 1; i < numFields; i++)
+            {
+                doc.Add(new TextField("field" + (i + 1), sb.ToString(), Field.Store.YES));
+            }
+            return doc;
+        }
+
+        internal static void ModifyIndex(int i, Directory dir)
+        {
+            switch (i)
+            {
+                case 0:
+                    {
+                        if (VERBOSE)
+                        {
+                            Console.WriteLine("TEST: modify index");
+                        }
+                        IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+                        w.DeleteDocuments(new Term("field2", "a11"));
+                        w.DeleteDocuments(new Term("field2", "b30"));
+                        w.Dispose();
+                        break;
+                    }
+                case 1:
+                    {
+                        IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+                        w.ForceMerge(1);
+                        w.Dispose();
+                        break;
+                    }
+                case 2:
+                    {
+                        IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+                        w.AddDocument(CreateDocument(101, 4));
+                        w.ForceMerge(1);
+                        w.AddDocument(CreateDocument(102, 4));
+                        w.AddDocument(CreateDocument(103, 4));
+                        w.Dispose();
+                        break;
+                    }
+                case 3:
+                    {
+                        IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+                        w.AddDocument(CreateDocument(101, 4));
+                        w.Dispose();
+                        break;
+                    }
+            }
+        }
+
+        internal static void AssertReaderClosed(IndexReader reader, bool checkSubReaders)
+        {
+            Assert.AreEqual(0, reader.RefCount);
+
+            if (checkSubReaders && reader is CompositeReader)
+            {
+                // we cannot use reader context here, as reader is
+                // already closed and calling getTopReaderContext() throws AlreadyClosed!
+                IList<IndexReader> subReaders = ((CompositeReader)reader).GetSequentialSubReaders();
+                foreach (IndexReader r in subReaders)
+                {
+                    AssertReaderClosed(r, checkSubReaders);
+                }
+            }
+        }
+
+        internal abstract class TestReopen
+        {
+            protected internal abstract DirectoryReader OpenReader();
+
+            protected internal abstract void ModifyIndex(int i);
+        }
+
+        internal class KeepAllCommits : IndexDeletionPolicy
+        {
+            public override void OnInit<T>(IList<T> commits)
+            {
+            }
+
+            public override void OnCommit<T>(IList<T> commits)
+            {
+            }
+        }
+
+        [Test]
+        public virtual void TestReopenOnCommit()
+        {
+            Directory dir = NewDirectory();
+            IndexWriter writer = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetIndexDeletionPolicy(new KeepAllCommits()).SetMaxBufferedDocs(-1).SetMergePolicy(NewLogMergePolicy(10)));
+            for (int i = 0; i < 4; i++)
+            {
+                Document doc = new Document();
+                doc.Add(NewStringField("id", "" + i, Field.Store.NO));
+                writer.AddDocument(doc);
+                IDictionary<string, string> data = new Dictionary<string, string>();
+                data["index"] = i + "";
+                writer.CommitData = data;
+                writer.Commit();
+            }
+            for (int i = 0; i < 4; i++)
+            {
+                writer.DeleteDocuments(new Term("id", "" + i));
+                IDictionary<string, string> data = new Dictionary<string, string>();
+                data["index"] = (4 + i) + "";
+                writer.CommitData = data;
+                writer.Commit();
+            }
+            writer.Dispose();
+
+            DirectoryReader r = DirectoryReader.Open(dir);
+            Assert.AreEqual(0, r.NumDocs);
+
+            ICollection<IndexCommit> commits = DirectoryReader.ListCommits(dir);
+            foreach (IndexCommit commit in commits)
+            {
+                DirectoryReader r2 = DirectoryReader.OpenIfChanged(r, commit);
+                Assert.IsNotNull(r2);
+                Assert.IsTrue(r2 != r);
+
+                IDictionary<string, string> s = commit.UserData;
+                int v;
+                if (s.Count == 0)
+                {
+                    // First commit created by IW
+                    v = -1;
+                }
+                else
+                {
+                    v = Convert.ToInt32(s["index"]);
+                }
+                if (v < 4)
+                {
+                    Assert.AreEqual(1 + v, r2.NumDocs);
+                }
+                else
+                {
+                    Assert.AreEqual(7 - v, r2.NumDocs);
+                }
+                r.Dispose();
+                r = r2;
+            }
+            r.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedNRTToCommit()
+        {
+            Directory dir = NewDirectory();
+
+            // Can't use RIW because it randomly commits:
+            IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            Document doc = new Document();
+            doc.Add(NewStringField("field", "value", Field.Store.NO));
+            w.AddDocument(doc);
+            w.Commit();
+            IList<IndexCommit> commits = DirectoryReader.ListCommits(dir);
+            Assert.AreEqual(1, commits.Count);
+            w.AddDocument(doc);
+            DirectoryReader r = DirectoryReader.Open(w, true);
+
+            Assert.AreEqual(2, r.NumDocs);
+            IndexReader r2 = DirectoryReader.OpenIfChanged(r, commits[0]);
+            Assert.IsNotNull(r2);
+            r.Dispose();
+            Assert.AreEqual(1, r2.NumDocs);
+            w.Dispose();
+            r2.Dispose();
+            dir.Dispose();
+        }
+    }
+}
\ No newline at end of file