| using J2N.Threading; |
| using Lucene.Net.Codecs; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Documents; |
| using Lucene.Net.Store; |
| using Lucene.Net.Util; |
| using System; |
| using System.Collections.Generic; |
| using System.IO; |
| using JCG = J2N.Collections.Generic; |
| using Console = Lucene.Net.Util.SystemConsole; |
| using Assert = Lucene.Net.TestFramework.Assert; |
| using Directory = Lucene.Net.Store.Directory; |
| using J2N.Collections.Generic.Extensions; |
| |
| #if TESTFRAMEWORK_MSTEST |
| using Test = Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute; |
| #elif TESTFRAMEWORK_NUNIT |
| using Test = NUnit.Framework.TestAttribute; |
| #elif TESTFRAMEWORK_XUNIT |
| using Test = Lucene.Net.TestFramework.SkippableFactAttribute; |
| #endif |
| |
| namespace Lucene.Net.Index |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary> |
| /// Abstract class to do basic tests for a <see cref="Codecs.PostingsFormat"/>. |
| /// NOTE: this test focuses on the postings |
| /// (docs/freqs/positions/payloads/offsets) impl, not the |
| /// terms dict. The [stretch] goal is for this test to be |
| /// so thorough in testing a new <see cref="Codecs.PostingsFormat"/> that if this |
| /// test passes, then all Lucene/Solr tests should also pass. Ie, |
| /// if there is some bug in a given <see cref="Codecs.PostingsFormat"/> that this |
| /// test fails to catch then this test needs to be improved! |
| /// </summary> |
| |
| // TODO can we make it easy for testing to pair up a "random terms dict impl" with your postings base format... |
| |
| // TODO test when you reuse after skipping a term or two, eg the block reuse case |
| |
| /* TODO |
| - threads |
| - assert doc=-1 before any nextDoc |
| - if a PF passes this test but fails other tests then this |
| test has a bug!! |
| - test tricky reuse cases, eg across fields |
| - verify you get null if you pass needFreq/needOffset but |
| they weren't indexed |
| */ |
| |
| //[TestFixture] |
| public abstract class BasePostingsFormatTestCase : BaseIndexFileFormatTestCase |
| #if TESTFRAMEWORK_XUNIT |
| , Xunit.IClassFixture<BeforeAfterClass> |
| { |
| public BasePostingsFormatTestCase(BeforeAfterClass beforeAfter) |
| : base(beforeAfter) |
| { |
| #if !FEATURE_INSTANCE_TESTDATA_INITIALIZATION |
| beforeAfter.SetBeforeAfterClassActions(BeforeClass, AfterClass); |
| #endif |
| } |
| #else |
| { |
| #endif |
| private enum Option |
| { |
| // Sometimes use .Advance(): |
| SKIPPING, |
| |
| // Sometimes reuse the Docs/AndPositionsEnum across terms: |
| REUSE_ENUMS, |
| |
| // Sometimes pass non-null live docs: |
| LIVE_DOCS, |
| |
| // Sometimes seek to term using previously saved TermState: |
| TERM_STATE, |
| |
| // Sometimes don't fully consume docs from the enum |
| PARTIAL_DOC_CONSUME, |
| |
| // Sometimes don't fully consume positions at each doc |
| PARTIAL_POS_CONSUME, |
| |
| // Sometimes check payloads |
| PAYLOADS, |
| |
| // Test w/ multiple threads |
| THREADS |
| } |
| |
| /// <summary> |
| /// Given the same random seed this always enumerates the |
| /// same random postings. |
| /// </summary> |
| private class SeedPostings : DocsAndPositionsEnum |
| { |
| // Used only to generate docIDs; this way if you pull w/ |
| // or w/o positions you get the same docID sequence: |
| private readonly Random docRandom; |
| |
| private readonly Random random; |
| public int DocFreq { get; set; } |
| private readonly int maxDocSpacing; |
| private readonly int payloadSize; |
| private readonly bool fixedPayloads; |
| private readonly IBits liveDocs; |
| private readonly BytesRef payload; |
| //private readonly IndexOptions options; // LUCENENET: Never read |
| private readonly bool doPositions; |
| |
| private int docID; |
| internal int freq; |
| public int Upto { get; set; } |
| |
| private int pos; |
| private int offset; |
| private int startOffset; |
| private int endOffset; |
| private int posSpacing; |
| private int posUpto; |
| |
| public SeedPostings(long seed, int minDocFreq, int maxDocFreq, IBits liveDocs, IndexOptions options) |
| { |
| random = new Random((int)seed); |
| docRandom = new Random(random.Next()); |
| DocFreq = TestUtil.NextInt32(random, minDocFreq, maxDocFreq); |
| this.liveDocs = liveDocs; |
| |
| // TODO: more realistic to inversely tie this to numDocs: |
| maxDocSpacing = TestUtil.NextInt32(random, 1, 100); |
| |
| if (random.Next(10) == 7) |
| { |
| // 10% of the time create big payloads: |
| payloadSize = 1 + random.Next(3); |
| } |
| else |
| { |
| payloadSize = 1 + random.Next(1); |
| } |
| |
| fixedPayloads = random.NextBoolean(); |
| var payloadBytes = new byte[payloadSize]; |
| payload = new BytesRef(payloadBytes); |
| //this.options = options; // LUCENENET: Never read |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| doPositions = IndexOptionsComparer.Default.Compare(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, options) <= 0; |
| } |
| |
| public override int NextDoc() |
| { |
| while (true) |
| { |
| _nextDoc(); |
| if (liveDocs == null || docID == NO_MORE_DOCS || liveDocs.Get(docID)) |
| { |
| return docID; |
| } |
| } |
| } |
| |
| #pragma warning disable IDE1006 // Naming Styles |
| private int _nextDoc() |
| #pragma warning restore IDE1006 // Naming Styles |
| { |
| // Must consume random: |
| while (posUpto < freq) |
| { |
| NextPosition(); |
| } |
| |
| if (Upto < DocFreq) |
| { |
| if (Upto == 0 && docRandom.NextBoolean()) |
| { |
| // Sometimes index docID = 0 |
| } |
| else if (maxDocSpacing == 1) |
| { |
| docID++; |
| } |
| else |
| { |
| // TODO: sometimes have a biggish gap here! |
| docID += TestUtil.NextInt32(docRandom, 1, maxDocSpacing); |
| } |
| |
| if (random.Next(200) == 17) |
| { |
| freq = TestUtil.NextInt32(random, 1, 1000); |
| } |
| else if (random.Next(10) == 17) |
| { |
| freq = TestUtil.NextInt32(random, 1, 20); |
| } |
| else |
| { |
| freq = TestUtil.NextInt32(random, 1, 4); |
| } |
| |
| pos = 0; |
| offset = 0; |
| posUpto = 0; |
| posSpacing = TestUtil.NextInt32(random, 1, 100); |
| |
| Upto++; |
| return docID; |
| } |
| else |
| { |
| return docID = NO_MORE_DOCS; |
| } |
| } |
| |
| public override int DocID => docID; |
| |
| public override int Freq => freq; |
| |
| public override int NextPosition() |
| { |
| if (!doPositions) |
| { |
| posUpto = freq; |
| return 0; |
| } |
| if (Debugging.AssertsEnabled) Debugging.Assert(posUpto < freq); |
| |
| if (posUpto == 0 && random.NextBoolean()) |
| { |
| // Sometimes index pos = 0 |
| } |
| else if (posSpacing == 1) |
| { |
| pos++; |
| } |
| else |
| { |
| pos += TestUtil.NextInt32(random, 1, posSpacing); |
| } |
| |
| if (payloadSize != 0) |
| { |
| if (fixedPayloads) |
| { |
| payload.Length = payloadSize; |
| random.NextBytes(payload.Bytes); |
| } |
| else |
| { |
| int thisPayloadSize = random.Next(payloadSize); |
| if (thisPayloadSize != 0) |
| { |
| payload.Length = payloadSize; |
| random.NextBytes(payload.Bytes); |
| } |
| else |
| { |
| payload.Length = 0; |
| } |
| } |
| } |
| else |
| { |
| payload.Length = 0; |
| } |
| |
| startOffset = offset + random.Next(5); |
| endOffset = startOffset + random.Next(10); |
| offset = endOffset; |
| |
| posUpto++; |
| return pos; |
| } |
| |
| public override int StartOffset => startOffset; |
| |
| public override int EndOffset => endOffset; |
| |
| public override BytesRef GetPayload() |
| { |
| return payload.Length == 0 ? null : payload; |
| } |
| |
| public override int Advance(int target) |
| { |
| return SlowAdvance(target); |
| } |
| |
| public override long GetCost() |
| { |
| return DocFreq; |
| } |
| } |
| |
| private class FieldAndTerm |
| { |
| internal string Field { get; set; } |
| internal BytesRef Term { get; set; } |
| |
| public FieldAndTerm(string field, BytesRef term) |
| { |
| this.Field = field; |
| this.Term = BytesRef.DeepCopyOf(term); |
| } |
| } |
| |
| // Holds all postings: |
| private static JCG.SortedDictionary<string, JCG.SortedDictionary<BytesRef, long>> fields; |
| |
| private static FieldInfos fieldInfos; |
| |
| private static FixedBitSet globalLiveDocs; |
| |
| private static IList<FieldAndTerm> allTerms; |
| private static int maxDoc; |
| |
| private static long totalPostings; |
| private static long totalPayloadBytes; |
| |
| private static SeedPostings GetSeedPostings(string term, long seed, bool withLiveDocs, IndexOptions options) |
| { |
| int minDocFreq, maxDocFreq; |
| if (term.StartsWith("big_", StringComparison.Ordinal)) |
| { |
| minDocFreq = RandomMultiplier * 50000; |
| maxDocFreq = RandomMultiplier * 70000; |
| } |
| else if (term.StartsWith("medium_", StringComparison.Ordinal)) |
| { |
| minDocFreq = RandomMultiplier * 3000; |
| maxDocFreq = RandomMultiplier * 6000; |
| } |
| else if (term.StartsWith("low_", StringComparison.Ordinal)) |
| { |
| minDocFreq = RandomMultiplier; |
| maxDocFreq = RandomMultiplier * 40; |
| } |
| else |
| { |
| minDocFreq = 1; |
| maxDocFreq = 3; |
| } |
| |
| return new SeedPostings(seed, minDocFreq, maxDocFreq, withLiveDocs ? globalLiveDocs : null, options); |
| } |
| |
| //#if TESTFRAMEWORK_MSTEST |
| // private static readonly IList<string> initalizationLock = new List<string>(); |
| |
| // // LUCENENET TODO: Add support for attribute inheritance when it is released (2.0.0) |
| // //[Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitialize(Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior.BeforeEachDerivedClass)] |
| // new public static void BeforeClass(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext context) |
| // { |
| // lock (initalizationLock) |
| // { |
| // if (!initalizationLock.Contains(context.FullyQualifiedTestClassName)) |
| // initalizationLock.Add(context.FullyQualifiedTestClassName); |
| // else |
| // return; // Only allow this class to initialize once (MSTest bug) |
| // } |
| //#else |
| #if TESTFRAMEWORK_NUNIT |
| [NUnit.Framework.OneTimeSetUp] |
| #endif |
| // new public static void BeforeClass() |
| // { |
| //#endif |
| //#else |
| //[OneTimeSetUp] |
| public override void BeforeClass() // Renamed from CreatePostings to ensure the base class setup is called before this one |
| { |
| base.BeforeClass(); |
| //#endif |
| |
| totalPostings = 0; |
| totalPayloadBytes = 0; |
| |
| // LUCENENET specific: Use StringComparer.Ordinal to get the same ordering as Java |
| fields = new JCG.SortedDictionary<string, JCG.SortedDictionary<BytesRef, long>>(StringComparer.Ordinal); |
| |
| int numFields = TestUtil.NextInt32(Random, 1, 5); |
| if (Verbose) |
| { |
| Console.WriteLine("TEST: " + numFields + " fields"); |
| } |
| maxDoc = 0; |
| |
| FieldInfo[] fieldInfoArray = new FieldInfo[numFields]; |
| int fieldUpto = 0; |
| while (fieldUpto < numFields) |
| { |
| string field = TestUtil.RandomSimpleString(Random); |
| if (fields.ContainsKey(field)) |
| { |
| continue; |
| } |
| |
| fieldInfoArray[fieldUpto] = new FieldInfo(field, true, fieldUpto, false, false, true, |
| IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, |
| DocValuesType.NONE, DocValuesType.NUMERIC, null); |
| fieldUpto++; |
| |
| JCG.SortedDictionary<BytesRef, long> postings = new JCG.SortedDictionary<BytesRef, long>(); |
| fields[field] = postings; |
| ISet<string> seenTerms = new JCG.HashSet<string>(); |
| |
| int numTerms; |
| if (Random.Next(10) == 7) |
| { |
| numTerms = AtLeast(50); |
| } |
| else |
| { |
| numTerms = TestUtil.NextInt32(Random, 2, 20); |
| } |
| |
| for (int termUpto = 0; termUpto < numTerms; termUpto++) |
| { |
| string term = TestUtil.RandomSimpleString(Random); |
| if (seenTerms.Contains(term)) |
| { |
| continue; |
| } |
| seenTerms.Add(term); |
| |
| if (TestNightly && termUpto == 0 && fieldUpto == 1) |
| { |
| // Make 1 big term: |
| term = "big_" + term; |
| } |
| else if (termUpto == 1 && fieldUpto == 1) |
| { |
| // Make 1 medium term: |
| term = "medium_" + term; |
| } |
| else if (Random.NextBoolean()) |
| { |
| // Low freq term: |
| term = "low_" + term; |
| } |
| else |
| { |
| // Very low freq term (don't multiply by RANDOM_MULTIPLIER): |
| term = "verylow_" + term; |
| } |
| |
| long termSeed = Random.NextInt64(); |
| postings[new BytesRef(term)] = termSeed; |
| |
| // NOTE: sort of silly: we enum all the docs just to |
| // get the maxDoc |
| DocsEnum docsEnum = GetSeedPostings(term, termSeed, false, IndexOptions.DOCS_ONLY); |
| int doc; |
| int lastDoc = 0; |
| while ((doc = docsEnum.NextDoc()) != DocsEnum.NO_MORE_DOCS) |
| { |
| lastDoc = doc; |
| } |
| maxDoc = Math.Max(lastDoc, maxDoc); |
| } |
| } |
| |
| fieldInfos = new FieldInfos(fieldInfoArray); |
| |
| // It's the count, not the last docID: |
| maxDoc++; |
| |
| globalLiveDocs = new FixedBitSet(maxDoc); |
| double liveRatio = Random.NextDouble(); |
| for (int i = 0; i < maxDoc; i++) |
| { |
| if (Random.NextDouble() <= liveRatio) |
| { |
| globalLiveDocs.Set(i); |
| } |
| } |
| |
| allTerms = new List<FieldAndTerm>(); |
| foreach (KeyValuePair<string, JCG.SortedDictionary<BytesRef, long>> fieldEnt in fields) |
| { |
| string field = fieldEnt.Key; |
| foreach (KeyValuePair<BytesRef, long> termEnt in fieldEnt.Value) |
| { |
| allTerms.Add(new FieldAndTerm(field, termEnt.Key)); |
| } |
| } |
| |
| if (Verbose) |
| { |
| Console.WriteLine("TEST: done init postings; " + allTerms.Count + " total terms, across " + fieldInfos.Count + " fields"); |
| } |
| } |
| |
| |
| //#if TESTFRAMEWORK_MSTEST |
| // // LUCENENET TODO: Add support for attribute inheritance when it is released (2.0.0) |
| // //[Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanup(Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior.BeforeEachDerivedClass)] |
| //# el |
| #if TESTFRAMEWORK_NUNIT |
| [NUnit.Framework.OneTimeTearDown] |
| #endif |
| // new public static void AfterClass() |
| //#else |
| //[OneTimeTearDown] |
| public override void AfterClass() |
| { |
| allTerms = null; |
| fieldInfos = null; |
| fields = null; |
| globalLiveDocs = null; |
| base.AfterClass(); |
| } |
| |
| // TODO maybe instead of @BeforeClass just make a single test run: build postings & index & test it? |
| |
| private FieldInfos currentFieldInfos; |
| |
| // LUCENENET specific - cache the list of index options so we don't need to look it up over and over |
| private static readonly IndexOptions[] ALL_INDEX_OPTIONS = (IndexOptions[])Enum.GetValues(typeof(IndexOptions)); |
| |
| // maxAllowed = the "highest" we can index, but we will still |
| // randomly index at lower IndexOption |
| private FieldsProducer BuildIndex(Directory dir, IndexOptions maxAllowed, bool allowPayloads, bool alwaysTestMax) |
| { |
| Codec codec = GetCodec(); |
| SegmentInfo segmentInfo = new SegmentInfo(dir, Constants.LUCENE_MAIN_VERSION, "_0", maxDoc, false, codec, null); |
| |
| int maxIndexOption = Array.IndexOf(ALL_INDEX_OPTIONS, maxAllowed); |
| if (Verbose) |
| { |
| Console.WriteLine("\nTEST: now build index"); |
| } |
| |
| int maxIndexOptionNoOffsets = Array.IndexOf(ALL_INDEX_OPTIONS, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS); |
| |
| // TODO use allowPayloads |
| |
| var newFieldInfoArray = new FieldInfo[fields.Count]; |
| for (int fieldUpto = 0; fieldUpto < fields.Count; fieldUpto++) |
| { |
| FieldInfo oldFieldInfo = fieldInfos.FieldInfo(fieldUpto); |
| |
| string pf = TestUtil.GetPostingsFormat(codec, oldFieldInfo.Name); |
| int fieldMaxIndexOption; |
| if (DoesntSupportOffsets.Contains(pf)) |
| { |
| fieldMaxIndexOption = Math.Min(maxIndexOptionNoOffsets, maxIndexOption); |
| } |
| else |
| { |
| fieldMaxIndexOption = maxIndexOption; |
| } |
| |
| // Randomly picked the IndexOptions to index this |
| // field with: |
| IndexOptions indexOptions = ALL_INDEX_OPTIONS[alwaysTestMax ? fieldMaxIndexOption : Random.Next(1, 1 + fieldMaxIndexOption)]; // LUCENENET: Skipping NONE option |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| bool doPayloads = IndexOptionsComparer.Default.Compare(indexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0 && allowPayloads; |
| |
| newFieldInfoArray[fieldUpto] = new FieldInfo(oldFieldInfo.Name, true, fieldUpto, false, false, doPayloads, indexOptions, DocValuesType.NONE, DocValuesType.NUMERIC, null); |
| } |
| |
| FieldInfos newFieldInfos = new FieldInfos(newFieldInfoArray); |
| |
| // Estimate that flushed segment size will be 25% of |
| // what we use in RAM: |
| long bytes = totalPostings * 8 + totalPayloadBytes; |
| |
| SegmentWriteState writeState = new SegmentWriteState(null, dir, segmentInfo, newFieldInfos, 32, null, new IOContext(new FlushInfo(maxDoc, bytes))); |
| |
| // LUCENENET specific - BUG: we must wrap this in a using block in case anything in the below loop throws |
| using (FieldsConsumer fieldsConsumer = codec.PostingsFormat.FieldsConsumer(writeState)) |
| { |
| |
| foreach (KeyValuePair<string, JCG.SortedDictionary<BytesRef, long>> fieldEnt in fields) |
| { |
| string field = fieldEnt.Key; |
| IDictionary<BytesRef, long> terms = fieldEnt.Value; |
| |
| FieldInfo fieldInfo = newFieldInfos.FieldInfo(field); |
| |
| IndexOptions indexOptions = fieldInfo.IndexOptions; |
| |
| if (Verbose) |
| { |
| Console.WriteLine("field=" + field + " indexOtions=" + indexOptions); |
| } |
| |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| bool doFreq = IndexOptionsComparer.Default.Compare(indexOptions, IndexOptions.DOCS_AND_FREQS) >= 0; |
| bool doPos = IndexOptionsComparer.Default.Compare(indexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0; |
| bool doPayloads = IndexOptionsComparer.Default.Compare(indexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0 && allowPayloads; |
| bool doOffsets = IndexOptionsComparer.Default.Compare(indexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0; |
| |
| TermsConsumer termsConsumer = fieldsConsumer.AddField(fieldInfo); |
| long sumTotalTF = 0; |
| long sumDF = 0; |
| FixedBitSet seenDocs = new FixedBitSet(maxDoc); |
| foreach (KeyValuePair<BytesRef, long> termEnt in terms) |
| { |
| BytesRef term = termEnt.Key; |
| SeedPostings postings = GetSeedPostings(term.Utf8ToString(), termEnt.Value, false, maxAllowed); |
| if (Verbose) |
| { |
| Console.WriteLine(" term=" + field + ":" + term.Utf8ToString() + " docFreq=" + postings.DocFreq + " seed=" + termEnt.Value); |
| } |
| |
| PostingsConsumer postingsConsumer = termsConsumer.StartTerm(term); |
| long totalTF = 0; |
| int docID = 0; |
| while ((docID = postings.NextDoc()) != DocsEnum.NO_MORE_DOCS) |
| { |
| int freq = postings.Freq; |
| if (Verbose) |
| { |
| Console.WriteLine(" " + postings.Upto + ": docID=" + docID + " freq=" + postings.freq); |
| } |
| postingsConsumer.StartDoc(docID, doFreq ? postings.freq : -1); |
| seenDocs.Set(docID); |
| if (doPos) |
| { |
| totalTF += postings.freq; |
| for (int posUpto = 0; posUpto < freq; posUpto++) |
| { |
| int pos = postings.NextPosition(); |
| BytesRef payload = postings.GetPayload(); |
| |
| if (Verbose) |
| { |
| if (doPayloads) |
| { |
| Console.WriteLine(" pos=" + pos + " payload=" + (payload == null ? "null" : payload.Length + " bytes")); |
| } |
| else |
| { |
| Console.WriteLine(" pos=" + pos); |
| } |
| } |
| postingsConsumer.AddPosition(pos, doPayloads ? payload : null, doOffsets ? postings.StartOffset : -1, doOffsets ? postings.EndOffset : -1); |
| } |
| } |
| else if (doFreq) |
| { |
| totalTF += freq; |
| } |
| else |
| { |
| totalTF++; |
| } |
| postingsConsumer.FinishDoc(); |
| } |
| termsConsumer.FinishTerm(term, new TermStats(postings.DocFreq, doFreq ? totalTF : -1)); |
| sumTotalTF += totalTF; |
| sumDF += postings.DocFreq; |
| } |
| |
| termsConsumer.Finish(doFreq ? sumTotalTF : -1, sumDF, seenDocs.Cardinality()); |
| } |
| |
| } |
| |
| if (Verbose) |
| { |
| Console.WriteLine("TEST: after indexing: files="); |
| foreach (string file in dir.ListAll()) |
| { |
| Console.WriteLine(" " + file + ": " + dir.FileLength(file) + " bytes"); |
| } |
| } |
| |
| currentFieldInfos = newFieldInfos; |
| |
| SegmentReadState readState = new SegmentReadState(dir, segmentInfo, newFieldInfos, IOContext.READ, 1); |
| |
| return codec.PostingsFormat.FieldsProducer(readState); |
| } |
| |
| private class ThreadState |
| { |
| // Only used with REUSE option: |
| public DocsEnum ReuseDocsEnum { get; set; } |
| |
| public DocsAndPositionsEnum ReuseDocsAndPositionsEnum { get; set; } |
| } |
| |
| private void VerifyEnum(ThreadState threadState, |
| string field, |
| BytesRef term, |
| TermsEnum termsEnum, |
| |
| // Maximum options (docs/freqs/positions/offsets) to test: |
| IndexOptions maxTestOptions, |
| |
| IndexOptions maxIndexOptions, |
| ISet<Option> options, |
| bool alwaysTestMax) |
| |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" verifyEnum: options=" + options + " maxTestOptions=" + maxTestOptions); |
| } |
| |
| // Make sure TermsEnum really is positioned on the |
| // expected term: |
| Assert.AreEqual(term, termsEnum.Term); |
| |
| // 50% of the time time pass liveDocs: |
| bool useLiveDocs = options.Contains(Option.LIVE_DOCS) && Random.NextBoolean(); |
| IBits liveDocs; |
| if (useLiveDocs) |
| { |
| liveDocs = globalLiveDocs; |
| if (Verbose) |
| { |
| Console.WriteLine(" use liveDocs"); |
| } |
| } |
| else |
| { |
| liveDocs = null; |
| if (Verbose) |
| { |
| Console.WriteLine(" no liveDocs"); |
| } |
| } |
| |
| FieldInfo fieldInfo = currentFieldInfos.FieldInfo(field); |
| |
| // NOTE: can be empty list if we are using liveDocs: |
| SeedPostings expected = GetSeedPostings(term.Utf8ToString(), |
| fields[field][term], |
| useLiveDocs, |
| maxIndexOptions); |
| Assert.AreEqual(expected.DocFreq, termsEnum.DocFreq); |
| |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| bool allowFreqs = IndexOptionsComparer.Default.Compare(fieldInfo.IndexOptions, IndexOptions.DOCS_AND_FREQS) >= 0 && |
| IndexOptionsComparer.Default.Compare(maxTestOptions, IndexOptions.DOCS_AND_FREQS) >= 0; |
| bool doCheckFreqs = allowFreqs && (alwaysTestMax || Random.Next(3) <= 2); |
| |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| bool allowPositions = IndexOptionsComparer.Default.Compare(fieldInfo.IndexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0 && |
| IndexOptionsComparer.Default.Compare(maxTestOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0; |
| bool doCheckPositions = allowPositions && (alwaysTestMax || Random.Next(3) <= 2); |
| |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| bool allowOffsets = IndexOptionsComparer.Default.Compare(fieldInfo.IndexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >=0 && |
| IndexOptionsComparer.Default.Compare(maxTestOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0; |
| bool doCheckOffsets = allowOffsets && (alwaysTestMax || Random.Next(3) <= 2); |
| |
| bool doCheckPayloads = options.Contains(Option.PAYLOADS) && allowPositions && fieldInfo.HasPayloads && (alwaysTestMax || Random.Next(3) <= 2); |
| |
| DocsEnum prevDocsEnum = null; |
| |
| DocsEnum docsEnum; |
| DocsAndPositionsEnum docsAndPositionsEnum; |
| |
| if (!doCheckPositions) |
| { |
| if (allowPositions && Random.Next(10) == 7) |
| { |
| // 10% of the time, even though we will not check positions, pull a DocsAndPositions enum |
| |
| if (options.Contains(Option.REUSE_ENUMS) && Random.Next(10) < 9) |
| { |
| prevDocsEnum = threadState.ReuseDocsAndPositionsEnum; |
| } |
| |
| DocsAndPositionsFlags flags = 0; |
| if (alwaysTestMax || Random.NextBoolean()) |
| { |
| flags |= DocsAndPositionsFlags.OFFSETS; |
| } |
| if (alwaysTestMax || Random.NextBoolean()) |
| { |
| flags |= DocsAndPositionsFlags.PAYLOADS; |
| } |
| |
| if (Verbose) |
| { |
| Console.WriteLine(" get DocsAndPositionsEnum (but we won't check positions) flags=" + flags); |
| } |
| |
| threadState.ReuseDocsAndPositionsEnum = termsEnum.DocsAndPositions(liveDocs, (DocsAndPositionsEnum)prevDocsEnum, flags); |
| docsEnum = threadState.ReuseDocsAndPositionsEnum; |
| docsAndPositionsEnum = threadState.ReuseDocsAndPositionsEnum; |
| } |
| else |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" get DocsEnum"); |
| } |
| if (options.Contains(Option.REUSE_ENUMS) && Random.Next(10) < 9) |
| { |
| prevDocsEnum = threadState.ReuseDocsEnum; |
| } |
| threadState.ReuseDocsEnum = termsEnum.Docs(liveDocs, prevDocsEnum, doCheckFreqs ? DocsFlags.FREQS : DocsFlags.NONE); |
| docsEnum = threadState.ReuseDocsEnum; |
| docsAndPositionsEnum = null; |
| } |
| } |
| else |
| { |
| if (options.Contains(Option.REUSE_ENUMS) && Random.Next(10) < 9) |
| { |
| prevDocsEnum = threadState.ReuseDocsAndPositionsEnum; |
| } |
| |
| DocsAndPositionsFlags flags = 0; |
| if (alwaysTestMax || doCheckOffsets || Random.Next(3) == 1) |
| { |
| flags |= DocsAndPositionsFlags.OFFSETS; |
| } |
| if (alwaysTestMax || doCheckPayloads || Random.Next(3) == 1) |
| { |
| flags |= DocsAndPositionsFlags.PAYLOADS; |
| } |
| |
| if (Verbose) |
| { |
| Console.WriteLine(" get DocsAndPositionsEnum flags=" + flags); |
| } |
| |
| threadState.ReuseDocsAndPositionsEnum = termsEnum.DocsAndPositions(liveDocs, (DocsAndPositionsEnum)prevDocsEnum, flags); |
| docsEnum = threadState.ReuseDocsAndPositionsEnum; |
| docsAndPositionsEnum = threadState.ReuseDocsAndPositionsEnum; |
| } |
| |
| Assert.IsNotNull(docsEnum, "null DocsEnum"); |
| int initialDocID = docsEnum.DocID; |
| Assert.AreEqual(-1, initialDocID, "inital docID should be -1" + docsEnum); |
| |
| if (Verbose) |
| { |
| if (prevDocsEnum == null) |
| { |
| Console.WriteLine(" got enum=" + docsEnum); |
| } |
| else if (prevDocsEnum == docsEnum) |
| { |
| Console.WriteLine(" got reuse enum=" + docsEnum); |
| } |
| else |
| { |
| Console.WriteLine(" got enum=" + docsEnum + " (reuse of " + prevDocsEnum + " failed)"); |
| } |
| } |
| |
| // 10% of the time don't consume all docs: |
| int stopAt; |
| if (!alwaysTestMax && options.Contains(Option.PARTIAL_DOC_CONSUME) && expected.DocFreq > 1 && Random.Next(10) == 7) |
| { |
| stopAt = Random.Next(expected.DocFreq - 1); |
| if (Verbose) |
| { |
| Console.WriteLine(" will not consume all docs (" + stopAt + " vs " + expected.DocFreq + ")"); |
| } |
| } |
| else |
| { |
| stopAt = expected.DocFreq; |
| if (Verbose) |
| { |
| Console.WriteLine(" consume all docs"); |
| } |
| } |
| |
| double skipChance = alwaysTestMax ? 0.5 : Random.NextDouble(); |
| int numSkips = expected.DocFreq < 3 ? 1 : TestUtil.NextInt32(Random, 1, Math.Min(20, expected.DocFreq / 3)); |
| int skipInc = expected.DocFreq / numSkips; |
| int skipDocInc = maxDoc / numSkips; |
| |
| // Sometimes do 100% skipping: |
| bool doAllSkipping = options.Contains(Option.SKIPPING) && Random.Next(7) == 1; |
| |
| double freqAskChance = alwaysTestMax ? 1.0 : Random.NextDouble(); |
| double payloadCheckChance = alwaysTestMax ? 1.0 : Random.NextDouble(); |
| double offsetCheckChance = alwaysTestMax ? 1.0 : Random.NextDouble(); |
| |
| if (Verbose) |
| { |
| if (options.Contains(Option.SKIPPING)) |
| { |
| Console.WriteLine(" skipChance=" + skipChance + " numSkips=" + numSkips); |
| } |
| else |
| { |
| Console.WriteLine(" no skipping"); |
| } |
| if (doCheckFreqs) |
| { |
| Console.WriteLine(" freqAskChance=" + freqAskChance); |
| } |
| if (doCheckPayloads) |
| { |
| Console.WriteLine(" payloadCheckChance=" + payloadCheckChance); |
| } |
| if (doCheckOffsets) |
| { |
| Console.WriteLine(" offsetCheckChance=" + offsetCheckChance); |
| } |
| } |
| |
| while (expected.Upto <= stopAt) |
| { |
| if (expected.Upto == stopAt) |
| { |
| if (stopAt == expected.DocFreq) |
| { |
| Assert.AreEqual(DocsEnum.NO_MORE_DOCS, docsEnum.NextDoc(), "DocsEnum should have ended but didn't"); |
| |
| // Common bug is to forget to set this.Doc=NO_MORE_DOCS in the enum!: |
| Assert.AreEqual(DocsEnum.NO_MORE_DOCS, docsEnum.DocID, "DocsEnum should have ended but didn't"); |
| } |
| break; |
| } |
| |
| if (options.Contains(Option.SKIPPING) && (doAllSkipping || Random.NextDouble() <= skipChance)) |
| { |
| int targetDocID = -1; |
| if (expected.Upto < stopAt && Random.NextBoolean()) |
| { |
| // Pick target we know exists: |
| int skipCount = TestUtil.NextInt32(Random, 1, skipInc); |
| for (int skip = 0; skip < skipCount; skip++) |
| { |
| if (expected.NextDoc() == DocsEnum.NO_MORE_DOCS) |
| { |
| break; |
| } |
| } |
| } |
| else |
| { |
| // Pick random target (might not exist): |
| int skipDocIDs = TestUtil.NextInt32(Random, 1, skipDocInc); |
| if (skipDocIDs > 0) |
| { |
| targetDocID = expected.DocID + skipDocIDs; |
| expected.Advance(targetDocID); |
| } |
| } |
| |
| if (expected.Upto >= stopAt) |
| { |
| int target = Random.NextBoolean() ? maxDoc : DocsEnum.NO_MORE_DOCS; |
| if (Verbose) |
| { |
| Console.WriteLine(" now advance to end (target=" + target + ")"); |
| } |
| Assert.AreEqual(DocsEnum.NO_MORE_DOCS, docsEnum.Advance(target), "DocsEnum should have ended but didn't"); |
| break; |
| } |
| else |
| { |
| if (Verbose) |
| { |
| if (targetDocID != -1) |
| { |
| Console.WriteLine(" now advance to random target=" + targetDocID + " (" + expected.Upto + " of " + stopAt + ") current=" + docsEnum.DocID); |
| } |
| else |
| { |
| Console.WriteLine(" now advance to known-exists target=" + expected.DocID + " (" + expected.Upto + " of " + stopAt + ") current=" + docsEnum.DocID); |
| } |
| } |
| int docID = docsEnum.Advance(targetDocID != -1 ? targetDocID : expected.DocID); |
| Assert.AreEqual(expected.DocID, docID, "docID is wrong"); |
| } |
| } |
| else |
| { |
| expected.NextDoc(); |
| if (Verbose) |
| { |
| Console.WriteLine(" now nextDoc to " + expected.DocID + " (" + expected.Upto + " of " + stopAt + ")"); |
| } |
| int docID = docsEnum.NextDoc(); |
| Assert.AreEqual(expected.DocID, docID, "docID is wrong"); |
| if (docID == DocsEnum.NO_MORE_DOCS) |
| { |
| break; |
| } |
| } |
| |
| if (doCheckFreqs && Random.NextDouble() <= freqAskChance) |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" now freq()=" + expected.Freq); |
| } |
| int freq = docsEnum.Freq; |
| Assert.AreEqual(expected.Freq, freq, "freq is wrong"); |
| } |
| |
| if (doCheckPositions) |
| { |
| int freq = docsEnum.Freq; |
| int numPosToConsume; |
| if (!alwaysTestMax && options.Contains(Option.PARTIAL_POS_CONSUME) && Random.Next(5) == 1) |
| { |
| numPosToConsume = Random.Next(freq); |
| } |
| else |
| { |
| numPosToConsume = freq; |
| } |
| |
| for (int i = 0; i < numPosToConsume; i++) |
| { |
| int pos = expected.NextPosition(); |
| if (Verbose) |
| { |
| Console.WriteLine(" now nextPosition to " + pos); |
| } |
| Assert.AreEqual(pos, docsAndPositionsEnum.NextPosition(), "position is wrong"); |
| |
| if (doCheckPayloads) |
| { |
| BytesRef expectedPayload = expected.GetPayload(); |
| if (Random.NextDouble() <= payloadCheckChance) |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" now check expectedPayload length=" + (expectedPayload == null ? 0 : expectedPayload.Length)); |
| } |
| if (expectedPayload == null || expectedPayload.Length == 0) |
| { |
| Assert.IsNull(docsAndPositionsEnum.GetPayload(), "should not have payload"); |
| } |
| else |
| { |
| BytesRef payload = docsAndPositionsEnum.GetPayload(); |
| Assert.IsNotNull(payload, "should have payload but doesn't"); |
| |
| Assert.AreEqual(expectedPayload.Length, payload.Length, "payload length is wrong"); |
| for (int byteUpto = 0; byteUpto < expectedPayload.Length; byteUpto++) |
| { |
| Assert.AreEqual(expectedPayload.Bytes[expectedPayload.Offset + byteUpto], payload.Bytes[payload.Offset + byteUpto], "payload bytes are wrong"); |
| } |
| |
| // make a deep copy |
| payload = BytesRef.DeepCopyOf(payload); |
| Assert.AreEqual(payload, docsAndPositionsEnum.GetPayload(), "2nd call to getPayload returns something different!"); |
| } |
| } |
| else |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" skip check payload length=" + (expectedPayload == null ? 0 : expectedPayload.Length)); |
| } |
| } |
| } |
| |
| if (doCheckOffsets) |
| { |
| if (Random.NextDouble() <= offsetCheckChance) |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" now check offsets: startOff=" + expected.StartOffset + " endOffset=" + expected.EndOffset); |
| } |
| Assert.AreEqual(expected.StartOffset, docsAndPositionsEnum.StartOffset, "startOffset is wrong"); |
| Assert.AreEqual(expected.EndOffset, docsAndPositionsEnum.EndOffset, "endOffset is wrong"); |
| } |
| else |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" skip check offsets"); |
| } |
| } |
| } |
| // LUCENENET specific - to avoid boxing, changed from CompareTo() to IndexOptionsComparer.Compare() |
| else if (IndexOptionsComparer.Default.Compare(fieldInfo.IndexOptions, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) < 0) |
| { |
| if (Verbose) |
| { |
| Console.WriteLine(" now check offsets are -1"); |
| } |
| Assert.AreEqual(-1, docsAndPositionsEnum.StartOffset, "startOffset isn't -1"); |
| Assert.AreEqual(-1, docsAndPositionsEnum.EndOffset, "endOffset isn't -1"); |
| } |
| } |
| } |
| } |
| } |
| |
| private class TestThread : ThreadJob |
| { |
| private Fields fieldsSource; |
| private readonly ISet<Option> options; // LUCENENET: marked readonly |
| private readonly IndexOptions maxIndexOptions; // LUCENENET: marked readonly |
| private readonly IndexOptions maxTestOptions; // LUCENENET: marked readonly |
| private readonly bool alwaysTestMax; // LUCENENET: marked readonly |
| private BasePostingsFormatTestCase testCase; |
| |
| public TestThread(BasePostingsFormatTestCase testCase, Fields fieldsSource, ISet<Option> options, IndexOptions maxTestOptions, IndexOptions maxIndexOptions, bool alwaysTestMax) |
| { |
| this.fieldsSource = fieldsSource; |
| this.options = options; |
| this.maxTestOptions = maxTestOptions; |
| this.maxIndexOptions = maxIndexOptions; |
| this.alwaysTestMax = alwaysTestMax; |
| this.testCase = testCase; |
| } |
| |
| public override void Run() |
| { |
| try |
| { |
| try |
| { |
| testCase.TestTermsOneThread(fieldsSource, options, maxTestOptions, maxIndexOptions, alwaysTestMax); |
| } |
| catch (Exception t) |
| { |
| throw new Exception(t.Message, t); |
| } |
| } |
| finally |
| { |
| fieldsSource = null; |
| testCase = null; |
| } |
| } |
| } |
| |
| private void TestTerms(Fields fieldsSource, ISet<Option> options, |
| IndexOptions maxTestOptions, |
| IndexOptions maxIndexOptions, |
| bool alwaysTestMax) |
| { |
| if (options.Contains(Option.THREADS)) |
| { |
| int numThreads = TestUtil.NextInt32(Random, 2, 5); |
| ThreadJob[] threads = new ThreadJob[numThreads]; |
| for (int threadUpto = 0; threadUpto < numThreads; threadUpto++) |
| { |
| threads[threadUpto] = new TestThread(this, fieldsSource, options, maxTestOptions, maxIndexOptions, alwaysTestMax); |
| threads[threadUpto].Start(); |
| } |
| for (int threadUpto = 0; threadUpto < numThreads; threadUpto++) |
| { |
| threads[threadUpto].Join(); |
| } |
| } |
| else |
| { |
| TestTermsOneThread(fieldsSource, options, maxTestOptions, maxIndexOptions, alwaysTestMax); |
| } |
| } |
| |
| private void TestTermsOneThread(Fields fieldsSource, ISet<Option> options, |
| IndexOptions maxTestOptions, |
| IndexOptions maxIndexOptions, |
| bool alwaysTestMax) |
| { |
| ThreadState threadState = new ThreadState(); |
| |
| // Test random terms/fields: |
| IList<TermState> termStates = new List<TermState>(); |
| IList<FieldAndTerm> termStateTerms = new List<FieldAndTerm>(); |
| |
| allTerms.Shuffle(Random); |
| int upto = 0; |
| while (upto < allTerms.Count) |
| { |
| bool useTermState = termStates.Count != 0 && Random.Next(5) == 1; |
| FieldAndTerm fieldAndTerm; |
| TermsEnum termsEnum; |
| |
| TermState termState = null; |
| |
| if (!useTermState) |
| { |
| // Seek by random field+term: |
| fieldAndTerm = allTerms[upto++]; |
| if (Verbose) |
| { |
| Console.WriteLine("\nTEST: seek to term=" + fieldAndTerm.Field + ":" + fieldAndTerm.Term.Utf8ToString()); |
| } |
| } |
| else |
| { |
| // Seek by previous saved TermState |
| int idx = Random.Next(termStates.Count); |
| fieldAndTerm = termStateTerms[idx]; |
| if (Verbose) |
| { |
| Console.WriteLine("\nTEST: seek using TermState to term=" + fieldAndTerm.Field + ":" + fieldAndTerm.Term.Utf8ToString()); |
| } |
| termState = termStates[idx]; |
| } |
| |
| Terms terms = fieldsSource.GetTerms(fieldAndTerm.Field); |
| Assert.IsNotNull(terms); |
| termsEnum = terms.GetEnumerator(); |
| |
| if (!useTermState) |
| { |
| Assert.IsTrue(termsEnum.SeekExact(fieldAndTerm.Term)); |
| } |
| else |
| { |
| termsEnum.SeekExact(fieldAndTerm.Term, termState); |
| } |
| |
| bool savedTermState = false; |
| |
| if (options.Contains(Option.TERM_STATE) && !useTermState && Random.Next(5) == 1) |
| { |
| // Save away this TermState: |
| termStates.Add(termsEnum.GetTermState()); |
| termStateTerms.Add(fieldAndTerm); |
| savedTermState = true; |
| } |
| |
| VerifyEnum(threadState, |
| fieldAndTerm.Field, |
| fieldAndTerm.Term, |
| termsEnum, |
| maxTestOptions, |
| maxIndexOptions, |
| options, |
| alwaysTestMax); |
| |
| // Sometimes save term state after pulling the enum: |
| if (options.Contains(Option.TERM_STATE) && !useTermState && !savedTermState && Random.Next(5) == 1) |
| { |
| // Save away this TermState: |
| termStates.Add(termsEnum.GetTermState()); |
| termStateTerms.Add(fieldAndTerm); |
| //useTermState = true; // LUCENENET: IDE0059: Remove unnecessary value assignment |
| } |
| |
| // 10% of the time make sure you can pull another enum |
| // from the same term: |
| if (alwaysTestMax || Random.Next(10) == 7) |
| { |
| // Try same term again |
| if (Verbose) |
| { |
| Console.WriteLine("TEST: try enum again on same term"); |
| } |
| |
| VerifyEnum(threadState, |
| fieldAndTerm.Field, |
| fieldAndTerm.Term, |
| termsEnum, |
| maxTestOptions, |
| maxIndexOptions, |
| options, |
| alwaysTestMax); |
| } |
| } |
| } |
| |
| private static void TestFields(Fields fields) // LUCENENET: CA1822: Mark members as static |
| { |
| using IEnumerator<string> iterator = fields.GetEnumerator(); |
| while (iterator.MoveNext()) |
| { |
| var _ = iterator.Current; |
| // .NET: Testing for iterator.Remove() isn't applicable |
| } |
| Assert.IsFalse(iterator.MoveNext()); |
| |
| // .NET: Testing for NoSuchElementException with .NET iterators isn't applicable |
| } |
| |
| /// <summary> |
| /// Indexes all fields/terms at the specified |
| /// <see cref="IndexOptions"/>, and fully tests at that <see cref="IndexOptions"/>. |
| /// </summary> |
| private void TestFull(IndexOptions options, bool withPayloads) |
| { |
| DirectoryInfo path = CreateTempDir("testPostingsFormat.testExact"); |
| using Directory dir = NewFSDirectory(path); |
| // TODO test thread safety of buildIndex too |
| using var fieldsProducer = BuildIndex(dir, options, withPayloads, true); |
| TestFields(fieldsProducer); |
| |
| // LUCENENET: A bit of extra work required since we don't have an easy way to filter out |
| // the "NONE" option we added to avoid having to use null |
| IndexOptions[] allOptions = new IndexOptions[ALL_INDEX_OPTIONS.Length - 1]; |
| Array.Copy(ALL_INDEX_OPTIONS, 1, allOptions, 0, allOptions.Length); // LUCENENET: Skip our NONE option |
| int maxIndexOption = Array.IndexOf(allOptions, options); |
| |
| ISet<Option> allOptionsHashSet = new JCG.HashSet<Option>((Option[])Enum.GetValues(typeof(Option))); |
| |
| for (int i = 0; i <= maxIndexOption; i++) |
| { |
| TestTerms(fieldsProducer, allOptionsHashSet, allOptions[i], options, true); |
| if (withPayloads) |
| { |
| // If we indexed w/ payloads, also test enums w/o accessing payloads: |
| |
| // LUCENENET: No EnumSet in .NET, so we have some extra work to do |
| // to populate the options. |
| ISet<Option> payloadsHashSet = new JCG.HashSet<Option>() { Option.PAYLOADS }; |
| var complementHashSet = new JCG.HashSet<Option>(allOptionsHashSet); |
| complementHashSet.SymmetricExceptWith(payloadsHashSet); // Complement of |
| TestTerms(fieldsProducer, complementHashSet, allOptions[i], options, true); |
| } |
| } |
| } |
| |
| [Test] |
| public virtual void TestDocsOnly() |
| { |
| TestFull(IndexOptions.DOCS_ONLY, false); |
| } |
| |
| [Test] |
| public virtual void TestDocsAndFreqs() |
| { |
| TestFull(IndexOptions.DOCS_AND_FREQS, false); |
| } |
| |
| [Test] |
| public virtual void TestDocsAndFreqsAndPositions() |
| { |
| TestFull(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, false); |
| } |
| |
| [Test] |
| public virtual void TestDocsAndFreqsAndPositionsAndPayloads() |
| { |
| TestFull(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, true); |
| } |
| |
| [Test] |
| public virtual void TestDocsAndFreqsAndPositionsAndOffsets() |
| { |
| TestFull(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, false); |
| } |
| |
| [Test] |
| public virtual void TestDocsAndFreqsAndPositionsAndOffsetsAndPayloads() |
| { |
| TestFull(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, true); |
| } |
| |
| [Test] |
| public virtual void TestRandom() |
| { |
| int iters = 5; |
| |
| for (int iter = 0; iter < iters; iter++) |
| { |
| DirectoryInfo path = CreateTempDir("testPostingsFormat"); |
| using (Directory dir = NewFSDirectory(path)) |
| { |
| |
| bool indexPayloads = Random.NextBoolean(); |
| // TODO test thread safety of buildIndex too |
| using FieldsProducer fieldsProducer = BuildIndex(dir, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, indexPayloads, false); |
| TestFields(fieldsProducer); |
| |
| // NOTE: you can also test "weaker" index options than |
| // you indexed with: |
| TestTerms(fieldsProducer, |
| // LUCENENET: No need to skip options here |
| new JCG.HashSet<Option>((Option[])Enum.GetValues(typeof(Option))), |
| IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, |
| IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, |
| false); |
| // fieldsProducer = null; // LUCENENET: No can do - out of scope |
| |
| } // dir.Dispose(); |
| System.IO.Directory.Delete(path.FullName, true); |
| } |
| } |
| |
| protected override void AddRandomFields(Document doc) |
| { |
| |
| foreach (IndexOptions opts in Enum.GetValues(typeof(IndexOptions))) |
| { |
| // LUCENENET: Skip our NONE option |
| if (opts == IndexOptions.NONE) |
| { |
| continue; |
| } |
| |
| string field = "f_" + opts; |
| string pf = TestUtil.GetPostingsFormat(Codec.Default, field); |
| if (opts == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS && DoesntSupportOffsets.Contains(pf)) |
| { |
| continue; |
| } |
| var ft = new FieldType { IndexOptions = opts, IsIndexed = true, OmitNorms = true}; |
| ft.Freeze(); |
| int numFields = Random.Next(5); |
| for (int j = 0; j < numFields; ++j) |
| { |
| doc.Add(new Field("f_" + opts, TestUtil.RandomSimpleString(Random, 2), ft)); |
| } |
| } |
| } |
| } |
| } |