blob: e6228ef621115b4af4d5753fb456a3a5206074cc [file] [log] [blame]
using Lucene.Net.Attributes;
using Lucene.Net.Documents;
using Lucene.Net.Index.Extensions;
using NUnit.Framework;
using System;
using System.IO;
using System.Text;
using Assert = Lucene.Net.TestFramework.Assert;
namespace Lucene.Net.Store
{
/*
* 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 BytesRef = Lucene.Net.Util.BytesRef;
using Document = Documents.Document;
using Field = Field;
using IndexInputSlicer = Lucene.Net.Store.Directory.IndexInputSlicer;
using IndexReader = Lucene.Net.Index.IndexReader;
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
using TestUtil = Lucene.Net.Util.TestUtil;
/// <summary>
/// Tests MMapDirectory's MultiMMapIndexInput
/// <p>
/// Because Java's ByteBuffer uses an int to address the
/// values, it's necessary to access a file >
/// Integer.MAX_VALUE in size using multiple byte buffers.
/// </summary>
[TestFixture]
public class TestMultiMMap : LuceneTestCase
{
[SetUp]
public override void SetUp()
{
base.SetUp();
// LUCENENET NOTE:
// Java seems to have issues releasing memory mapped resources when calling close()
// http://stackoverflow.com/a/2973059/181087
// However, according to MSDN, the Dispose() method of the MemoryMappedFile class will "release all resources".
// https://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile(v=vs.110).aspx
// Therefore, I am assuming removing the below line is the correct choice for .NET.
//AssumeTrue("test requires a jre that supports unmapping", MMapDirectory.UNMAP_SUPPORTED);
}
[Test]
public virtual void TestCloneSafety()
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testCloneSafety"));
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
io.WriteVInt32(5);
io.Dispose();
IndexInput one = mmapDir.OpenInput("bytes", IOContext.DEFAULT);
IndexInput two = (IndexInput)one.Clone();
IndexInput three = (IndexInput)two.Clone(); // clone of clone
one.Dispose();
try
{
one.ReadVInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
try
{
two.ReadVInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
try
{
three.ReadVInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
two.Dispose();
three.Dispose();
// test double close of master:
one.Dispose();
mmapDir.Dispose();
}
[Test]
public virtual void TestCloneClose()
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testCloneClose"));
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
io.WriteVInt32(5);
io.Dispose();
IndexInput one = mmapDir.OpenInput("bytes", IOContext.DEFAULT);
IndexInput two = (IndexInput)one.Clone();
IndexInput three = (IndexInput)two.Clone(); // clone of clone
two.Dispose();
Assert.AreEqual(5, one.ReadVInt32());
try
{
two.ReadVInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
Assert.AreEqual(5, three.ReadVInt32());
one.Dispose();
three.Dispose();
mmapDir.Dispose();
}
[Test]
public virtual void TestCloneSliceSafety()
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testCloneSliceSafety"));
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
io.WriteInt32(1);
io.WriteInt32(2);
io.Dispose();
IndexInputSlicer slicer = mmapDir.CreateSlicer("bytes", NewIOContext(Random));
IndexInput one = slicer.OpenSlice("first int", 0, 4);
IndexInput two = slicer.OpenSlice("second int", 4, 4);
IndexInput three = (IndexInput)one.Clone(); // clone of clone
IndexInput four = (IndexInput)two.Clone(); // clone of clone
slicer.Dispose();
try
{
one.ReadInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
try
{
two.ReadInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
try
{
three.ReadInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
try
{
four.ReadInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
one.Dispose();
two.Dispose();
three.Dispose();
four.Dispose();
// test double-close of slicer:
slicer.Dispose();
mmapDir.Dispose();
}
[Test]
public virtual void TestCloneSliceClose()
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testCloneSliceClose"));
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
io.WriteInt32(1);
io.WriteInt32(2);
io.Dispose();
IndexInputSlicer slicer = mmapDir.CreateSlicer("bytes", NewIOContext(Random));
IndexInput one = slicer.OpenSlice("first int", 0, 4);
IndexInput two = slicer.OpenSlice("second int", 4, 4);
one.Dispose();
try
{
one.ReadInt32();
Assert.Fail("Must throw ObjectDisposedException");
}
#pragma warning disable 168
catch (ObjectDisposedException ignore)
#pragma warning restore 168
{
// pass
}
Assert.AreEqual(2, two.ReadInt32());
// reopen a new slice "one":
one = slicer.OpenSlice("first int", 0, 4);
Assert.AreEqual(1, one.ReadInt32());
one.Dispose();
two.Dispose();
slicer.Dispose();
mmapDir.Dispose();
}
[Test]
public virtual void TestSeekZero()
{
for (int i = 0; i < 31; i++)
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testSeekZero"), null, 1 << i);
IndexOutput io = mmapDir.CreateOutput("zeroBytes", NewIOContext(Random));
io.Dispose();
IndexInput ii = mmapDir.OpenInput("zeroBytes", NewIOContext(Random));
ii.Seek(0L);
ii.Dispose();
mmapDir.Dispose();
}
}
[Test]
public virtual void TestSeekSliceZero()
{
for (int i = 0; i < 31; i++)
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testSeekSliceZero"), null, 1 << i);
IndexOutput io = mmapDir.CreateOutput("zeroBytes", NewIOContext(Random));
io.Dispose();
IndexInputSlicer slicer = mmapDir.CreateSlicer("zeroBytes", NewIOContext(Random));
IndexInput ii = slicer.OpenSlice("zero-length slice", 0, 0);
ii.Seek(0L);
ii.Dispose();
slicer.Dispose();
mmapDir.Dispose();
}
}
[Test]
public virtual void TestSeekEnd()
{
for (int i = 0; i < 17; i++)
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testSeekEnd"), null, 1 << i);
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
var bytes = new byte[1 << i];
Random.NextBytes(bytes);
io.WriteBytes(bytes, bytes.Length);
io.Dispose();
IndexInput ii = mmapDir.OpenInput("bytes", NewIOContext(Random));
var actual = new byte[1 << i];
ii.ReadBytes(actual, 0, actual.Length);
Assert.AreEqual(new BytesRef(bytes), new BytesRef(actual));
ii.Seek(1 << i);
ii.Dispose();
mmapDir.Dispose();
}
}
[Test]
public virtual void TestSeekSliceEnd()
{
for (int i = 0; i < 17; i++)
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testSeekSliceEnd"), null, 1 << i);
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
var bytes = new byte[1 << i];
Random.NextBytes(bytes);
io.WriteBytes(bytes, bytes.Length);
io.Dispose();
IndexInputSlicer slicer = mmapDir.CreateSlicer("bytes", NewIOContext(Random));
IndexInput ii = slicer.OpenSlice("full slice", 0, bytes.Length);
var actual = new byte[1 << i];
ii.ReadBytes(actual, 0, actual.Length);
Assert.AreEqual(new BytesRef(bytes), new BytesRef(actual));
ii.Seek(1 << i);
ii.Dispose();
slicer.Dispose();
mmapDir.Dispose();
}
}
[Test]
[Slow]
public virtual void TestSeeking()
{
for (int i = 0; i < 10; i++)
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testSeeking"), null, 1 << i);
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
var bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
Random.NextBytes(bytes);
io.WriteBytes(bytes, bytes.Length);
io.Dispose();
IndexInput ii = mmapDir.OpenInput("bytes", NewIOContext(Random));
var actual = new byte[1 << (i + 1)]; // first read all bytes
ii.ReadBytes(actual, 0, actual.Length);
Assert.AreEqual(new BytesRef(bytes), new BytesRef(actual));
for (int sliceStart = 0; sliceStart < bytes.Length; sliceStart++)
{
for (int sliceLength = 0; sliceLength < bytes.Length - sliceStart; sliceLength++)
{
var slice = new byte[sliceLength];
ii.Seek(sliceStart);
ii.ReadBytes(slice, 0, slice.Length);
Assert.AreEqual(new BytesRef(bytes, sliceStart, sliceLength), new BytesRef(slice));
}
}
ii.Dispose();
mmapDir.Dispose();
}
}
// note instead of seeking to offset and reading length, this opens slices at the
// the various offset+length and just does readBytes.
[Test]
[Slow]
public virtual void TestSlicedSeeking()
{
for (int i = 0; i < 10; i++)
{
MMapDirectory mmapDir = new MMapDirectory(CreateTempDir("testSlicedSeeking"), null, 1 << i);
IndexOutput io = mmapDir.CreateOutput("bytes", NewIOContext(Random));
var bytes = new byte[1 << (i + 1)]; // make sure we switch buffers
Random.NextBytes(bytes);
io.WriteBytes(bytes, bytes.Length);
io.Dispose();
IndexInput ii = mmapDir.OpenInput("bytes", NewIOContext(Random));
var actual = new byte[1 << (i + 1)]; // first read all bytes
ii.ReadBytes(actual, 0, actual.Length);
ii.Dispose();
Assert.AreEqual(new BytesRef(bytes), new BytesRef(actual));
IndexInputSlicer slicer = mmapDir.CreateSlicer("bytes", NewIOContext(Random));
for (int sliceStart = 0; sliceStart < bytes.Length; sliceStart++)
{
for (int sliceLength = 0; sliceLength < bytes.Length - sliceStart; sliceLength++)
{
var slice = new byte[sliceLength];
IndexInput input = slicer.OpenSlice("bytesSlice", sliceStart, slice.Length);
input.ReadBytes(slice, 0, slice.Length);
input.Dispose();
Assert.AreEqual(new BytesRef(bytes, sliceStart, sliceLength), new BytesRef(slice));
}
}
slicer.Dispose();
mmapDir.Dispose();
}
}
[Test]
public virtual void TestRandomChunkSizes()
{
int num = AtLeast(10);
for (int i = 0; i < num; i++)
{
AssertChunking(Random, TestUtil.NextInt32(Random, 20, 100));
}
}
private void AssertChunking(Random random, int chunkSize)
{
DirectoryInfo path = CreateTempDir("mmap" + chunkSize);
MMapDirectory mmapDir = new MMapDirectory(path, null, chunkSize);
// LUCENENET specific - unmap hack not needed
//// we will map a lot, try to turn on the unmap hack
//if (MMapDirectory.UNMAP_SUPPORTED)
//{
// mmapDir.UseUnmap = true;
//}
MockDirectoryWrapper dir = new MockDirectoryWrapper(random, mmapDir);
RandomIndexWriter writer = new RandomIndexWriter(random, dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)).SetMergePolicy(NewLogMergePolicy()));
Document doc = new Document();
Field docid = NewStringField("docid", "0", Field.Store.YES);
Field junk = NewStringField("junk", "", Field.Store.YES);
doc.Add(docid);
doc.Add(junk);
int numDocs = 100;
for (int i = 0; i < numDocs; i++)
{
docid.SetStringValue("" + i);
junk.SetStringValue(TestUtil.RandomUnicodeString(random));
writer.AddDocument(doc);
}
IndexReader reader = writer.GetReader();
writer.Dispose();
int numAsserts = AtLeast(100);
for (int i = 0; i < numAsserts; i++)
{
int docID = random.Next(numDocs);
Assert.AreEqual("" + docID, reader.Document(docID).Get("docid"));
}
reader.Dispose();
dir.Dispose();
}
[Test, LuceneNetSpecific]
public void TestDisposeIndexInput()
{
string name = "foobar";
var dir = CreateTempDir("testDisposeIndexInput");
string fileName = Path.Combine(dir.FullName, name);
// Create a zero byte file, and close it immediately
File.WriteAllText(fileName, string.Empty, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false) /* No BOM */);
MMapDirectory mmapDir = new MMapDirectory(dir);
using (var indexInput = mmapDir.OpenInput(name, NewIOContext(Random)))
{
} // Dispose
// Now it should be possible to delete the file. This is the condition we are testing for.
File.Delete(fileName);
}
}
}