LUCENE-2037: switch to junit 4.7

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@887569 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2009-12-05 18:27:27 +00:00
parent 9ee4ce0fd5
commit f159d7006d
13 changed files with 624 additions and 274 deletions

View File

@ -63,6 +63,9 @@ Build
Test Cases
* LUCENE-2037 Allow Junit4 tests in our envrionment (Erick Erickson
via Mike McCandless)
* LUCENE-1844: Speed up the unit tests (Mark Miller, Erick Erickson,
Mike McCandless)

View File

@ -47,7 +47,7 @@
<property name="year" value="2000-${current.year}"/>
<property name="final.name" value="lucene-${name}-${version}"/>
<property name="junit.jar" value="junit-3.8.2.jar"/>
<property name="junit.jar" value="junit-4.7.jar"/>
<property name="junit-location.jar" value="${common.dir}/lib/${junit.jar}"/>
<path id="junit-path">
<pathelement location="${junit-location.jar}"/>

View File

@ -1,2 +0,0 @@
AnyObjectId[c8f711d050eff209321f799d85ebb3bbe305d481] was removed in git history.
Apache SVN contains full history.

2
lib/junit-4.7.jar Executable file
View File

@ -0,0 +1,2 @@
AnyObjectId[700ad6952c76177568f705f2666ea994da565ba4] was removed in git history.
Apache SVN contains full history.

View File

