lucenenet-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From synhers...@apache.org
Subject [10/26] lucenenet git commit: first commit of facet porting, failing tests will be fixed in next commits.
Date Fri, 14 Nov 2014 11:59:25 GMT
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/982eaf60/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestConcurrentFacetedIndexing.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestConcurrentFacetedIndexing.cs b/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestConcurrentFacetedIndexing.cs
new file mode 100644
index 0000000..86dd3a9
--- /dev/null
+++ b/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestConcurrentFacetedIndexing.cs
@@ -0,0 +1,228 @@
+using System;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Threading;
+using Lucene.Net.Support;
+using NUnit.Framework;
+
+namespace Lucene.Net.Facet.Taxonomy.Directory
+{
+
+
+    using Document = Lucene.Net.Documents.Document;
+    using TaxonomyWriterCache = Lucene.Net.Facet.Taxonomy.WriterCache.TaxonomyWriterCache;
+    using Cl2oTaxonomyWriterCache = Lucene.Net.Facet.Taxonomy.WriterCache.Cl2oTaxonomyWriterCache;
+    using LruTaxonomyWriterCache = Lucene.Net.Facet.Taxonomy.WriterCache.LruTaxonomyWriterCache;
+    using IndexWriter = Lucene.Net.Index.IndexWriter;
+    using OpenMode = Lucene.Net.Index.IndexWriterConfig.OpenMode_e;
+    using Directory = Lucene.Net.Store.Directory;
+    using IOUtils = Lucene.Net.Util.IOUtils;
+
+    /*
+     * 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.
+     */
+    [TestFixture]
+    public class TestConcurrentFacetedIndexing : FacetTestCase
+    {
+
+        // A No-Op TaxonomyWriterCache which always discards all given categories, and
+        // always returns true in put(), to indicate some cache entries were cleared.
+        private static TaxonomyWriterCache NO_OP_CACHE = new TaxonomyWriterCacheAnonymousInnerClassHelper();
+
+        private class TaxonomyWriterCacheAnonymousInnerClassHelper : TaxonomyWriterCache
+        {
+            public TaxonomyWriterCacheAnonymousInnerClassHelper()
+            {
+            }
+
+
+            public virtual void Close()
+            {
+            }
+            public virtual int Get(FacetLabel categoryPath)
+            {
+                return -1;
+            }
+            public virtual bool Put(FacetLabel categoryPath, int ordinal)
+            {
+                return true;
+            }
+            public virtual bool Full
+            {
+                get
+                {
+                    return true;
+                }
+            }
+            public virtual void Clear()
+            {
+            }
+
+        }
+
+        internal static FacetField NewCategory()
+        {
+            Random r = Random();
+            string l1 = "l1." + r.Next(10); // l1.0-l1.9 (10 categories)
+            string l2 = "l2." + r.Next(30); // l2.0-l2.29 (30 categories)
+            string l3 = "l3." + r.Next(100); // l3.0-l3.99 (100 categories)
+            return new FacetField(l1, l2, l3);
+        }
+
+        internal static TaxonomyWriterCache NewTaxoWriterCache(int ndocs)
+        {
+            double d = Random().NextDouble();
+            if (d < 0.7)
+            {
+                // this is the fastest, yet most memory consuming
+                return new Cl2oTaxonomyWriterCache(1024, 0.15f, 3);
+            }
+            else if (TEST_NIGHTLY && d > 0.98)
+            {
+                // this is the slowest, but tests the writer concurrency when no caching is done.
+                // only pick it during NIGHTLY tests, and even then, with very low chances.
+                return NO_OP_CACHE;
+            }
+            else
+            {
+                // this is slower than CL2O, but less memory consuming, and exercises finding categories on disk too.
+                return new LruTaxonomyWriterCache(ndocs / 10);
+            }
+        }
+
+        [Test]
+        public virtual void TestConcurrency()
+        {
+            AtomicInteger numDocs = new AtomicInteger(AtLeast(10000));
+            Directory indexDir = NewDirectory();
+            Directory taxoDir = NewDirectory();
+            ConcurrentDictionary<string, string> values = new ConcurrentDictionary<string, string>();
+            IndexWriter iw = new IndexWriter(indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, null));
+            var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE, NewTaxoWriterCache(numDocs.Get()));
+            ThreadClass[] indexThreads = new ThreadClass[AtLeast(4)];
+            FacetsConfig config = new FacetsConfig();
+            for (int i = 0; i < 10; i++)
+            {
+                config.SetHierarchical("l1." + i, true);
+                config.SetMultiValued("l1." + i, true);
+            }
+
+            for (int i = 0; i < indexThreads.Length; i++)
+            {
+                indexThreads[i] = new ThreadAnonymousInnerClassHelper(this, numDocs, values, iw, tw, config);
+            }
+
+            foreach (ThreadClass t in indexThreads)
+            {
+                t.Start();
+            }
+            foreach (ThreadClass t in indexThreads)
+            {
+                t.Join();
+            }
+
+            var tr = new DirectoryTaxonomyReader(tw);
+            // +1 for root category
+            if (values.Count + 1 != tr.Size)
+            {
+                foreach (string value in values.Keys)
+                {
+                    FacetLabel label = new FacetLabel(FacetsConfig.StringToPath(value));
+                    if (tr.GetOrdinal(label) == -1)
+                    {
+                        Console.WriteLine("FAIL: path=" + label + " not recognized");
+                    }
+                }
+                Fail("mismatch number of categories");
+            }
+            int[] parents = tr.ParallelTaxonomyArrays.Parents();
+            foreach (string cat in values.Keys)
+            {
+                FacetLabel cp = new FacetLabel(FacetsConfig.StringToPath(cat));
+                Assert.True(tr.GetOrdinal(cp) > 0, "category not found " + cp);
+                int level = cp.length;
+                int parentOrd = 0; // for root, parent is always virtual ROOT (ord=0)
+                FacetLabel path = null;
+                for (int i = 0; i < level; i++)
+                {
+                    path = cp.Subpath(i + 1);
+                    int ord = tr.GetOrdinal(path);
+                    Assert.AreEqual(parentOrd, parents[ord], "invalid parent for cp=" + path);
+                    parentOrd = ord; // next level should have this parent
+                }
+            }
+
+            IOUtils.Close(tw, iw, tr, taxoDir, indexDir);
+        }
+
+        private class ThreadAnonymousInnerClassHelper : ThreadClass
+        {
+            private readonly TestConcurrentFacetedIndexing outerInstance;
+
+            private AtomicInteger numDocs;
+            private ConcurrentDictionary<string, string> values;
+            private IndexWriter iw;
+            private Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter tw;
+            private FacetsConfig config;
+
+            public ThreadAnonymousInnerClassHelper(TestConcurrentFacetedIndexing outerInstance, AtomicInteger numDocs, ConcurrentDictionary<string, string> values, IndexWriter iw, Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter tw, FacetsConfig config)
+            {
+                this.outerInstance = outerInstance;
+                this.numDocs = numDocs;
+                this.values = values;
+                this.iw = iw;
+                this.tw = tw;
+                this.config = config;
+            }
+
+
+            public override void Run()
+            {
+                Random random = Random();
+                while (numDocs.DecrementAndGet() > 0)
+                {
+                    try
+                    {
+                        Document doc = new Document();
+                        int numCats = random.Next(3) + 1; // 1-3
+                        while (numCats-- > 0)
+                        {
+                            FacetField ff = NewCategory();
+                            doc.Add(ff);
+
+                            FacetLabel label = new FacetLabel(ff.dim, ff.path);
+                            // add all prefixes to values
+                            int level = label.length;
+                            while (level > 0)
+                            {
+                                string s = FacetsConfig.PathToString(label.components, level);
+                                values[s] = s;
+                                --level;
+                            }
+                        }
+                        iw.AddDocument(config.Build(tw, doc));
+                    }
+                    catch (IOException e)
+                    {
+                        throw new Exception(e.Message, e);
+                    }
+                }
+            }
+        }
+
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/982eaf60/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyReader.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyReader.cs b/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyReader.cs
new file mode 100644
index 0000000..d933618
--- /dev/null
+++ b/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyReader.cs
@@ -0,0 +1,624 @@
+using System;
+using System.Collections.Generic;
+using Lucene.Net.Randomized.Generators;
+using Lucene.Net.Support;
+using NUnit.Framework;
+
+namespace Lucene.Net.Facet.Taxonomy.Directory
+{
+
+
+    using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+    using ChildrenIterator = Lucene.Net.Facet.Taxonomy.TaxonomyReader.ChildrenIterator;
+    using IndexWriter = Lucene.Net.Index.IndexWriter;
+    using IndexWriterConfig = Lucene.Net.Index.IndexWriterConfig;
+    using OpenMode = Lucene.Net.Index.IndexWriterConfig.OpenMode_e;
+    using LogByteSizeMergePolicy = Lucene.Net.Index.LogByteSizeMergePolicy;
+    using LogMergePolicy = Lucene.Net.Index.LogMergePolicy;
+    using AlreadyClosedException = Lucene.Net.Store.AlreadyClosedException;
+    using Directory = Lucene.Net.Store.Directory;
+    using RAMDirectory = Lucene.Net.Store.RAMDirectory;
+    using IOUtils = Lucene.Net.Util.IOUtils;
+
+    /*
+     * 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.
+     */
+
+    public class TestDirectoryTaxonomyReader : FacetTestCase
+    {
+
+        [Test]
+        public virtual void TestCloseAfterIncRef()
+        {
+            Directory dir = NewDirectory();
+            var ltw = new DirectoryTaxonomyWriter(dir);
+            ltw.AddCategory(new FacetLabel("a"));
+            ltw.Dispose();
+
+            DirectoryTaxonomyReader ltr = new DirectoryTaxonomyReader(dir);
+            ltr.IncRef();
+            ltr.Dispose();
+
+            // should not fail as we IncRef() before close
+            var tmpSie = ltr.Size;
+            ltr.DecRef();
+
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestCloseTwice()
+        {
+            Directory dir = NewDirectory();
+            var ltw = new DirectoryTaxonomyWriter(dir);
+            ltw.AddCategory(new FacetLabel("a"));
+            ltw.Dispose();
+
+            var ltr = new DirectoryTaxonomyReader(dir);
+            (ltr).Dispose();
+            (ltr).Dispose(); // no exception should be thrown
+
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedResult()
+        {
+            Directory dir = null;
+            DirectoryTaxonomyWriter ltw = null;
+            DirectoryTaxonomyReader ltr = null;
+
+            try
+            {
+                dir = NewDirectory();
+                ltw = new DirectoryTaxonomyWriter(dir);
+
+                ltw.AddCategory(new FacetLabel("a"));
+                ltw.Commit();
+
+                ltr = new DirectoryTaxonomyReader(dir);
+                Assert.Null(TaxonomyReader.OpenIfChanged(ltr), "Nothing has changed");
+
+                ltw.AddCategory(new FacetLabel("b"));
+                ltw.Commit();
+
+                DirectoryTaxonomyReader newtr = TaxonomyReader.OpenIfChanged(ltr);
+                Assert.NotNull(newtr, "changes were committed");
+                Assert.Null(TaxonomyReader.OpenIfChanged(newtr), "Nothing has changed");
+                (newtr).Dispose();
+            }
+            finally
+            {
+                IOUtils.Close(ltw, ltr, dir);
+            }
+        }
+
+        [Test]
+        public virtual void TestAlreadyClosed()
+        {
+            Directory dir = NewDirectory();
+            var ltw = new DirectoryTaxonomyWriter(dir);
+            ltw.AddCategory(new FacetLabel("a"));
+            ltw.Dispose();
+
+            var ltr = new DirectoryTaxonomyReader(dir);
+            ltr.Dispose();
+            try
+            {
+                var tmpSize = ltr.Size;
+                Fail("An AlreadyClosedException should have been thrown here");
+            }
+            catch (AlreadyClosedException)
+            {
+                // good!
+            }
+            dir.Dispose();
+        }
+
+        /// <summary>
+        /// recreating a taxonomy should work well with a freshly opened taxonomy reader 
+        /// </summary>
+        [Test]
+        public virtual void TestFreshReadRecreatedTaxonomy()
+        {
+            doTestReadRecreatedTaxonomy(Random(), true);
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedReadRecreatedTaxonomy()
+        {
+            doTestReadRecreatedTaxonomy(Random(), false);
+        }
+        
+        private void doTestReadRecreatedTaxonomy(Random random, bool closeReader)
+        {
+            Directory dir = null;
+            TaxonomyWriter tw = null;
+            TaxonomyReader tr = null;
+
+            // prepare a few categories
+            int n = 10;
+            FacetLabel[] cp = new FacetLabel[n];
+            for (int i = 0; i < n; i++)
+            {
+                cp[i] = new FacetLabel("a", Convert.ToString(i));
+            }
+
+            try
+            {
+                dir = NewDirectory();
+
+                tw = new DirectoryTaxonomyWriter(dir);
+                tw.AddCategory(new FacetLabel("a"));
+                tw.Dispose();
+
+                tr = new DirectoryTaxonomyReader(dir);
+                int baseNumCategories = tr.Size;
+
+                for (int i = 0; i < n; i++)
+                {
+                    int k = random.Next(n);
+                    tw = new DirectoryTaxonomyWriter(dir, IndexWriterConfig.OpenMode_e.CREATE);
+                    for (int j = 0; j <= k; j++)
+                    {
+                        tw.AddCategory(cp[j]);
+                    }
+                    tw.Dispose();
+                    if (closeReader)
+                    {
+                        tr.Dispose(true);
+                        tr = new DirectoryTaxonomyReader(dir);
+                    }
+                    else
+                    {
+                        var newtr = TaxonomyReader.OpenIfChanged(tr);
+                        Assert.NotNull(newtr);
+                        tr.Dispose(true);
+                        tr = newtr;
+                    }
+                    Assert.AreEqual(baseNumCategories + 1 + k, tr.Size, "Wrong #categories in taxonomy (i=" + i + ", k=" + k + ")");
+                }
+            }
+            finally
+            {
+                IOUtils.Close(tr as DirectoryTaxonomyReader, tw, dir);
+            }
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedAndRefCount()
+        {
+            Directory dir = new RAMDirectory(); // no need for random directories here
+
+            var taxoWriter = new DirectoryTaxonomyWriter(dir);
+            taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.Commit();
+
+            var taxoReader = new DirectoryTaxonomyReader(dir);
+            Assert.AreEqual(1, taxoReader.RefCount, "wrong refCount");
+
+            taxoReader.IncRef();
+            Assert.AreEqual(2, taxoReader.RefCount, "wrong refCount");
+
+            taxoWriter.AddCategory(new FacetLabel("a", "b"));
+            taxoWriter.Commit();
+            var newtr = TaxonomyReader.OpenIfChanged(taxoReader);
+            Assert.NotNull(newtr);
+            taxoReader.Dispose();
+            taxoReader = newtr;
+            Assert.AreEqual(1, taxoReader.RefCount, "wrong refCount");
+
+            taxoWriter.Dispose();
+            taxoReader.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedManySegments()
+        {
+            // test openIfChanged() when the taxonomy contains many segments
+            Directory dir = NewDirectory();
+
+            DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriterAnonymousInnerClassHelper(this, dir);
+            var reader = new DirectoryTaxonomyReader(writer);
+
+            int numRounds = Random().Next(10) + 10;
+            int numCategories = 1; // one for root
+            for (int i = 0; i < numRounds; i++)
+            {
+                int numCats = Random().Next(4) + 1;
+                for (int j = 0; j < numCats; j++)
+                {
+                    writer.AddCategory(new FacetLabel(Convert.ToString(i), Convert.ToString(j)));
+                }
+                numCategories += numCats + 1; // one for round-parent
+                var newtr = TaxonomyReader.OpenIfChanged(reader);
+                Assert.NotNull(newtr);
+                reader.Dispose();
+                reader = newtr;
+
+                // assert categories
+                Assert.AreEqual(numCategories, reader.Size);
+                int roundOrdinal = reader.GetOrdinal(new FacetLabel(Convert.ToString(i)));
+                int[] parents = reader.ParallelTaxonomyArrays.Parents();
+                Assert.AreEqual(0, parents[roundOrdinal]); // round's parent is root
+                for (int j = 0; j < numCats; j++)
+                {
+                    int ord = reader.GetOrdinal(new FacetLabel(Convert.ToString(i), Convert.ToString(j)));
+                    Assert.AreEqual(roundOrdinal, parents[ord]); // round's parent is root
+                }
+            }
+
+            reader.Dispose();
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        private class DirectoryTaxonomyWriterAnonymousInnerClassHelper : DirectoryTaxonomyWriter
+        {
+            private readonly TestDirectoryTaxonomyReader outerInstance;
+
+            public DirectoryTaxonomyWriterAnonymousInnerClassHelper(TestDirectoryTaxonomyReader outerInstance, Directory dir)
+                : base(dir)
+            {
+                this.outerInstance = outerInstance;
+            }
+
+            protected override IndexWriterConfig CreateIndexWriterConfig(IndexWriterConfig.OpenMode_e openMode)
+            {
+                IndexWriterConfig conf = base.CreateIndexWriterConfig(openMode);
+                LogMergePolicy lmp = (LogMergePolicy)conf.MergePolicy;
+                lmp.MergeFactor = 2;
+                return conf;
+            }
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedMergedSegment()
+        {
+            // test openIfChanged() when all index segments were merged - used to be
+            // a bug in ParentArray, caught by testOpenIfChangedManySegments - only
+            // this test is not random
+            Directory dir = NewDirectory();
+
+            // hold onto IW to forceMerge
+            // note how we don't close it, since DTW will close it.
+            IndexWriter iw = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(new LogByteSizeMergePolicy()));
+            var writer = new DirectoryTaxonomyWriterAnonymousInnerClassHelper2(this, dir, iw) as DirectoryTaxonomyWriter;
+
+            var reader = new DirectoryTaxonomyReader(writer);
+            Assert.AreEqual(1, reader.Size);
+            Assert.AreEqual(1, reader.ParallelTaxonomyArrays.Parents().Length);
+
+            // add category and call forceMerge -- this should flush IW and merge segments down to 1
+            // in ParentArray.initFromReader, this used to fail assuming there are no parents.
+            writer.AddCategory(new FacetLabel("1"));
+            iw.ForceMerge(1);
+
+            // now calling openIfChanged should trip on the bug
+            var newtr = TaxonomyReader.OpenIfChanged(reader);
+            Assert.NotNull(newtr);
+            reader.Dispose();
+            reader = newtr;
+            Assert.AreEqual(2, reader.Size);
+            Assert.AreEqual(2, reader.ParallelTaxonomyArrays.Parents().Length);
+
+            reader.Dispose();
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        private class DirectoryTaxonomyWriterAnonymousInnerClassHelper2 : DirectoryTaxonomyWriter
+        {
+            private readonly TestDirectoryTaxonomyReader outerInstance;
+
+            private IndexWriter iw;
+            private IndexWriterConfig config;
+
+            public DirectoryTaxonomyWriterAnonymousInnerClassHelper2(TestDirectoryTaxonomyReader outerInstance, Directory dir, IndexWriter iw) : base(dir)
+            {
+                this.outerInstance = outerInstance;
+                this.iw = iw;
+            }
+
+            protected override IndexWriter OpenIndexWriter(Directory directory, IndexWriterConfig config) 
+            {   
+                return iw;
+            }
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedNoChangesButSegmentMerges()
+        {
+            // test openIfChanged() when the taxonomy hasn't really changed, but segments
+            // were merged. The NRT reader will be reopened, and ParentArray used to assert
+            // that the new reader contains more ordinals than were given from the old
+            // TaxReader version
+            Directory dir = NewDirectory();
+
+            // hold onto IW to forceMerge
+            // note how we don't close it, since DTW will close it.
+            var iw = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())).SetMergePolicy(new LogByteSizeMergePolicy()));
+            DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriterAnonymousInnerClassHelper3(this, dir, iw);
+
+
+            // add a category so that the following DTR open will cause a flush and 
+            // a new segment will be created
+            writer.AddCategory(new FacetLabel("a"));
+
+            var reader = new DirectoryTaxonomyReader(writer);
+            Assert.AreEqual(2, reader.Size);
+            Assert.AreEqual(2, reader.ParallelTaxonomyArrays.Parents().Length);
+
+            // merge all the segments so that NRT reader thinks there's a change 
+            iw.ForceMerge(1);
+
+            // now calling openIfChanged should trip on the wrong assert in ParetArray's ctor
+            var newtr = TaxonomyReader.OpenIfChanged(reader);
+            Assert.NotNull(newtr);
+            reader.Dispose();
+            reader = newtr;
+            Assert.AreEqual(2, reader.Size);
+            Assert.AreEqual(2, reader.ParallelTaxonomyArrays.Parents().Length);
+
+            reader.Dispose();
+            writer.Dispose();
+            dir.Dispose();
+        }
+
+        private class DirectoryTaxonomyWriterAnonymousInnerClassHelper3 : DirectoryTaxonomyWriter
+        {
+            private readonly TestDirectoryTaxonomyReader outerInstance;
+
+            private IndexWriter iw;
+
+            public DirectoryTaxonomyWriterAnonymousInnerClassHelper3(TestDirectoryTaxonomyReader outerInstance, Directory dir, IndexWriter iw)
+                : base(dir)
+            {
+                this.outerInstance = outerInstance;
+                this.iw = iw;
+            }
+
+            protected override IndexWriter OpenIndexWriter(Directory directory, IndexWriterConfig config)
+            {
+                return iw;
+            }
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedReuseAfterRecreate()
+        {
+            // tests that if the taxonomy is recreated, no data is reused from the previous taxonomy
+            Directory dir = NewDirectory();
+            DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriter(dir);
+            FacetLabel cp_a = new FacetLabel("a");
+            writer.AddCategory(cp_a);
+            writer.Dispose();
+
+            DirectoryTaxonomyReader r1 = new DirectoryTaxonomyReader(dir);
+            // fill r1's caches
+            Assert.AreEqual(1, r1.GetOrdinal(cp_a));
+            Assert.AreEqual(cp_a, r1.GetPath(1));
+
+            // now recreate, add a different category
+            writer = new DirectoryTaxonomyWriter(dir, IndexWriterConfig.OpenMode_e.CREATE);
+            FacetLabel cp_b = new FacetLabel("b");
+            writer.AddCategory(cp_b);
+            writer.Dispose();
+
+            DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1);
+            Assert.NotNull(r2);
+
+            // fill r2's caches
+            Assert.AreEqual(1, r2.GetOrdinal(cp_b));
+            Assert.AreEqual(cp_b, r2.GetPath(1));
+
+            // check that r1 doesn't see cp_b
+            Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b));
+            Assert.AreEqual(cp_a, r1.GetPath(1));
+
+            // check that r2 doesn't see cp_a
+            Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r2.GetOrdinal(cp_a));
+            Assert.AreEqual(cp_b, r2.GetPath(1));
+
+            (r2).Dispose();
+            (r1).Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedReuse()
+        {
+            // test the reuse of data from the old DTR instance
+            foreach (bool nrt in new bool[] { false, true })
+            {
+                Directory dir = NewDirectory();
+                DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriter(dir);
+
+                FacetLabel cp_a = new FacetLabel("a");
+                writer.AddCategory(cp_a);
+                if (!nrt)
+                {
+                    writer.Commit();
+                }
+
+                DirectoryTaxonomyReader r1 = nrt ? new DirectoryTaxonomyReader(writer) : new DirectoryTaxonomyReader(dir);
+                // fill r1's caches
+                Assert.AreEqual(1, r1.GetOrdinal(cp_a));
+                Assert.AreEqual(cp_a, r1.GetPath(1));
+
+                FacetLabel cp_b = new FacetLabel("b");
+                writer.AddCategory(cp_b);
+                if (!nrt)
+                {
+                    writer.Commit();
+                }
+
+                DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1);
+                Assert.NotNull(r2);
+
+                // add r2's categories to the caches
+                Assert.AreEqual(2, r2.GetOrdinal(cp_b));
+                Assert.AreEqual(cp_b, r2.GetPath(2));
+
+                // check that r1 doesn't see cp_b
+                Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b));
+                Assert.Null(r1.GetPath(2));
+
+                (r1).Dispose();
+                (r2).Dispose();
+                writer.Dispose();
+                dir.Dispose();
+            }
+        }
+
+        [Test]
+        public virtual void TestOpenIfChangedReplaceTaxonomy()
+        {
+            // test openIfChanged when replaceTaxonomy is called, which is equivalent to recreate
+            // only can work with NRT as well
+            Directory src = NewDirectory();
+            DirectoryTaxonomyWriter w = new DirectoryTaxonomyWriter(src);
+            FacetLabel cp_b = new FacetLabel("b");
+            w.AddCategory(cp_b);
+            w.Dispose();
+
+            foreach (bool nrt in new bool[] { false, true })
+            {
+                Directory dir = NewDirectory();
+                var writer = new DirectoryTaxonomyWriter(dir);
+
+                FacetLabel cp_a = new FacetLabel("a");
+                writer.AddCategory(cp_a);
+                if (!nrt)
+                {
+                    writer.Commit();
+                }
+
+                DirectoryTaxonomyReader r1 = nrt ? new DirectoryTaxonomyReader(writer) : new DirectoryTaxonomyReader(dir);
+                // fill r1's caches
+                Assert.AreEqual(1, r1.GetOrdinal(cp_a));
+                Assert.AreEqual(cp_a, r1.GetPath(1));
+
+                // now replace taxonomy
+                writer.ReplaceTaxonomy(src);
+                if (!nrt)
+                {
+                    writer.Commit();
+                }
+
+                DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1);
+                Assert.NotNull(r2);
+
+                // fill r2's caches
+                Assert.AreEqual(1, r2.GetOrdinal(cp_b));
+                Assert.AreEqual(cp_b, r2.GetPath(1));
+
+                // check that r1 doesn't see cp_b
+                Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b));
+                Assert.AreEqual(cp_a, r1.GetPath(1));
+
+                // check that r2 doesn't see cp_a
+                Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r2.GetOrdinal(cp_a));
+                Assert.AreEqual(cp_b, r2.GetPath(1));
+
+                (r2).Dispose();
+                (r1).Dispose();
+                writer.Dispose();
+                dir.Dispose();
+            }
+
+            src.Dispose();
+        }
+
+        [Test]
+        public virtual void TestGetChildren()
+        {
+            Directory dir = NewDirectory();
+            var taxoWriter = new DirectoryTaxonomyWriter(dir);
+            int numCategories = AtLeast(10);
+            int numA = 0, numB = 0;
+            Random random = Random();
+            // add the two categories for which we'll also add children (so asserts are simpler)
+            taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.AddCategory(new FacetLabel("b"));
+            for (int i = 0; i < numCategories; i++)
+            {
+                if (random.NextBoolean())
+                {
+                    taxoWriter.AddCategory(new FacetLabel("a", Convert.ToString(i)));
+                    ++numA;
+                }
+                else
+                {
+                    taxoWriter.AddCategory(new FacetLabel("b", Convert.ToString(i)));
+                    ++numB;
+                }
+            }
+            // add category with no children
+            taxoWriter.AddCategory(new FacetLabel("c"));
+            taxoWriter.Dispose();
+
+            var taxoReader = new DirectoryTaxonomyReader(dir);
+
+            // non existing category
+            TaxonomyReader.ChildrenIterator it = taxoReader.GetChildren(taxoReader.GetOrdinal(new FacetLabel("invalid")));
+            Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next());
+
+            // a category with no children
+            it = taxoReader.GetChildren(taxoReader.GetOrdinal(new FacetLabel("c")));
+            Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next());
+
+            // arbitrary negative ordinal
+            it = taxoReader.GetChildren(-2);
+            Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next());
+
+            // root's children
+            var roots = new HashSet<string>(Arrays.AsList("a", "b", "c"));
+            it = taxoReader.GetChildren(TaxonomyReader.ROOT_ORDINAL);
+            while (roots.Count > 0)
+            {
+                FacetLabel root = taxoReader.GetPath(it.Next());
+                Assert.AreEqual(1, root.length);
+                Assert.True(roots.Remove(root.components[0]));
+            }
+            Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next());
+
+            for (int i = 0; i < 2; i++)
+            {
+                FacetLabel cp = i == 0 ? new FacetLabel("a") : new FacetLabel("b");
+                int ordinal = taxoReader.GetOrdinal(cp);
+                it = taxoReader.GetChildren(ordinal);
+                int numChildren = 0;
+                int child;
+                while ((child = it.Next()) != TaxonomyReader.INVALID_ORDINAL)
+                {
+                    FacetLabel path = taxoReader.GetPath(child);
+                    Assert.AreEqual(2, path.length);
+                    Assert.AreEqual(path.components[0], i == 0 ? "a" : "b");
+                    ++numChildren;
+                }
+                int expected = i == 0 ? numA : numB;
+                Assert.AreEqual(expected, numChildren, "invalid num children");
+            }
+            taxoReader.Dispose();
+
+            dir.Dispose();
+        }
+
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/982eaf60/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyWriter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyWriter.cs b/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyWriter.cs
new file mode 100644
index 0000000..ebf5455
--- /dev/null
+++ b/src/Lucene.Net.Tests/core/Facet/Taxonomy/Directory/TestDirectoryTaxonomyWriter.cs
@@ -0,0 +1,588 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Threading;
+using Lucene.Net.Support;
+using NUnit.Framework;
+
+namespace Lucene.Net.Facet.Taxonomy.Directory
+{
+
+
+    using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
+    using Document = Lucene.Net.Documents.Document;
+    using MemoryOrdinalMap = Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter.MemoryOrdinalMap;
+    using TaxonomyWriterCache = Lucene.Net.Facet.Taxonomy.WriterCache.TaxonomyWriterCache;
+    using Cl2oTaxonomyWriterCache = Lucene.Net.Facet.Taxonomy.WriterCache.Cl2oTaxonomyWriterCache;
+    using LruTaxonomyWriterCache = Lucene.Net.Facet.Taxonomy.WriterCache.LruTaxonomyWriterCache;
+    using DirectoryReader = Lucene.Net.Index.DirectoryReader;
+    using IndexReader = Lucene.Net.Index.IndexReader;
+    using IndexWriter = Lucene.Net.Index.IndexWriter;
+    using OpenMode = Lucene.Net.Index.IndexWriterConfig.OpenMode_e;
+    using IndexWriterConfig = Lucene.Net.Index.IndexWriterConfig;
+    using SegmentInfos = Lucene.Net.Index.SegmentInfos;
+    using IndexSearcher = Lucene.Net.Search.IndexSearcher;
+    using AlreadyClosedException = Lucene.Net.Store.AlreadyClosedException;
+    using Directory = Lucene.Net.Store.Directory;
+    using IOUtils = Lucene.Net.Util.IOUtils;
+    using TestUtil = Lucene.Net.Util.TestUtil;
+
+    /*
+     * 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.
+     */
+
+    public class TestDirectoryTaxonomyWriter : FacetTestCase
+    {
+
+        // A No-Op TaxonomyWriterCache which always discards all given categories, and
+        // always returns true in put(), to indicate some cache entries were cleared.
+        private static TaxonomyWriterCache NO_OP_CACHE = new TaxonomyWriterCacheAnonymousInnerClassHelper();
+
+        private class TaxonomyWriterCacheAnonymousInnerClassHelper : TaxonomyWriterCache
+        {
+            public TaxonomyWriterCacheAnonymousInnerClassHelper()
+            {
+            }
+
+
+            public virtual void Close()
+            {
+            }
+            public virtual int Get(FacetLabel categoryPath)
+            {
+                return -1;
+            }
+            public virtual bool Put(FacetLabel categoryPath, int ordinal)
+            {
+                return true;
+            }
+            public virtual bool Full
+            {
+                get
+                {
+                    return true;
+                }
+            }
+            public virtual void Clear()
+            {
+            }
+
+        }
+
+        [Test]
+        public virtual void TestCommit()
+        {
+            // Verifies that nothing is committed to the underlying Directory, if
+            // commit() wasn't called.
+            Directory dir = NewDirectory();
+            var ltw = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE_OR_APPEND, NO_OP_CACHE);
+            Assert.False(DirectoryReader.IndexExists(dir));
+            ltw.Commit(); // first commit, so that an index will be created
+            ltw.AddCategory(new FacetLabel("a"));
+
+            IndexReader r = DirectoryReader.Open(dir);
+            Assert.AreEqual(1, r.NumDocs, "No categories should have been committed to the underlying directory");
+            r.Dispose();
+            ltw.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestCommitUserData()
+        {
+            // Verifies taxonomy commit data
+            Directory dir = NewDirectory();
+            var taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE_OR_APPEND, NO_OP_CACHE);
+            taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.AddCategory(new FacetLabel("b"));
+            IDictionary<string, string> userCommitData = new Dictionary<string, string>();
+            userCommitData["testing"] = "1 2 3";
+            taxoWriter.CommitData = userCommitData;
+            taxoWriter.Dispose();
+            var r = DirectoryReader.Open(dir);
+            Assert.AreEqual(3, r.NumDocs, "2 categories plus root should have been committed to the underlying directory");
+            var readUserCommitData = r.IndexCommit.UserData;
+            Assert.True("1 2 3".Equals(readUserCommitData["testing"]), "wrong value extracted from commit data");
+            Assert.NotNull(DirectoryTaxonomyWriter.INDEX_EPOCH + " not found in commitData", readUserCommitData[DirectoryTaxonomyWriter.INDEX_EPOCH]);
+            r.Dispose();
+
+            // open DirTaxoWriter again and commit, INDEX_EPOCH should still exist
+            // in the commit data, otherwise DirTaxoReader.refresh() might not detect
+            // that the taxonomy index has been recreated.
+            taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE_OR_APPEND, NO_OP_CACHE);
+            taxoWriter.AddCategory(new FacetLabel("c")); // add a category so that commit will happen
+
+
+            taxoWriter.CommitData = new Dictionary<string, string>()
+		    {
+			    {"just", "data"}
+		    };
+            taxoWriter.Commit();
+
+            // verify taxoWriter.getCommitData()
+            Assert.NotNull(DirectoryTaxonomyWriter.INDEX_EPOCH + " not found in taoxWriter.commitData", taxoWriter.CommitData[DirectoryTaxonomyWriter.INDEX_EPOCH]);
+            taxoWriter.Dispose();
+
+            r = DirectoryReader.Open(dir);
+            readUserCommitData = r.IndexCommit.UserData;
+            Assert.NotNull(DirectoryTaxonomyWriter.INDEX_EPOCH + " not found in commitData", readUserCommitData[DirectoryTaxonomyWriter.INDEX_EPOCH]);
+            r.Dispose();
+
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestRollback()
+        {
+            // Verifies that if rollback is called, DTW is closed.
+            Directory dir = NewDirectory();
+            var dtw = new DirectoryTaxonomyWriter(dir);
+            dtw.AddCategory(new FacetLabel("a"));
+            dtw.Rollback();
+            try
+            {
+                dtw.AddCategory(new FacetLabel("a"));
+                Fail("should not have succeeded to add a category following rollback.");
+            }
+            catch (AlreadyClosedException)
+            {
+                // expected
+            }
+
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestRecreateRollback()
+        {
+            // Tests rollback with OpenMode.CREATE
+            Directory dir = NewDirectory();
+            (new DirectoryTaxonomyWriter(dir)).Dispose();
+            Assert.AreEqual(1, getEpoch(dir));
+            (new DirectoryTaxonomyWriter(dir, OpenMode.CREATE)).Rollback();
+            Assert.AreEqual(1, getEpoch(dir));
+
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestEnsureOpen()
+        {
+            // verifies that an exception is thrown if DTW was closed
+            Directory dir = NewDirectory();
+            DirectoryTaxonomyWriter dtw = new DirectoryTaxonomyWriter(dir);
+            dtw.Dispose();
+            try
+            {
+                dtw.AddCategory(new FacetLabel("a"));
+                Fail("should not have succeeded to add a category following close.");
+            }
+            catch (AlreadyClosedException)
+            {
+                // expected
+            }
+            dir.Dispose();
+        }
+
+        [Test]
+        private void TouchTaxo(DirectoryTaxonomyWriter taxoWriter, FacetLabel cp)
+        {
+            taxoWriter.AddCategory(cp);
+            taxoWriter.CommitData = new Dictionary<string, string>()
+		{
+			{"just", "data"}
+		};
+            taxoWriter.Commit();
+        }
+
+        [Test]
+        public virtual void TestRecreateAndRefresh()
+        {
+            // DirTaxoWriter lost the INDEX_EPOCH property if it was opened in
+            // CREATE_OR_APPEND (or commit(userData) called twice), which could lead to
+            // DirTaxoReader succeeding to refresh().
+            Directory dir = NewDirectory();
+
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE_OR_APPEND, NO_OP_CACHE);
+            TouchTaxo(taxoWriter, new FacetLabel("a"));
+
+            var taxoReader = new DirectoryTaxonomyReader(dir);
+
+            TouchTaxo(taxoWriter, new FacetLabel("b"));
+
+            var newtr = TaxonomyReader.OpenIfChanged(taxoReader);
+            taxoReader.Dispose();
+            taxoReader = newtr;
+            Assert.AreEqual(1, Convert.ToInt32(taxoReader.CommitUserData[DirectoryTaxonomyWriter.INDEX_EPOCH]));
+
+            // now recreate the taxonomy, and check that the epoch is preserved after opening DirTW again.
+            taxoWriter.Dispose();
+            taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE, NO_OP_CACHE);
+            TouchTaxo(taxoWriter, new FacetLabel("c"));
+            taxoWriter.Dispose();
+
+            taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE_OR_APPEND, NO_OP_CACHE);
+            TouchTaxo(taxoWriter, new FacetLabel("d"));
+            taxoWriter.Dispose();
+
+            newtr = TaxonomyReader.OpenIfChanged(taxoReader);
+            taxoReader.Dispose();
+            taxoReader = newtr;
+            Assert.AreEqual(2, Convert.ToInt32(taxoReader.CommitUserData[DirectoryTaxonomyWriter.INDEX_EPOCH]));
+
+            taxoReader.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestBackwardsCompatibility()
+        {
+            // tests that if the taxonomy index doesn't have the INDEX_EPOCH
+            // property (supports pre-3.6 indexes), all still works.
+            Directory dir = NewDirectory();
+
+            // create an empty index first, so that DirTaxoWriter initializes indexEpoch to 1.
+            (new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, null))).Dispose();
+
+            var taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE_OR_APPEND, NO_OP_CACHE);
+            taxoWriter.Dispose();
+
+            var taxoReader = new DirectoryTaxonomyReader(dir);
+            Assert.AreEqual(1, Convert.ToInt32(taxoReader.CommitUserData[DirectoryTaxonomyWriter.INDEX_EPOCH]));
+            Assert.Null(TaxonomyReader.OpenIfChanged(taxoReader));
+            (taxoReader).Dispose();
+
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestConcurrency()
+        {
+            int ncats = AtLeast(100000); // add many categories
+            int range = ncats * 3; // affects the categories selection
+            AtomicInteger numCats = new AtomicInteger(ncats);
+            Directory dir = NewDirectory();
+            var values = new ConcurrentDictionary<string, string>();
+            double d = Random().NextDouble();
+            TaxonomyWriterCache cache;
+            if (d < 0.7)
+            {
+                // this is the fastest, yet most memory consuming
+                cache = new Cl2oTaxonomyWriterCache(1024, 0.15f, 3);
+            }
+            else if (TEST_NIGHTLY && d > 0.98)
+            {
+                // this is the slowest, but tests the writer concurrency when no caching is done.
+                // only pick it during NIGHTLY tests, and even then, with very low chances.
+                cache = NO_OP_CACHE;
+            }
+            else
+            {
+                // this is slower than CL2O, but less memory consuming, and exercises finding categories on disk too.
+                cache = new LruTaxonomyWriterCache(ncats / 10);
+            }
+            if (VERBOSE)
+            {
+                Console.WriteLine("TEST: use cache=" + cache);
+            }
+            var tw = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE, cache);
+            ThreadClass[] addThreads = new ThreadClass[AtLeast(4)];
+            for (int z = 0; z < addThreads.Length; z++)
+            {
+                addThreads[z] = new ThreadAnonymousInnerClassHelper(this, range, numCats, values, tw);
+            }
+
+            foreach (var t in addThreads)
+            {
+                t.Start();
+            }
+            foreach (var t in addThreads)
+            {
+                t.Join();
+            }
+            tw.Dispose();
+
+            DirectoryTaxonomyReader dtr = new DirectoryTaxonomyReader(dir);
+            // +1 for root category
+            if (values.Count + 1 != dtr.Size)
+            {
+                foreach (string value in values.Keys)
+                {
+                    FacetLabel label = new FacetLabel(FacetsConfig.StringToPath(value));
+                    if (dtr.GetOrdinal(label) == -1)
+                    {
+                        Console.WriteLine("FAIL: path=" + label + " not recognized");
+                    }
+                }
+                Fail("mismatch number of categories");
+            }
+
+            int[] parents = dtr.ParallelTaxonomyArrays.Parents();
+            foreach (string cat in values.Keys)
+            {
+                FacetLabel cp = new FacetLabel(FacetsConfig.StringToPath(cat));
+                Assert.True(dtr.GetOrdinal(cp) > 0, "category not found " + cp);
+                int level = cp.length;
+                int parentOrd = 0; // for root, parent is always virtual ROOT (ord=0)
+                FacetLabel path = new FacetLabel();
+                for (int i = 0; i < level; i++)
+                {
+                    path = cp.Subpath(i + 1);
+                    int ord = dtr.GetOrdinal(path);
+                    Assert.AreEqual(parentOrd, parents[ord], "invalid parent for cp=" + path);
+                    parentOrd = ord; // next level should have this parent
+                }
+            }
+
+            IOUtils.Close(dtr, dir);
+        }
+
+        private class ThreadAnonymousInnerClassHelper : ThreadClass
+        {
+            private readonly TestDirectoryTaxonomyWriter outerInstance;
+
+            private int range;
+            private AtomicInteger numCats;
+            private ConcurrentDictionary<string, string> values;
+            private Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter tw;
+
+            public ThreadAnonymousInnerClassHelper(TestDirectoryTaxonomyWriter outerInstance, int range, AtomicInteger numCats, ConcurrentDictionary<string, string> values, Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter tw)
+            {
+                this.outerInstance = outerInstance;
+                this.range = range;
+                this.numCats = numCats;
+                this.values = values;
+                this.tw = tw;
+            }
+
+            public override void Run()
+            {
+                Random random = Random();
+                while (numCats.DecrementAndGet() > 0)
+                {
+                    try
+                    {
+                        int value = random.Next(range);
+                        FacetLabel cp = new FacetLabel(Convert.ToString(value / 1000), Convert.ToString(value / 10000), Convert.ToString(value / 100000), Convert.ToString(value));
+                        int ord = tw.AddCategory(cp);
+                        Assert.True(tw.GetParent(ord) != -1, "invalid parent for ordinal " + ord + ", category " + cp);
+                        string l1 = FacetsConfig.PathToString(cp.components, 1);
+                        string l2 = FacetsConfig.PathToString(cp.components, 2);
+                        string l3 = FacetsConfig.PathToString(cp.components, 3);
+                        string l4 = FacetsConfig.PathToString(cp.components, 4);
+                        values[l1] = l1;
+                        values[l2] = l2;
+                        values[l3] = l3;
+                        values[l4] = l4;
+                    }
+                    catch (IOException e)
+                    {
+                        throw new Exception(e.Message, e);
+                    }
+                }
+            }
+        }
+
+        private long getEpoch(Directory taxoDir)
+        {
+            SegmentInfos infos = new SegmentInfos();
+            infos.Read(taxoDir);
+            return Convert.ToInt64(infos.UserData[DirectoryTaxonomyWriter.INDEX_EPOCH]);
+        }
+
+        [Test]
+        public virtual void TestReplaceTaxonomy()
+        {
+            Directory input = NewDirectory();
+            var taxoWriter = new DirectoryTaxonomyWriter(input);
+            int ordA = taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.Dispose();
+
+            Directory dir = NewDirectory();
+            taxoWriter = new DirectoryTaxonomyWriter(dir);
+            int ordB = taxoWriter.AddCategory(new FacetLabel("b"));
+            taxoWriter.AddCategory(new FacetLabel("c"));
+            taxoWriter.Commit();
+
+            long origEpoch = getEpoch(dir);
+
+            // replace the taxonomy with the input one
+            taxoWriter.ReplaceTaxonomy(input);
+
+            // LUCENE-4633: make sure that category "a" is not added again in any case
+            taxoWriter.AddTaxonomy(input, new MemoryOrdinalMap());
+            Assert.AreEqual(2, taxoWriter.Size, "no categories should have been added"); // root + 'a'
+            Assert.AreEqual(ordA, taxoWriter.AddCategory(new FacetLabel("a")), "category 'a' received new ordinal?");
+
+            // add the same category again -- it should not receive the same ordinal !
+            int newOrdB = taxoWriter.AddCategory(new FacetLabel("b"));
+            Assert.AreNotSame(ordB, newOrdB, "new ordinal cannot be the original ordinal");
+            Assert.AreEqual(2, newOrdB, "ordinal should have been 2 since only one category was added by replaceTaxonomy");
+
+            taxoWriter.Dispose();
+
+            long newEpoch = getEpoch(dir);
+            Assert.True(origEpoch < newEpoch, "index epoch should have been updated after replaceTaxonomy");
+
+            dir.Dispose();
+            input.Dispose();
+        }
+
+        [Test]
+        public virtual void TestReaderFreshness()
+        {
+            // ensures that the internal index reader is always kept fresh. Previously,
+            // this simple scenario failed, if the cache just evicted the category that
+            // is being added.
+            Directory dir = NewDirectory();
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE, NO_OP_CACHE);
+            int o1 = taxoWriter.AddCategory(new FacetLabel("a"));
+            int o2 = taxoWriter.AddCategory(new FacetLabel("a"));
+            Assert.True(o1 == o2, "ordinal for same category that is added twice should be the same !");
+            taxoWriter.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestCommitNoEmptyCommits()
+        {
+            // LUCENE-4972: DTW used to create empty commits even if no changes were made
+            Directory dir = NewDirectory();
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(dir);
+            taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.Commit();
+
+            long gen1 = SegmentInfos.GetLastCommitGeneration(dir);
+            taxoWriter.Commit();
+            long gen2 = SegmentInfos.GetLastCommitGeneration(dir);
+            Assert.AreEqual(gen1, gen2, "empty commit should not have changed the index");
+
+            taxoWriter.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestCloseNoEmptyCommits()
+        {
+            // LUCENE-4972: DTW used to create empty commits even if no changes were made
+            Directory dir = NewDirectory();
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(dir);
+            taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.Commit();
+
+            long gen1 = SegmentInfos.GetLastCommitGeneration(dir);
+            taxoWriter.Dispose();
+            long gen2 = SegmentInfos.GetLastCommitGeneration(dir);
+            Assert.AreEqual(gen1, gen2, "empty commit should not have changed the index");
+
+            taxoWriter.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestPrepareCommitNoEmptyCommits()
+        {
+            // LUCENE-4972: DTW used to create empty commits even if no changes were made
+            Directory dir = NewDirectory();
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(dir);
+            taxoWriter.AddCategory(new FacetLabel("a"));
+            taxoWriter.PrepareCommit();
+            taxoWriter.Commit();
+
+            long gen1 = SegmentInfos.GetLastCommitGeneration(dir);
+            taxoWriter.PrepareCommit();
+            taxoWriter.Commit();
+            long gen2 = SegmentInfos.GetLastCommitGeneration(dir);
+            Assert.AreEqual(gen1, gen2, "empty commit should not have changed the index");
+
+            taxoWriter.Dispose();
+            dir.Dispose();
+        }
+
+        [Test]
+        public virtual void TestHugeLabel()
+        {
+            Directory indexDir = NewDirectory(), taxoDir = NewDirectory();
+            IndexWriter indexWriter = new IndexWriter(indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random())));
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE, new Cl2oTaxonomyWriterCache(2, 1f, 1));
+            FacetsConfig config = new FacetsConfig();
+
+            // Add one huge label:
+            string bigs = null;
+            int ordinal = -1;
+
+            int len = FacetLabel.MAX_CATEGORY_PATH_LENGTH - 4; // for the dimension and separator
+            bigs = TestUtil.RandomSimpleString(Random(), len, len);
+            FacetField ff = new FacetField("dim", bigs);
+            FacetLabel cp = new FacetLabel("dim", bigs);
+            ordinal = taxoWriter.AddCategory(cp);
+            Document doc = new Document();
+            doc.Add(ff);
+            indexWriter.AddDocument(config.Build(taxoWriter, doc));
+
+            // Add tiny ones to cause a re-hash
+            for (int i = 0; i < 3; i++)
+            {
+                string s = TestUtil.RandomSimpleString(Random(), 1, 10);
+                taxoWriter.AddCategory(new FacetLabel("dim", s));
+                doc = new Document();
+                doc.Add(new FacetField("dim", s));
+                indexWriter.AddDocument(config.Build(taxoWriter, doc));
+            }
+
+            // when too large components were allowed to be added, this resulted in a new added category
+            Assert.AreEqual(ordinal, taxoWriter.AddCategory(cp));
+
+            IOUtils.Close(indexWriter, taxoWriter);
+
+            DirectoryReader indexReader = DirectoryReader.Open(indexDir);
+            var taxoReader = new DirectoryTaxonomyReader(taxoDir);
+            IndexSearcher searcher = new IndexSearcher(indexReader);
+            DrillDownQuery ddq = new DrillDownQuery(new FacetsConfig());
+            ddq.Add("dim", bigs);
+            Assert.AreEqual(1, searcher.Search(ddq, 10).TotalHits);
+
+            IOUtils.Close(indexReader, taxoReader, indexDir, taxoDir);
+        }
+
+        [Test]
+        public virtual void TestReplaceTaxoWithLargeTaxonomy()
+        {
+            var srcTaxoDir = NewDirectory();
+            var targetTaxoDir = NewDirectory();
+
+            // build source, large, taxonomy
+            DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(srcTaxoDir);
+            int ord = taxoWriter.AddCategory(new FacetLabel("A", "1", "1", "1", "1", "1", "1"));
+            taxoWriter.Dispose();
+
+            taxoWriter = new DirectoryTaxonomyWriter(targetTaxoDir);
+            int ordinal = taxoWriter.AddCategory(new FacetLabel("B", "1"));
+            Assert.AreEqual(1, taxoWriter.GetParent(ordinal)); // call getParent to initialize taxoArrays
+            taxoWriter.Commit();
+
+            taxoWriter.ReplaceTaxonomy(srcTaxoDir);
+            Assert.AreEqual(ord - 1, taxoWriter.GetParent(ord));
+            taxoWriter.Dispose();
+
+            srcTaxoDir.Dispose();
+            targetTaxoDir.Dispose();
+        }
+
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/982eaf60/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestCachedOrdinalsReader.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestCachedOrdinalsReader.cs b/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestCachedOrdinalsReader.cs
new file mode 100644
index 0000000..d528276
--- /dev/null
+++ b/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestCachedOrdinalsReader.cs
@@ -0,0 +1,116 @@
+using System;
+using System.IO;
+using System.Threading;
+using Lucene.Net.Support;
+using NUnit.Framework;
+
+namespace Lucene.Net.Facet.Taxonomy
+{
+
+    /*
+     * 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 Document = Lucene.Net.Documents.Document;
+    using DirectoryTaxonomyWriter = Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter;
+    using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
+    using DirectoryReader = Lucene.Net.Index.DirectoryReader;
+    using IndexWriter = Lucene.Net.Index.IndexWriter;
+    using IndexWriterConfig = Lucene.Net.Index.IndexWriterConfig;
+    using Directory = Lucene.Net.Store.Directory;
+    using IOUtils = Lucene.Net.Util.IOUtils;
+    [TestFixture]
+    public class TestCachedOrdinalsReader : FacetTestCase
+    {
+
+        [Test]
+        public virtual void TestWithThreads()
+        {
+            // LUCENE-5303: OrdinalsCache used the ThreadLocal BinaryDV instead of reader.getCoreCacheKey().
+            Store.Directory indexDir = NewDirectory();
+            Store.Directory taxoDir = NewDirectory();
+            IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random()));
+            IndexWriter writer = new IndexWriter(indexDir, conf);
+            var taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
+            FacetsConfig config = new FacetsConfig();
+
+            Document doc = new Document();
+            doc.Add(new FacetField("A", "1"));
+            writer.AddDocument(config.Build(taxoWriter, doc));
+            doc = new Document();
+            doc.Add(new FacetField("A", "2"));
+            writer.AddDocument(config.Build(taxoWriter, doc));
+
+            var reader = DirectoryReader.Open(writer, true);
+            CachedOrdinalsReader ordsReader = new CachedOrdinalsReader(new DocValuesOrdinalsReader(FacetsConfig.DEFAULT_INDEX_FIELD_NAME));
+            ThreadClass[] threads = new ThreadClass[3];
+            for (int i = 0; i < threads.Length; i++)
+            {
+                threads[i] = new ThreadAnonymousInnerClassHelper(this, "CachedOrdsThread-" + i, reader, ordsReader);
+            }
+
+            long ramBytesUsed = 0;
+            foreach (ThreadClass t in threads)
+            {
+                t.Start();
+                t.Join();
+                if (ramBytesUsed == 0)
+                {
+                    ramBytesUsed = ordsReader.RamBytesUsed();
+                }
+                else
+                {
+                    Assert.AreEqual(ramBytesUsed, ordsReader.RamBytesUsed());
+                }
+            }
+
+            IOUtils.Close(writer, taxoWriter, reader, indexDir, taxoDir);
+        }
+
+        private class ThreadAnonymousInnerClassHelper : ThreadClass
+        {
+            private readonly TestCachedOrdinalsReader outerInstance;
+
+            private DirectoryReader reader;
+            private Lucene.Net.Facet.Taxonomy.CachedOrdinalsReader ordsReader;
+
+            public ThreadAnonymousInnerClassHelper(TestCachedOrdinalsReader outerInstance, string CachedOrdsThread, DirectoryReader reader, Lucene.Net.Facet.Taxonomy.CachedOrdinalsReader ordsReader)
+                : base("CachedOrdsThread-")
+            {
+                this.outerInstance = outerInstance;
+                this.reader = reader;
+                this.ordsReader = ordsReader;
+            }
+
+            public override void Run()
+            {
+                foreach (var context in reader.Leaves)
+                {
+                    try
+                    {
+                        ordsReader.GetReader(context);
+                    }
+                    catch (IOException e)
+                    {
+                        throw new Exception(e.Message, e);
+                    }
+                }
+            }
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/982eaf60/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestFacetLabel.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestFacetLabel.cs b/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestFacetLabel.cs
new file mode 100644
index 0000000..7f09146
--- /dev/null
+++ b/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestFacetLabel.cs
@@ -0,0 +1,360 @@
+using System;
+using Lucene.Net.Support;
+using NUnit.Framework;
+
+namespace Lucene.Net.Facet.Taxonomy
+{
+
+    using SortedSetDocValuesFacetField = Lucene.Net.Facet.SortedSet.SortedSetDocValuesFacetField;
+    using BytesRef = Lucene.Net.Util.BytesRef;
+    using TestUtil = Lucene.Net.Util.TestUtil;
+
+    /*
+     * 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.
+     */
+    [TestFixture]
+    public class TestFacetLabel : FacetTestCase
+    {
+
+        [Test]
+        public virtual void TestBasic()
+        {
+            Assert.AreEqual(0, (new FacetLabel()).length);
+            Assert.AreEqual(1, (new FacetLabel("hello")).length);
+            Assert.AreEqual(2, (new FacetLabel("hello", "world")).length);
+        }
+
+        [Test]
+        public virtual void TestToString()
+        {
+            // When the category is empty, we expect an empty string
+            Assert.AreEqual("FacetLabel: []", (new FacetLabel()).ToString());
+            // one category
+            Assert.AreEqual("FacetLabel: [hello]", (new FacetLabel("hello")).ToString());
+            // more than one category
+            Assert.AreEqual("FacetLabel: [hello, world]", (new FacetLabel("hello", "world")).ToString());
+        }
+
+        [Test]
+        public virtual void TestGetComponent()
+        {
+            string[] components = new string[AtLeast(10)];
+            for (int i = 0; i < components.Length; i++)
+            {
+                components[i] = Convert.ToString(i);
+            }
+            FacetLabel cp = new FacetLabel(components);
+            for (int i = 0; i < components.Length; i++)
+            {
+                Assert.AreEqual(i, Convert.ToInt32(cp.components[i]));
+            }
+        }
+
+        [Test]
+        public virtual void TestDefaultConstructor()
+        {
+            // test that the default constructor (no parameters) currently
+            // defaults to creating an object with a 0 initial capacity.
+            // If we change this default later, we also need to change this
+            // test.
+            FacetLabel p = new FacetLabel();
+            Assert.AreEqual(0, p.length);
+            Assert.AreEqual("FacetLabel: []", p.ToString());
+        }
+
+        [Test]
+        public virtual void TestSubPath()
+        {
+            FacetLabel p = new FacetLabel("hi", "there", "man");
+            Assert.AreEqual(p.length, 3);
+
+            FacetLabel p1 = p.Subpath(2);
+            Assert.AreEqual(2, p1.length);
+            Assert.AreEqual("FacetLabel: [hi, there]", p1.ToString());
+
+            p1 = p.Subpath(1);
+            Assert.AreEqual(1, p1.length);
+            Assert.AreEqual("FacetLabel: [hi]", p1.ToString());
+
+            p1 = p.Subpath(0);
+            Assert.AreEqual(0, p1.length);
+            Assert.AreEqual("FacetLabel: []", p1.ToString());
+
+            // with all the following lengths, the prefix should be the whole path 
+            int[] lengths = new int[] { 3, -1, 4 };
+            for (int i = 0; i < lengths.Length; i++)
+            {
+                p1 = p.Subpath(lengths[i]);
+                Assert.AreEqual(3, p1.length);
+                Assert.AreEqual("FacetLabel: [hi, there, man]", p1.ToString());
+                Assert.AreEqual(p, p1);
+            }
+        }
+
+        [Test]
+        public virtual void TestEquals()
+        {
+            Assert.AreEqual(new FacetLabel(), new FacetLabel());
+            Assert.False((new FacetLabel()).Equals(new FacetLabel("hi")));
+            Assert.False((new FacetLabel()).Equals(Convert.ToInt32(3)));
+            Assert.AreEqual(new FacetLabel("hello", "world"), new FacetLabel("hello", "world"));
+        }
+
+        [Test]
+        public virtual void TestHashCode()
+        {
+            Assert.AreEqual((new FacetLabel()).GetHashCode(), (new FacetLabel()).GetHashCode());
+            Assert.False((new FacetLabel()).GetHashCode() == (new FacetLabel("hi")).GetHashCode());
+            Assert.AreEqual((new FacetLabel("hello", "world")).GetHashCode(), (new FacetLabel("hello", "world")).GetHashCode());
+        }
+
+        [Test]
+        public virtual void TestLongHashCode()
+        {
+            Assert.AreEqual((new FacetLabel()).LongHashCode(), (new FacetLabel()).LongHashCode());
+            Assert.False((new FacetLabel()).LongHashCode() == (new FacetLabel("hi")).LongHashCode());
+            Assert.AreEqual((new FacetLabel("hello", "world")).LongHashCode(), (new FacetLabel("hello", "world")).LongHashCode());
+        }
+
+        [Test]
+        public virtual void TestArrayConstructor()
+        {
+            FacetLabel p = new FacetLabel("hello", "world", "yo");
+            Assert.AreEqual(3, p.length);
+            Assert.AreEqual("FacetLabel: [hello, world, yo]", p.ToString());
+        }
+
+        [Test]
+        public virtual void TestCompareTo()
+        {
+            FacetLabel p = new FacetLabel("a", "b", "c", "d");
+            FacetLabel pother = new FacetLabel("a", "b", "c", "d");
+            Assert.AreEqual(0, pother.CompareTo(p));
+            Assert.AreEqual(0, p.CompareTo(pother));
+            pother = new FacetLabel();
+            Assert.True(pother.CompareTo(p) < 0);
+            Assert.True(p.CompareTo(pother) > 0);
+            pother = new FacetLabel("a", "b_", "c", "d");
+            Assert.True(pother.CompareTo(p) > 0);
+            Assert.True(p.CompareTo(pother) < 0);
+            pother = new FacetLabel("a", "b", "c");
+            Assert.True(pother.CompareTo(p) < 0);
+            Assert.True(p.CompareTo(pother) > 0);
+            pother = new FacetLabel("a", "b", "c", "e");
+            Assert.True(pother.CompareTo(p) > 0);
+            Assert.True(p.CompareTo(pother) < 0);
+        }
+
+        [Test]
+        public virtual void TestEmptyNullComponents()
+        {
+            // LUCENE-4724: CategoryPath should not allow empty or null components
+            string[][] components_tests = new string[][]
+		{
+			new string[] {"", "test"},
+			new string[] {"test", ""},
+			new string[] {"test", "", "foo"},
+			new string[] {null, "test"},
+			new string[] {"test", null},
+			new string[] {"test", null, "foo"}
+		};
+
+            foreach (string[] components in components_tests)
+            {
+                try
+                {
+                    Assert.NotNull(new FacetLabel(components));
+                    Fail("empty or null components should not be allowed: " + Arrays.ToString(components));
+                }
+                catch (System.ArgumentException)
+                {
+                    // expected
+                }
+                try
+                {
+                    new FacetField("dim", components);
+                    Fail("empty or null components should not be allowed: " + Arrays.ToString(components));
+                }
+                catch (System.ArgumentException)
+                {
+                    // expected
+                }
+                try
+                {
+                    new AssociationFacetField(new BytesRef(), "dim", components);
+                    Fail("empty or null components should not be allowed: " + Arrays.ToString(components));
+                }
+                catch (System.ArgumentException)
+                {
+                    // expected
+                }
+                try
+                {
+                    new IntAssociationFacetField(17, "dim", components);
+                    Fail("empty or null components should not be allowed: " + Arrays.ToString(components));
+                }
+                catch (System.ArgumentException)
+                {
+                    // expected
+                }
+                try
+                {
+                    new FloatAssociationFacetField(17.0f, "dim", components);
+                    Fail("empty or null components should not be allowed: " + Arrays.ToString(components));
+                }
+                catch (System.ArgumentException)
+                {
+                    // expected
+                }
+            }
+            try
+            {
+                new FacetField(null, new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new FacetField("", new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new IntAssociationFacetField(17, null, new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new IntAssociationFacetField(17, "", new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new FloatAssociationFacetField(17.0f, null, new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new FloatAssociationFacetField(17.0f, "", new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new AssociationFacetField(new BytesRef(), null, new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new AssociationFacetField(new BytesRef(), "", new string[] { "abc" });
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new SortedSetDocValuesFacetField(null, "abc");
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new SortedSetDocValuesFacetField("", "abc");
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new SortedSetDocValuesFacetField("dim", null);
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+            try
+            {
+                new SortedSetDocValuesFacetField("dim", "");
+                Fail("empty or null components should not be allowed");
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+        }
+
+        [Test]
+        public virtual void TestLongPath()
+        {
+            string bigComp = null;
+            while (true)
+            {
+                int len = FacetLabel.MAX_CATEGORY_PATH_LENGTH;
+                bigComp = TestUtil.RandomSimpleString(Random(), len, len);
+                if (bigComp.IndexOf('\u001f') != -1)
+                {
+                    continue;
+                }
+                break;
+            }
+
+            try
+            {
+                Assert.NotNull(new FacetLabel("dim", bigComp));
+                Fail("long paths should not be allowed; len=" + bigComp.Length);
+            }
+            catch (System.ArgumentException)
+            {
+                // expected
+            }
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/982eaf60/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestLRUHashMap.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestLRUHashMap.cs b/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestLRUHashMap.cs
new file mode 100644
index 0000000..71b62c5
--- /dev/null
+++ b/src/Lucene.Net.Tests/core/Facet/Taxonomy/TestLRUHashMap.cs
@@ -0,0 +1,65 @@
+using NUnit.Framework;
+
+namespace Lucene.Net.Facet.Taxonomy
+{
+
+    /*
+     * 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 Lucene.Net.Facet.Taxonomy;
+    [TestFixture]
+    public class TestLRUHashMap : FacetTestCase
+    {
+        // testLRU() tests that the specified size limit is indeed honored, and
+        // the remaining objects in the map are indeed those that have been most
+        // recently used
+        [Test]
+        public virtual void TestLru()
+        {
+            LRUHashMap<string, string> lru = new LRUHashMap<string, string>(3,1);
+            Assert.AreEqual(0, lru.Size());
+            lru.Put("one", "Hello world");
+            Assert.AreEqual(1, lru.Size());
+            lru.Put("two", "Hi man");
+            Assert.AreEqual(2, lru.Size());
+            lru.Put("three", "Bonjour");
+            Assert.AreEqual(3, lru.Size());
+            lru.Put("four", "Shalom");
+            Assert.AreEqual(3, lru.Size());
+            Assert.NotNull(lru.Get("three"));
+            Assert.NotNull(lru.Get("two"));
+            Assert.NotNull(lru.Get("four"));
+            Assert.Null(lru.Get("one"));
+            lru.Put("five", "Yo!");
+            Assert.AreEqual(3, lru.Size());
+            Assert.Null(lru.Get("three")); // three was last used, so it got removed
+            Assert.NotNull(lru.Get("five"));
+            lru.Get("four");
+            lru.Put("six", "hi");
+            lru.Put("seven", "hey dude");
+            Assert.AreEqual(3, lru.Size());
+            Assert.Null(lru.Get("one"));
+            Assert.Null(lru.Get("two"));
+            Assert.Null(lru.Get("three"));
+            Assert.NotNull(lru.Get("four"));
+            Assert.Null(lru.Get("five"));
+            Assert.NotNull(lru.Get("six"));
+            Assert.NotNull(lru.Get("seven"));
+        }
+    }
+
+}
\ No newline at end of file


Mime
View raw message