http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6da4dd20/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs b/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs new file mode 100644 index 0000000..645888a --- /dev/null +++ b/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyReplicationClientTest.cs @@ -0,0 +1,518 @@ +//STATUS: DRAFT - 4.8.0 + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Lucene.Net.Documents; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Taxonomy; +using Lucene.Net.Facet.Taxonomy.Directory; +using Lucene.Net.Index; +using Lucene.Net.Replicator; +using Lucene.Net.Search; +using Lucene.Net.Store; +using Lucene.Net.Support; +using Lucene.Net.Util; +using NUnit.Framework; +using Directory = Lucene.Net.Store.Directory; + +namespace Lucene.Net.Tests.Replicator +{ + [TestFixture] + public class IndexAndTaxonomyReplicationClientTest : ReplicatorTestCase + { + private class IndexAndTaxonomyReadyCallback : IDisposable + { + private Directory indexDir, taxoDir; + private DirectoryReader indexReader; + private DirectoryTaxonomyReader taxoReader; + private FacetsConfig config; + private long lastIndexGeneration = -1; + + public IndexAndTaxonomyReadyCallback(MockDirectoryWrapper indexDir, MockDirectoryWrapper taxoDir) + { + this.indexDir = indexDir; + this.taxoDir = taxoDir; + config = new FacetsConfig(); + config.SetHierarchical("A", true); + if (DirectoryReader.IndexExists(indexDir)) + { + indexReader = DirectoryReader.Open(indexDir); + lastIndexGeneration = indexReader.IndexCommit.Generation; + taxoReader = new DirectoryTaxonomyReader(taxoDir); + } + } + + public bool? Call() + { + if (indexReader == null) + { + indexReader = DirectoryReader.Open(indexDir); + lastIndexGeneration = indexReader.IndexCommit.Generation; + taxoReader = new DirectoryTaxonomyReader(taxoDir); + } + else + { + // verify search index + DirectoryReader newReader = DirectoryReader.OpenIfChanged(indexReader); + assertNotNull("should not have reached here if no changes were made to the index", newReader); + long newGeneration = newReader.IndexCommit.Generation; + assertTrue("expected newer generation; current=" + lastIndexGeneration + " new=" + newGeneration, newGeneration > lastIndexGeneration); + indexReader.Dispose(); + indexReader = newReader; + lastIndexGeneration = newGeneration; + TestUtil.CheckIndex(indexDir); + + // verify taxonomy index + DirectoryTaxonomyReader newTaxoReader = TaxonomyReader.OpenIfChanged(taxoReader); + if (newTaxoReader != null) + { + taxoReader.Dispose(); + taxoReader = newTaxoReader; + } + TestUtil.CheckIndex(taxoDir); + + // verify faceted search + int id = int.Parse(indexReader.IndexCommit.UserData[VERSION_ID], NumberStyles.HexNumber); + IndexSearcher searcher = new IndexSearcher(indexReader); + FacetsCollector fc = new FacetsCollector(); + searcher.Search(new MatchAllDocsQuery(), fc); + Facets facets = new FastTaxonomyFacetCounts(taxoReader, config, fc); + assertEquals(1, (int)facets.GetSpecificValue("A", id.ToString("X"))); + + DrillDownQuery drillDown = new DrillDownQuery(config); + drillDown.Add("A", id.ToString("X")); + TopDocs docs = searcher.Search(drillDown, 10); + assertEquals(1, docs.TotalHits); + } + return null; + } + + public void Dispose() + { + IOUtils.Dispose(indexReader, taxoReader); + } + } + + private Directory publishIndexDir, publishTaxoDir; + private MockDirectoryWrapper handlerIndexDir, handlerTaxoDir; + private IReplicator replicator; + private ISourceDirectoryFactory sourceDirFactory; + private ReplicationClient client; + private IReplicationHandler handler; + private IndexWriter publishIndexWriter; + private IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter publishTaxoWriter; + private FacetsConfig config; + private IndexAndTaxonomyReadyCallback callback; + private DirectoryInfo clientWorkDir; + + private const string VERSION_ID = "version"; + + private void AssertHandlerRevision(int expectedId, Directory dir) + { + //JAVA: private void assertHandlerRevision(int expectedID, Directory dir) throws IOException { + //JAVA: // loop as long as client is alive. test-framework will terminate us if + //JAVA: // there's a serious bug, e.g. client doesn't really update. otherwise, + //JAVA: // introducing timeouts is not good, can easily lead to false positives. + //JAVA: while (client.isUpdateThreadAlive()) { + //JAVA: // give client a chance to update + //JAVA: try { + //JAVA: Thread.sleep(100); + //JAVA: } catch (InterruptedException e) { + //JAVA: throw new ThreadInterruptedException(e); + //JAVA: } + //JAVA: + //JAVA: try { + //JAVA: DirectoryReader reader = DirectoryReader.open(dir); + //JAVA: try { + //JAVA: int handlerID = Integer.parseInt(reader.getIndexCommit().getUserData().get(VERSION_ID), 16); + //JAVA: if (expectedID == handlerID) { + //JAVA: return; + //JAVA: } + //JAVA: } finally { + //JAVA: reader.close(); + //JAVA: } + //JAVA: } catch (Exception e) { + //JAVA: // we can hit IndexNotFoundException or e.g. EOFException (on + //JAVA: // segments_N) because it is being copied at the same time it is read by + //JAVA: // DirectoryReader.open(). + //JAVA: } + //JAVA: } + //JAVA: } + + // loop as long as client is alive. test-framework will terminate us if + // there's a serious bug, e.g. client doesn't really update. otherwise, + // introducing timeouts is not good, can easily lead to false positives. + while (client.IsUpdateThreadAlive) + { + Thread.Sleep(100); + + try + { + DirectoryReader reader = DirectoryReader.Open(dir); + try + { + int handlerId = int.Parse(reader.IndexCommit.UserData[VERSION_ID], NumberStyles.HexNumber); + if (expectedId == handlerId) + { + return; + } + } + finally + { + reader.Dispose(); + } + } + catch (Exception) + { + // we can hit IndexNotFoundException or e.g. EOFException (on + // segments_N) because it is being copied at the same time it is read by + // DirectoryReader.open(). + } + } + } + + private IRevision CreateRevision(int id) + { + //JAVA: private Revision createRevision(final int id) throws IOException { + //JAVA: publishIndexWriter.addDocument(newDocument(publishTaxoWriter, id)); + //JAVA: publishIndexWriter.setCommitData(new HashMap<String, String>() {{ + //JAVA: put(VERSION_ID, Integer.toString(id, 16)); + //JAVA: }}); + //JAVA: publishIndexWriter.commit(); + //JAVA: publishTaxoWriter.commit(); + //JAVA: return new IndexAndTaxonomyRevision(publishIndexWriter, publishTaxoWriter); + //JAVA: } + publishIndexWriter.AddDocument(NewDocument(publishTaxoWriter, id)); + publishIndexWriter.SetCommitData(new Dictionary<string, string>{ + { VERSION_ID, id.ToString("X") } + }); + publishIndexWriter.Commit(); + publishTaxoWriter.Commit(); + return new IndexAndTaxonomyRevision(publishIndexWriter, publishTaxoWriter); + } + + private Document NewDocument(ITaxonomyWriter taxoWriter, int id) + { + Document doc = new Document(); + doc.Add(new FacetField("A", id.ToString("X"))); + return config.Build(taxoWriter, doc); + } + + public override void SetUp() + { + base.SetUp(); + + publishIndexDir = NewDirectory(); + publishTaxoDir = NewDirectory(); + handlerIndexDir = NewMockDirectory(); + handlerTaxoDir = NewMockDirectory(); + clientWorkDir = CreateTempDir("replicationClientTest"); + sourceDirFactory = new PerSessionDirectoryFactory(clientWorkDir.FullName); + replicator = new LocalReplicator(); + callback = new IndexAndTaxonomyReadyCallback(handlerIndexDir, handlerTaxoDir); + handler = new IndexAndTaxonomyReplicationHandler(handlerIndexDir, handlerTaxoDir, callback.Call); + client = new ReplicationClient(replicator, handler, sourceDirFactory); + + IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + publishIndexWriter = new IndexWriter(publishIndexDir, conf); + publishTaxoWriter = new IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter(publishTaxoDir); + config = new FacetsConfig(); + config.SetHierarchical("A", true); + } + + public override void TearDown() + { + IOUtils.Dispose(client, callback, publishIndexWriter, publishTaxoWriter, replicator, publishIndexDir, publishTaxoDir, + handlerIndexDir, handlerTaxoDir); + base.TearDown(); + } + + [Test] + public void TestNoUpdateThread() + { + assertNull("no version expected at start", handler.CurrentVersion); + + // Callback validates the replicated index + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + + // make sure updating twice, when in fact there's nothing to update, works + client.UpdateNow(); + + replicator.Publish(CreateRevision(2)); + client.UpdateNow(); + + // Publish two revisions without update, handler should be upgraded to latest + replicator.Publish(CreateRevision(3)); + replicator.Publish(CreateRevision(4)); + client.UpdateNow(); + } + + [Test] + public void TestRestart() + { + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + + replicator.Publish(CreateRevision(2)); + client.UpdateNow(); + + client.StopUpdateThread(); + client.Dispose(); + client = new ReplicationClient(replicator, handler, sourceDirFactory); + + // Publish two revisions without update, handler should be upgraded to latest + replicator.Publish(CreateRevision(3)); + replicator.Publish(CreateRevision(4)); + client.UpdateNow(); + } + + [Test] + public void TestUpdateThread() + { + client.StartUpdateThread(10, "indexTaxo"); + + replicator.Publish(CreateRevision(1)); + AssertHandlerRevision(1, handlerIndexDir); + + replicator.Publish(CreateRevision(2)); + AssertHandlerRevision(2, handlerIndexDir); + + // Publish two revisions without update, handler should be upgraded to latest + replicator.Publish(CreateRevision(3)); + replicator.Publish(CreateRevision(4)); + AssertHandlerRevision(4, handlerIndexDir); + } + + [Test] + public void TestRecreateTaxonomy() + { + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + + // recreate index and taxonomy + Directory newTaxo = NewDirectory(); + new DirectoryTaxonomyWriter(newTaxo).Dispose(); + publishTaxoWriter.ReplaceTaxonomy(newTaxo); + publishIndexWriter.DeleteAll(); + replicator.Publish(CreateRevision(2)); + + client.UpdateNow(); + newTaxo.Dispose(); + } + + //JAVA: /* + //JAVA: * This test verifies that the client and handler do not end up in a corrupt + //JAVA: * index if exceptions are thrown at any point during replication. Either when + //JAVA: * a client copies files from the server to the temporary space, or when the + //JAVA: * handler copies them to the index directory. + //JAVA: */ + [Test] + public void TestConsistencyOnExceptions() + { + // so the handler's index isn't empty + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + client.Dispose(); + callback.Dispose(); + + // Replicator violates write-once policy. It may be that the + // handler copies files to the index dir, then fails to copy a + // file and reverts the copy operation. On the next attempt, it + // will copy the same file again. There is nothing wrong with this + // in a real system, but it does violate write-once, and MDW + // doesn't like it. Disabling it means that we won't catch cases + // where the handler overwrites an existing index file, but + // there's nothing currently we can do about it, unless we don't + // use MDW. + handlerIndexDir.PreventDoubleWrite=(false); + handlerTaxoDir.PreventDoubleWrite = (false); + + // wrap sourceDirFactory to return a MockDirWrapper so we can simulate errors + ISourceDirectoryFactory @in = sourceDirFactory; + AtomicInt32 failures = new AtomicInt32(AtLeast(10)); + + sourceDirFactory = new SourceDirectoryFactoryAnonymousInnerClass(this, @in, failures); + handler = new IndexAndTaxonomyReplicationHandler(handlerIndexDir, handlerTaxoDir, () => + { + if (Random().NextDouble() < 0.2 && failures.Get() > 0) + throw new Exception("random exception from callback"); + return null; + }); + client = new ReplicationClientAnonymousInnerClass(this, replicator, handler, @in, failures); + client.StartUpdateThread(10, "indexAndTaxo"); + + Directory baseHandlerIndexDir = handlerIndexDir.Delegate; + int numRevisions = AtLeast(20) + 2; + for (int i = 2; i < numRevisions; i++) + { + replicator.Publish(CreateRevision(i)); + AssertHandlerRevision(i, baseHandlerIndexDir); + } + + // disable errors -- maybe randomness didn't exhaust all allowed failures, + // and we don't want e.g. CheckIndex to hit false errors. + handlerIndexDir.MaxSizeInBytes=(0); + handlerIndexDir.RandomIOExceptionRate=(0.0); + handlerIndexDir.RandomIOExceptionRateOnOpen=(0.0); + handlerTaxoDir.MaxSizeInBytes=(0); + handlerTaxoDir.RandomIOExceptionRate=(0.0); + handlerTaxoDir.RandomIOExceptionRateOnOpen=(0.0); + } + + private class SourceDirectoryFactoryAnonymousInnerClass : ISourceDirectoryFactory + { + private long clientMaxSize = 100, handlerIndexMaxSize = 100, handlerTaxoMaxSize = 100; + private double clientExRate = 1.0, handlerIndexExRate = 1.0, handlerTaxoExRate = 1.0; + + private readonly IndexAndTaxonomyReplicationClientTest test; + private readonly ISourceDirectoryFactory @in; + private readonly AtomicInt32 failures; + + public SourceDirectoryFactoryAnonymousInnerClass(IndexAndTaxonomyReplicationClientTest test, ISourceDirectoryFactory @in, AtomicInt32 failures) + { + this.test = test; + this.@in = @in; + this.failures = failures; + } + + public void CleanupSession(string sessionId) + { + @in.CleanupSession(sessionId); + } + + public Directory GetDirectory(string sessionId, string source) + { + Directory dir = @in.GetDirectory(sessionId, source); + if (Random().nextBoolean() && failures.Get() > 0) + { // client should fail, return wrapped dir + MockDirectoryWrapper mdw = new MockDirectoryWrapper(Random(), dir); + mdw.RandomIOExceptionRateOnOpen = clientExRate; + mdw.MaxSizeInBytes = clientMaxSize; + mdw.RandomIOExceptionRate = clientExRate; + mdw.CheckIndexOnClose = false; + clientMaxSize *= 2; + clientExRate /= 2; + return mdw; + } + + if (failures.Get() > 0 && Random().nextBoolean()) + { // handler should fail + if (Random().nextBoolean()) + { // index dir fail + test.handlerIndexDir.MaxSizeInBytes=(handlerIndexMaxSize); + test.handlerIndexDir.RandomIOExceptionRate = (handlerIndexExRate); + test.handlerIndexDir.RandomIOExceptionRateOnOpen = (handlerIndexExRate); + handlerIndexMaxSize *= 2; + handlerIndexExRate /= 2; + } + else + { // taxo dir fail + test.handlerTaxoDir.MaxSizeInBytes = (handlerTaxoMaxSize); + test.handlerTaxoDir.RandomIOExceptionRate = (handlerTaxoExRate); + test.handlerTaxoDir.RandomIOExceptionRateOnOpen = (handlerTaxoExRate); + test.handlerTaxoDir.CheckIndexOnClose = (false); + handlerTaxoMaxSize *= 2; + handlerTaxoExRate /= 2; + } + } + else + { + // disable all errors + test.handlerIndexDir.MaxSizeInBytes = (0); + test.handlerIndexDir.RandomIOExceptionRate = (0.0); + test.handlerIndexDir.RandomIOExceptionRateOnOpen = (0.0); + test.handlerTaxoDir.MaxSizeInBytes = (0); + test.handlerTaxoDir.RandomIOExceptionRate = (0.0); + test.handlerTaxoDir.RandomIOExceptionRateOnOpen = (0.0); + } + return dir; + } + } + + + + private class ReplicationClientAnonymousInnerClass : ReplicationClient + { + private readonly IndexAndTaxonomyReplicationClientTest test; + private readonly AtomicInt32 failures; + + public ReplicationClientAnonymousInnerClass(IndexAndTaxonomyReplicationClientTest test, IReplicator replicator, IReplicationHandler handler, ISourceDirectoryFactory factory, AtomicInt32 failures) + : base(replicator, handler, factory) + { + this.test = test; + this.failures = failures; + } + + protected override void HandleUpdateException(Exception exception) + { + if (exception is IOException) + { + try + { + if (VERBOSE) + { + Console.WriteLine("hit exception during update: " + exception); + } + + // test that the index can be read and also some basic statistics + DirectoryReader reader = DirectoryReader.Open(test.handlerIndexDir.Delegate); + try + { + int numDocs = reader.NumDocs; + int version = int.Parse(reader.IndexCommit.UserData[VERSION_ID], NumberStyles.HexNumber); + assertEquals(numDocs, version); + } + finally + { + reader.Dispose(); + } + // verify index consistency + TestUtil.CheckIndex(test.handlerIndexDir.Delegate); + + // verify taxonomy index is fully consistent (since we only add one + // category to all documents, there's nothing much more to validate + TestUtil.CheckIndex(test.handlerTaxoDir.Delegate); + } + //TODO: Java had this, but considering what it does do we need it? + //JAVA: catch (IOException e) + //JAVA: { + //JAVA: throw new RuntimeException(e); + //JAVA: } + finally + { + // count-down number of failures + failures.DecrementAndGet(); + Debug.Assert(failures.Get() >= 0, "handler failed too many times: " + failures.Get()); + if (VERBOSE) + { + if (failures.Get() == 0) + { + Console.WriteLine("no more failures expected"); + } + else + { + Console.WriteLine("num failures left: " + failures.Get()); + } + } + } + } + else + { + //JAVA: if (t instanceof RuntimeException) throw (RuntimeException) t; + //JAVA: throw new RuntimeException(t); + throw exception; + } + } + } + + } +}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6da4dd20/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyRevisionTest.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyRevisionTest.cs b/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyRevisionTest.cs new file mode 100644 index 0000000..dd20864 --- /dev/null +++ b/src/Lucene.Net.Tests.Replicator/IndexAndTaxonomyRevisionTest.cs @@ -0,0 +1,188 @@ +//STATUS: DRAFT - 4.8.0 + +using System; +using System.Collections.Generic; +using System.Linq; +using Lucene.Net.Documents; +using Lucene.Net.Facet; +using Lucene.Net.Facet.Taxonomy; +using Lucene.Net.Index; +using Lucene.Net.Replicator; +using Lucene.Net.Store; +using Lucene.Net.Util; +using NUnit.Framework; + +namespace Lucene.Net.Tests.Replicator +{ + /* + * 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 IndexAndTaxonomyRevisionTest : ReplicatorTestCase + { + private Document NewDocument(ITaxonomyWriter taxoWriter) + { + FacetsConfig config = new FacetsConfig(); + Document doc = new Document(); + doc.Add(new FacetField("A", "1")); + return config.Build(taxoWriter, doc); + } + + [Test] + public void TestNoCommit() + { + Directory indexDir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter indexWriter = new IndexWriter(indexDir, conf); + + Directory taxoDir = NewDirectory(); + IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter taxoWriter = new IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter(taxoDir); + try + { + assertNotNull(new IndexAndTaxonomyRevision(indexWriter, taxoWriter)); + fail("should have failed when there are no commits to snapshot"); + } + catch (System.InvalidOperationException) + { + // expected + } + finally + { + IOUtils.Dispose(indexWriter, taxoWriter, taxoDir, indexDir); + } + } + + [Test] + public void TestRevisionRelease() + { + Directory indexDir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter indexWriter = new IndexWriter(indexDir, conf); + + Directory taxoDir = NewDirectory(); + IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter taxoWriter = new IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter(taxoDir); + try + { + indexWriter.AddDocument(NewDocument(taxoWriter)); + indexWriter.Commit(); + taxoWriter.Commit(); + IRevision rev1 = new IndexAndTaxonomyRevision(indexWriter, taxoWriter); + // releasing that revision should not delete the files + rev1.Release(); + assertTrue(SlowFileExists(indexDir, IndexFileNames.SEGMENTS + "_1")); + assertTrue(SlowFileExists(taxoDir, IndexFileNames.SEGMENTS + "_1")); + + rev1 = new IndexAndTaxonomyRevision(indexWriter, taxoWriter); // create revision again, so the files are snapshotted + indexWriter.AddDocument(NewDocument(taxoWriter)); + indexWriter.Commit(); + taxoWriter.Commit(); + assertNotNull(new IndexAndTaxonomyRevision(indexWriter, taxoWriter)); + rev1.Release(); // this release should trigger the delete of segments_1 + assertFalse(SlowFileExists(indexDir, IndexFileNames.SEGMENTS + "_1")); + } + finally + { + IOUtils.Dispose(indexWriter, taxoWriter, taxoDir, indexDir); + } + } + + [Test] + public void TestSegmentsFileLast() + { + Directory indexDir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter indexWriter = new IndexWriter(indexDir, conf); + + Directory taxoDir = NewDirectory(); + IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter taxoWriter = new IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter(taxoDir); + try + { + indexWriter.AddDocument(NewDocument(taxoWriter)); + indexWriter.Commit(); + taxoWriter.Commit(); + IRevision rev = new IndexAndTaxonomyRevision(indexWriter, taxoWriter); + var sourceFiles = rev.SourceFiles; + assertEquals(2, sourceFiles.Count); + foreach (var files in sourceFiles.Values) + { + string lastFile = files.Last().FileName; + assertTrue(lastFile.StartsWith(IndexFileNames.SEGMENTS, StringComparison.Ordinal) && !lastFile.Equals(IndexFileNames.SEGMENTS_GEN)); + } + } + finally + { + IOUtils.Dispose(indexWriter, taxoWriter, taxoDir, indexDir); + } + } + + [Test] + public void TestOpen() + { + Directory indexDir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter indexWriter = new IndexWriter(indexDir, conf); + + Directory taxoDir = NewDirectory(); + IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter taxoWriter = new IndexAndTaxonomyRevision.SnapshotDirectoryTaxonomyWriter(taxoDir); + try + { + indexWriter.AddDocument(NewDocument(taxoWriter)); + indexWriter.Commit(); + taxoWriter.Commit(); + IRevision rev = new IndexAndTaxonomyRevision(indexWriter, taxoWriter); + foreach (var e in rev.SourceFiles) + { + string source = e.Key; + Directory dir = source.Equals(IndexAndTaxonomyRevision.INDEX_SOURCE) ? indexDir : taxoDir; + foreach (RevisionFile file in e.Value) + { + IndexInput src = dir.OpenInput(file.FileName, IOContext.READ_ONCE); + System.IO.Stream @in = rev.Open(source, file.FileName); + assertEquals(src.Length, @in.Length); + byte[] srcBytes = new byte[(int)src.Length]; + byte[] inBytes = new byte[(int)src.Length]; + int offset = 0; + if (Random().nextBoolean()) + { + int skip = Random().Next(10); + if (skip >= src.Length) + { + skip = 0; + } + //JAVA: in.skip(skip); + byte[] skips = new byte[skip]; + @in.Read(skips, 0, skip); + src.Seek(skip); + offset = skip; + } + src.ReadBytes(srcBytes, offset, srcBytes.Length - offset); + @in.Read(inBytes, offset, inBytes.Length - offset); + assertArrayEquals(srcBytes, inBytes); + IOUtils.Dispose(src, @in); + } + } + } + finally + { + IOUtils.Dispose(indexWriter, taxoWriter, taxoDir, indexDir); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6da4dd20/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs b/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs new file mode 100644 index 0000000..6a56c77 --- /dev/null +++ b/src/Lucene.Net.Tests.Replicator/IndexReplicationClientTest.cs @@ -0,0 +1,513 @@ +//STATUS: DRAFT - 4.8.0 + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Threading; +using Lucene.Net.Documents; +using Lucene.Net.Index; +using Lucene.Net.Replicator; +using Lucene.Net.Store; +using Lucene.Net.Support; +using Lucene.Net.Util; +using NUnit.Framework; +using Directory = Lucene.Net.Store.Directory; + +namespace Lucene.Net.Tests.Replicator +{ + [TestFixture] + public class IndexReplicationClientTest : ReplicatorTestCase + { + private class IndexReadyCallback : IDisposable + { + private readonly Directory indexDir; + private DirectoryReader reader; + private long lastGeneration = -1; + + public IndexReadyCallback(Directory indexDir) + { + //JAVA: public IndexReadyCallback(Directory indexDir) throws IOException { + //JAVA: this.indexDir = indexDir; + //JAVA: if (DirectoryReader.indexExists(indexDir)) { + //JAVA: reader = DirectoryReader.open(indexDir); + //JAVA: lastGeneration = reader.getIndexCommit().getGeneration(); + //JAVA: } + //JAVA: } + + this.indexDir = indexDir; + if (DirectoryReader.IndexExists(indexDir)) + { + reader = DirectoryReader.Open(indexDir); + lastGeneration = reader.IndexCommit.Generation; + } + } + + public bool? Call() + { + //JAVA: public Boolean call() throws Exception { + //JAVA: if (reader == null) { + //JAVA: reader = DirectoryReader.open(indexDir); + //JAVA: lastGeneration = reader.getIndexCommit().getGeneration(); + //JAVA: } else { + //JAVA: DirectoryReader newReader = DirectoryReader.openIfChanged(reader); + //JAVA: assertNotNull("should not have reached here if no changes were made to the index", newReader); + //JAVA: long newGeneration = newReader.getIndexCommit().getGeneration(); + //JAVA: assertTrue("expected newer generation; current=" + lastGeneration + " new=" + newGeneration, newGeneration > lastGeneration); + //JAVA: reader.close(); + //JAVA: reader = newReader; + //JAVA: lastGeneration = newGeneration; + //JAVA: TestUtil.checkIndex(indexDir); + //JAVA: } + //JAVA: return null; + //JAVA: } + if (reader == null) + { + reader = DirectoryReader.Open(indexDir); + lastGeneration = reader.IndexCommit.Generation; + } + else + { + DirectoryReader newReader = DirectoryReader.OpenIfChanged(reader); + assertNotNull("should not have reached here if no changes were made to the index", newReader); + long newGeneration = newReader.IndexCommit.Generation; + assertTrue("expected newer generation; current=" + lastGeneration + " new=" + newGeneration, newGeneration > lastGeneration); + reader.Dispose(); + reader = newReader; + lastGeneration = newGeneration; + TestUtil.CheckIndex(indexDir); + } + return null; + } + public void Dispose() + { + IOUtils.Dispose(reader); + } + } + + + private MockDirectoryWrapper publishDir, handlerDir; + private IReplicator replicator; + private ISourceDirectoryFactory sourceDirFactory; + private ReplicationClient client; + private IReplicationHandler handler; + private IndexWriter publishWriter; + private IndexReadyCallback callback; + //JAVA: private IndexReadyCallback callback; + + private const string VERSION_ID = "version"; + + private void AssertHandlerRevision(int expectedId, Directory dir) + { + //JAVA: private void assertHandlerRevision(int expectedID, Directory dir) throws IOException { + //JAVA: // loop as long as client is alive. test-framework will terminate us if + //JAVA: // there's a serious bug, e.g. client doesn't really update. otherwise, + //JAVA: // introducing timeouts is not good, can easily lead to false positives. + //JAVA: while (client.isUpdateThreadAlive()) { + //JAVA: // give client a chance to update + //JAVA: try { + //JAVA: Thread.sleep(100); + //JAVA: } catch (InterruptedException e) { + //JAVA: throw new ThreadInterruptedException(e); + //JAVA: } + //JAVA: + //JAVA: try { + //JAVA: DirectoryReader reader = DirectoryReader.open(dir); + //JAVA: try { + //JAVA: int handlerID = Integer.parseInt(reader.getIndexCommit().getUserData().get(VERSION_ID), 16); + //JAVA: if (expectedID == handlerID) { + //JAVA: return; + //JAVA: } else if (VERBOSE) { + //JAVA: System.out.println("expectedID=" + expectedID + " actual=" + handlerID + " generation=" + reader.getIndexCommit().getGeneration()); + //JAVA: } + //JAVA: } finally { + //JAVA: reader.close(); + //JAVA: } + //JAVA: } catch (Exception e) { + //JAVA: // we can hit IndexNotFoundException or e.g. EOFException (on + //JAVA: // segments_N) because it is being copied at the same time it is read by + //JAVA: // DirectoryReader.open(). + //JAVA: } + //JAVA: } + //JAVA: } + + // loop as long as client is alive. test-framework will terminate us if + // there's a serious bug, e.g. client doesn't really update. otherwise, + // introducing timeouts is not good, can easily lead to false positives. + while (client.IsUpdateThreadAlive) + { + // give client a chance to update + Thread.Sleep(100); + try + { + DirectoryReader reader = DirectoryReader.Open(dir); + try + { + int handlerId = int.Parse(reader.IndexCommit.UserData[VERSION_ID], NumberStyles.HexNumber); + if (expectedId == handlerId) + { + return; + } + else if (VERBOSE) + { + Console.WriteLine("expectedID=" + expectedId + " actual=" + handlerId + " generation=" + reader.IndexCommit.Generation); + } + } + finally + { + reader.Dispose(); + } + } + catch (Exception) + { + // we can hit IndexNotFoundException or e.g. EOFException (on + // segments_N) because it is being copied at the same time it is read by + // DirectoryReader.open(). + } + } + } + + private IRevision CreateRevision(int id) + { + //JAVA: private Revision createRevision(final int id) throws IOException { + //JAVA: publishWriter.addDocument(new Document()); + //JAVA: publishWriter.setCommitData(new HashMap<String, String>() {{ + //JAVA: put(VERSION_ID, Integer.toString(id, 16)); + //JAVA: }}); + //JAVA: publishWriter.commit(); + //JAVA: return new IndexRevision(publishWriter); + //JAVA: } + publishWriter.AddDocument(new Document()); + publishWriter.SetCommitData(new Dictionary<string, string>{ + { VERSION_ID, id.ToString("X") } + }); + publishWriter.Commit(); + return new IndexRevision(publishWriter); + } + + public override void SetUp() + { + //JAVA: public void setUp() throws Exception { + //JAVA: super.setUp(); + //JAVA: publishDir = newMockDirectory(); + //JAVA: handlerDir = newMockDirectory(); + //JAVA: sourceDirFactory = new PerSessionDirectoryFactory(createTempDir("replicationClientTest")); + //JAVA: replicator = new LocalReplicator(); + //JAVA: callback = new IndexReadyCallback(handlerDir); + //JAVA: handler = new IndexReplicationHandler(handlerDir, callback); + //JAVA: client = new ReplicationClient(replicator, handler, sourceDirFactory); + //JAVA: + //JAVA: IndexWriterConfig conf = newIndexWriterConfig(TEST_VERSION_CURRENT, null); + //JAVA: conf.setIndexDeletionPolicy(new SnapshotDeletionPolicy(conf.getIndexDeletionPolicy())); + //JAVA: publishWriter = new IndexWriter(publishDir, conf); + //JAVA: } + base.SetUp(); + + publishDir = NewMockDirectory(); + handlerDir = NewMockDirectory(); + sourceDirFactory = new PerSessionDirectoryFactory(CreateTempDir("replicationClientTest").FullName); + replicator = new LocalReplicator(); + callback = new IndexReadyCallback(handlerDir); + handler = new IndexReplicationHandler(handlerDir, callback.Call); + client = new ReplicationClient(replicator, handler, sourceDirFactory); + + IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + publishWriter = new IndexWriter(publishDir, conf); + } + + public override void TearDown() + { + //JAVA: public void tearDown() throws Exception { + //JAVA: IOUtils.close(client, callback, publishWriter, replicator, publishDir, handlerDir); + //JAVA: super.tearDown(); + //JAVA: } + IOUtils.Dispose(client, callback, publishWriter, replicator, publishDir, handlerDir); + base.TearDown(); + } + + [Test] + public void TestNoUpdateThread() + { + //JAVA: public void testNoUpdateThread() throws Exception { + //JAVA: assertNull("no version expected at start", handler.currentVersion()); + //JAVA: + //JAVA: // Callback validates the replicated index + //JAVA: replicator.publish(createRevision(1)); + //JAVA: client.updateNow(); + //JAVA: + //JAVA: replicator.publish(createRevision(2)); + //JAVA: client.updateNow(); + //JAVA: + //JAVA: // Publish two revisions without update, handler should be upgraded to latest + //JAVA: replicator.publish(createRevision(3)); + //JAVA: replicator.publish(createRevision(4)); + //JAVA: client.updateNow(); + //JAVA: } + assertNull("no version expected at start", handler.CurrentVersion); + + // Callback validates the replicated ind + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + + replicator.Publish(CreateRevision(2)); + client.UpdateNow(); + + // Publish two revisions without update, + replicator.Publish(CreateRevision(3)); + replicator.Publish(CreateRevision(4)); + client.UpdateNow(); + } + + + [Test] + public void TestUpdateThread() + { + //JAVA: public void testUpdateThread() throws Exception { + //JAVA: client.startUpdateThread(10, "index"); + //JAVA: + //JAVA: replicator.publish(createRevision(1)); + //JAVA: assertHandlerRevision(1, handlerDir); + //JAVA: + //JAVA: replicator.publish(createRevision(2)); + //JAVA: assertHandlerRevision(2, handlerDir); + //JAVA: + //JAVA: // Publish two revisions without update, handler should be upgraded to latest + //JAVA: replicator.publish(createRevision(3)); + //JAVA: replicator.publish(createRevision(4)); + //JAVA: assertHandlerRevision(4, handlerDir); + //JAVA: } + + client.StartUpdateThread(10, "index"); + + replicator.Publish(CreateRevision(1)); + AssertHandlerRevision(1, handlerDir); + + replicator.Publish(CreateRevision(2)); + AssertHandlerRevision(2, handlerDir); + + // Publish two revisions without update, handler should be upgraded to latest + replicator.Publish(CreateRevision(3)); + replicator.Publish(CreateRevision(4)); + AssertHandlerRevision(4, handlerDir); + } + + [Test] + public void TestRestart() + { + //JAVA: public void testRestart() throws Exception { + //JAVA: replicator.publish(createRevision(1)); + //JAVA: client.updateNow(); + //JAVA: + //JAVA: replicator.publish(createRevision(2)); + //JAVA: client.updateNow(); + //JAVA: + //JAVA: client.stopUpdateThread(); + //JAVA: client.close(); + //JAVA: client = new ReplicationClient(replicator, handler, sourceDirFactory); + //JAVA: + //JAVA: // Publish two revisions without update, handler should be upgraded to latest + //JAVA: replicator.publish(createRevision(3)); + //JAVA: replicator.publish(createRevision(4)); + //JAVA: client.updateNow(); + //JAVA: } + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + + replicator.Publish(CreateRevision(2)); + client.UpdateNow(); + + client.StopUpdateThread(); + client.Dispose(); + client = new ReplicationClient(replicator, handler, sourceDirFactory); + + // Publish two revisions without update, handler should be upgraded to latest + replicator.Publish(CreateRevision(3)); + replicator.Publish(CreateRevision(4)); + client.UpdateNow(); + } + + //JAVA: /* + //JAVA: * This test verifies that the client and handler do not end up in a corrupt + //JAVA: * index if exceptions are thrown at any point during replication. Either when + //JAVA: * a client copies files from the server to the temporary space, or when the + //JAVA: * handler copies them to the index directory. + //JAVA: */ + [Test] + public void TestConsistencyOnExceptions() + { + // so the handler's index isn't empty + replicator.Publish(CreateRevision(1)); + client.UpdateNow(); + client.Dispose(); + callback.Dispose(); + + // Replicator violates write-once policy. It may be that the + // handler copies files to the index dir, then fails to copy a + // file and reverts the copy operation. On the next attempt, it + // will copy the same file again. There is nothing wrong with this + // in a real system, but it does violate write-once, and MDW + // doesn't like it. Disabling it means that we won't catch cases + // where the handler overwrites an existing index file, but + // there's nothing currently we can do about it, unless we don't + // use MDW. + //JAVA: handlerDir.setPreventDoubleWrite(false); + handlerDir.PreventDoubleWrite = false; + + // wrap sourceDirFactory to return a MockDirWrapper so we can simulate errors + ISourceDirectoryFactory @in = sourceDirFactory; + AtomicInt32 failures = new AtomicInt32(AtLeast(10)); + + // wrap sourceDirFactory to return a MockDirWrapper so we can simulate errors + sourceDirFactory = new SourceDirectoryFactoryAnonymousInnerClass(this, @in, failures); + handler = new IndexReplicationHandler(handlerDir, () => + { + if (Random().NextDouble() < 0.2 && failures.Get() > 0) + throw new Exception("random exception from callback"); + return null; + }); + client = new ReplicationClientAnonymousInnerClass(this, replicator, handler, sourceDirFactory, failures); + client.StartUpdateThread(10, "index"); + + Directory baseHandlerDir = handlerDir.Delegate; + int numRevisions = AtLeast(20); + for (int i = 2; i < numRevisions; i++) + { + replicator.Publish(CreateRevision(i)); + AssertHandlerRevision(i, baseHandlerDir); + } + + // disable errors -- maybe randomness didn't exhaust all allowed failures, + // and we don't want e.g. CheckIndex to hit false errors. + handlerDir.MaxSizeInBytes = 0; + handlerDir.RandomIOExceptionRate=0.0; + handlerDir.RandomIOExceptionRateOnOpen=0.0; + } + + private class SourceDirectoryFactoryAnonymousInnerClass : ISourceDirectoryFactory + { + private long clientMaxSize = 100, handlerMaxSize = 100; + private double clientExRate = 1.0, handlerExRate = 1.0; + + private readonly IndexReplicationClientTest test; + private readonly ISourceDirectoryFactory @in; + private readonly AtomicInt32 failures; + + public SourceDirectoryFactoryAnonymousInnerClass(IndexReplicationClientTest test, ISourceDirectoryFactory @in, AtomicInt32 failures) + { + this.test = test; + this.@in = @in; + this.failures = failures; + } + + public void CleanupSession(string sessionId) + { + @in.CleanupSession(sessionId); + } + + public Directory GetDirectory(string sessionId, string source) + { + Directory dir = @in.GetDirectory(sessionId, source); + if (Random().nextBoolean() && failures.Get() > 0) + { // client should fail, return wrapped dir + MockDirectoryWrapper mdw = new MockDirectoryWrapper(Random(), dir); + mdw.RandomIOExceptionRateOnOpen = clientExRate; + mdw.MaxSizeInBytes = clientMaxSize; + mdw.RandomIOExceptionRate = clientExRate; + mdw.CheckIndexOnClose = false; + clientMaxSize *= 2; + clientExRate /= 2; + return mdw; + } + + if (failures.Get() > 0 && Random().nextBoolean()) + { // handler should fail + test.handlerDir.MaxSizeInBytes = handlerMaxSize; + test.handlerDir.RandomIOExceptionRateOnOpen = handlerExRate; + test.handlerDir.RandomIOExceptionRate = handlerExRate; + handlerMaxSize *= 2; + handlerExRate /= 2; + } + else + { + // disable errors + test.handlerDir.MaxSizeInBytes = 0; + test.handlerDir.RandomIOExceptionRate = 0; + test.handlerDir.RandomIOExceptionRateOnOpen = 0.0; + } + return dir; + } + } + + private class ReplicationClientAnonymousInnerClass : ReplicationClient + { + private readonly IndexReplicationClientTest test; + private readonly AtomicInt32 failures; + + public ReplicationClientAnonymousInnerClass(IndexReplicationClientTest test, IReplicator replicator, IReplicationHandler handler, ISourceDirectoryFactory factory, AtomicInt32 failures) + : base(replicator, handler, factory) + { + this.test = test; + this.failures = failures; + } + + protected override void HandleUpdateException(Exception exception) + { + if (exception is IOException) + { + if (VERBOSE) + { + Console.WriteLine("hit exception during update: " + exception); + } + + try + { + // test that the index can be read and also some basic statistics + DirectoryReader reader = DirectoryReader.Open(test.handlerDir.Delegate); + try + { + int numDocs = reader.NumDocs; + int version = int.Parse(reader.IndexCommit.UserData[VERSION_ID], NumberStyles.HexNumber); + assertEquals(numDocs, version); + } + finally + { + reader.Dispose(); + } + // verify index consistency + TestUtil.CheckIndex(test.handlerDir.Delegate); + } + //TODO: Java had this, but considering what it does do we need it? + //JAVA: catch (IOException e) + //JAVA: { + //JAVA: // exceptions here are bad, don't ignore them + //JAVA: throw new RuntimeException(e); + //JAVA: } + finally + { + // count-down number of failures + failures.DecrementAndGet(); + Debug.Assert(failures.Get() >= 0, "handler failed too many times: " + failures.Get()); + if (VERBOSE) + { + if (failures.Get() == 0) + { + Console.WriteLine("no more failures expected"); + } + else + { + Console.WriteLine("num failures left: " + failures.Get()); + } + } + } + } else { + //JAVA: if (t instanceof RuntimeException) throw (RuntimeException) t; + //JAVA: throw new RuntimeException(t); + throw exception; + } + } + } + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6da4dd20/src/Lucene.Net.Tests.Replicator/IndexRevisionTest.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests.Replicator/IndexRevisionTest.cs b/src/Lucene.Net.Tests.Replicator/IndexRevisionTest.cs new file mode 100644 index 0000000..de4dbb4 --- /dev/null +++ b/src/Lucene.Net.Tests.Replicator/IndexRevisionTest.cs @@ -0,0 +1,177 @@ +//STATUS: DRAFT - 4.8.0 + +using System; +using System.Linq; +using Lucene.Net.Documents; +using Lucene.Net.Index; +using Lucene.Net.Replicator; +using Lucene.Net.Store; +using Lucene.Net.Util; +using NUnit.Framework; + +namespace Lucene.Net.Tests.Replicator +{ + /* + * 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 IndexRevisionTest : ReplicatorTestCase + { + [Test] + public void TestNoSnapshotDeletionPolicy() + { + Directory dir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new KeepOnlyLastCommitDeletionPolicy(); + IndexWriter writer = new IndexWriter(dir, conf); + try + { + assertNotNull(new IndexRevision(writer)); + fail("should have failed when IndexDeletionPolicy is not Snapshot"); + } + catch (ArgumentException) + { + // expected + } + finally + { + IOUtils.Dispose(writer, dir); + } + } + + [Test] + public void TestNoCommit() + { + Directory dir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter writer = new IndexWriter(dir, conf); + try + { + assertNotNull(new IndexRevision(writer)); + fail("should have failed when there are no commits to snapshot"); + } + catch (InvalidOperationException) + { + // expected + } + finally + { + IOUtils.Dispose(writer, dir); + } + } + + [Test] + public void TestRevisionRelease() + { + Directory dir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter writer = new IndexWriter(dir, conf); + try + { + writer.AddDocument(new Document()); + writer.Commit(); + IRevision rev1 = new IndexRevision(writer); + // releasing that revision should not delete the files + rev1.Release(); + assertTrue(SlowFileExists(dir, IndexFileNames.SEGMENTS + "_1")); + + rev1 = new IndexRevision(writer); // create revision again, so the files are snapshotted + writer.AddDocument(new Document()); + writer.Commit(); + assertNotNull(new IndexRevision(writer)); + rev1.Release(); // this release should trigger the delete of segments_1 + assertFalse(SlowFileExists(dir, IndexFileNames.SEGMENTS + "_1")); + } + finally + { + IOUtils.Dispose(writer, dir); + } + } + + [Test] + public void TestSegmentsFileLast() + { + Directory dir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter writer = new IndexWriter(dir, conf); + try + { + writer.AddDocument(new Document()); + writer.Commit(); + IRevision rev = new IndexRevision(writer); + var sourceFiles = rev.SourceFiles; + assertEquals(1, sourceFiles.Count); + var files = sourceFiles.Values.First(); + string lastFile = files.Last().FileName; + assertTrue(lastFile.StartsWith(IndexFileNames.SEGMENTS, StringComparison.Ordinal) && !lastFile.Equals(IndexFileNames.SEGMENTS_GEN)); + } + finally + { + IOUtils.Dispose(writer, dir); + } + } + + [Test] + public void TestOpen() + { + Directory dir = NewDirectory(); + IndexWriterConfig conf = new IndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + IndexWriter writer = new IndexWriter(dir, conf); + try + { + writer.AddDocument(new Document()); + writer.Commit(); + IRevision rev = new IndexRevision(writer); + var sourceFiles = rev.SourceFiles; + string source = sourceFiles.Keys.First(); + foreach (RevisionFile file in sourceFiles.Values.First()) + { + IndexInput src = dir.OpenInput(file.FileName, IOContext.READ_ONCE); + System.IO.Stream @in = rev.Open(source, file.FileName); + assertEquals(src.Length, @in.Length); + byte[] srcBytes = new byte[(int) src.Length]; + byte[] inBytes = new byte[(int) src.Length]; + int offset = 0; + if (Random().nextBoolean()) + { + int skip = Random().Next(10); + if (skip >= src.Length) + { + skip = 0; + } + //JAVA: in.skip(skip); + byte[] skips = new byte[skip]; + @in.Read(skips, 0, skip); + src.Seek(skip); + offset = skip; + } + src.ReadBytes(srcBytes, offset, srcBytes.Length - offset); + @in.Read(inBytes, offset, inBytes.Length - offset); + assertArrayEquals(srcBytes, inBytes); + IOUtils.Dispose(src, @in); + } + } + finally + { + IOUtils.Dispose(writer, dir); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6da4dd20/src/Lucene.Net.Tests.Replicator/LocalReplicatorTest.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests.Replicator/LocalReplicatorTest.cs b/src/Lucene.Net.Tests.Replicator/LocalReplicatorTest.cs new file mode 100644 index 0000000..9946457 --- /dev/null +++ b/src/Lucene.Net.Tests.Replicator/LocalReplicatorTest.cs @@ -0,0 +1,225 @@ +//STATUS: DRAFT - 4.8.0 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using Lucene.Net.Documents; +using Lucene.Net.Index; +using Lucene.Net.Replicator; +using Lucene.Net.Support; +using Lucene.Net.Support.C5; +using Lucene.Net.Util; +using NUnit.Framework; +using Directory = Lucene.Net.Store.Directory; + +namespace Lucene.Net.Tests.Replicator +{ + /* + * 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 LocalReplicatorTest : ReplicatorTestCase + { + private const string VERSION_ID = "version"; + + private LocalReplicator replicator; + private Directory sourceDirectory; + private IndexWriter sourceWriter; + + public override void SetUp() + { + base.SetUp(); + + sourceDirectory = NewDirectory(); + IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, null); + conf.IndexDeletionPolicy = new SnapshotDeletionPolicy(conf.IndexDeletionPolicy); + sourceWriter = new IndexWriter(sourceDirectory, conf); + replicator = new LocalReplicator(); + } + + public override void TearDown() + { + IOUtils.Dispose(replicator, sourceWriter, sourceDirectory); + base.TearDown(); + } + + private IRevision CreateRevision(int id) + { + sourceWriter.AddDocument(new Document()); + //JAVA: sourceWriter.setCommitData(new HashMap<String, String>() {{ + //JAVA: put(VERSION_ID, Integer.toString(id, 16)); + //JAVA: } + sourceWriter.SetCommitData(new Dictionary<string, string> { + { VERSION_ID, id.ToString() } + }); + sourceWriter.Commit(); + return new IndexRevision(sourceWriter); + } + + [Test] + public void TestCheckForUpdateNoRevisions() + { + assertNull(replicator.CheckForUpdate(null)); + } + + [Test] + public void TestObtainFileAlreadyClosed() + { + replicator.Publish(CreateRevision(1)); + SessionToken res = replicator.CheckForUpdate(null); + assertNotNull(res); + assertEquals(1, res.SourceFiles.Count); + System.Collections.Generic.KeyValuePair<string, System.Collections.Generic.IList<RevisionFile>> entry = res.SourceFiles.First(); + replicator.Dispose(); + try + { + replicator.ObtainFile(res.Id, entry.Key, entry.Value.First().FileName); + fail("should have failed on AlreadyClosedException"); + } + catch (ObjectDisposedException) + { + // expected + } + } + + [Test] + public void TestPublishAlreadyClosed() + { + replicator.Dispose(); + try + { + replicator.Publish(CreateRevision(2)); + fail("should have failed on AlreadyClosedException"); + } + catch (ObjectDisposedException) + { + // expected + } + } + + [Test] + public void TestUpdateAlreadyClosed() + { + replicator.Dispose(); + try + { + replicator.CheckForUpdate(null); + fail("should have failed on AlreadyClosedException"); + } + catch (ObjectDisposedException) + { + // expected + } + } + + [Test] + public void TestPublishSameRevision() + { + IRevision rev = CreateRevision(1); + replicator.Publish(rev); + SessionToken res = replicator.CheckForUpdate(null); + assertNotNull(res); + assertEquals(rev.Version, res.Version); + replicator.Release(res.Id); + replicator.Publish(new IndexRevision(sourceWriter)); + res = replicator.CheckForUpdate(res.Version); + assertNull(res); + + // now make sure that publishing same revision doesn't leave revisions + // "locked", i.e. that replicator releases revisions even when they are not + // kept + replicator.Publish(CreateRevision(2)); + assertEquals(1, DirectoryReader.ListCommits(sourceDirectory).size()); + } + + [Test] + public void TestPublishOlderRev() + { + replicator.Publish(CreateRevision(1)); + IRevision old = new IndexRevision(sourceWriter); + replicator.Publish(CreateRevision(2)); + try + { + replicator.Publish(old); + fail("should have failed to publish an older revision"); + } + catch (System.ArgumentException) + { + // expected + } + assertEquals(1, DirectoryReader.ListCommits(sourceDirectory).size()); + } + + [Test] + public void TestObtainMissingFile() + { + replicator.Publish(CreateRevision(1)); + SessionToken res = replicator.CheckForUpdate(null); + try + { + replicator.ObtainFile(res.Id, res.SourceFiles.Keys.First(), "madeUpFile"); + fail("should have failed obtaining an unrecognized file"); + } + //JAVA: } catch (FileNotFoundException | NoSuchFileException e) { -> Could not find a "NoSuchFileException" ?NoSuchItemException + catch (Exception e) when (e is FileNotFoundException)//|| e is NoSuchItemException) + { + // expected + } + } + + [Test] + public void TestSessionExpiration() + { + replicator.Publish(CreateRevision(1)); + SessionToken session = replicator.CheckForUpdate(null); + replicator.ExpirationThreshold = 5; // expire quickly + Thread.Sleep(50); // sufficient for expiration + try + { + replicator.ObtainFile(session.Id, session.SourceFiles.Keys.First(), session.SourceFiles.Values.First().First().FileName); + fail("should have failed to obtain a file for an expired session"); + } + catch (SessionExpiredException) + { + // expected + } + } + + [Test] + public void TestUpdateToLatest() + { + replicator.Publish(CreateRevision(1)); + IRevision rev = CreateRevision(2); + replicator.Publish(rev); + SessionToken res = replicator.CheckForUpdate(null); + assertNotNull(res); + assertEquals(0, rev.CompareTo(res.Version)); + } + + [Test] + public void TestRevisionRelease() + { + replicator.Publish(CreateRevision(1)); + assertTrue(SlowFileExists(sourceDirectory, IndexFileNames.SEGMENTS + "_1")); + replicator.Publish(CreateRevision(2)); + // now the files of revision 1 can be deleted + assertTrue(SlowFileExists(sourceDirectory, IndexFileNames.SEGMENTS + "_2")); + assertFalse("segments_1 should not be found in index directory after revision is released", SlowFileExists(sourceDirectory, IndexFileNames.SEGMENTS + "_1")); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/lucenenet/blob/6da4dd20/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj b/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj new file mode 100644 index 0000000..60adc6e --- /dev/null +++ b/src/Lucene.Net.Tests.Replicator/Lucene.Net.Tests.Replicator.csproj @@ -0,0 +1,219 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{418E9D8E-2369-4B52-8D2F-5A987213999B}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Lucene.Net.Tests.Replicator</RootNamespace> + <AssemblyName>Lucene.Net.Tests.Replicator</AssemblyName> + <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.AspNetCore.Hosting, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Hosting.1.0.3\lib\net451\Microsoft.AspNetCore.Hosting.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Hosting.Abstractions.1.0.3\lib\net451\Microsoft.AspNetCore.Hosting.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Hosting.Server.Abstractions.1.0.3\lib\net451\Microsoft.AspNetCore.Hosting.Server.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.Http, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Http.1.0.3\lib\net451\Microsoft.AspNetCore.Http.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.Http.Abstractions, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Http.Abstractions.1.0.3\lib\net451\Microsoft.AspNetCore.Http.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.Http.Extensions, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Http.Extensions.1.0.3\lib\net451\Microsoft.AspNetCore.Http.Extensions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.Http.Features, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.Http.Features.1.0.3\lib\net451\Microsoft.AspNetCore.Http.Features.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.TestHost, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.TestHost.1.0.3\lib\net451\Microsoft.AspNetCore.TestHost.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.AspNetCore.WebUtilities, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.AspNetCore.WebUtilities.1.0.3\lib\net451\Microsoft.AspNetCore.WebUtilities.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Configuration, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Configuration.1.0.2\lib\netstandard1.1\Microsoft.Extensions.Configuration.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Configuration.Abstractions.1.0.2\lib\netstandard1.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Configuration.EnvironmentVariables, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Configuration.EnvironmentVariables.1.0.2\lib\net451\Microsoft.Extensions.Configuration.EnvironmentVariables.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.DependencyInjection, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.1.0.2\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.0.2\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.FileProviders.Abstractions, Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.FileProviders.Abstractions.1.0.1\lib\netstandard1.0\Microsoft.Extensions.FileProviders.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.FileProviders.Physical, Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.FileProviders.Physical.1.0.1\lib\net451\Microsoft.Extensions.FileProviders.Physical.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.FileSystemGlobbing, Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.FileSystemGlobbing.1.0.1\lib\net451\Microsoft.Extensions.FileSystemGlobbing.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Logging, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Logging.1.0.2\lib\netstandard1.1\Microsoft.Extensions.Logging.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.0.2\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.ObjectPool, Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.ObjectPool.1.0.1\lib\net451\Microsoft.Extensions.ObjectPool.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Options, Version=1.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Options.1.0.2\lib\netstandard1.0\Microsoft.Extensions.Options.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.PlatformAbstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.PlatformAbstractions.1.0.0\lib\net451\Microsoft.Extensions.PlatformAbstractions.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Extensions.Primitives, Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Extensions.Primitives.1.0.1\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Microsoft.Net.Http.Headers, Version=1.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Microsoft.Net.Http.Headers.1.0.3\lib\netstandard1.1\Microsoft.Net.Http.Headers.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> + <HintPath>..\..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Buffers, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Buffers.4.0.0\lib\netstandard1.1\System.Buffers.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System.Collections.Immutable, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Collections.Immutable.1.2.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System.ComponentModel.Composition" /> + <Reference Include="System.Core" /> + <Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.0.0\lib\portable-net45+win8+wpa81\System.Diagnostics.DiagnosticSource.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System.Reflection.Metadata, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Reflection.Metadata.1.3.0\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System.Text.Encodings.Web, Version=4.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\..\packages\System.Text.Encodings.Web.4.0.1\lib\netstandard1.0\System.Text.Encodings.Web.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="..\CommonAssemblyInfo.cs"> + <Link>Properties\CommonAssemblyInfo.cs</Link> + </Compile> + <Compile Include="Http\HttpReplicatorTest.cs" /> + <Compile Include="Http\ReplicationServlet.cs" /> + <Compile Include="IndexAndTaxonomyReplicationClientTest.cs" /> + <Compile Include="IndexAndTaxonomyRevisionTest.cs" /> + <Compile Include="IndexReplicationClientTest.cs" /> + <Compile Include="IndexRevisionTest.cs" /> + <Compile Include="LocalReplicatorTest.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="ReplicatorTestCase.cs" /> + <Compile Include="SessionTokenTest.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Lucene.Net.Facet\Lucene.Net.Facet.csproj"> + <Project>{48F7884A-9454-4E88-8413-9D35992CB440}</Project> + <Name>Lucene.Net.Facet</Name> + </ProjectReference> + <ProjectReference Include="..\Lucene.Net.Replicator.AspNetCore\Lucene.Net.Replicator.AspNetCore.csproj"> + <Project>{763ccb5a-e397-456a-af47-7c6e228b1852}</Project> + <Name>Lucene.Net.Replicator.AspNetCore</Name> + </ProjectReference> + <ProjectReference Include="..\Lucene.Net.Replicator\Lucene.Net.Replicator.csproj"> + <Project>{1F70D2DB-C1B3-4F78-9598-3E04E0C7EB06}</Project> + <Name>Lucene.Net.Replicator</Name> + </ProjectReference> + <ProjectReference Include="..\Lucene.Net.TestFramework\Lucene.Net.TestFramework.csproj"> + <Project>{B2C0D749-CE34-4F62-A15E-00CB2FF5DDB3}</Project> + <Name>Lucene.Net.TestFramework</Name> + </ProjectReference> + <ProjectReference Include="..\Lucene.Net\Lucene.Net.csproj"> + <Project>{5D4AD9BE-1FFB-41AB-9943-25737971BF57}</Project> + <Name>Lucene.Net</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file