@ -25,20 +25,23 @@ import org.apache.lucene.document.Fieldable;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.LuceneTestCaseJ4;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
/**
* Setup for function tests
*/
public abstract class FunctionTestSetup extends LuceneTestCase {
@Ignore
public class FunctionTestSetup extends LuceneTestCaseJ4 {
/**
* Actual score computation order is slightly different than assumptios
* this allows for a small amount of variation
*/
public static float TEST_SCORE_TOLERANCE_DELTA = 0.001f;
protected static float TEST_SCORE_TOLERANCE_DELTA = 0.001f;
protected static final boolean DBG = false; // change to true for logging to print
protected static final int N_DOCS = 17; // select a primary number > 2
@ -47,62 +50,57 @@ public abstract class FunctionTestSetup extends LuceneTestCase {
protected static final String TEXT_FIELD = "text";
protected static final String INT_FIELD = "iii";
protected static final String FLOAT_FIELD = "fff";
private static final String DOC_TEXT_LINES[] = {
"Well, this is just some plain text we use for creating the ",
"test documents. It used to be a text from an online collection ",
"devoted to first aid, but if there was there an (online) lawyers ",
"first aid collection with legal advices, \"it\" might have quite ",
"probably advised one not to include \"it\"'s text or the text of ",
"any other online collection in one's code, unless one has money ",
"that one don't need and one is happy to donate for lawyers ",
"charity. Anyhow at some point, rechecking the usage of this text, ",
"it became uncertain that this text is free to use, because ",
"the web site in the disclaimer of he eBook containing that text ",
"was not responding anymore, and at the same time, in projGut, ",
"searching for first aid no longer found that eBook as well. ",
"So here we are, with a perhaps much less interesting ",
"text for the test, but oh much much safer. ",
};
protected Directory dir;
protected Analyzer anlzr;
/* @override constructor */
public FunctionTestSetup(String name) {
super(name);
}
/* @override */
private static final String DOC_TEXT_LINES[] = {
"Well, this is just some plain text we use for creating the ",
"test documents. It used to be a text from an online collection ",
"devoted to first aid, but if there was there an (online) lawyers ",
"first aid collection with legal advices, \"it\" might have quite ",
"probably advised one not to include \"it\"'s text or the text of ",
"any other online collection in one's code, unless one has money ",
"that one don't need and one is happy to donate for lawyers ",
"charity. Anyhow at some point, rechecking the usage of this text, ",
"it became uncertain that this text is free to use, because ",
"the web site in the disclaimer of he eBook containing that text ",
"was not responding anymore, and at the same time, in projGut, ",
"searching for first aid no longer found that eBook as well. ",
"So here we are, with a perhaps much less interesting ",
"text for the test, but oh much much safer. ",
};
protected Directory dir = null;
protected Analyzer anlzr = null;
@Override
protected void tearDown() throws Exception {
@After
public void tearDown() throws Exception {
super.tearDown();
dir = null;
anlzr = null;
}
/* @override */
@Override
protected void setUp() throws Exception {
@Before
public void setUp() throws Exception {
super.setUp();
// prepare a small index with just a few documents.
super.setUp();
dir = new RAMDirectory();
anlzr = new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_CURRENT);
IndexWriter iw = new IndexWriter(dir, anlzr,
IndexWriter.MaxFieldLength.LIMITED);
IndexWriter.MaxFieldLength.LIMITED);
// add docs not exactly in natural ID order, to verify we do check the order of docs by scores
int remaining = N_DOCS;
boolean done[] = new boolean[N_DOCS];
int i = 0;
while (remaining>0) {
while (remaining > 0) {
if (done[i]) {
throw new Exception("to set this test correctly N_DOCS="+N_DOCS+" must be primary and greater than 2!");
throw new Exception("to set this test correctly N_DOCS=" + N_DOCS + " must be primary and greater than 2!");
}
addDoc(iw,i);
addDoc(iw, i);
done[i] = true;
i = (i+4)%N_DOCS;
remaining --;
i = (i + 4) % N_DOCS;
remaining--;
}
iw.close();
}
@ -110,36 +108,36 @@ public abstract class FunctionTestSetup extends LuceneTestCase {
private void addDoc(IndexWriter iw, int i) throws Exception {
Document d = new Document();
Fieldable f;
int scoreAndID = i+1;
f = new Field(ID_FIELD,id2String(scoreAndID),Field.Store.YES,Field.Index.NOT_ANALYZED); // for debug purposes
int scoreAndID = i + 1;
f = new Field(ID_FIELD, id2String(scoreAndID), Field.Store.YES, Field.Index.NOT_ANALYZED); // for debug purposes
f.setOmitNorms(true);
d.add(f);
f = new Field(TEXT_FIELD,"text of doc"+scoreAndID+textLine(i),Field.Store.NO,Field.Index.ANALYZED); // for regular search
f = new Field(TEXT_FIELD, "text of doc" + scoreAndID + textLine(i), Field.Store.NO, Field.Index.ANALYZED); // for regular search
f.setOmitNorms(true);
d.add(f);
f = new Field(INT_FIELD,""+scoreAndID,Field.Store.NO,Field.Index.NOT_ANALYZED); // for function scoring
f = new Field(INT_FIELD, "" + scoreAndID, Field.Store.NO, Field.Index.NOT_ANALYZED); // for function scoring
f.setOmitNorms(true);
d.add(f);
f = new Field(FLOAT_FIELD,scoreAndID+".000",Field.Store.NO,Field.Index.NOT_ANALYZED); // for function scoring
f = new Field(FLOAT_FIELD, scoreAndID + ".000", Field.Store.NO, Field.Index.NOT_ANALYZED); // for function scoring
f.setOmitNorms(true);
d.add(f);
iw.addDocument(d);
log("added: "+d);
log("added: " + d);
}
// 17 --> ID00017
protected String id2String(int scoreAndID) {
String s = "000000000"+scoreAndID;
int n = (""+N_DOCS).length() + 3;
int k = s.length() - n;
return "ID"+s.substring(k);
String s = "000000000" + scoreAndID;
int n = ("" + N_DOCS).length() + 3;
int k = s.length() - n;
return "ID" + s.substring(k);
}
// some text line for regular search
private String textLine(int docNum) {
return DOC_TEXT_LINES[docNum % DOC_TEXT_LINES.length];
@ -147,11 +145,11 @@ public abstract class FunctionTestSetup extends LuceneTestCase {
// extract expected doc score from its ID Field: "ID7" --> 7.0
protected float expectedFieldScore(String docIDFieldVal) {
return Float.parseFloat(docIDFieldVal.substring(2));
return Float.parseFloat(docIDFieldVal.substring(2));
}
// debug messages (change DBG to true for anything to print)
protected void log (Object o) {
protected void log(Object o) {
if (DBG) {
System.out.println(o.toString());
}

View File

@ -17,11 +17,11 @@ package org.apache.lucene.search.function;
* limitations under the License.
*/
import java.io.IOException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
/**
* Holds all implementations of classes in the o.a.l.s.function package as a
* back-compatibility test. It does not run any tests per-se, however if
@ -34,7 +34,6 @@ final class JustCompileSearchFunction {
private static final String UNSUPPORTED_MSG = "unsupported: used for back-compat testing only !";
static final class JustCompileDocValues extends DocValues {
@Override
public float floatVal(int doc) {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
@ -44,7 +43,7 @@ final class JustCompileSearchFunction {
public String toString(int doc) {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
}
}
static final class JustCompileFieldCacheSource extends FieldCacheSource {
@ -65,14 +64,13 @@ final class JustCompileSearchFunction {
@Override
public DocValues getCachedFieldValues(FieldCache cache, String field,
IndexReader reader) throws IOException {
IndexReader reader) throws IOException {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
}
}
static final class JustCompileValueSource extends ValueSource {
@Override
public String description() {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
@ -92,7 +90,7 @@ final class JustCompileSearchFunction {
public int hashCode() {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
}
}
}

View File

@ -17,98 +17,114 @@ package org.apache.lucene.search.function;
* limitations under the License.
*/
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.*;
import org.apache.lucene.util.Version;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.HashMap;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.util.Version;
import java.util.Map;
/**
* Test CustomScoreQuery search.
*/
@SuppressWarnings({"MagicNumber"})
public class TestCustomScoreQuery extends FunctionTestSetup {
/* @override constructor */
public TestCustomScoreQuery(String name) {
super(name);
}
/** Test that CustomScoreQuery of Type.BYTE returns the expected scores. */
public void testCustomScoreByte () throws CorruptIndexException, Exception {
/**
* Test that CustomScoreQuery of Type.BYTE returns the expected scores.
*/
@Test
public void testCustomScoreByte() throws Exception, ParseException {
// INT field values are small enough to be parsed as byte
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.BYTE,1.0);
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.BYTE,2.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.BYTE, 1.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.BYTE, 2.0);
}
/** Test that CustomScoreQuery of Type.SHORT returns the expected scores. */
public void testCustomScoreShort () throws CorruptIndexException, Exception {
/**
* Test that CustomScoreQuery of Type.SHORT returns the expected scores.
*/
@Test
public void testCustomScoreShort() throws Exception, ParseException {
// INT field values are small enough to be parsed as short
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.SHORT,1.0);
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.SHORT,3.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.SHORT, 1.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.SHORT, 3.0);
}
/** Test that CustomScoreQuery of Type.INT returns the expected scores. */
public void testCustomScoreInt () throws CorruptIndexException, Exception {
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.INT,1.0);
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.INT,4.0);
/**
* Test that CustomScoreQuery of Type.INT returns the expected scores.
*/
@Test
public void testCustomScoreInt() throws Exception, ParseException {
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.INT, 1.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.INT, 4.0);
}
/** Test that CustomScoreQuery of Type.FLOAT returns the expected scores. */
public void testCustomScoreFloat () throws CorruptIndexException, Exception {
/**
* Test that CustomScoreQuery of Type.FLOAT returns the expected scores.
*/
@Test
public void testCustomScoreFloat() throws Exception, ParseException {
// INT field can be parsed as float
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.FLOAT,1.0);
doTestCustomScore(INT_FIELD,FieldScoreQuery.Type.FLOAT,5.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.FLOAT, 1.0);
doTestCustomScore(INT_FIELD, FieldScoreQuery.Type.FLOAT, 5.0);
// same values, but in flot format
doTestCustomScore(FLOAT_FIELD,FieldScoreQuery.Type.FLOAT,1.0);
doTestCustomScore(FLOAT_FIELD,FieldScoreQuery.Type.FLOAT,6.0);
doTestCustomScore(FLOAT_FIELD, FieldScoreQuery.Type.FLOAT, 1.0);
doTestCustomScore(FLOAT_FIELD, FieldScoreQuery.Type.FLOAT, 6.0);
}
// must have static class otherwise serialization tests fail
@SuppressWarnings({"SerializableHasSerializationMethods", "serial"})
private static class CustomAddQuery extends CustomScoreQuery {
// constructor
CustomAddQuery (Query q, ValueSourceQuery qValSrc) {
super(q,qValSrc);
CustomAddQuery(Query q, ValueSourceQuery qValSrc) {
super(q, qValSrc);
}
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#name() */
@Override
public String name() {
return "customAdd";
}
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#customScore(int, float, float) */
@Override
public float customScore(int doc, float subQueryScore, float valSrcScore) {
return subQueryScore + valSrcScore;
}
/* (non-Javadoc)@see org.apache.lucene.search.function.CustomScoreQuery#customExplain(int, org.apache.lucene.search.Explanation, org.apache.lucene.search.Explanation)*/
@Override
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) {
float valSrcScore = valSrcExpl==null ? 0 : valSrcExpl.getValue();
Explanation exp = new Explanation( valSrcScore + subQueryExpl.getValue(), "custom score: sum of:");
float valSrcScore = valSrcExpl == null ? 0 : valSrcExpl.getValue();
Explanation exp = new Explanation(valSrcScore + subQueryExpl.getValue(), "custom score: sum of:");
exp.addDetail(subQueryExpl);
if (valSrcExpl != null) {
exp.addDetail(valSrcExpl);
}
return exp;
}
return exp;
}
}
// must have static class otherwise serialization tests fail
@SuppressWarnings({"SerializableHasSerializationMethods", "serial"})
private static class CustomMulAddQuery extends CustomScoreQuery {
// constructor
CustomMulAddQuery(Query q, ValueSourceQuery qValSrc1, ValueSourceQuery qValSrc2) {
super(q,new ValueSourceQuery[]{qValSrc1,qValSrc2});
super(q, new ValueSourceQuery[]{qValSrc1, qValSrc2});
}
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#name() */
@Override
public String name() {
return "customMulAdd";
}
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#customScore(int, float, float) */
@Override
public float customScore(int doc, float subQueryScore, float valSrcScores[]) {
@ -119,7 +135,8 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
return subQueryScore + valSrcScores[0];
}
return (subQueryScore + valSrcScores[0]) * valSrcScores[1]; // we know there are two
}
}
/* (non-Javadoc)@see org.apache.lucene.search.function.CustomScoreQuery#customExplain(int, org.apache.lucene.search.Explanation, org.apache.lucene.search.Explanation)*/
@Override
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) {
@ -136,67 +153,67 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
Explanation exp2 = new Explanation(valSrcExpls[1].getValue() * exp.getValue(), "custom score: product of:");
exp2.addDetail(valSrcExpls[1]);
exp2.addDetail(exp);
return exp2;
}
return exp2;
}
}
// Test that FieldScoreQuery returns docs with expected score.
private void doTestCustomScore (String field, FieldScoreQuery.Type tp, double dboost) throws CorruptIndexException, Exception {
private void doTestCustomScore(String field, FieldScoreQuery.Type tp, double dboost) throws Exception, ParseException {
float boost = (float) dboost;
IndexSearcher s = new IndexSearcher(dir, true);
FieldScoreQuery qValSrc = new FieldScoreQuery(field,tp); // a query that would score by the field
QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, TEXT_FIELD,anlzr);
FieldScoreQuery qValSrc = new FieldScoreQuery(field, tp); // a query that would score by the field
QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, TEXT_FIELD, anlzr);
String qtxt = "first aid text"; // from the doc texts in FunctionQuerySetup.
// regular (boolean) query.
Query q1 = qp.parse(qtxt);
Query q1 = qp.parse(qtxt);
log(q1);
// custom query, that should score the same as q1.
CustomScoreQuery q2CustomNeutral = new CustomScoreQuery(q1);
Query q2CustomNeutral = new CustomScoreQuery(q1);
q2CustomNeutral.setBoost(boost);
log(q2CustomNeutral);
// custom query, that should (by default) multiply the scores of q1 by that of the field
CustomScoreQuery q3CustomMul = new CustomScoreQuery(q1,qValSrc);
CustomScoreQuery q3CustomMul = new CustomScoreQuery(q1, qValSrc);
q3CustomMul.setStrict(true);
q3CustomMul.setBoost(boost);
log(q3CustomMul);
// custom query, that should add the scores of q1 to that of the field
CustomScoreQuery q4CustomAdd = new CustomAddQuery(q1,qValSrc);
CustomScoreQuery q4CustomAdd = new CustomAddQuery(q1, qValSrc);
q4CustomAdd.setStrict(true);
q4CustomAdd.setBoost(boost);
log(q4CustomAdd);
// custom query, that multiplies and adds the field score to that of q1
CustomScoreQuery q5CustomMulAdd = new CustomMulAddQuery(q1,qValSrc,qValSrc);
CustomScoreQuery q5CustomMulAdd = new CustomMulAddQuery(q1, qValSrc, qValSrc);
q5CustomMulAdd.setStrict(true);
q5CustomMulAdd.setBoost(boost);
log(q5CustomMulAdd);
// do al the searches
TopDocs td1 = s.search(q1,null,1000);
TopDocs td2CustomNeutral = s.search(q2CustomNeutral,null,1000);
TopDocs td3CustomMul = s.search(q3CustomMul,null,1000);
TopDocs td4CustomAdd = s.search(q4CustomAdd,null,1000);
TopDocs td5CustomMulAdd = s.search(q5CustomMulAdd,null,1000);
TopDocs td1 = s.search(q1, null, 1000);
TopDocs td2CustomNeutral = s.search(q2CustomNeutral, null, 1000);
TopDocs td3CustomMul = s.search(q3CustomMul, null, 1000);
TopDocs td4CustomAdd = s.search(q4CustomAdd, null, 1000);
TopDocs td5CustomMulAdd = s.search(q5CustomMulAdd, null, 1000);
// put results in map so we can verify the scores although they have changed
HashMap<Integer,Float> h1 = topDocsToMap(td1);
HashMap<Integer,Float> h2CustomNeutral = topDocsToMap(td2CustomNeutral);
HashMap<Integer,Float> h3CustomMul = topDocsToMap(td3CustomMul);
HashMap<Integer,Float> h4CustomAdd = topDocsToMap(td4CustomAdd);
HashMap<Integer,Float> h5CustomMulAdd = topDocsToMap(td5CustomMulAdd);
Map<Integer,Float> h1 = topDocsToMap(td1);
Map<Integer,Float> h2CustomNeutral = topDocsToMap(td2CustomNeutral);
Map<Integer,Float> h3CustomMul = topDocsToMap(td3CustomMul);
Map<Integer,Float> h4CustomAdd = topDocsToMap(td4CustomAdd);
Map<Integer,Float> h5CustomMulAdd = topDocsToMap(td5CustomMulAdd);
verifyResults(boost, s,
h1, h2CustomNeutral, h3CustomMul, h4CustomAdd, h5CustomMulAdd,
q1, q2CustomNeutral, q3CustomMul, q4CustomAdd, q5CustomMulAdd);
}
// verify results are as expected.
private void verifyResults(float boost, IndexSearcher s,
HashMap<Integer,Float> h1, HashMap<Integer,Float> h2customNeutral, HashMap<Integer,Float> h3CustomMul, HashMap<Integer,Float> h4CustomAdd, HashMap<Integer,Float> h5CustomMulAdd,
Map<Integer,Float> h1, Map<Integer,Float> h2customNeutral, Map<Integer,Float> h3CustomMul, Map<Integer,Float> h4CustomAdd, Map<Integer,Float> h5CustomMulAdd,
Query q1, Query q2, Query q3, Query q4, Query q5) throws Exception {
// verify numbers of matches
@ -213,37 +230,36 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
QueryUtils.check(q5,s);
// verify scores ratios
for (final Integer x : h1.keySet()) {
for (final Integer doc : h1.keySet()) {
int doc = x.intValue();
log("doc = "+doc);
float fieldScore = expectedFieldScore(s.getIndexReader().document(doc).get(ID_FIELD));
log("fieldScore = "+fieldScore);
assertTrue("fieldScore should not be 0",fieldScore>0);
log("fieldScore = " + fieldScore);
assertTrue("fieldScore should not be 0", fieldScore > 0);
float score1 = h1.get(x).floatValue();
float score1 = h1.get(doc);
logResult("score1=", s, q1, doc, score1);
float score2 = h2customNeutral.get(x).floatValue();
float score2 = h2customNeutral.get(doc);
logResult("score2=", s, q2, doc, score2);
assertEquals("same score (just boosted) for neutral", boost * score1, score2, TEST_SCORE_TOLERANCE_DELTA);
float score3 = h3CustomMul.get(x).floatValue();
float score3 = h3CustomMul.get(doc);
logResult("score3=", s, q3, doc, score3);
assertEquals("new score for custom mul", boost * fieldScore * score1, score3, TEST_SCORE_TOLERANCE_DELTA);
float score4 = h4CustomAdd.get(x).floatValue();
float score4 = h4CustomAdd.get(doc);
logResult("score4=", s, q4, doc, score4);
assertEquals("new score for custom add", boost * (fieldScore + score1), score4, TEST_SCORE_TOLERANCE_DELTA);
float score5 = h5CustomMulAdd.get(x).floatValue();
float score5 = h5CustomMulAdd.get(doc);
logResult("score5=", s, q5, doc, score5);
assertEquals("new score for custom mul add", boost * fieldScore * (score1 + fieldScore), score5, TEST_SCORE_TOLERANCE_DELTA);
}
}
private void logResult(String msg, IndexSearcher s, Query q, int doc, float score1) throws IOException {
private void logResult(String msg, Searcher s, Query q, int doc, float score1) throws IOException {
log(msg+" "+score1);
log("Explain by: "+q);
log(s.explain(q,doc));
@ -251,10 +267,10 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
// since custom scoring modifies the order of docs, map results
// by doc ids so that we can later compare/verify them
private HashMap<Integer,Float> topDocsToMap(TopDocs td) {
HashMap<Integer,Float> h = new HashMap<Integer,Float>();
private Map<Integer,Float> topDocsToMap(TopDocs td) {
Map<Integer,Float> h = new HashMap<Integer,Float>();
for (int i=0; i<td.totalHits; i++) {
h.put(Integer.valueOf(td.scoreDocs[i].doc), Float.valueOf(td.scoreDocs[i].score));
h.put(td.scoreDocs[i].doc, td.scoreDocs[i].score);
}
return h;
}

View File

@ -17,18 +17,17 @@ package org.apache.lucene.search.function;
* limitations under the License.
*/
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.LuceneTestCaseJ4;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* DocValues TestCase
*/
public class TestDocValues extends LuceneTestCase {
/* @override constructor */
public TestDocValues(String name) {
super(name);
}
public class TestDocValues extends LuceneTestCaseJ4 {
@Test
public void testGetMinValue() {
float[] innerArray = new float[] { 1.0f, 2.0f, -1.0f, 100.0f };
DocValuesTestImpl docValues = new DocValuesTestImpl(innerArray);
@ -41,7 +40,7 @@ public class TestDocValues extends LuceneTestCase {
assertTrue("max is NaN - no values in inner array", Float.isNaN(docValues
.getMinValue()));
}
@Test
public void testGetMaxValue() {
float[] innerArray = new float[] { 1.0f, 2.0f, -1.0f, 10.0f };
DocValuesTestImpl docValues = new DocValuesTestImpl(innerArray);
@ -66,6 +65,7 @@ public class TestDocValues extends LuceneTestCase {
.getMaxValue()));
}
@Test
public void testGetAverageValue() {
float[] innerArray = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };
DocValuesTestImpl docValues = new DocValuesTestImpl(innerArray);
@ -97,7 +97,6 @@ public class TestDocValues extends LuceneTestCase {
/**
* @see org.apache.lucene.search.function.DocValues#floatVal(int)
*/
/* @Override */
@Override
public float floatVal(int doc) {
return innerArray[doc];
@ -106,7 +105,6 @@ public class TestDocValues extends LuceneTestCase {
/**
* @see org.apache.lucene.search.function.DocValues#toString(int)
*/
/* @Override */
@Override
public String toString(int doc) {
return Integer.toString(doc);

View File

@ -18,14 +18,16 @@ package org.apache.lucene.search.function;
*/
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test FieldScoreQuery search.
@ -38,32 +40,32 @@ import org.apache.lucene.search.TopDocs;
* <p>
* The exact score tests use TopDocs top to verify the exact score.
*/
@SuppressWarnings({"UseOfSystemOutOrSystemErr"})
public class TestFieldScoreQuery extends FunctionTestSetup {
/* @override constructor */
public TestFieldScoreQuery(String name) {
super(name);
}
/** Test that FieldScoreQuery of Type.BYTE returns docs in expected order. */
public void testRankByte () throws CorruptIndexException, Exception {
@Test
public void testRankByte () throws Exception {
// INT field values are small enough to be parsed as byte
doTestRank(INT_FIELD,FieldScoreQuery.Type.BYTE);
}
/** Test that FieldScoreQuery of Type.SHORT returns docs in expected order. */
public void testRankShort () throws CorruptIndexException, Exception {
@Test
public void testRankShort () throws Exception {
// INT field values are small enough to be parsed as short
doTestRank(INT_FIELD,FieldScoreQuery.Type.SHORT);
}
/** Test that FieldScoreQuery of Type.INT returns docs in expected order. */
public void testRankInt () throws CorruptIndexException, Exception {
@Test
public void testRankInt () throws Exception {
doTestRank(INT_FIELD,FieldScoreQuery.Type.INT);
}
/** Test that FieldScoreQuery of Type.FLOAT returns docs in expected order. */
public void testRankFloat () throws CorruptIndexException, Exception {
@Test
public void testRankFloat () throws Exception {
// INT field can be parsed as float
doTestRank(INT_FIELD,FieldScoreQuery.Type.FLOAT);
// same values, but in flot format
@ -71,7 +73,7 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
}
// Test that FieldScoreQuery returns docs in expected order.
private void doTestRank (String field, FieldScoreQuery.Type tp) throws CorruptIndexException, Exception {
private void doTestRank (String field, FieldScoreQuery.Type tp) throws Exception {
IndexSearcher s = new IndexSearcher(dir, true);
Query q = new FieldScoreQuery(field,tp);
log("test: "+q);
@ -89,24 +91,28 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
}
/** Test that FieldScoreQuery of Type.BYTE returns the expected scores. */
public void testExactScoreByte () throws CorruptIndexException, Exception {
@Test
public void testExactScoreByte () throws Exception {
// INT field values are small enough to be parsed as byte
doTestExactScore(INT_FIELD,FieldScoreQuery.Type.BYTE);
}
/** Test that FieldScoreQuery of Type.SHORT returns the expected scores. */
public void testExactScoreShort () throws CorruptIndexException, Exception {
@Test
public void testExactScoreShort () throws Exception {
// INT field values are small enough to be parsed as short
doTestExactScore(INT_FIELD,FieldScoreQuery.Type.SHORT);
}
/** Test that FieldScoreQuery of Type.INT returns the expected scores. */
public void testExactScoreInt () throws CorruptIndexException, Exception {
@Test
public void testExactScoreInt () throws Exception {
doTestExactScore(INT_FIELD,FieldScoreQuery.Type.INT);
}
/** Test that FieldScoreQuery of Type.FLOAT returns the expected scores. */
public void testExactScoreFloat () throws CorruptIndexException, Exception {
@Test
public void testExactScoreFloat () throws Exception {
// INT field can be parsed as float
doTestExactScore(INT_FIELD,FieldScoreQuery.Type.FLOAT);
// same values, but in flot format
@ -114,40 +120,44 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
}
// Test that FieldScoreQuery returns docs with expected score.
private void doTestExactScore (String field, FieldScoreQuery.Type tp) throws CorruptIndexException, Exception {
private void doTestExactScore (String field, FieldScoreQuery.Type tp) throws Exception {
IndexSearcher s = new IndexSearcher(dir, true);
Query q = new FieldScoreQuery(field,tp);
TopDocs td = s.search(q,null,1000);
assertEquals("All docs should be matched!",N_DOCS,td.totalHits);
ScoreDoc sd[] = td.scoreDocs;
for (int i=0; i<sd.length; i++) {
float score = sd[i].score;
log(s.explain(q,sd[i].doc));
String id = s.getIndexReader().document(sd[i].doc).get(ID_FIELD);
for (ScoreDoc aSd : sd) {
float score = aSd.score;
log(s.explain(q, aSd.doc));
String id = s.getIndexReader().document(aSd.doc).get(ID_FIELD);
float expectedScore = expectedFieldScore(id); // "ID7" --> 7.0
assertEquals("score of "+id+" shuould be "+expectedScore+" != "+score, expectedScore, score, TEST_SCORE_TOLERANCE_DELTA);
assertEquals("score of " + id + " shuould be " + expectedScore + " != " + score, expectedScore, score, TEST_SCORE_TOLERANCE_DELTA);
}
}
/** Test that FieldScoreQuery of Type.BYTE caches/reuses loaded values and consumes the proper RAM resources. */
public void testCachingByte () throws CorruptIndexException, Exception {
@Test
public void testCachingByte () throws Exception {
// INT field values are small enough to be parsed as byte
doTestCaching(INT_FIELD,FieldScoreQuery.Type.BYTE);
}
/** Test that FieldScoreQuery of Type.SHORT caches/reuses loaded values and consumes the proper RAM resources. */
public void testCachingShort () throws CorruptIndexException, Exception {
@Test
public void testCachingShort () throws Exception {
// INT field values are small enough to be parsed as short
doTestCaching(INT_FIELD,FieldScoreQuery.Type.SHORT);
}
/** Test that FieldScoreQuery of Type.INT caches/reuses loaded values and consumes the proper RAM resources. */
public void testCachingInt () throws CorruptIndexException, Exception {
@Test
public void testCachingInt () throws Exception {
doTestCaching(INT_FIELD,FieldScoreQuery.Type.INT);
}
/** Test that FieldScoreQuery of Type.FLOAT caches/reuses loaded values and consumes the proper RAM resources. */
public void testCachingFloat () throws CorruptIndexException, Exception {
@Test
public void testCachingFloat () throws Exception {
// INT field values can be parsed as float
doTestCaching(INT_FIELD,FieldScoreQuery.Type.FLOAT);
// same values, but in flot format
@ -155,7 +165,7 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
}
// Test that values loaded for FieldScoreQuery are cached properly and consumes the proper RAM resources.
private void doTestCaching (String field, FieldScoreQuery.Type tp) throws CorruptIndexException, Exception {
private void doTestCaching (String field, FieldScoreQuery.Type tp) throws Exception {
// prepare expected array types for comparison
HashMap<FieldScoreQuery.Type,Object> expectedArrayTypes = new HashMap<FieldScoreQuery.Type,Object>();
expectedArrayTypes.put(FieldScoreQuery.Type.BYTE, new byte[0]);
@ -223,7 +233,7 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
}
private String testName() {
return getClass().getName()+"."+getName();
return getClass().getName()+"."+ getName();
}
}

View File

@ -19,42 +19,42 @@ package org.apache.lucene.search.function;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Test search based on OrdFieldSource and ReverseOrdFieldSource.
* <p>
* <p/>
* Tests here create an index with a few documents, each having
* an indexed "id" field.
* The ord values of this field are later used for scoring.
* <p>
* <p/>
* The order tests use Hits to verify that docs are ordered as expected.
* <p>
* The exact score tests use TopDocs top to verify the exact score.
* <p/>
* The exact score tests use TopDocs top to verify the exact score.
*/
@SuppressWarnings({"UseOfSystemOutOrSystemErr"})
public class TestOrdValues extends FunctionTestSetup {
/* @override constructor */
public TestOrdValues(String name) {
super(name);
/**
* Test OrdFieldSource
*/
@Test
public void testOrdFieldRank() throws CorruptIndexException, Exception {
doTestRank(ID_FIELD, true);
}
/** Test OrdFieldSource */
public void testOrdFieldRank () throws CorruptIndexException, Exception {
doTestRank(ID_FIELD,true);
}
/** Test ReverseOrdFieldSource */
public void testReverseOrdFieldRank () throws CorruptIndexException, Exception {
doTestRank(ID_FIELD,false);
/**
* Test ReverseOrdFieldSource
*/
@Test
public void testReverseOrdFieldRank() throws CorruptIndexException, Exception {
doTestRank(ID_FIELD, false);
}
// Test that queries based on reverse/ordFieldScore scores correctly
private void doTestRank (String field, boolean inOrder) throws CorruptIndexException, Exception {
private void doTestRank(String field, boolean inOrder) throws CorruptIndexException, Exception {
IndexSearcher s = new IndexSearcher(dir, true);
ValueSource vs;
if (inOrder) {
@ -62,42 +62,48 @@ public class TestOrdValues extends FunctionTestSetup {
} else {
vs = new ReverseOrdFieldSource(field);
}
Query q = new ValueSourceQuery(vs);
log("test: "+q);
QueryUtils.check(q,s);
log("test: " + q);
QueryUtils.check(q, s);
ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
assertEquals("All docs should be matched!",N_DOCS,h.length);
assertEquals("All docs should be matched!", N_DOCS, h.length);
String prevID = inOrder
? "IE" // greater than all ids of docs in this test ("ID0001", etc.)
: "IC"; // smaller than all ids of docs in this test ("ID0001", etc.)
for (int i=0; i<h.length; i++) {
? "IE" // greater than all ids of docs in this test ("ID0001", etc.)
: "IC"; // smaller than all ids of docs in this test ("ID0001", etc.)
for (int i = 0; i < h.length; i++) {
String resID = s.doc(h[i].doc).get(ID_FIELD);
log(i+". score="+h[i].score+" - "+resID);
log(s.explain(q,h[i].doc));
log(i + ". score=" + h[i].score + " - " + resID);
log(s.explain(q, h[i].doc));
if (inOrder) {
assertTrue("res id "+resID+" should be < prev res id "+prevID, resID.compareTo(prevID)<0);
assertTrue("res id " + resID + " should be < prev res id " + prevID, resID.compareTo(prevID) < 0);
} else {
assertTrue("res id "+resID+" should be > prev res id "+prevID, resID.compareTo(prevID)>0);
assertTrue("res id " + resID + " should be > prev res id " + prevID, resID.compareTo(prevID) > 0);
}
prevID = resID;
}
}
/** Test exact score for OrdFieldSource */
public void testOrdFieldExactScore () throws CorruptIndexException, Exception {
doTestExactScore(ID_FIELD,true);
/**
* Test exact score for OrdFieldSource
*/
@Test
public void testOrdFieldExactScore() throws CorruptIndexException, Exception {
doTestExactScore(ID_FIELD, true);
}
/** Test exact score for ReverseOrdFieldSource */
public void testReverseOrdFieldExactScore () throws CorruptIndexException, Exception {
doTestExactScore(ID_FIELD,false);
/**
* Test exact score for ReverseOrdFieldSource
*/
@Test
public void testReverseOrdFieldExactScore() throws CorruptIndexException, Exception {
doTestExactScore(ID_FIELD, false);
}
// Test that queries based on reverse/ordFieldScore returns docs with expected score.
private void doTestExactScore (String field, boolean inOrder) throws CorruptIndexException, Exception {
private void doTestExactScore(String field, boolean inOrder) throws CorruptIndexException, Exception {
IndexSearcher s = new IndexSearcher(dir, true);
ValueSource vs;
if (inOrder) {
@ -106,41 +112,47 @@ public class TestOrdValues extends FunctionTestSetup {
vs = new ReverseOrdFieldSource(field);
}
Query q = new ValueSourceQuery(vs);
TopDocs td = s.search(q,null,1000);
assertEquals("All docs should be matched!",N_DOCS,td.totalHits);
TopDocs td = s.search(q, null, 1000);
assertEquals("All docs should be matched!", N_DOCS, td.totalHits);
ScoreDoc sd[] = td.scoreDocs;
for (int i=0; i<sd.length; i++) {
for (int i = 0; i < sd.length; i++) {
float score = sd[i].score;
String id = s.getIndexReader().document(sd[i].doc).get(ID_FIELD);
log("-------- "+i+". Explain doc "+id);
log(s.explain(q,sd[i].doc));
float expectedScore = N_DOCS-i;
assertEquals("score of result "+i+" shuould be "+expectedScore+" != "+score, expectedScore, score, TEST_SCORE_TOLERANCE_DELTA);
String expectedId = inOrder
? id2String(N_DOCS-i) // in-order ==> larger values first
: id2String(i+1); // reverse ==> smaller values first
assertTrue("id of result "+i+" shuould be "+expectedId+" != "+score, expectedId.equals(id));
log("-------- " + i + ". Explain doc " + id);
log(s.explain(q, sd[i].doc));
float expectedScore = N_DOCS - i;
assertEquals("score of result " + i + " shuould be " + expectedScore + " != " + score, expectedScore, score, TEST_SCORE_TOLERANCE_DELTA);
String expectedId = inOrder
? id2String(N_DOCS - i) // in-order ==> larger values first
: id2String(i + 1); // reverse ==> smaller values first
assertTrue("id of result " + i + " shuould be " + expectedId + " != " + score, expectedId.equals(id));
}
}
/** Test caching OrdFieldSource */
public void testCachingOrd () throws CorruptIndexException, Exception {
doTestCaching(ID_FIELD,true);
/**
* Test caching OrdFieldSource
*/
@Test
public void testCachingOrd() throws CorruptIndexException, Exception {
doTestCaching(ID_FIELD, true);
}
/** Test caching for ReverseOrdFieldSource */
public void tesCachingReverseOrd () throws CorruptIndexException, Exception {
doTestCaching(ID_FIELD,false);
/**
* Test caching for ReverseOrdFieldSource
*/
@Test
public void testCachingReverseOrd() throws CorruptIndexException, Exception {
doTestCaching(ID_FIELD, false);
}
// Test that values loaded for FieldScoreQuery are cached properly and consumes the proper RAM resources.
private void doTestCaching (String field, boolean inOrder) throws CorruptIndexException, Exception {
private void doTestCaching(String field, boolean inOrder) throws CorruptIndexException, Exception {
IndexSearcher s = new IndexSearcher(dir, true);
Object innerArray = null;
boolean warned = false; // print warning once
for (int i=0; i<10; i++) {
for (int i = 0; i < 10; i++) {
ValueSource vs;
if (inOrder) {
vs = new OrdFieldSource(field);
@ -150,30 +162,29 @@ public class TestOrdValues extends FunctionTestSetup {
ValueSourceQuery q = new ValueSourceQuery(vs);
ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
try {
assertEquals("All docs should be matched!",N_DOCS,h.length);
assertEquals("All docs should be matched!", N_DOCS, h.length);
IndexReader[] readers = s.getIndexReader().getSequentialSubReaders();
for(int j = 0; j < readers.length; j++) {
IndexReader reader = readers[j];
if (i==0) {
for (IndexReader reader : readers) {
if (i == 0) {
innerArray = q.valSrc.getValues(reader).getInnerArray();
} else {
log(i+". compare: "+innerArray+" to "+q.valSrc.getValues(reader).getInnerArray());
log(i + ". compare: " + innerArray + " to " + q.valSrc.getValues(reader).getInnerArray());
assertSame("field values should be cached and reused!", innerArray, q.valSrc.getValues(reader).getInnerArray());
}
}
} catch (UnsupportedOperationException e) {
if (!warned) {
System.err.println("WARNING: "+testName()+" cannot fully test values of "+q);
System.err.println("WARNING: " + testName() + " cannot fully test values of " + q);
warned = true;
}
}
}
ValueSource vs;
ValueSourceQuery q;
ScoreDoc[] h;
// verify that different values are loaded for a different field
String field2 = INT_FIELD;
assertFalse(field.equals(field2)); // otherwise this test is meaningless.
@ -184,21 +195,20 @@ public class TestOrdValues extends FunctionTestSetup {
}
q = new ValueSourceQuery(vs);
h = s.search(q, null, 1000).scoreDocs;
assertEquals("All docs should be matched!",N_DOCS,h.length);
assertEquals("All docs should be matched!", N_DOCS, h.length);
IndexReader[] readers = s.getIndexReader().getSequentialSubReaders();
for (int j = 0; j < readers.length; j++) {
IndexReader reader = readers[j];
for (IndexReader reader : readers) {
try {
log("compare (should differ): " + innerArray + " to "
+ q.valSrc.getValues(reader).getInnerArray());
+ q.valSrc.getValues(reader).getInnerArray());
assertNotSame(
"different values shuold be loaded for a different field!",
innerArray, q.valSrc.getValues(reader).getInnerArray());
"different values shuold be loaded for a different field!",
innerArray, q.valSrc.getValues(reader).getInnerArray());
} catch (UnsupportedOperationException e) {
if (!warned) {
System.err.println("WARNING: " + testName()
+ " cannot fully test values of " + q);
+ " cannot fully test values of " + q);
warned = true;
}
}
@ -213,21 +223,20 @@ public class TestOrdValues extends FunctionTestSetup {
}
q = new ValueSourceQuery(vs);
h = s.search(q, null, 1000).scoreDocs;
assertEquals("All docs should be matched!",N_DOCS,h.length);
assertEquals("All docs should be matched!", N_DOCS, h.length);
readers = s.getIndexReader().getSequentialSubReaders();
for (int j = 0; j < readers.length; j++) {
IndexReader reader = readers[j];
for (IndexReader reader : readers) {
try {
log("compare (should differ): " + innerArray + " to "
+ q.valSrc.getValues(reader).getInnerArray());
+ q.valSrc.getValues(reader).getInnerArray());
assertNotSame(
"cached field values should not be reused if reader as changed!",
innerArray, q.valSrc.getValues(reader).getInnerArray());
"cached field values should not be reused if reader as changed!",
innerArray, q.valSrc.getValues(reader).getInnerArray());
} catch (UnsupportedOperationException e) {
if (!warned) {
System.err.println("WARNING: " + testName()
+ " cannot fully test values of " + q);
+ " cannot fully test values of " + q);
warned = true;
}
}
@ -235,7 +244,7 @@ public class TestOrdValues extends FunctionTestSetup {
}
private String testName() {
return getClass().getName()+"."+getName();
return getClass().getName() + "." + getName();
}
}

View File

@ -0,0 +1,60 @@
package org.apache.lucene.util;
/**
* 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 org.junit.rules.TestWatchman;
import org.junit.runners.model.FrameworkMethod;
import java.lang.reflect.Method;
public final class InterceptTestCaseEvents extends TestWatchman {
private Object obj;
public InterceptTestCaseEvents(Object obj) {
this.obj = obj;
}
@Override
public void failed(Throwable e, FrameworkMethod method) {
try {
Method reporter = method.getMethod().getDeclaringClass().getMethod("reportAdditionalFailureInfo",(Class<?>[]) null);
reporter.invoke(obj, (Object[])null);
} catch (Exception e1) {
System.err.println("InterceptTestCaseEvents.failed(). Cannot invoke reportAdditionalFailureInfo() method in" +
" consuming class, is it declared and public?");
}
super.failed(e, method);
}
@Override
public void finished(FrameworkMethod method) {
super.finished(method);
}
@Override
public void starting(FrameworkMethod method) {
super.starting(method);
}
@Override
public void succeeded(FrameworkMethod method) {
super.succeeded(method);
}
}

View File

@ -46,7 +46,11 @@ import org.apache.lucene.util.FieldCacheSanityChecker.Insanity;
* <code>super.tearDown()</code>
* </p>
* @see #assertSaneFieldCaches
*
* @deprecated Replaced by {@link #LuceneTestCaseJ4}
*
*/
@Deprecated
public abstract class LuceneTestCase extends TestCase {
public LuceneTestCase() {

View File

@ -0,0 +1,254 @@
package org.apache.lucene.util;
/**
* 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 org.apache.lucene.index.ConcurrentMergeScheduler;
import org.apache.lucene.search.FieldCache;
import org.apache.lucene.search.FieldCache.CacheEntry;
import org.apache.lucene.util.FieldCacheSanityChecker.Insanity;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestWatchman;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Base class for all Lucene unit tests, Junit4 variant.
* Replaces LuceneTestCase.
* <p>
* </p>
* <p>
* If you
* override either <code>setUp()</code> or
* <code>tearDown()</code> in your unit test, make sure you
* call <code>super.setUp()</code> and
* <code>super.tearDown()</code>
* </p>
*
* @After - replaces setup
* @Before - replaces teardown
* @Test - any public method with this annotation is a test case, regardless
* of its name
* <p/>
* <p/>
* See Junit4 documentation for a complete list of features at
* http://junit.org/junit/javadoc/4.7/
* <p/>
* Import from org.junit rather than junit.framework.
* <p/>
* You should be able to use this class anywhere you used LuceneTestCase
* if you annotate your derived class correctly with the annotations above
* @see assertSaneFieldCaches
* <p/>
*/
// If we really need functionality in runBare override from LuceneTestCase,
// we can introduce RunBareWrapper and override runChild, and add the
// @RunWith annotation as below. runChild will be called for
// every test. But the functionality we used to
// get from that override is provided by InterceptTestCaseEvents
//@RunWith(RunBareWrapper.class)
public class LuceneTestCaseJ4 extends TestWatchman {
// This is how we get control when errors occur.
// Think of this as start/end/success/failed
// events.
@Rule
public InterceptTestCaseEvents intercept = new InterceptTestCaseEvents(this);
public LuceneTestCaseJ4() {
}
public LuceneTestCaseJ4(String name) {
this.name = name;
}
@Before
public void setUp() throws Exception {
ConcurrentMergeScheduler.setTestMode();
seed = null;
}
/**
* Forcible purges all cache entries from the FieldCache.
* <p>
* This method will be called by tearDown to clean up FieldCache.DEFAULT.
* If a (poorly written) test has some expectation that the FieldCache
* will persist across test methods (ie: a static IndexReader) this
* method can be overridden to do nothing.
* </p>
*
* @see FieldCache#purgeAllCaches()
*/
protected void purgeFieldCache(final FieldCache fc) {
fc.purgeAllCaches();
}
protected String getTestLabel() {
return getClass().getName() + "." + getName();
}
@After
public void tearDown() throws Exception {
try {
// this isn't as useful as calling directly from the scope where the
// index readers are used, because they could be gc'ed just before
// tearDown is called.
// But it's better then nothing.
assertSaneFieldCaches(getTestLabel());
if (ConcurrentMergeScheduler.anyUnhandledExceptions()) {
// Clear the failure so that we don't just keep
// failing subsequent test cases
ConcurrentMergeScheduler.clearUnhandledExceptions();
fail("ConcurrentMergeScheduler hit unhandled exceptions");
}
} finally {
purgeFieldCache(FieldCache.DEFAULT);
}
}
/**
* Asserts that FieldCacheSanityChecker does not detect any
* problems with FieldCache.DEFAULT.
* <p>
* If any problems are found, they are logged to System.err
* (allong with the msg) when the Assertion is thrown.
* </p>
* <p>
* This method is called by tearDown after every test method,
* however IndexReaders scoped inside test methods may be garbage
* collected prior to this method being called, causing errors to
* be overlooked. Tests are encouraged to keep their IndexReaders
* scoped at the class level, or to explicitly call this method
* directly in the same scope as the IndexReader.
* </p>
*
* @see FieldCacheSanityChecker
*/
protected void assertSaneFieldCaches(final String msg) {
final CacheEntry[] entries = FieldCache.DEFAULT.getCacheEntries();
Insanity[] insanity = null;
try {
try {
insanity = FieldCacheSanityChecker.checkSanity(entries);
} catch (RuntimeException e) {
dumpArray(msg + ": FieldCache", entries, System.err);
throw e;
}
assertEquals(msg + ": Insane FieldCache usage(s) found",
0, insanity.length);
insanity = null;
} finally {
// report this in the event of any exception/failure
// if no failure, then insanity will be null anyway
if (null != insanity) {
dumpArray(msg + ": Insane FieldCache usage(s)", insanity, System.err);
}
}
}
/**
* Convinience method for logging an iterator.
*
* @param label String logged before/after the items in the iterator
* @param iter Each next() is toString()ed and logged on it's own line. If iter is null this is logged differnetly then an empty iterator.
* @param stream Stream to log messages to.
*/
public static void dumpIterator(String label, Iterator iter,
PrintStream stream) {
stream.println("*** BEGIN " + label + " ***");
if (null == iter) {
stream.println(" ... NULL ...");
} else {
while (iter.hasNext()) {
stream.println(iter.next().toString());
}
}
stream.println("*** END " + label + " ***");
}
/**
* Convinience method for logging an array. Wraps the array in an iterator and delegates
*
* @see dumpIterator(String,Iterator,PrintStream)
*/
public static void dumpArray(String label, Object[] objs,
PrintStream stream) {
Iterator iter = (null == objs) ? null : Arrays.asList(objs).iterator();
dumpIterator(label, iter, stream);
}
/**
* Returns a {@link Random} instance for generating random numbers during the test.
* The random seed is logged during test execution and printed to System.out on any failure
* for reproducing the test using {@link #newRandom(long)} with the recorded seed
* .
*/
public Random newRandom() {
if (seed != null) {
throw new IllegalStateException("please call LuceneTestCase.newRandom only once per test");
}
return newRandom(seedRnd.nextLong());
}
/**
* Returns a {@link Random} instance for generating random numbers during the test.
* If an error occurs in the test that is not reproducible, you can use this method to
* initialize the number generator with the seed that was printed out during the failing test.
*/
public Random newRandom(long seed) {
if (this.seed != null) {
throw new IllegalStateException("please call LuceneTestCase.newRandom only once per test");
}
this.seed = Long.valueOf(seed);
return new Random(seed);
}
public String getName() {
return this.name;
}
// We get here fro InterceptTestCaseEvents on the 'failed' event....
public void reportAdditionalFailureInfo() {
if (seed != null) {
System.out.println("NOTE: random seed of testcase '" + getName() + "' was: " + seed);
}
}
// recorded seed
protected Long seed = null;
// static members
private static final Random seedRnd = new Random();
private String name = "";
}