LUCENE-5501: Improved out-of-order collection testing.

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1584829 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Adrien Grand 2014-04-04 17:08:01 +00:00
parent 6414781969
commit f49524fb57
6 changed files with 165 additions and 93 deletions

View File

@ -138,7 +138,8 @@ public class TestBooleanScorer extends LuceneTestCase {
w.addDocument(doc); w.addDocument(doc);
final IndexReader r = w.getReader(); final IndexReader r = w.getReader();
w.close(); w.close();
final IndexSearcher s = newSearcher(r); // we don't wrap with AssertingIndexSearcher in order to have the original scorer in setScorer.
final IndexSearcher s = newSearcher(r, true, false);
final BooleanQuery q = new BooleanQuery(); final BooleanQuery q = new BooleanQuery();
for(int term=0;term<33;term++) { for(int term=0;term<33;term++) {
@ -154,7 +155,7 @@ public class TestBooleanScorer extends LuceneTestCase {
@Override @Override
public void setScorer(Scorer scorer) { public void setScorer(Scorer scorer) {
// Make sure we got BooleanScorer: // Make sure we got BooleanScorer:
final Class<?> clazz = scorer instanceof AssertingScorer ? ((AssertingScorer) scorer).getIn().getClass() : scorer.getClass(); final Class<?> clazz = scorer.getClass();
assertEquals("Scorer is implemented by wrong class", FakeScorer.class.getName(), clazz.getName()); assertEquals("Scorer is implemented by wrong class", FakeScorer.class.getName(), clazz.getName());
} }

View File

@ -91,7 +91,8 @@ public class TestConstantScoreQuery extends LuceneTestCase {
reader = writer.getReader(); reader = writer.getReader();
writer.close(); writer.close();
searcher = newSearcher(reader); // we don't wrap with AssertingIndexSearcher in order to have the original scorer in setScorer.
searcher = newSearcher(reader, true, false);
// set a similarity that does not normalize our boost away // set a similarity that does not normalize our boost away
searcher.setSimilarity(new DefaultSimilarity() { searcher.setSimilarity(new DefaultSimilarity() {

View File

@ -18,93 +18,37 @@ package org.apache.lucene.search;
*/ */
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.WeakHashMap;
import org.apache.lucene.index.DocsEnum; /** A crazy {@link BulkScorer} that wraps another {@link BulkScorer}
import org.apache.lucene.util.VirtualMethod;
/** A crazy {@link BulkScorer} that wraps a {@link Scorer}
* but shuffles the order of the collected documents. */ * but shuffles the order of the collected documents. */
public class AssertingBulkOutOfOrderScorer extends BulkScorer { public class AssertingBulkOutOfOrderScorer extends BulkScorer {
final BulkScorer in;
final Random random; final Random random;
final Scorer scorer;
public AssertingBulkOutOfOrderScorer(Random random, Scorer scorer) { public AssertingBulkOutOfOrderScorer(Random random, BulkScorer in) {
this.in = in;
this.random = random; this.random = random;
this.scorer = scorer;
}
private void shuffle(int[] docIDs, float[] scores, int[] freqs, int size) {
for (int i = size - 1; i > 0; --i) {
final int other = random.nextInt(i + 1);
final int tmpDoc = docIDs[i];
docIDs[i] = docIDs[other];
docIDs[other] = tmpDoc;
final float tmpScore = scores[i];
scores[i] = scores[other];
scores[other] = tmpScore;
final int tmpFreq = freqs[i];
freqs[i] = freqs[other];
freqs[other] = tmpFreq;
}
}
private static void flush(int[] docIDs, float[] scores, int[] freqs, int size,
FakeScorer scorer, LeafCollector collector) throws IOException {
for (int i = 0; i < size; ++i) {
scorer.doc = docIDs[i];
scorer.freq = freqs[i];
scorer.score = scores[i];
collector.collect(scorer.doc);
}
} }
@Override @Override
public boolean score(LeafCollector collector, int max) throws IOException { public boolean score(LeafCollector collector, int max) throws IOException {
if (scorer.docID() == -1) { final RandomOrderCollector randomCollector = new RandomOrderCollector(random, collector);
scorer.nextDoc(); final boolean remaining = in.score(randomCollector, max);
} randomCollector.flush();
return remaining;
}
FakeScorer fake = new FakeScorer(); @Override
collector.setScorer(fake); public void score(LeafCollector collector) throws IOException {
final RandomOrderCollector randomCollector = new RandomOrderCollector(random, collector);
final int bufferSize = 1 + random.nextInt(100); in.score(randomCollector);
final int[] docIDs = new int[bufferSize]; randomCollector.flush();
final float[] scores = new float[bufferSize];
final int[] freqs = new int[bufferSize];
int buffered = 0;
int doc = scorer.docID();
while (doc < max) {
docIDs[buffered] = doc;
scores[buffered] = scorer.score();
freqs[buffered] = scorer.freq();
if (++buffered == bufferSize) {
shuffle(docIDs, scores, freqs, buffered);
flush(docIDs, scores, freqs, buffered, fake, collector);
buffered = 0;
}
doc = scorer.nextDoc();
}
shuffle(docIDs, scores, freqs, buffered);
flush(docIDs, scores, freqs, buffered, fake, collector);
return doc != Scorer.NO_MORE_DOCS;
} }
@Override @Override
public String toString() { public String toString() {
return "AssertingBulkOutOfOrderScorer(" + scorer + ")"; return "AssertingBulkOutOfOrderScorer(" + in + ")";
} }
} }

View File

@ -79,22 +79,20 @@ class AssertingWeight extends Weight {
if (AssertingBulkScorer.shouldWrap(inScorer)) { if (AssertingBulkScorer.shouldWrap(inScorer)) {
// The incoming scorer already has a specialized // The incoming scorer already has a specialized
// implementation for BulkScorer, so we should use it: // implementation for BulkScorer, so we should use it:
return AssertingBulkScorer.wrap(new Random(random.nextLong()), inScorer); inScorer = AssertingBulkScorer.wrap(new Random(random.nextLong()), inScorer);
} else if (scoreDocsInOrder == false && random.nextBoolean()) { } else if (random.nextBoolean()) {
// Let super wrap this.scorer instead, so we use
// AssertingScorer:
inScorer = super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
}
if (scoreDocsInOrder == false && random.nextBoolean()) {
// The caller claims it can handle out-of-order // The caller claims it can handle out-of-order
// docs; let's confirm that by pulling docs and // docs; let's confirm that by pulling docs and
// randomly shuffling them before collection: // randomly shuffling them before collection:
//Scorer scorer = in.scorer(context, acceptDocs); inScorer = new AssertingBulkOutOfOrderScorer(new Random(random.nextLong()), inScorer);
Scorer scorer = scorer(context, acceptDocs);
// Scorer should not be null if bulkScorer wasn't:
assert scorer != null;
return new AssertingBulkOutOfOrderScorer(new Random(random.nextLong()), scorer);
} else {
// Let super wrap this.scorer instead, so we use
// AssertingScorer:
return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
} }
return inScorer;
} }
@Override @Override

View File

@ -0,0 +1,106 @@
package org.apache.lucene.search;
/*
* 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.
*/
import java.io.IOException;
import java.util.Random;
/** Randomize collection order. Don't forget to call {@link #flush()} when
* collection is finished to collect buffered documents. */
final class RandomOrderCollector extends FilterLeafCollector {
final Random random;
Scorer scorer;
FakeScorer fakeScorer;
int buffered;
final int bufferSize;
final int[] docIDs;
final float[] scores;
final int[] freqs;
RandomOrderCollector(Random random, LeafCollector in) {
super(in);
if (!in.acceptsDocsOutOfOrder()) {
throw new IllegalArgumentException();
}
this.random = random;
bufferSize = 1 + random.nextInt(100);
docIDs = new int[bufferSize];
scores = new float[bufferSize];
freqs = new int[bufferSize];
buffered = 0;
}
@Override
public void setScorer(Scorer scorer) throws IOException {
this.scorer = scorer;
fakeScorer = new FakeScorer();
in.setScorer(fakeScorer);
}
private void shuffle() {
for (int i = buffered - 1; i > 0; --i) {
final int other = random.nextInt(i + 1);
final int tmpDoc = docIDs[i];
docIDs[i] = docIDs[other];
docIDs[other] = tmpDoc;
final float tmpScore = scores[i];
scores[i] = scores[other];
scores[other] = tmpScore;
final int tmpFreq = freqs[i];
freqs[i] = freqs[other];
freqs[other] = tmpFreq;
}
}
public void flush() throws IOException {
shuffle();
for (int i = 0; i < buffered; ++i) {
fakeScorer.doc = docIDs[i];
fakeScorer.freq = freqs[i];
fakeScorer.score = scores[i];
in.collect(fakeScorer.doc);
}
buffered = 0;
}
@Override
public void collect(int doc) throws IOException {
docIDs[buffered] = doc;
scores[buffered] = scorer.score();
try {
freqs[buffered] = scorer.freq();
} catch (UnsupportedOperationException e) {
freqs[buffered] = -1;
}
if (++buffered == bufferSize) {
flush();
}
}
@Override
public boolean acceptsDocsOutOfOrder() {
return in.acceptsDocsOutOfOrder();
}
}

View File

@ -1287,13 +1287,23 @@ public abstract class LuceneTestCase extends Assert {
public static IndexSearcher newSearcher(IndexReader r) { public static IndexSearcher newSearcher(IndexReader r) {
return newSearcher(r, true); return newSearcher(r, true);
} }
/**
* Create a new searcher over the reader. This searcher might randomly use
* threads.
*/
public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) {
return newSearcher(r, maybeWrap, true);
}
/** /**
* Create a new searcher over the reader. This searcher might randomly use * Create a new searcher over the reader. This searcher might randomly use
* threads. if <code>maybeWrap</code> is true, this searcher might wrap the * threads. if <code>maybeWrap</code> is true, this searcher might wrap the
* reader with one that returns null for getSequentialSubReaders. * reader with one that returns null for getSequentialSubReaders. If
* <code>wrapWithAssertions</code> is true, this searcher might be an
* {@link AssertingIndexSearcher} instance.
*/ */
public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) { public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap, boolean wrapWithAssertions) {
Random random = random(); Random random = random();
if (usually()) { if (usually()) {
if (maybeWrap) { if (maybeWrap) {
@ -1314,7 +1324,12 @@ public abstract class LuceneTestCase extends Assert {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
IndexSearcher ret = random.nextBoolean() ? new AssertingIndexSearcher(random, r) : new AssertingIndexSearcher(random, r.getContext()); final IndexSearcher ret;
if (wrapWithAssertions) {
ret = random.nextBoolean() ? new AssertingIndexSearcher(random, r) : new AssertingIndexSearcher(random, r.getContext());
} else {
ret = random.nextBoolean() ? new IndexSearcher(r) : new IndexSearcher(r.getContext());
}
ret.setSimilarity(classEnvRule.similarity); ret.setSimilarity(classEnvRule.similarity);
return ret; return ret;
} else { } else {
@ -1341,9 +1356,16 @@ public abstract class LuceneTestCase extends Assert {
} }
}); });
} }
IndexSearcher ret = random.nextBoolean() IndexSearcher ret;
? new AssertingIndexSearcher(random, r, ex) if (wrapWithAssertions) {
: new AssertingIndexSearcher(random, r.getContext(), ex); ret = random.nextBoolean()
? new AssertingIndexSearcher(random, r, ex)
: new AssertingIndexSearcher(random, r.getContext(), ex);
} else {
ret = random.nextBoolean()
? new IndexSearcher(r, ex)
: new IndexSearcher(r.getContext(), ex);
}
ret.setSimilarity(classEnvRule.similarity); ret.setSimilarity(classEnvRule.similarity);
return ret; return ret;
} }