blob: 1798840f425f1210510c742e235176c9038dbdcd [file] [log] [blame]
/*
* 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 java.util.Random;
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 {
Random random = random();
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
int maxIterations = TEST_NIGHTLY ? Integer.MAX_VALUE : 10;
int iterations = 0;
for (int inc = 2; inc < 1000; inc += TestUtil.nextInt(random, 1, 100)) {
// don't let this test run too many times, even if it gets unlucky with "inc"
if (iterations >= maxIterations) {
break;
}
iterations++;
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 {
Random random = random();
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 {
Random random = random();
// 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;
}
}