Index: src/test/org/apache/lucene/search/TestExplanations.java =================================================================== --- src/test/org/apache/lucene/search/TestExplanations.java (revision 544197) +++ src/test/org/apache/lucene/search/TestExplanations.java (working copy) @@ -83,13 +83,14 @@ return qp.parse(queryText); } + /** check the expDocNrs first, then check the query (and the explanations) */ public void qtest(String queryText, int[] expDocNrs) throws Exception { qtest(makeQuery(queryText), expDocNrs); } + + /** check the expDocNrs first, then check the query (and the explanations) */ public void qtest(Query q, int[] expDocNrs) throws Exception { - // check that the expDocNrs first, then check the explanations CheckHits.checkHitCollector(q, FIELD, searcher, expDocNrs); - CheckHits.checkExplanations(q, FIELD, searcher); } /** Index: src/test/org/apache/lucene/search/CheckHits.java =================================================================== --- src/test/org/apache/lucene/search/CheckHits.java (revision 544197) +++ src/test/org/apache/lucene/search/CheckHits.java (working copy) @@ -29,6 +29,13 @@ public class CheckHits { /** + * Some explains methods calculate their vlaues though a slightly + * differnet order of operations from the acctaul scoring method ... + * this allows for a small amount of variation + */ + public static float EXPLAIN_SCORE_TOLERANCE_DELTA = 0.00005f; + + /** * Tests that all documents up to maxDoc which are *not* in the * expected result set, have an explanation which indicates no match * (ie: Explanation value of 0.0f) @@ -226,10 +233,13 @@ } /** - * Asserts that the score explanation for every document matching a - * query corrisponds with the true score. + * Asserts that the explanation value for every document matching a + * query corresponds with the true score. * * @see ExplanationAsserter + * @see #checkExplanations(Query, String, Searcher, boolean) for a + * "deep" testing of the explanation details. + * * @param query the query to test * @param searcher the searcher to test the query against * @param defaultFieldName used for displaing the query in assertion messages @@ -237,16 +247,123 @@ public static void checkExplanations(Query query, String defaultFieldName, Searcher searcher) throws IOException { + checkExplanations(query, defaultFieldName, searcher, false); + } + /** + * Asserts that the explanation value for every document matching a + * query corresponds with the true score. Optionally does "deep" + * testing of the explanation details. + * + * @see ExplanationAsserter + * @param query the query to test + * @param searcher the searcher to test the query against + * @param defaultFieldName used for displaing the query in assertion messages + * @param deep indicates whether a deep comparison of sub-Explanation details should be executed + */ + public static void checkExplanations(Query query, + String defaultFieldName, + Searcher searcher, + boolean deep) throws IOException { + searcher.search(query, new ExplanationAsserter - (query, defaultFieldName, searcher)); + (query, defaultFieldName, searcher, deep)); } + /** + * Assert that an explanation has the expected score, and optionally that its + * sub-details max/sum/factor match to that score. + * + * @param q String representation of the query for assertion messages + * @param doc Document ID for assertion messages + * @param score Real score value of doc with query q + * @param deep indicates whether a deep comparison of sub-Explanation details should be executed + * @param expl The Explanation to match against score + */ + public static void verifyExplanation(String q, + int doc, + float score, + boolean deep, + Explanation expl) { + float value = expl.getValue(); + TestCase.assertEquals(q+": score(doc="+doc+")="+score+ + " != explanationScore="+value+" Explanation: "+expl, + score,value,EXPLAIN_SCORE_TOLERANCE_DELTA); + + if (!deep) return; + + Explanation detail[] = expl.getDetails(); + if (detail!=null) { + if (detail.length==1) { + // simple containment, no matter what the description says, + // just verify contained expl has same score + verifyExplanation(q,doc,score,deep,detail[0]); + } else { + // explanation must either: + // - end with one of: "product of:", "sum of:", "max of:", or + // - have "max plus times others" (where is float). + float x = 0; + String descr = expl.getDescription().toLowerCase(); + boolean productOf = descr.endsWith("product of:"); + boolean sumOf = descr.endsWith("sum of:"); + boolean maxOf = descr.endsWith("max of:"); + boolean maxTimesOthers = false; + if (!(productOf || sumOf || maxOf)) { + // maybe 'max plus x times others' + int k1 = descr.indexOf("max plus "); + if (k1>=0) { + k1 += "max plus ".length(); + int k2 = descr.indexOf(" ",k1); + try { + x = Float.parseFloat(descr.substring(k1,k2).trim()); + if (descr.substring(k2).trim().equals("times others of:")) { + maxTimesOthers = true; + } + } catch (NumberFormatException e) { + } + } + } + TestCase.assertTrue( + q+": multi valued explanation description=\""+descr + +"\" must be 'max of plus x times others' or end with 'prodoct of'" + +" or 'sum of:' or 'max of:' - "+expl, + productOf || sumOf || maxOf || maxTimesOthers); + float sum = 0; + float product = 1; + float max = 0; + for (int i=0; i