diff options
author | Håkon Hallingstad <hakon@oath.com> | 2019-01-22 12:54:54 +0100 |
---|---|---|
committer | Håkon Hallingstad <hakon@oath.com> | 2019-01-22 12:54:54 +0100 |
commit | 1db80c6ab30aafd0e8c3cc09091f9949e9488145 (patch) | |
tree | e4534ee6925175563beb5c294976f70755903d02 /container-search/src/test/java/com/yahoo/search/rendering | |
parent | b1a1038d66df81646bed462295761d1aeed2c1e9 (diff) | |
parent | 8372a883c5a5fa100f88fc9b80824359b5bb70cd (diff) |
Merge branch 'master' into hakonhall/define-enable-logserver-flag
Diffstat (limited to 'container-search/src/test/java/com/yahoo/search/rendering')
3 files changed, 346 insertions, 133 deletions
diff --git a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java index 5b3b5ca6d73..10a3b695f64 100644 --- a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java @@ -1,14 +1,13 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.rendering; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; +import com.yahoo.component.ComponentId; import com.yahoo.component.chain.Chain; +import com.yahoo.container.QrSearchersConfig; import com.yahoo.data.access.simple.Value; import com.yahoo.data.access.slime.SlimeAdapter; import com.yahoo.document.DataType; @@ -19,8 +18,13 @@ import com.yahoo.document.datatypes.StringFieldValue; import com.yahoo.document.datatypes.Struct; import com.yahoo.document.datatypes.TensorFieldValue; import com.yahoo.document.predicate.Predicate; +import com.yahoo.prelude.Index; +import com.yahoo.prelude.IndexFacts; +import com.yahoo.prelude.IndexModel; +import com.yahoo.prelude.SearchDefinition; import com.yahoo.prelude.fastsearch.FastHit; import com.yahoo.prelude.hitfield.JSONString; +import com.yahoo.prelude.searcher.JuniperSearcher; import com.yahoo.search.Query; import com.yahoo.search.Result; import com.yahoo.search.Searcher; @@ -38,6 +42,7 @@ import com.yahoo.search.result.NanNumber; import com.yahoo.search.result.Relevance; import com.yahoo.search.result.StructuredData; import com.yahoo.search.searchchain.Execution; +import com.yahoo.search.searchchain.testutil.DocumentSourceSearcher; import com.yahoo.search.statistics.ElapsedTimeTestCase; import com.yahoo.search.statistics.ElapsedTimeTestCase.CreativeTimeSource; import com.yahoo.search.statistics.ElapsedTimeTestCase.UselessSearcher; @@ -51,32 +56,31 @@ import com.yahoo.yolean.trace.TraceNode; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.times; /** * Functional testing of {@link JsonRenderer}. * * @author Steinar Knutsen + * @author bratseth */ public class JsonRendererTestCase { - JsonRenderer originalRenderer; - JsonRenderer renderer; + private JsonRenderer originalRenderer; + private JsonRenderer renderer; public JsonRendererTestCase() { originalRenderer = new JsonRenderer(); @@ -84,23 +88,11 @@ public class JsonRendererTestCase { @Before public void setUp() throws Exception { - // Do the same dance as in production + // Use the shared renderer as a prototype object, as specified in the API contract renderer = (JsonRenderer) originalRenderer.clone(); renderer.init(); } - @After - public void tearDown() throws Exception { - renderer = null; - } - - private static final class Thingie { - @Override - public String toString() { - return "thingie"; - } - } - @Test public void testDocumentId() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" @@ -130,14 +122,6 @@ public class JsonRendererTestCase { assertEqualJson(expected, summary); } - private Result newEmptyResult(String[] args) { - return new Result(new Query("/?" + String.join("&", args))); - } - - private Result newEmptyResult() { - return newEmptyResult(new String[] {"query=a"}); - } - @Test public void testDataTypes() throws IOException, InterruptedException, ExecutionException, JSONException { String expected = "{\n" @@ -188,7 +172,7 @@ public class JsonRendererTestCase { @Test - public final void testTracing() throws IOException, InterruptedException, ExecutionException { + public void testTracing() throws IOException, InterruptedException, ExecutionException { // which clearly shows a trace child is created once too often... String expected = "{\n" + " \"root\": {\n" @@ -243,7 +227,7 @@ public class JsonRendererTestCase { } @Test - public final void testEmptyTracing() throws IOException, InterruptedException, ExecutionException { + public void testEmptyTracing() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"fields\": {\n" @@ -272,7 +256,7 @@ public class JsonRendererTestCase { @SuppressWarnings("unchecked") @Test - public final void testTracingWithEmptySubtree() throws IOException, InterruptedException, ExecutionException { + public void testTracingWithEmptySubtree() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"fields\": {\n" @@ -346,15 +330,8 @@ public class JsonRendererTestCase { assertEqualJson(expected, summary); } - private void subExecution(Execution execution, String color, int traceLevel) { - Execution e2 = new Execution(new Chain<Searcher>(), execution.context()); - Query subQuery = new Query("/?query=b&tracelevel=" + traceLevel); - e2.search(subQuery); - subQuery.trace(color, 1); - } - @Test - public final void testTracingOfNodesWithBothChildrenAndData() throws IOException, InterruptedException, ExecutionException { + public void testTracingOfNodesWithBothChildrenAndData() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"fields\": {\n" @@ -400,7 +377,7 @@ public class JsonRendererTestCase { @Test - public final void testTracingOfNodesWithBothChildrenAndDataAndEmptySubnode() throws IOException, InterruptedException, ExecutionException { + public void testTracingOfNodesWithBothChildrenAndDataAndEmptySubnode() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"fields\": {\n" @@ -441,7 +418,7 @@ public class JsonRendererTestCase { } @Test - public final void testTracingOfNestedNodesWithDataAndSubnodes() throws IOException, InterruptedException, ExecutionException { + public void testTracingOfNestedNodesWithDataAndSubnodes() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"fields\": {\n" @@ -490,7 +467,7 @@ public class JsonRendererTestCase { @Test - public final void test() throws IOException, InterruptedException, ExecutionException { + public void test() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"children\": [\n" @@ -498,8 +475,7 @@ public class JsonRendererTestCase { + " \"children\": [\n" + " {\n" + " \"fields\": {\n" - + " \"c\": \"d\",\n" - + " \"uri\": \"http://localhost/1\"\n" + + " \"c\": \"d\"\n" + " },\n" + " \"id\": \"http://localhost/1\",\n" + " \"relevance\": 0.9,\n" @@ -523,8 +499,7 @@ public class JsonRendererTestCase { + " },\n" + " {\n" + " \"fields\": {\n" - + " \"b\": \"foo\",\n" - + " \"uri\": \"http://localhost/\"\n" + + " \"b\": \"foo\"\n" + " },\n" + " \"id\": \"http://localhost/\",\n" + " \"relevance\": 0.95,\n" @@ -555,7 +530,7 @@ public class JsonRendererTestCase { + " \"relevance\": 1.0\n" + " }\n" + "}"; - Query q = new Query("/?query=a&tracelevel=5&reportCoverage=true"); + Query q = new Query("/?query=a&tracelevel=5"); Execution execution = new Execution(Execution.Context.createContextStub()); Result r = new Result(q); r.setCoverage(new Coverage(500, 500,1,1)); @@ -603,7 +578,7 @@ public class JsonRendererTestCase { + " \"relevance\": 1.0\n" + " }\n" + "}"; - Query q = new Query("/?query=a&tracelevel=5&reportCoverage=true"); + Query q = new Query("/?query=a&tracelevel=5"); Execution execution = new Execution(Execution.Context.createContextStub()); Result r = new Result(q); r.setCoverage(new Coverage(500, 600).setDegradedReason(5)); @@ -725,7 +700,7 @@ public class JsonRendererTestCase { + " \"relevance\": 1.0\n" + " }\n" + "}\n"; - Query q = new Query("/?query=a&tracelevel=5&reportCoverage=true"); + Query q = new Query("/?query=a&tracelevel=5"); Result r = new Result(q); Throwable t = new Throwable(); StackTraceElement[] stack = new StackTraceElement[1]; @@ -885,7 +860,7 @@ public class JsonRendererTestCase { }); GroupList gl = new GroupList("customer"); Group g = new Group(new DoubleBucketId(1.0, 2.0), new Relevance(1.0)); - g.setField("something()", Integer.valueOf(7)); + g.setField("something()", 7); gl.add(g); rg.add(gl); r.hits().add(rg); @@ -958,7 +933,7 @@ public class JsonRendererTestCase { } @Test - public final void testFieldValueInHit() throws IOException, InterruptedException, ExecutionException, JSONException { + public void testFieldValueInHit() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"children\": [\n" @@ -993,7 +968,7 @@ public class JsonRendererTestCase { } @Test - public final void testHiddenFields() throws IOException, InterruptedException, ExecutionException, JSONException { + public void testHiddenFields() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"children\": [\n" @@ -1017,17 +992,8 @@ public class JsonRendererTestCase { assertEqualJson(expected, summary); } - private Hit createHitWithOnlyHiddenFields() { - Hit h = new Hit("hiddenFields"); - h.setField("NaN", NanNumber.NaN); - h.setField("emptyString", ""); - h.setField("emptyStringFieldValue", new StringFieldValue("")); - h.setField("$vespaImplementationDetail", "Hello, World!"); - return h; - } - @Test - public final void testDebugRendering() throws IOException, InterruptedException, ExecutionException, JSONException { + public void testDebugRendering() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"children\": [\n" @@ -1058,7 +1024,7 @@ public class JsonRendererTestCase { } @Test - public final void testTimingRendering() throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException { + public void testTimingRendering() throws InterruptedException, ExecutionException, IOException { String expected = "{" + " \"root\": {" + " \"fields\": {" @@ -1093,7 +1059,7 @@ public class JsonRendererTestCase { } @Test - public final void testJsonCallback() throws IOException, InterruptedException, ExecutionException, JSONException { + public void testJsonCallback() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"children\": [\n" @@ -1131,7 +1097,7 @@ public class JsonRendererTestCase { } @Test - public final void testMapInField() throws IOException, InterruptedException, ExecutionException, JSONException { + public void testMapInField() throws IOException, InterruptedException, ExecutionException { String expected = "{\n" + " \"root\": {\n" + " \"children\": [\n" @@ -1187,6 +1153,54 @@ public class JsonRendererTestCase { + "}"; assertEquals("Duplicate key \"duplicate\"", validateJSON(json)); } + + @Test + public void testDynamicSummary() throws Exception { + String content = "\uFFF9Feeding\uFFFAfeed\uFFFB \u001F\uFFF9documents\uFFFAdocument\uFFFB\u001F into Vespa \uFFF9is\uFFFAbe\u001Eincrement of a set of \u001F\uFFF9documents\uFFFAdocument\uFFFB\u001F fed into Vespa \uFFF9is\u001Efloat in XML when \u001Fdocument\u001F attribute \uFFF9is\uFFFAbe\uFFFB int\u001E"; + Result result = createResult("one", content, true); + + String summary = render(result); + + String expected = + "{ \n" + + " \"root\":{ " + + " \"id\":\"toplevel\"," + + " \"relevance\":1.0," + + " \"fields\":{ " + + " \"totalCount\":0" + + " }," + + " \"children\":[ " + + " { " + + " \"id\":\"http://abc.html/\"," + + " \"relevance\":1.0," + + " \"fields\":{ " + + " \"sddocname\":\"one\",\n" + + " \"dynteaser\":\"Feeding <hi>documents</hi> into Vespa is<sep />increment of a set of <hi>documents</hi> fed into Vespa <sep />float in XML when <hi>document</hi> attribute is int<sep />\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + "}\n"; + assertEqualJson(expected, summary); + } + + private Result newEmptyResult(String[] args) { + return new Result(new Query("/?" + String.join("&", args))); + } + + private Result newEmptyResult() { + return newEmptyResult(new String[] {"query=a"}); + } + + private Hit createHitWithOnlyHiddenFields() { + Hit h = new Hit("hiddenFields"); + h.setField("NaN", NanNumber.NaN); + h.setField("emptyString", ""); + h.setField("emptyStringFieldValue", new StringFieldValue("")); + h.setField("$vespaImplementationDetail", "Hello, World!"); + return h; + } + private String render(Result r) throws InterruptedException, ExecutionException { Execution execution = new Execution(Execution.Context.createContextStub()); return render(execution, r); @@ -1209,6 +1223,7 @@ public class JsonRendererTestCase { assertEquals("", validateJSON(expected)); assertEquals("", validateJSON(generated)); } + private String validateJSON(String presumablyValidJson) { try { new JSONObject(presumablyValidJson); @@ -1218,4 +1233,76 @@ public class JsonRendererTestCase { } } + private static final class Thingie { + @Override + public String toString() { + return "thingie"; + } + } + + private Result createResult(String sdName, String content, boolean bolding) { + Chain<Searcher> chain = createSearchChain(sdName, content); + Query query = new Query("?query=12"); + if ( ! bolding) + query = new Query("?query=12&bolding=false"); + Execution execution = createExecution(chain); + Result result = execution.search(query); + execution.fill(result); + return result; + } + + /** + * Creates a search chain which always returns a result with one hit containing information given in this + * + * @param sdName the search definition type of the returned hit + * @param content the content of the "dynteaser" field of the returned hit + */ + private Chain<Searcher> createSearchChain(String sdName, String content) { + JuniperSearcher searcher = new JuniperSearcher(new ComponentId("test"), + new QrSearchersConfig(new QrSearchersConfig.Builder())); + + DocumentSourceSearcher docsource = new DocumentSourceSearcher(); + addResult(new Query("?query=12"), sdName, content, docsource); + addResult(new Query("?query=12&bolding=false"), sdName, content, docsource); + return new Chain<>(searcher, docsource); + } + + private void addResult(Query query, String sdName, String content, DocumentSourceSearcher docsource) { + Result r = new Result(query); + FastHit hit = new FastHit(); + hit.setId("http://abc.html"); + hit.setRelevance(new Relevance(1)); + hit.setField(Hit.SDDOCNAME_FIELD, sdName); + hit.setField("dynteaser", content); + r.hits().add(hit); + docsource.addResult(query, r); + } + + private Execution createExecution(Chain<Searcher> chain) { + Map<String, List<String>> clusters = new LinkedHashMap<>(); + Map<String, SearchDefinition> searchDefs = new LinkedHashMap<>(); + searchDefs.put("one", createSearchDefinitionOne()); + SearchDefinition union = new SearchDefinition("union"); + IndexModel indexModel = new IndexModel(clusters, searchDefs, union); + return new Execution(chain, Execution.Context.createContextStub(new IndexFacts(indexModel))); + } + + private SearchDefinition createSearchDefinitionOne() { + SearchDefinition one = new SearchDefinition("one"); + + Index dynteaser = new Index("dynteaser"); + dynteaser.setDynamicSummary(true); + one.addIndex(dynteaser); + + Index bigteaser = new Index("bigteaser"); + dynteaser.setHighlightSummary(true); + one.addIndex(bigteaser); + + Index otherteaser = new Index("otherteaser"); + otherteaser.setDynamicSummary(true); + one.addIndex(otherteaser); + + return one; + } + } diff --git a/container-search/src/test/java/com/yahoo/search/rendering/SyncDefaultRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/SyncDefaultRendererTestCase.java index 1d2187376a4..acb69670b14 100644 --- a/container-search/src/test/java/com/yahoo/search/rendering/SyncDefaultRendererTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/rendering/SyncDefaultRendererTestCase.java @@ -58,8 +58,8 @@ public class SyncDefaultRendererTestCase { @SuppressWarnings("deprecation") @Test - public final void testRenderWriterResult() throws InterruptedException, ExecutionException { - Query q = new Query("/?query=a&tracelevel=5&reportCoverage=true"); + public void testRenderWriterResult() throws InterruptedException, ExecutionException { + Query q = new Query("/?query=a&tracelevel=5"); q.getPresentation().setTiming(true); Result r = new Result(q); r.setCoverage(new Coverage(500, 1)); @@ -96,7 +96,7 @@ public class SyncDefaultRendererTestCase { assertTrue(f.get()); String summary = Utf8.toString(bs.toByteArray()); // TODO figure out a reasonably strict and reasonably flexible way to test - assertTrue(summary.length() > 1000); + assertTrue(summary.length() > 900); } } diff --git a/container-search/src/test/java/com/yahoo/search/rendering/XMLRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/XMLRendererTestCase.java index c95019d37d6..184a8c1aa43 100644 --- a/container-search/src/test/java/com/yahoo/search/rendering/XMLRendererTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/rendering/XMLRendererTestCase.java @@ -4,13 +4,21 @@ package com.yahoo.search.rendering; import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; - -import com.yahoo.search.handler.SearchHandler; -import org.junit.After; -import org.junit.Before; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.yahoo.component.ComponentId; +import com.yahoo.container.QrSearchersConfig; +import com.yahoo.prelude.Index; +import com.yahoo.prelude.IndexFacts; +import com.yahoo.prelude.IndexModel; +import com.yahoo.prelude.SearchDefinition; +import com.yahoo.prelude.searcher.JuniperSearcher; +import com.yahoo.search.result.Hit; +import com.yahoo.search.result.Relevance; +import com.yahoo.search.searchchain.Execution; +import com.yahoo.search.searchchain.testutil.DocumentSourceSearcher; import org.junit.Test; import com.google.common.util.concurrent.ListenableFuture; @@ -25,50 +33,112 @@ import com.yahoo.search.result.HitGroup; import com.yahoo.search.statistics.ElapsedTimeTestCase; import com.yahoo.search.statistics.TimeTracker; import com.yahoo.search.statistics.ElapsedTimeTestCase.CreativeTimeSource; -import com.yahoo.search.statistics.ElapsedTimeTestCase.UselessSearcher; import com.yahoo.text.Utf8; /** * Test the XML renderer * * @author Steinar Knutsen + * @author bratseth */ public class XMLRendererTestCase { - DefaultRenderer d; - - @Before - public void setUp() throws Exception { - d = new DefaultRenderer(); - d.init(); + @Test + public void testGetEncoding() { + XmlRenderer renderer = new XmlRenderer(); + renderer.init(); + assertEquals("utf-8", renderer.getEncoding()); } - @After - public void tearDown() throws Exception { + @Test + public void testGetMimeType() { + XmlRenderer renderer = new XmlRenderer(); + renderer.init(); + assertEquals("text/xml", renderer.getMimeType()); } @Test - public final void testGetEncoding() { - assertEquals("utf-8", d.getEncoding()); + public void testXmlRendering() throws Exception { + Query q = new Query("/?query=a"); + + Result result = new Result(q); + result.setCoverage(new Coverage(500, 1)); + + FastHit h = new FastHit("http://localhost/", .95); + h.setField("$a", "Hello, world."); + h.setField("b", "foo"); + result.hits().add(h); + + HitGroup g = new HitGroup("usual"); + h = new FastHit("http://localhost/1", .90); + h.setField("c", "d"); + g.add(h); + result.hits().add(g); + + HitGroup gg = new HitGroup("type grouphit"); + gg.types().add("grouphit"); + gg.setField("e", "f"); + result.hits().add(gg); + result.hits().addError(ErrorMessage.createInternalServerError("message")); + + String summary = render(result); + + String expected = + "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + + "<result total-hit-count=\"0\" coverage-docs=\"500\" coverage-nodes=\"1\" coverage-full=\"false\" coverage=\"0\" results-full=\"0\" results=\"1\">\n" + + " <error code=\"18\">Internal server error.</error>\n" + + " <errordetails>\n" + + " <error error=\"Internal server error.\" code=\"18\">message</error>\n" + + " </errordetails>\n" + + " <group relevancy=\"1.0\">\n" + + " <hit type=\"summary\" relevancy=\"0.9\">\n" + + " <field name=\"relevancy\">0.9</field>\n" + + " <field name=\"c\">d</field>\n" + + " </hit>\n" + + " </group>\n" + + " <hit type=\"grouphit\" relevancy=\"1.0\">\n" + + " <id>type grouphit</id>\n" + + " </hit>\n" + + " <hit type=\"summary\" relevancy=\"0.95\">\n" + + " <field name=\"relevancy\">0.95</field>\n" + + " <field name=\"b\">foo</field>\n" + + " </hit>\n" + + "</result>\n"; + + assertEquals(expected, summary); } @Test - public final void testGetMimeType() { - assertEquals("text/xml", d.getMimeType()); + public void testXmlRenderingOfDynamicSummary() throws Exception { + String content = "\uFFF9Feeding\uFFFAfeed\uFFFB \u001F\uFFF9documents\uFFFAdocument\uFFFB\u001F into Vespa \uFFF9is\uFFFAbe\u001Eincrement of a set of \u001F\uFFF9documents\uFFFAdocument\uFFFB\u001F fed into Vespa \uFFF9is\u001Efloat in XML when \u001Fdocument\u001F attribute \uFFF9is\uFFFAbe\uFFFB int\u001E"; + Result result = createResult("one", content, true); + + String summary = render(result); + + String expected = + "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + + "<result total-hit-count=\"0\">\n" + + " <hit relevancy=\"1.0\">\n" + + " <field name=\"relevancy\">1.0</field>\n" + + " <field name=\"sddocname\">one</field>\n" + + " <field name=\"dynteaser\">Feeding <hi>documents</hi> into Vespa is<sep />increment of a set of <hi>documents</hi> fed into Vespa <sep />float in XML when <hi>document</hi> attribute is int<sep /></field>\n" + + " </hit>\n" + + "</result>\n"; + assertEquals(expected, summary); } @Test - public final void testImplicitDefaultRender() throws Exception { - Query q = new Query("/?query=a&tracelevel=5&reportCoverage=true"); + public void testXmlRenderingWithTimeTracking() throws Exception { + Query q = new Query("/?query=a&tracelevel=5"); q.getPresentation().setTiming(true); - Result r = new Result(q); - r.setCoverage(new Coverage(500, 1)); - - TimeTracker t = new TimeTracker(new Chain<Searcher>( - new UselessSearcher("first"), new UselessSearcher("second"), - new UselessSearcher("third"))); - ElapsedTimeTestCase.doInjectTimeSource(t, new CreativeTimeSource( - new long[] { 1L, 2L, 3L, 4L, 5L, 6L, 7L })); + + Result result = new Result(q); + result.setCoverage(new Coverage(500, 1)); + + TimeTracker t = new TimeTracker(new Chain<Searcher>(new NoopSearcher("first"), + new NoopSearcher("second"), + new NoopSearcher("third"))); + ElapsedTimeTestCase.doInjectTimeSource(t, new CreativeTimeSource(new long[] { 1L, 2L, 3L, 4L, 5L, 6L, 7L })); t.sampleSearch(0, true); t.sampleSearch(1, true); t.sampleSearch(2, true); @@ -76,47 +146,103 @@ public class XMLRendererTestCase { t.sampleSearchReturn(2, true, null); t.sampleSearchReturn(1, true, null); t.sampleSearchReturn(0, true, null); - r.getElapsedTime().add(t); - FastHit h = new FastHit("http://localhost/", .95); - h.setField("$a", "Hello, world."); - h.setField("b", "foo"); - r.hits().add(h); - HitGroup g = new HitGroup("usual"); - h = new FastHit("http://localhost/1", .90); - h.setField("c", "d"); - g.add(h); - r.hits().add(g); - HitGroup gg = new HitGroup("type grouphit"); - gg.types().add("grouphit"); - gg.setField("e", "f"); - r.hits().add(gg); - r.hits().addError(ErrorMessage.createInternalServerError("boom")); + result.getElapsedTime().add(t); + String summary = render(result); + + assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<result total-hit-count=\"0\"", + summary.substring(0, 67)); + assertTrue(summary.contains("querytime=")); + assertTrue(summary.contains("summaryfetchtime=")); + assertTrue(summary.contains("searchtime=")); + assertTrue(summary.contains("<meta type=\"context\">")); + } + + private String render(Result result) throws Exception { + XmlRenderer renderer = new XmlRenderer(); + renderer.init(); ByteArrayOutputStream bs = new ByteArrayOutputStream(); - ListenableFuture<Boolean> f = d.render(bs, r, null, null); + ListenableFuture<Boolean> f = renderer.render(bs, result, null, null); assertTrue(f.get()); - String summary = Utf8.toString(bs.toByteArray()); + return Utf8.toString(bs.toByteArray()); + } - assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + - "<result total-hit-count=\"0\"", - summary.substring(0, 67) - ); - assertTrue(summary.contains("<meta type=\"context\">")); - assertTrue(summary.contains("<error code=\"18\">Internal server error.</error>")); - assertTrue(summary.contains("<hit type=\"grouphit\" relevancy=\"1.0\">")); - assertTrue(summary.contains("<hit type=\"summary\" relevancy=\"0.95\">")); - assertEquals(2, occurrences("<error ", summary)); - assertTrue(summary.length() > 1000); + private Result createResult(String sdName, String content, boolean bolding) { + Chain<Searcher> chain = createSearchChain(sdName, content); + Query query = new Query("?query=12"); + if ( ! bolding) + query = new Query("?query=12&bolding=false"); + Execution execution = createExecution(chain); + Result result = execution.search(query); + execution.fill(result); + return result; + } + + /** + * Creates a search chain which always returns a result with one hit containing information given in this + * + * @param sdName the search definition type of the returned hit + * @param content the content of the "dynteaser" field of the returned hit + */ + private Chain<Searcher> createSearchChain(String sdName, String content) { + JuniperSearcher searcher = new JuniperSearcher(new ComponentId("test"), + new QrSearchersConfig(new QrSearchersConfig.Builder())); + + DocumentSourceSearcher docsource = new DocumentSourceSearcher(); + addResult(new Query("?query=12"), sdName, content, docsource); + addResult(new Query("?query=12&bolding=false"), sdName, content, docsource); + return new Chain<>(searcher, docsource); + } + + private void addResult(Query query, String sdName, String content, DocumentSourceSearcher docsource) { + Result r = new Result(query); + FastHit hit = new FastHit(); + hit.setId("http://abc.html"); + hit.setRelevance(new Relevance(1)); + hit.setField(Hit.SDDOCNAME_FIELD, sdName); + hit.setField("dynteaser", content); + r.hits().add(hit); + docsource.addResult(query, r); + } + + private Execution createExecution(Chain<Searcher> chain) { + Map<String, List<String>> clusters = new LinkedHashMap<>(); + Map<String, SearchDefinition> searchDefs = new LinkedHashMap<>(); + searchDefs.put("one", createSearchDefinitionOne()); + SearchDefinition union = new SearchDefinition("union"); + IndexModel indexModel = new IndexModel(clusters, searchDefs, union); + return new Execution(chain, Execution.Context.createContextStub(new IndexFacts(indexModel))); } - private int occurrences(String fragment, String string) { - int occurrences = 0; - int cursor = 0; - while ( -1 != (cursor = string.indexOf(fragment, cursor))) { - occurrences++; - cursor += fragment.length(); + private SearchDefinition createSearchDefinitionOne() { + SearchDefinition one = new SearchDefinition("one"); + + Index dynteaser = new Index("dynteaser"); + dynteaser.setDynamicSummary(true); + one.addIndex(dynteaser); + + Index bigteaser = new Index("bigteaser"); + dynteaser.setHighlightSummary(true); + one.addIndex(bigteaser); + + Index otherteaser = new Index("otherteaser"); + otherteaser.setDynamicSummary(true); + one.addIndex(otherteaser); + + return one; + } + + public static class NoopSearcher extends Searcher { + + public NoopSearcher(String name) { + super(new ComponentId(name)); } - return occurrences; + + @Override + public Result search(Query query, Execution execution) { + return execution.search(query); + } + } } |