/* * 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. */ package org.apache.lucene.util; import static org.apache.lucene.util.BaseBitSetTestCase.randomSet; import java.io.IOException; import java.util.BitSet; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; /** Base test class for {@link DocIdSet}s. */ public abstract class BaseDocIdSetTestCase<T extends DocIdSet> extends LuceneTestCase { /** Create a copy of the given {@link BitSet} which has <code>length</code> bits. */ public abstract T copyOf(BitSet bs, int length) throws IOException; /** Test length=0. */ public void testNoBit() throws IOException { final BitSet bs = new BitSet(1); final T copy = copyOf(bs, 1); assertEquals(1, bs, copy); } /** Test length=1. */ public void test1Bit() throws IOException { final BitSet bs = new BitSet(1); if (random().nextBoolean()) { bs.set(0); } final T copy = copyOf(bs, 1); assertEquals(1, bs, copy); } /** Test length=2. */ public void test2Bits() throws IOException { final BitSet bs = new BitSet(2); if (random().nextBoolean()) { bs.set(0); } if (random().nextBoolean()) { bs.set(1); } final T copy = copyOf(bs, 2); assertEquals(2, bs, copy); } /** Compare the content of the set against a {@link BitSet}. */ public void testAgainstBitSet() throws IOException { final int numBits = TestUtil.nextInt(random(), 100, 1 << 20); // test various random sets with various load factors for (float percentSet : new float[] {0f, 0.0001f, random().nextFloat(), 0.9f, 1f}) { final BitSet set = randomSet(numBits, percentSet); final T copy = copyOf(set, numBits); assertEquals(numBits, set, copy); } // test one doc BitSet set = new BitSet(numBits); set.set(0); // 0 first T copy = copyOf(set, numBits); assertEquals(numBits, set, copy); set.clear(0); set.set(random().nextInt(numBits)); copy = copyOf(set, numBits); // then random index assertEquals(numBits, set, copy); // test regular increments for (int inc = 2; inc < 1000; inc += TestUtil.nextInt(random(), 1, 100)) { set = new BitSet(numBits); for (int d = random().nextInt(10); d < numBits; d += inc) { set.set(d); } copy = copyOf(set, numBits); assertEquals(numBits, set, copy); } } /** Test ram usage estimation. */ public void testRamBytesUsed() throws IOException { final int iters = 100; for (int i = 0; i < iters; ++i) { final int pow = random().nextInt(20); final int maxDoc = TestUtil.nextInt(random(), 1, 1 << pow); final int numDocs = TestUtil.nextInt(random(), 0, Math.min(maxDoc, 1 << TestUtil.nextInt(random(), 0, pow))); final BitSet set = randomSet(maxDoc, numDocs); final DocIdSet copy = copyOf(set, maxDoc); final long actualBytes = ramBytesUsed(copy, maxDoc); final long expectedBytes = copy.ramBytesUsed(); assertEquals(expectedBytes, actualBytes); } } /** Assert that the content of the {@link DocIdSet} is the same as the content of the {@link BitSet}. */ public void assertEquals(int numBits, BitSet ds1, T ds2) throws IOException { // nextDoc DocIdSetIterator it2 = ds2.iterator(); if (it2 == null) { assertEquals(-1, ds1.nextSetBit(0)); } else { assertEquals(-1, it2.docID()); for (int doc = ds1.nextSetBit(0); doc != -1; doc = ds1.nextSetBit(doc + 1)) { assertEquals(doc, it2.nextDoc()); assertEquals(doc, it2.docID()); } assertEquals(DocIdSetIterator.NO_MORE_DOCS, it2.nextDoc()); assertEquals(DocIdSetIterator.NO_MORE_DOCS, it2.docID()); } // nextDoc / advance it2 = ds2.iterator(); if (it2 == null) { assertEquals(-1, ds1.nextSetBit(0)); } else { for (int doc = -1; doc != DocIdSetIterator.NO_MORE_DOCS;) { if (random().nextBoolean()) { doc = ds1.nextSetBit(doc + 1); if (doc == -1) { doc = DocIdSetIterator.NO_MORE_DOCS; } assertEquals(doc, it2.nextDoc()); assertEquals(doc, it2.docID()); } else { final int target = doc + 1 + random().nextInt(random().nextBoolean() ? 64 : Math.max(numBits / 8, 1)); doc = ds1.nextSetBit(target); if (doc == -1) { doc = DocIdSetIterator.NO_MORE_DOCS; } assertEquals(doc, it2.advance(target)); assertEquals(doc, it2.docID()); } } } // bits() final Bits bits = ds2.bits(); if (bits != null) { // test consistency between bits and iterator it2 = ds2.iterator(); for (int previousDoc = -1, doc = it2.nextDoc(); ; previousDoc = doc, doc = it2.nextDoc()) { final int max = doc == DocIdSetIterator.NO_MORE_DOCS ? bits.length() : doc; for (int i = previousDoc + 1; i < max; ++i) { assertEquals(false, bits.get(i)); } if (doc == DocIdSetIterator.NO_MORE_DOCS) { break; } assertEquals(true, bits.get(doc)); } } } private static class Dummy { @SuppressWarnings("unused") Object o1, o2; } // same as RamUsageTester.sizeOf but tries to not take into account resources // that might be shared across instances private long ramBytesUsed(DocIdSet set, int length) throws IOException { Dummy dummy = new Dummy(); dummy.o1 = copyOf(new BitSet(length), length); dummy.o2 = set; long bytes1 = RamUsageTester.sizeOf(dummy); dummy.o2 = null; long bytes2 = RamUsageTester.sizeOf(dummy); return bytes1 - bytes2; } }