summaryrefslogtreecommitdiffstats
path: root/application/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'application/src/test/java')
-rw-r--r--application/src/test/java/com/yahoo/application/ApplicationFacade.java161
-rw-r--r--application/src/test/java/com/yahoo/application/ApplicationTest.java373
-rw-r--r--application/src/test/java/com/yahoo/application/MockResultSearcher.java22
-rw-r--r--application/src/test/java/com/yahoo/application/TestDocProc.java23
-rw-r--r--application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java153
-rw-r--r--application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java125
-rw-r--r--application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java87
-rw-r--r--application/src/test/java/com/yahoo/application/container/MockClient.java43
-rw-r--r--application/src/test/java/com/yahoo/application/container/MockServer.java31
-rw-r--r--application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java65
-rw-r--r--application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java34
-rw-r--r--application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java54
-rw-r--r--application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java366
-rw-r--r--application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java40
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java59
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java8
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java17
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java59
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java33
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java53
-rw-r--r--application/src/test/java/com/yahoo/application/container/handlers/WriteException.java8
-rw-r--r--application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java26
-rw-r--r--application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java32
-rw-r--r--application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java22
24 files changed, 1894 insertions, 0 deletions
diff --git a/application/src/test/java/com/yahoo/application/ApplicationFacade.java b/application/src/test/java/com/yahoo/application/ApplicationFacade.java
new file mode 100644
index 00000000000..2c8ff05e2cc
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/ApplicationFacade.java
@@ -0,0 +1,161 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application;
+
+import com.google.common.annotations.Beta;
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.application.container.handler.Response;
+import com.yahoo.component.Component;
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.container.Container;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+import com.yahoo.document.DocumentOperation;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.jdisc.service.ClientProvider;
+import com.yahoo.jdisc.service.ServerProvider;
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+
+/**
+ * Convenience access methods into some application content.
+ * These are not generally applicable and are therefore in test.
+ * This can also auto close its application.
+ *
+ * @author bratseth
+ */
+public class ApplicationFacade implements AutoCloseable {
+
+ private static final String DEFAULT_CHAIN = "default";
+
+ private final Application application;
+
+ public ApplicationFacade(Application application) {
+ this.application = application;
+ }
+
+ /** Returns the application wrapped by this */
+ public Application application() { return application; }
+
+ /**
+ * Process a single document through the default chain.
+ *
+ * @param document the document to process
+ * @return the docproc return status
+ */
+ @Beta
+ public DocumentProcessor.Progress process(final DocumentOperation document) {
+ return process(DEFAULT_CHAIN, document);
+ }
+
+ /**
+ * Process a single document through the default chain.
+ *
+ * @param chain process using this chain
+ * @param op the document operation to process
+ * @return the docproc return status
+ */
+ @Beta
+ public DocumentProcessor.Progress process(String chain, final DocumentOperation op) {
+ return application.getJDisc("default").documentProcessing().process(ComponentSpecification.fromString(chain),
+ Processing.of(op));
+ }
+
+ /**
+ * Pass a processing object through the
+ *
+ * @param processing the processing object to process
+ * @return the docproc return status
+ */
+ @Beta
+ public DocumentProcessor.Progress process(final Processing processing) {
+ return process(DEFAULT_CHAIN, processing);
+ }
+
+ /**
+ * Pass a processing object through the
+ *
+ * @param chain process using this chain
+ * @param processing the processing object to process
+ * @return the docproc return status
+ */
+ @Beta
+ public DocumentProcessor.Progress process(String chain, final Processing processing) {
+ return application.getJDisc("default").documentProcessing().process(ComponentSpecification.fromString(chain), processing);
+ }
+
+ /**
+ * Pass query object to the default search chain
+ *
+ * @param query the query
+ * @return the search result
+ */
+ @Beta
+ public Result search(final Query query) {
+ return application.getJDisc("default").search().process(ComponentSpecification.fromString(DEFAULT_CHAIN), query);
+ }
+
+ /**
+ * Pass query object to the default search chain
+ *
+ * @param chain the search chain to use
+ * @param query the query
+ * @return the search result
+ */
+ @Beta
+ public Result search(String chain, final Query query) {
+ return application.getJDisc("default").search().process(ComponentSpecification.fromString(chain), query);
+ }
+
+ /**
+ * Pass query object to the default search chain
+ *
+ * @param request the request to process
+ * @return the response object
+ */
+ @Beta
+ public Response handleRequest(Request request) {
+ return application.getJDisc("default").handleRequest(request);
+ }
+
+ /**
+ * @param componentId name of the component (usually YourClass.class.getName())
+ * @return the component object, or null if not found
+ */
+ @Beta
+ public Component getComponentById(String componentId) {
+ return Container.get().getComponentRegistry().getComponent(new ComponentId(componentId));
+ }
+
+ /**
+ * @param componentId name of the component (usually YourClass.class.getName())
+ * @return the request handler object, or null if not found
+ */
+ @Beta
+ public RequestHandler getRequestHandlerById(String componentId) {
+ return Container.get().getRequestHandlerRegistry().getComponent(new ComponentId(componentId));
+ }
+
+ /**
+ * @param componentId name of the component (usually YourClass.class.getName())
+ * @return the client object, or null if not found
+ */
+ @Beta
+ public ClientProvider getClientById(String componentId) {
+ return Container.get().getClientProviderRegistry().getComponent(new ComponentId(componentId));
+ }
+
+ /**
+ * @param componentId name of the component (usually YourClass.class.getName())
+ * @return the client object, or null if not found
+ */
+ @Beta
+ public ServerProvider getServerById(String componentId) {
+ return Container.get().getServerProviderRegistry().getComponent(new ComponentId(componentId));
+ }
+
+ @Override
+ public void close() throws Exception {
+ application.close();
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/ApplicationTest.java b/application/src/test/java/com/yahoo/application/ApplicationTest.java
new file mode 100644
index 00000000000..7f14a3e4e16
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/ApplicationTest.java
@@ -0,0 +1,373 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application;
+
+import com.yahoo.application.container.MockClient;
+import com.yahoo.application.container.MockServer;
+import com.yahoo.application.container.docprocs.MockDispatchDocproc;
+import com.yahoo.application.container.docprocs.MockDocproc;
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.application.container.handler.Response;
+import com.yahoo.application.container.handlers.MockHttpHandler;
+import com.yahoo.application.container.renderers.MockRenderer;
+import com.yahoo.application.container.searchers.MockSearcher;
+import com.yahoo.component.Component;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.document.DocumentRemove;
+import com.yahoo.document.DocumentType;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.handler.SearchHandler;
+import com.yahoo.vespa.defaults.Defaults;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ConnectException;
+import java.net.ServerSocket;
+import java.util.Map;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static com.yahoo.application.container.JDiscTest.getListenPort;
+
+/**
+ * @author bratseth
+ */
+public class ApplicationTest {
+
+ @Test
+ public void minimal_application_can_be_constructed() throws Exception {
+ try (Application application = Application.fromServicesXml("<jdisc version=\"1.0\"/>", Networking.disable)) {
+ }
+ }
+
+ /** Tests that an application with search chains referencing a content cluster can be constructed. */
+ @Test
+ public void container_and_referenced_content() throws Exception {
+ try (Application application =
+ Application.fromApplicationPackage(new File("src/test/app-packages/withcontent"), Networking.disable)) {
+ Result result = application.getJDisc("default").search().process(new ComponentSpecification("default"),
+ new Query("?query=substring:foobar&tracelevel=3"));
+ assertEquals("AND substring:fo substring:oo substring:ob substring:ba substring:ar", result.hits().get("hasQuery").getQuery().getModel().getQueryTree().toString());
+ }
+ }
+
+ private void printTrace(Result result) {
+ for (String message : result.getQuery().getContext(true).getTrace().traceNode().descendants(String.class))
+ System.out.println(message);
+ }
+
+ @Test
+ public void empty_container() throws Exception {
+ try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container())))) {
+ try {
+ app.process(new DocumentRemove(null));
+ fail("expected exception");
+ } catch (Exception ignore) {
+ // no op
+ }
+
+ try {
+ app.process(new Processing());
+ fail("expected exception");
+ } catch (Exception ignore) {
+ // no op
+ }
+
+ try {
+ app.search(new Query("?foo"));
+ fail("expected exception");
+ } catch (Exception ignore) {
+ // no op
+ }
+ }
+ }
+
+ @Test
+ public void config() throws Exception {
+ try (
+ ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .documentProcessor("docproc", "default", MockDocproc.class)
+ .config(new MockApplicationConfig(new MockApplicationConfig.Builder()
+ .mystruct(new MockApplicationConfig.Mystruct.Builder().id("structid").value("structvalue"))
+ .mystructlist(new MockApplicationConfig.Mystructlist.Builder().id("listid1").value("listvalue1"))
+ .mystructlist(new MockApplicationConfig.Mystructlist.Builder().id("listid2").value("listvalue2"))
+ .mylist("item1")
+ .mylist("item2")
+ .mymap("key1", "value1")
+ .mymap("key2", "value2")
+ .mymapstruct("key1", new MockApplicationConfig.Mymapstruct.Builder().id("mapid1").value("mapvalue1"))
+ .mymapstruct("key2", new MockApplicationConfig.Mymapstruct.Builder().id("mapid2").value("mapvalue2")))))))
+ ) {
+
+ MockDocproc docproc = (MockDocproc) app.getComponentById("docproc@default");
+ assertNotNull(docproc);
+
+ // struct
+ assertEquals(docproc.getConfig().mystruct().id(), "structid");
+ assertEquals(docproc.getConfig().mystruct().value(), "structvalue");
+
+ // struct list
+ assertEquals(docproc.getConfig().mystructlist().size(), 2);
+ assertEquals(docproc.getConfig().mystructlist().get(0).id(), "listid1");
+ assertEquals(docproc.getConfig().mystructlist().get(0).value(), "listvalue1");
+ assertEquals(docproc.getConfig().mystructlist().get(1).id(), "listid2");
+ assertEquals(docproc.getConfig().mystructlist().get(1).value(), "listvalue2");
+
+ // list
+ assertEquals(docproc.getConfig().mylist().size(), 2);
+ assertEquals(docproc.getConfig().mylist().get(0), "item1");
+ assertEquals(docproc.getConfig().mylist().get(1), "item2");
+
+ // map
+ assertEquals(docproc.getConfig().mymap().size(), 2);
+ assertTrue(docproc.getConfig().mymap().containsKey("key1"));
+ assertEquals(docproc.getConfig().mymap().get("key1"), "value1");
+ assertTrue(docproc.getConfig().mymap().containsKey("key2"));
+ assertEquals(docproc.getConfig().mymap().get("key2"), "value2");
+
+ // map struct
+ assertEquals(docproc.getConfig().mymapstruct().size(), 2);
+ assertTrue(docproc.getConfig().mymapstruct().containsKey("key1"));
+ assertEquals(docproc.getConfig().mymapstruct().get("key1").id(), "mapid1");
+ assertEquals(docproc.getConfig().mymapstruct().get("key1").value(), "mapvalue1");
+ assertTrue(docproc.getConfig().mymapstruct().containsKey("key2"));
+ assertEquals(docproc.getConfig().mymapstruct().get("key2").id(), "mapid2");
+ assertEquals(docproc.getConfig().mymapstruct().get("key2").value(), "mapvalue2");
+ }
+ }
+
+ @Test
+ public void handler() throws Exception {
+ try (
+ ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .handler("http://*/*", MockHttpHandler.class))))
+ ) {
+
+ RequestHandler handler = app.getRequestHandlerById(MockHttpHandler.class.getName());
+ assertNotNull(handler);
+
+ Request request = new Request("http://localhost:" + Defaults.getDefaults().vespaWebServicePort() + "/");
+ Response response = app.handleRequest(request);
+ assertNotNull(response);
+ assertEquals(response.getStatus(), 200);
+ assertEquals(response.getBodyAsString(), "OK");
+
+ request = new Request("http://localhost");
+ response = app.handleRequest(request);
+ assertNotNull(response);
+ assertEquals(response.getStatus(), 200);
+ assertEquals(response.getBodyAsString(), "OK");
+
+ request = new Request("http://localhost/query=foo");
+ response = app.handleRequest(request);
+ assertNotNull(response);
+ assertEquals(response.getStatus(), 200);
+ assertEquals(response.getBodyAsString(), "OK");
+ }
+ }
+
+ @Test
+ public void renderer() throws Exception {
+ try (
+ ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .renderer("mock", MockRenderer.class))))
+ ) {
+
+ Request request = new Request("http://localhost:" + Defaults.getDefaults().vespaWebServicePort() + "/search/?format=mock");
+ Response response = app.handleRequest(request);
+ assertNotNull(response);
+ assertEquals(response.getStatus(), 200);
+ assertEquals(response.getBodyAsString(), "<mock hits=\"0\" />");
+ }
+ }
+
+ @Test
+ public void search_default() throws Exception {
+ try (
+ ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .searcher(MockSearcher.class))))
+ ) {
+ Result result = app.search(new Query("?query=foo"));
+ assertEquals(1, result.hits().size());
+ }
+ }
+
+ @Test
+ public void search() throws Exception {
+ try (
+ ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .searcher("foo", MockSearcher.class))))
+ ) {
+ Result result = app.search("foo", new Query("?query=foo"));
+ assertEquals(1, result.hits().size());
+ }
+ }
+
+ @Test
+ public void builder_with_networking() throws Exception {
+ try (
+ Application app = Application.fromBuilder(new Application.Builder().networking(Networking.enable).container("default", new Application.Builder.Container().handler("http://*/*", MockHttpHandler.class)))
+ ) {
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpResponse response = client.execute(new HttpGet("http://localhost:" + getListenPort() + "/query=foo"));
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ BufferedReader r = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+ String line;
+ StringBuilder sb = new StringBuilder();
+ while ((line = r.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ assertEquals("OK\n", sb.toString());
+ }
+ }
+
+ @Test
+ public void document_type() throws Exception {
+ try (
+ Application app = Application.fromBuilder(new Application.Builder()
+ .documentType("test", IOUtils.toString(this.getClass().getResourceAsStream("/test.sd")))
+ .container("default", new Application.Builder.Container()
+ .documentProcessor(MockDocproc.class)
+ .config(new MockApplicationConfig(new MockApplicationConfig.Builder().mystruct(new MockApplicationConfig.Mystruct.Builder().id("foo").value("bar"))))))
+ ) {
+ Map<String, DocumentType> typeMap = app.getJDisc("jdisc").documentProcessing().getDocumentTypes();
+ assertNotNull(typeMap);
+ assertTrue(typeMap.containsKey("test"));
+ }
+ }
+
+ @Test
+ public void get_search_handler() throws Exception {
+ try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container().search(true))))) {
+ SearchHandler searchHandler = (SearchHandler) app.getRequestHandlerById("com.yahoo.search.handler.SearchHandler");
+ assertNotNull(searchHandler);
+ }
+ }
+
+ @Test
+ public void component() throws Exception {
+ try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .component(MockSearcher.class))))) {
+ Component c = app.getComponentById(MockSearcher.class.getName());
+ assertNotNull(c);
+ }
+ }
+
+ @Test
+ public void component_with_config() throws Exception {
+ MockApplicationConfig config = new MockApplicationConfig(new MockApplicationConfig.Builder().mystruct(new MockApplicationConfig.Mystruct.Builder().id("foo").value("bar")));
+ try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .component("foo", MockDocproc.class, config))))) {
+ Component c = app.getComponentById("foo");
+ assertNotNull(c);
+ }
+ }
+
+ @Test
+ public void client() throws Exception {
+ try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder()
+ .documentType("test", IOUtils.toString(this.getClass().getResourceAsStream("/test.sd")))
+ .container("default", new Application.Builder.Container()
+ .client("mbus://*/*", MockClient.class)
+ .documentProcessor(MockDispatchDocproc.class)
+ ))
+ )) {
+
+ Map<String, DocumentType> typeMap = app.application().getJDisc("jdisc").documentProcessing().getDocumentTypes();
+ assertNotNull(typeMap);
+
+ DocumentType docType = typeMap.get("test");
+ Document doc = new Document(docType, "id:foo:test::bar");
+ doc.setFieldValue("title", "hello");
+
+ assertEquals(DocumentProcessor.Progress.DONE, app.process(new DocumentPut(doc)));
+
+ MockClient client = (MockClient) app.getClientById(MockClient.class.getName());
+ assertNotNull(client);
+ assertEquals(1, client.getCounter());
+
+ MockDispatchDocproc docproc = (MockDispatchDocproc) app.getComponentById(MockDispatchDocproc.class.getName() + "@default");
+ assertNotNull(docproc);
+ assertEquals(1, docproc.getResponses().size());
+ assertEquals(200, docproc.getResponses().get(0).getStatus());
+ }
+ }
+
+ @Test
+ public void server() throws Exception {
+ try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container()
+ .server("foo", MockServer.class)))
+ )) {
+ MockServer server = (MockServer) app.getServerById("foo");
+ assertNotNull(server);
+ assertTrue(server.isStarted());
+ }
+ }
+
+ @Test
+ public void query_profile() throws Exception {
+ try (Application app = Application.fromBuilder(new Application.Builder()
+ .queryProfile("default", "<query-profile id=\"default\">\n" +
+ "<field name=\"defaultage\">7d</field>\n" +
+ "</query-profile>")
+ .queryProfileType("type", "<query-profile-type id=\"type\">\n" +
+ "<field name=\"defaultage\" type=\"string\" />\n" +
+ "</query-profile-type>")
+ .rankExpression("re", "commonfirstphase(globalstaticrank)")
+ .documentType("test", IOUtils.toString(this.getClass().getResourceAsStream("/test.sd")))
+ .container("default", new Application.Builder.Container()
+ .search(true)
+ ))) {
+ }
+ }
+
+ @Test(expected = ConnectException.class)
+ public void http_interface_is_off_when_networking_is_disabled() throws Exception {
+ int httpPort = getFreePort();
+ try (Application application = Application.fromServicesXml(servicesXmlWithServer(httpPort), Networking.disable)) {
+ HttpClient client = new DefaultHttpClient();
+ int statusCode = client.execute(new HttpGet("http://localhost:" + httpPort)).getStatusLine().getStatusCode();
+ fail("Networking.disable is specified, but the network interface is enabled! Got status code: " + statusCode);
+ }
+ }
+
+ @Test
+ public void http_interface_is_on_when_networking_is_enabled() throws Exception {
+ int httpPort = getFreePort();
+ try (Application application = Application.fromServicesXml(servicesXmlWithServer(httpPort), Networking.enable)) {
+ HttpClient client = new DefaultHttpClient();
+ int statusCode = client.execute(new HttpGet("http://localhost:" + httpPort)).getStatusLine().getStatusCode();
+ assertEquals(200, statusCode);
+ }
+ }
+
+ private static int getFreePort() throws IOException {
+ try (ServerSocket socket = new ServerSocket(0)) {
+ socket.setReuseAddress(true);
+ return socket.getLocalPort();
+ }
+ }
+
+ private static String servicesXmlWithServer(int port) {
+ return "<jdisc version='1.0'>" +
+ " <http> <server port='" + port +"' id='foo'/> </http>" +
+ "</jdisc>";
+ }
+
+}
diff --git a/application/src/test/java/com/yahoo/application/MockResultSearcher.java b/application/src/test/java/com/yahoo/application/MockResultSearcher.java
new file mode 100644
index 00000000000..2dd3a34a6ec
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/MockResultSearcher.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application;
+
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.result.Hit;
+import com.yahoo.search.searchchain.Execution;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class MockResultSearcher extends Searcher {
+
+ @Override
+ public Result search(Query query, Execution execution) {
+ Result result = new Result(query);
+ result.hits().add(new Hit("hasQuery", query));
+ return result;
+ }
+
+}
diff --git a/application/src/test/java/com/yahoo/application/TestDocProc.java b/application/src/test/java/com/yahoo/application/TestDocProc.java
new file mode 100644
index 00000000000..1631c7cd812
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/TestDocProc.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application;
+
+import com.yahoo.config.subscription.ConfigSubscriber;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+import com.yahoo.document.config.DocumentmanagerConfig;
+
+/**
+ * @author bratseth
+ */
+public class TestDocProc extends DocumentProcessor {
+
+ public TestDocProc(DocumentmanagerConfig config) {
+ new ConfigSubscriber().subscribe(DocumentmanagerConfig.class, "client");
+ }
+
+ @Override
+ public Progress process(Processing processing) {
+ return Progress.DONE;
+ }
+
+}
diff --git a/application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java b/application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java
new file mode 100644
index 00000000000..d6c0e08a570
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java
@@ -0,0 +1,153 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container;
+
+import com.yahoo.application.Application;
+import com.yahoo.application.ApplicationBuilder;
+import com.yahoo.application.Networking;
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.application.container.docprocs.Rot13DocumentProcessor;
+import com.yahoo.container.Container;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.document.DocumentType;
+import com.yahoo.processing.execution.chain.ChainRegistry;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class JDiscContainerDocprocTest {
+
+ private static final String DOCUMENT = "document music {\n"
+ + " field title type string { }\n"
+ + "}\n";
+ private static final String CHAIN_NAME = "myChain";
+
+
+ private static String getXML(String chainName, String... processorIds) {
+ String xml =
+ "<container version=\"1.0\">\n" +
+ " <document-processing>\n" +
+ " <chain id=\"" + chainName + "\">\n";
+ for (String processorId : processorIds) {
+ xml += " <documentprocessor id=\"" + processorId + "\"/>\n";
+ }
+ xml +=
+ " </chain>\n" +
+ " </document-processing>\n" +
+ "</container>\n";
+ return xml;
+ }
+
+ @Before
+ public void resetContainer() {
+ Container.resetInstance();
+ }
+
+ @Test
+ public void requireThatBasicDocumentProcessingWorks() throws Exception {
+ try (Application app = new ApplicationBuilder()
+ .servicesXml(getXML(CHAIN_NAME, Rot13DocumentProcessor.class.getCanonicalName()))
+ .documentType("music", DOCUMENT).build()) {
+
+ JDisc container = app.getJDisc("container");
+ DocumentProcessing docProc = container.documentProcessing();
+ DocumentType type = docProc.getDocumentTypes().get("music");
+
+ ChainRegistry<DocumentProcessor> chains = docProc.getChains();
+ assertTrue(chains.allComponentsById().containsKey(new ComponentId(CHAIN_NAME)));
+
+ Document doc = new Document(type, "doc:this:is:a:great:album");
+ doc.setFieldValue("title", "Great Album!");
+ com.yahoo.docproc.Processing processing;
+ DocumentProcessor.Progress progress;
+ DocumentPut put = new DocumentPut(doc);
+
+ processing = com.yahoo.docproc.Processing.of(put);
+ progress = docProc.process(ComponentSpecification.fromString(CHAIN_NAME), processing);
+ assertThat(progress, sameInstance(DocumentProcessor.Progress.DONE));
+ assertThat(doc.getFieldValue("title").toString(), equalTo("Terng Nyohz!"));
+
+ processing = com.yahoo.docproc.Processing.of(put);
+ progress = docProc.process(ComponentSpecification.fromString(CHAIN_NAME), processing);
+ assertThat(progress, sameInstance(DocumentProcessor.Progress.DONE));
+ assertThat(doc.getFieldValue("title").toString(), equalTo("Great Album!"));
+ }
+ }
+
+ @Test
+ public void requireThatLaterDocumentProcessingWorks() throws Exception {
+ try (Application app = new ApplicationBuilder()
+ .servicesXml(getXML(CHAIN_NAME, Rot13DocumentProcessor.class.getCanonicalName()))
+ .networking(Networking.disable)
+ .documentType("music", DOCUMENT).build()) {
+ JDisc container = app.getJDisc("container");
+ DocumentProcessing docProc = container.documentProcessing();
+ DocumentType type = docProc.getDocumentTypes().get("music");
+
+ ChainRegistry<DocumentProcessor> chains = docProc.getChains();
+ assertTrue(chains.allComponentsById().containsKey(new ComponentId(CHAIN_NAME)));
+
+ Document doc = new Document(type, "doc:this:is:a:great:album");
+ doc.setFieldValue("title", "Great Album!");
+ com.yahoo.docproc.Processing processing;
+ DocumentProcessor.Progress progress;
+ DocumentPut put = new DocumentPut(doc);
+
+ processing = com.yahoo.docproc.Processing.of(put);
+
+ progress = docProc.processOnce(ComponentSpecification.fromString(CHAIN_NAME), processing);
+ assertThat(progress, instanceOf(DocumentProcessor.LaterProgress.class));
+ assertThat(doc.getFieldValue("title").toString(), equalTo("Great Album!"));
+
+ progress = docProc.processOnce(ComponentSpecification.fromString(CHAIN_NAME), processing);
+ assertThat(progress, instanceOf(DocumentProcessor.LaterProgress.class));
+ assertThat(doc.getFieldValue("title").toString(), equalTo("Great Album!"));
+
+ progress = docProc.processOnce(ComponentSpecification.fromString(CHAIN_NAME), processing);
+ assertThat(progress, sameInstance(DocumentProcessor.Progress.DONE));
+ assertThat(doc.getFieldValue("title").toString(), equalTo("Terng Nyohz!"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void requireThatUnknownChainThrows() {
+ try (JDisc container = JDisc.fromServicesXml(
+ getXML("foo", Rot13DocumentProcessor.class.getCanonicalName()),
+ Networking.disable)) {
+ container.documentProcessing().process(ComponentSpecification.fromString("unknown"),
+ new com.yahoo.docproc.Processing());
+ }
+
+ }
+
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void requireThatProcessingFails() {
+ try (JDisc container = JDisc.fromServicesXml(
+ getXML("foo", Rot13DocumentProcessor.class.getCanonicalName()),
+ Networking.disable)) {
+ container.processing();
+ }
+
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void requireThatSearchFails() {
+ try (JDisc container = JDisc.fromServicesXml(
+ getXML("foo", Rot13DocumentProcessor.class.getCanonicalName()),
+ Networking.disable)) {
+ container.search();
+ }
+
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java b/application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java
new file mode 100644
index 00000000000..a95068ce3a4
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java
@@ -0,0 +1,125 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container;
+
+import com.yahoo.application.Networking;
+import com.yahoo.application.container.processors.Rot13Processor;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.container.Container;
+import com.yahoo.processing.Request;
+import com.yahoo.processing.Response;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class JDiscContainerProcessingTest {
+
+ private static String getXML(String chainName, String... processorIds) {
+ String xml =
+ "<container version=\"1.0\">\n" +
+ " <processing>\n" +
+ " <chain id=\"" + chainName + "\">\n";
+ for (String processorId : processorIds) {
+ xml += " <processor id=\"" + processorId + "\"/>\n";
+ }
+ xml +=
+ " </chain>\n" +
+ " </processing>\n" +
+ "</container>\n";
+ return xml;
+ }
+
+ @Before
+ public void resetContainer() {
+ Container.resetInstance();
+ }
+
+ @Test
+ public void requireThatBasicProcessingWorks() {
+ try (JDisc container = getContainerWithRot13()) {
+ Processing processing = container.processing();
+
+ Request req = new Request();
+ req.properties().set("title", "Good day!");
+ Response response = processing.process(ComponentSpecification.fromString("foo"), req);
+
+ assertThat(response.data().get(0).toString(), equalTo("Tbbq qnl!"));
+ }
+ }
+
+ @Test
+ public void requireThatBasicProcessingDoesNotTruncateBigResponse() {
+ final int SIZE = 50*1000;
+ StringBuilder foo = new StringBuilder();
+ for (int j = 0 ; j < SIZE ; j++) {
+ foo.append('b');
+ }
+
+ try (JDisc container = getContainerWithRot13()) {
+ final int NUM_TIMES = 10000;
+ for (int i = 0; i < NUM_TIMES; i++) {
+
+
+ com.yahoo.application.container.handler.Response response =
+ container.handleRequest(
+ new com.yahoo.application.container.handler.Request("http://foo/processing/?chain=foo&title=" + foo.toString()));
+
+ assertThat(response.getBody().length, is(SIZE+26));
+ }
+ }
+ }
+
+ @Test
+ public void processing_and_rendering_works() throws Exception {
+ try (JDisc container = getContainerWithRot13()) {
+ Processing processing = container.processing();
+
+ Request req = new Request();
+ req.properties().set("title", "Good day!");
+
+ byte[] rendered = processing.processAndRender(ComponentSpecification.fromString("foo"),
+ ComponentSpecification.fromString("default"), req);
+ String renderedAsString = new String(rendered, "utf-8");
+
+ assertThat(renderedAsString, containsString("Tbbq qnl!"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void requireThatUnknownChainThrows() {
+ try (JDisc container = getContainerWithRot13()) {
+ container.processing().process(ComponentSpecification.fromString("unknown"), new Request());
+ }
+
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void requireThatDocprocFails() {
+ try (JDisc container = getContainerWithRot13()) {
+ container.documentProcessing();
+ }
+
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void requireThatSearchFails() {
+ try (JDisc container = getContainerWithRot13()) {
+ container.search();
+ }
+
+ }
+
+ private JDisc getContainerWithRot13() {
+ return JDisc.fromServicesXml(
+ getXML("foo", Rot13Processor.class.getCanonicalName()),
+ Networking.disable);
+ }
+
+}
diff --git a/application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java b/application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java
new file mode 100644
index 00000000000..1ab91547f04
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java
@@ -0,0 +1,87 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container;
+
+import com.yahoo.application.Networking;
+import com.yahoo.application.container.handler.Request;
+import com.yahoo.application.container.handler.Response;
+import com.yahoo.application.container.handlers.DelayedThrowingInWriteRequestHandler;
+import com.yahoo.application.container.handlers.DelayedWriteException;
+import com.yahoo.application.container.handlers.EchoRequestHandler;
+import com.yahoo.application.container.handlers.HeaderEchoRequestHandler;
+import com.yahoo.application.container.handlers.ThrowingInWriteRequestHandler;
+import com.yahoo.application.container.handlers.WriteException;
+import com.yahoo.text.Utf8;
+import org.junit.Test;
+
+import java.nio.charset.CharacterCodingException;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class JDiscContainerRequestTest {
+
+ private static String getXML(String className, String binding) {
+ return "<container version=\"1.0\">\n" +
+ " <handler id=\"test-handler\" class=\"" +
+ className +
+ "\">\n" +
+ " <binding>" +
+ binding +
+ "</binding>\n" +
+ " </handler>\n" +
+ "</container>";
+ }
+
+ @Test
+ public void requireThatRequestBodyWorks() throws InterruptedException, CharacterCodingException {
+ final String DATA = "we have no bananas today";
+ Request req = new Request("http://banana/echo", DATA.getBytes(Utf8.getCharset()));
+
+ try (JDisc container = JDisc.fromServicesXml(getXML(EchoRequestHandler.class.getCanonicalName(), "http://*/echo"), Networking.disable)) {
+ Response response = container.handleRequest(req);
+ assertThat(response.getBodyAsString(), equalTo(DATA));
+ req.toString();
+ response.toString();
+ }
+ }
+
+ @Test
+ public void requireThatCustomRequestHeadersWork() throws InterruptedException {
+ Request req = new Request("http://banana/echo");
+ req.getHeaders().add("X-Foo", "Bar");
+
+ try (JDisc container = JDisc.fromServicesXml(getXML(HeaderEchoRequestHandler.class.getCanonicalName(), "http://*/echo"), Networking.disable)) {
+ Response response = container.handleRequest(req);
+ assertThat(response.getHeaders().contains("X-Foo", "Bar"), is(true));
+ req.toString();
+ response.toString();
+ }
+ }
+
+ @Test(expected = WriteException.class)
+ public void requireThatRequestHandlerThatThrowsInWriteWorks() throws InterruptedException {
+ final String DATA = "we have no bananas today";
+ Request req = new Request("http://banana/throwwrite", DATA.getBytes(Utf8.getCharset()));
+
+ try (JDisc container = JDisc.fromServicesXml(getXML(ThrowingInWriteRequestHandler.class.getCanonicalName(), "http://*/throwwrite"), Networking.disable)) {
+ Response response = container.handleRequest(req);
+ req.toString();
+ }
+ }
+
+
+ @Test(expected = DelayedWriteException.class)
+ public void requireThatRequestHandlerThatThrowsDelayedInWriteWorks() throws InterruptedException {
+ final String DATA = "we have no bananas today";
+ Request req = new Request("http://banana/delayedthrowwrite", DATA.getBytes(Utf8.getCharset()));
+
+ try (JDisc container = JDisc.fromServicesXml(getXML(DelayedThrowingInWriteRequestHandler.class.getCanonicalName(), "http://*/delayedthrowwrite"), Networking.disable)) {
+ Response response = container.handleRequest(req);
+ req.toString();
+ }
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/MockClient.java b/application/src/test/java/com/yahoo/application/container/MockClient.java
new file mode 100644
index 00000000000..1f885cfb737
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/MockClient.java
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container;
+
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.service.AbstractClientProvider;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockClient extends AbstractClientProvider {
+ private final AtomicInteger counter = new AtomicInteger();
+
+ @Override
+ public ContentChannel handleRequest(Request request, ResponseHandler handler) {
+ counter.incrementAndGet();
+ final ContentChannel responseContentChannel = handler.handleResponse(new Response(200));
+ responseContentChannel.close(NOOP_COMPLETION_HANDLER);
+ return null;
+ }
+
+ public int getCounter() {
+ return counter.get();
+ }
+
+ private static final CompletionHandler NOOP_COMPLETION_HANDLER = new CompletionHandler() {
+ @Override
+ public void completed() {
+ // Ignored
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ // Ignored
+ }
+ };
+}
diff --git a/application/src/test/java/com/yahoo/application/container/MockServer.java b/application/src/test/java/com/yahoo/application/container/MockServer.java
new file mode 100644
index 00000000000..bc79671c13a
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/MockServer.java
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container;
+
+import com.yahoo.jdisc.service.AbstractServerProvider;
+import com.yahoo.jdisc.service.CurrentContainer;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockServer extends AbstractServerProvider {
+ private boolean started = false;
+
+ public MockServer(CurrentContainer container) {
+ super(container);
+ }
+
+ @Override
+ public void start() {
+ started = true;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ public boolean isStarted() {
+ return started;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java b/application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java
new file mode 100644
index 00000000000..3c11cbdad8a
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java
@@ -0,0 +1,65 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.docprocs;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentOperation;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.documentapi.messagebus.protocol.DocumentMessage;
+import com.yahoo.documentapi.messagebus.protocol.PutDocumentMessage;
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.handler.RequestDispatch;
+import com.yahoo.jdisc.service.CurrentContainer;
+import com.yahoo.messagebus.jdisc.MbusRequest;
+import com.yahoo.messagebus.routing.Route;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * @author Christian Andersen
+ */
+public class MockDispatchDocproc extends DocumentProcessor {
+ private final Route route;
+ private final URI uri;
+ private final CurrentContainer currentContainer;
+ private final List<Response> responses = new ArrayList<>();
+
+ public MockDispatchDocproc(CurrentContainer currentContainer) {
+ this.route = Route.parse("default");
+ this.uri = URI.create("mbus://remotehost/source");
+ this.currentContainer = currentContainer;
+ }
+
+ @Override
+ public Progress process(Processing processing) {
+ for (DocumentOperation op : processing.getDocumentOperations()) {
+ PutDocumentMessage message = new PutDocumentMessage((DocumentPut)op);
+ ListenableFuture<Response> future = createRequest(message).dispatch();
+ try {
+ responses.add(future.get());
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return Progress.DONE;
+ }
+
+ private RequestDispatch createRequest(final DocumentMessage message) {
+ return new RequestDispatch() {
+ @Override
+ protected Request newRequest() {
+ return new MbusRequest(currentContainer, uri, message.setRoute(route)).setServerRequest(false);
+ }
+ };
+ }
+
+ public List<Response> getResponses() {
+ return responses;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java b/application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java
new file mode 100644
index 00000000000..c07655cfe5c
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java
@@ -0,0 +1,34 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.docprocs;
+
+import com.google.inject.Inject;
+import com.yahoo.application.MockApplicationConfig;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+
+/**
+ * @author Christian Andersen
+ */
+public class MockDocproc extends DocumentProcessor {
+ private Processing lastProcessing;
+ private MockApplicationConfig config;
+
+ @Inject
+ public MockDocproc(MockApplicationConfig config) {
+ this.config = config;
+ }
+
+ @Override
+ public Progress process(Processing processing) {
+ this.lastProcessing = processing;
+ return Progress.DONE;
+ }
+
+ public Processing getLastProcessing() {
+ return lastProcessing;
+ }
+
+ public MockApplicationConfig getConfig() {
+ return config;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java b/application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java
new file mode 100644
index 00000000000..ec23b3671b9
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java
@@ -0,0 +1,54 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.docprocs;
+
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentOperation;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.document.datatypes.StringFieldValue;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class Rot13DocumentProcessor extends DocumentProcessor {
+ private static final String FIELD_NAME = "title";
+
+ private AtomicInteger counter = new AtomicInteger(1);
+
+ public static String rot13(String s) {
+ StringBuilder output = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M') {
+ c += 13;
+ } else if (c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z') {
+ c -= 13;
+ }
+ output.append(c);
+ }
+ return output.toString();
+ }
+
+ @Override
+ public Progress process(Processing processing) {
+ int oldVal = counter.getAndIncrement();
+ if ((oldVal % 3) != 0) {
+ return Progress.LATER;
+ }
+
+ for (DocumentOperation op : processing.getDocumentOperations()) {
+ if (op instanceof DocumentPut) {
+ Document document = ((DocumentPut)op).getDocument();
+
+ StringFieldValue oldTitle = (StringFieldValue) document.getFieldValue(FIELD_NAME);
+ if (oldTitle != null) {
+ document.setFieldValue(FIELD_NAME, rot13(oldTitle.getString()));
+ }
+ }
+ }
+ return Progress.DONE;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java b/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java
new file mode 100644
index 00000000000..7f4a32c7df6
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java
@@ -0,0 +1,366 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handler;
+
+import org.junit.Test;
+
+import java.util.*;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class HeadersTestCase {
+
+ @Test
+ public void requireThatSizeWorksAsExpected() {
+ Headers headers = new Headers();
+ assertEquals(0, headers.size());
+ headers.add("foo", "bar");
+ assertEquals(1, headers.size());
+ headers.add("foo", "baz");
+ assertEquals(1, headers.size());
+ headers.add("bar", "baz");
+ assertEquals(2, headers.size());
+ headers.remove("foo");
+ assertEquals(1, headers.size());
+ headers.remove("bar");
+ assertEquals(0, headers.size());
+ }
+
+ @Test
+ public void requireThatIsEmptyWorksAsExpected() {
+ Headers headers = new Headers();
+ assertTrue(headers.isEmpty());
+ headers.add("foo", "bar");
+ assertFalse(headers.isEmpty());
+ headers.remove("foo");
+ assertTrue(headers.isEmpty());
+ }
+
+ @Test
+ public void requireThatContainsKeyWorksAsExpected() {
+ Headers headers = new Headers();
+ assertFalse(headers.containsKey("foo"));
+ assertFalse(headers.containsKey("FOO"));
+ headers.add("foo", "bar");
+ assertTrue(headers.containsKey("foo"));
+ assertTrue(headers.containsKey("FOO"));
+ }
+
+ @Test
+ public void requireThatContainsValueWorksAsExpected() {
+ Headers headers = new Headers();
+ assertFalse(headers.containsValue(Arrays.asList("bar")));
+ headers.add("foo", "bar");
+ assertTrue(headers.containsValue(Arrays.asList("bar")));
+ }
+
+ @Test
+ public void requireThatContainsWorksAsExpected() {
+ Headers headers = new Headers();
+ assertFalse(headers.contains("foo", "bar"));
+ assertFalse(headers.contains("FOO", "bar"));
+ assertFalse(headers.contains("foo", "BAR"));
+ assertFalse(headers.contains("FOO", "BAR"));
+ headers.add("foo", "bar");
+ assertTrue(headers.contains("foo", "bar"));
+ assertTrue(headers.contains("FOO", "bar"));
+ assertFalse(headers.contains("foo", "BAR"));
+ assertFalse(headers.contains("FOO", "BAR"));
+ }
+
+ @Test
+ public void requireThatContainsIgnoreCaseWorksAsExpected() {
+ Headers headers = new Headers();
+ assertFalse(headers.containsIgnoreCase("foo", "bar"));
+ assertFalse(headers.containsIgnoreCase("FOO", "bar"));
+ assertFalse(headers.containsIgnoreCase("foo", "BAR"));
+ assertFalse(headers.containsIgnoreCase("FOO", "BAR"));
+ headers.add("foo", "bar");
+ assertTrue(headers.containsIgnoreCase("foo", "bar"));
+ assertTrue(headers.containsIgnoreCase("FOO", "bar"));
+ assertTrue(headers.containsIgnoreCase("foo", "BAR"));
+ assertTrue(headers.containsIgnoreCase("FOO", "BAR"));
+ }
+
+ @Test
+ public void requireThatAddStringWorksAsExpected() {
+ Headers headers = new Headers();
+ assertNull(headers.get("foo"));
+ headers.add("foo", "bar");
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ headers.add("foo", "baz");
+ assertEquals(Arrays.asList("bar", "baz"), headers.get("foo"));
+ }
+
+ @Test
+ public void requireThatAddListWorksAsExpected() {
+ Headers headers = new Headers();
+ assertNull(headers.get("foo"));
+ headers.add("foo", Arrays.asList("bar"));
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ headers.add("foo", Arrays.asList("baz", "cox"));
+ assertEquals(Arrays.asList("bar", "baz", "cox"), headers.get("foo"));
+ }
+
+ @Test
+ public void requireThatAddAllWorksAsExpected() {
+ Headers headers = new Headers();
+ headers.add("foo", "bar");
+ headers.add("bar", "baz");
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ assertEquals(Arrays.asList("baz"), headers.get("bar"));
+
+ Map<String, List<String>> map = new HashMap<>();
+ map.put("foo", Arrays.asList("baz", "cox"));
+ map.put("bar", Arrays.asList("cox"));
+ headers.addAll(map);
+
+ assertEquals(Arrays.asList("bar", "baz", "cox"), headers.get("foo"));
+ assertEquals(Arrays.asList("baz", "cox"), headers.get("bar"));
+ }
+
+ @Test
+ public void requireThatPutStringWorksAsExpected() {
+ Headers headers = new Headers();
+ assertNull(headers.get("foo"));
+ headers.put("foo", "bar");
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ headers.put("foo", "baz");
+ assertEquals(Arrays.asList("baz"), headers.get("foo"));
+ }
+
+ @Test
+ public void requireThatPutListWorksAsExpected() {
+ Headers headers = new Headers();
+ assertNull(headers.get("foo"));
+ headers.put("foo", Arrays.asList("bar"));
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ headers.put("foo", Arrays.asList("baz", "cox"));
+ assertEquals(Arrays.asList("baz", "cox"), headers.get("foo"));
+ }
+
+ @Test
+ public void requireThatPutAllWorksAsExpected() {
+ Headers headers = new Headers();
+ headers.add("foo", "bar");
+ headers.add("bar", "baz");
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ assertEquals(Arrays.asList("baz"), headers.get("bar"));
+
+ Map<String, List<String>> map = new HashMap<>();
+ map.put("foo", Arrays.asList("baz", "cox"));
+ map.put("bar", Arrays.asList("cox"));
+ headers.putAll(map);
+
+ assertEquals(Arrays.asList("baz", "cox"), headers.get("foo"));
+ assertEquals(Arrays.asList("cox"), headers.get("bar"));
+ }
+
+ @Test
+ public void requireThatRemoveWorksAsExpected() {
+ Headers headers = new Headers();
+ headers.put("foo", Arrays.asList("bar", "baz"));
+ assertEquals(Arrays.asList("bar", "baz"), headers.get("foo"));
+ assertEquals(Arrays.asList("bar", "baz"), headers.remove("foo"));
+ assertNull(headers.get("foo"));
+ assertNull(headers.remove("foo"));
+ }
+
+ @Test
+ public void requireThatRemoveStringWorksAsExpected() {
+ Headers headers = new Headers();
+ headers.put("foo", Arrays.asList("bar", "baz"));
+ assertEquals(Arrays.asList("bar", "baz"), headers.get("foo"));
+ assertTrue(headers.remove("foo", "bar"));
+ assertFalse(headers.remove("foo", "cox"));
+ assertEquals(Arrays.asList("baz"), headers.get("foo"));
+ assertTrue(headers.remove("foo", "baz"));
+ assertFalse(headers.remove("foo", "cox"));
+ assertNull(headers.get("foo"));
+ }
+
+ @Test
+ public void requireThatClearWorksAsExpected() {
+ Headers headers = new Headers();
+ headers.add("foo", "bar");
+ headers.add("bar", "baz");
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ assertEquals(Arrays.asList("baz"), headers.get("bar"));
+ headers.clear();
+ assertNull(headers.get("foo"));
+ assertNull(headers.get("bar"));
+ }
+
+ @Test
+ public void requireThatGetWorksAsExpected() {
+ Headers headers = new Headers();
+ assertNull(headers.get("foo"));
+ headers.add("foo", "bar");
+ assertEquals(Arrays.asList("bar"), headers.get("foo"));
+ }
+
+ @Test
+ public void requireThatGetFirstWorksAsExpected() {
+ Headers headers = new Headers();
+ assertNull(headers.getFirst("foo"));
+ headers.add("foo", Arrays.asList("bar", "baz"));
+ assertEquals("bar", headers.getFirst("foo"));
+ }
+
+ @Test
+ public void requireThatIsTrueWorksAsExpected() {
+ Headers headers = new Headers();
+ assertFalse(headers.isTrue("foo"));
+ headers.put("foo", Arrays.asList("true"));
+ assertTrue(headers.isTrue("foo"));
+ headers.put("foo", Arrays.asList("true", "true"));
+ assertTrue(headers.isTrue("foo"));
+ headers.put("foo", Arrays.asList("true", "false"));
+ assertFalse(headers.isTrue("foo"));
+ headers.put("foo", Arrays.asList("false", "true"));
+ assertFalse(headers.isTrue("foo"));
+ headers.put("foo", Arrays.asList("false", "false"));
+ assertFalse(headers.isTrue("foo"));
+ headers.put("foo", Arrays.asList("false"));
+ assertFalse(headers.isTrue("foo"));
+ }
+
+ @Test
+ public void requireThatKeySetWorksAsExpected() {
+ Headers headers = new Headers();
+ assertEquals(Collections.<Set<String>>emptySet(), headers.keySet());
+ headers.add("foo", "bar");
+ assertEquals(new HashSet<>(Arrays.asList("foo")), headers.keySet());
+ headers.add("bar", "baz");
+ assertEquals(new HashSet<>(Arrays.asList("foo", "bar")), headers.keySet());
+ }
+
+ @Test
+ public void requireThatValuesWorksAsExpected() {
+ Headers headers = new Headers();
+ assertTrue(headers.values().isEmpty());
+ headers.add("foo", "bar");
+ Collection<List<String>> values = headers.values();
+ assertEquals(1, values.size());
+ assertTrue(values.contains(Arrays.asList("bar")));
+
+ headers.add("bar", "baz");
+ values = headers.values();
+ assertEquals(2, values.size());
+ assertTrue(values.contains(Arrays.asList("bar")));
+ assertTrue(values.contains(Arrays.asList("baz")));
+ }
+
+ @Test
+ public void requireThatEntrySetWorksAsExpected() {
+ Headers headers = new Headers();
+ assertEquals(Collections.emptySet(), headers.entrySet());
+ headers.put("foo", Arrays.asList("bar", "baz"));
+
+ Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
+ assertEquals(1, entries.size());
+ Map.Entry<String, List<String>> entry = entries.iterator().next();
+ assertNotNull(entry);
+ assertEquals("foo", entry.getKey());
+ assertEquals(Arrays.asList("bar", "baz"), entry.getValue());
+ }
+
+ @Test
+ public void requireThatEntriesWorksAsExpected() {
+ Headers headers = new Headers();
+ assertEquals(Collections.emptyList(), headers.entries());
+ headers.put("foo", Arrays.asList("bar", "baz"));
+
+ List<Map.Entry<String, String>> entries = headers.entries();
+ assertEquals(2, entries.size());
+
+ Map.Entry<String, String> entry = entries.get(0);
+ assertNotNull(entry);
+ assertEquals("foo", entry.getKey());
+ assertEquals("bar", entry.getValue());
+
+ assertNotNull(entry = entries.get(1));
+ assertEquals("foo", entry.getKey());
+ assertEquals("baz", entry.getValue());
+ }
+
+ @Test
+ public void requireThatEntryIsUnmodifiable() {
+ Headers headers = new Headers();
+ headers.put("foo", "bar");
+ Map.Entry<String, String> entry = headers.entries().get(0);
+ try {
+ entry.setValue("baz");
+ fail();
+ } catch (UnsupportedOperationException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatEntriesAreUnmodifiable() {
+ Headers headers = new Headers();
+ headers.put("foo", "bar");
+ List<Map.Entry<String, String>> entries = headers.entries();
+ try {
+ entries.add(new MyEntry());
+ fail();
+ } catch (UnsupportedOperationException e) {
+
+ }
+ try {
+ entries.remove(new MyEntry());
+ fail();
+ } catch (UnsupportedOperationException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatEqualsWorksAsExpected() {
+ Headers lhs = new Headers();
+ Headers rhs = new Headers();
+ assertTrue(lhs.equals(rhs));
+ lhs.add("foo", "bar");
+ assertFalse(lhs.equals(rhs));
+ rhs.add("foo", "bar");
+ assertTrue(lhs.equals(rhs));
+ }
+
+ @Test
+ public void requireThatHashCodeWorksAsExpected() {
+ Headers lhs = new Headers();
+ Headers rhs = new Headers();
+ assertTrue(lhs.hashCode() == rhs.hashCode());
+ lhs.add("foo", "bar");
+ assertTrue(lhs.hashCode() != rhs.hashCode());
+ rhs.add("foo", "bar");
+ assertTrue(lhs.hashCode() == rhs.hashCode());
+ }
+
+ private static class MyEntry implements Map.Entry<String, String> {
+
+ @Override
+ public String getKey() {
+ return "key";
+ }
+
+ @Override
+ public String getValue() {
+ return "value";
+ }
+
+ @Override
+ public String setValue(String value) {
+ return "value";
+ }
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java b/application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java
new file mode 100644
index 00000000000..53b62753523
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handler;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class ResponseTestCase {
+
+ @Test
+ public void requireThatCharsetParsingWorks() {
+ assertThat(Response.charset("text/foobar").toString().toLowerCase(), equalTo("utf-8"));
+ assertThat(Response.charset("adsf").toString().toLowerCase(), equalTo("utf-8"));
+ assertThat(Response.charset("").toString().toLowerCase(), equalTo("utf-8"));
+ assertThat(Response.charset(null).toString().toLowerCase(), equalTo("utf-8"));
+
+ assertThat(Response.charset("something; charset=US-ASCII").toString().toLowerCase(), equalTo("us-ascii"));
+ assertThat(Response.charset("something; charset=iso-8859-1").toString().toLowerCase(), equalTo("iso-8859-1"));
+
+ assertThat(Response.charset("something; charset=").toString().toLowerCase(), equalTo("utf-8"));
+ assertThat(Response.charset("something; charset=bananarama").toString().toLowerCase(), equalTo("utf-8"));
+ }
+
+ @Test
+ public void testDefaultResponseBody() {
+ Response res1 = new Response();
+ Response res2 = new Response(new byte[0]);
+
+ assertThat(res1.getBody(), notNullValue());
+ assertThat(res1.getBody().length, is(0));
+ assertThat(res2.getBody(), notNullValue());
+ assertThat(res2.getBody().length, is(0));
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java
new file mode 100644
index 00000000000..54c850efd37
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java
@@ -0,0 +1,59 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+import com.yahoo.jdisc.handler.AbstractRequestHandler;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseHandler;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+*/
+public class DelayedThrowingInWriteRequestHandler extends AbstractRequestHandler {
+ private ExecutorService responseExecutor = Executors.newSingleThreadExecutor();
+
+ @Override
+ public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
+ responseExecutor.execute(new DelayedThrowingInWriteTask(handler));
+ return new DelayedThrowingInWriteContentChannel();
+ }
+
+
+ private static class DelayedThrowingInWriteTask implements Runnable {
+ private final ResponseHandler handler;
+
+ public DelayedThrowingInWriteTask(ResponseHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public void run() {
+ ContentChannel responseChannel = handler.handleResponse(
+ new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK));
+ responseChannel.close(null);
+ }
+ }
+
+
+ private static class DelayedThrowingInWriteContentChannel implements ContentChannel {
+ private List<CompletionHandler> writeCompletionHandlers = new ArrayList<>();
+ @Override
+ public void write(ByteBuffer buf, CompletionHandler handler) {
+ writeCompletionHandlers.add(handler);
+ }
+
+ @Override
+ public void close(CompletionHandler handler) {
+ for (CompletionHandler writeCompletionHandler : writeCompletionHandlers) {
+ writeCompletionHandler.failed(new DelayedWriteException());
+ }
+ handler.completed();
+ }
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java b/application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java
new file mode 100644
index 00000000000..e856ac310cb
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class DelayedWriteException extends RuntimeException {
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java
new file mode 100644
index 00000000000..30fe379d864
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java
@@ -0,0 +1,17 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.handler.AbstractRequestHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseHandler;
+
+/**
+* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+*/
+public class EchoRequestHandler extends AbstractRequestHandler {
+ @Override
+ public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
+ return handler.handleResponse(new com.yahoo.jdisc.Response(Response.Status.OK));
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java
new file mode 100644
index 00000000000..4eea69ea162
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java
@@ -0,0 +1,59 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.handler.AbstractRequestHandler;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseHandler;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+*/
+public class HeaderEchoRequestHandler extends AbstractRequestHandler {
+ private ExecutorService responseExecutor = Executors.newSingleThreadExecutor();
+
+ @Override
+ public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
+ responseExecutor.execute(new HeaderEchoTask(request, handler));
+ return new HeaderEchoContentChannel();
+ }
+
+
+ private static class HeaderEchoTask implements Runnable {
+ private final Request request;
+ private final ResponseHandler handler;
+
+ public HeaderEchoTask(Request request, ResponseHandler handler) {
+ this.request = request;
+ this.handler = handler;
+ }
+
+ @Override
+ public void run() {
+ com.yahoo.jdisc.Response response = new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK);
+ response.headers().addAll(request.headers());
+ ContentChannel responseChannel = handler.handleResponse(response);
+ responseChannel.close(null);
+ }
+ }
+
+
+ private static class HeaderEchoContentChannel implements ContentChannel {
+ @Override
+ public void write(ByteBuffer buf, CompletionHandler handler) {
+ //we will not accept header body data
+ handler.completed();
+ }
+
+ @Override
+ public void close(CompletionHandler handler) {
+ //we will not accept header body data
+ handler.completed();
+ }
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java
new file mode 100644
index 00000000000..b3287015b28
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.concurrent.Executor;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockHttpHandler extends ThreadedHttpRequestHandler {
+ public MockHttpHandler(Executor executor) {
+ super(executor);
+ }
+
+ @Override
+ public HttpResponse handle(HttpRequest request) {
+ return new HttpResponse(200) {
+ @Override
+ public void render(OutputStream outputStream) throws IOException {
+ PrintStream out = new PrintStream(outputStream);
+ out.print("OK");
+ out.flush();
+ }
+ };
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java
new file mode 100644
index 00000000000..9cde4eaf9f8
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java
@@ -0,0 +1,53 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+import com.yahoo.jdisc.handler.AbstractRequestHandler;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseHandler;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+*/
+public class ThrowingInWriteRequestHandler extends AbstractRequestHandler {
+ private ExecutorService responseExecutor = Executors.newSingleThreadExecutor();
+
+ @Override
+ public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) {
+ responseExecutor.execute(new ThrowingInWriteTask(handler));
+ return new ThrowingInWriteContentChannel();
+ }
+
+
+ private static class ThrowingInWriteTask implements Runnable {
+ private final ResponseHandler handler;
+
+ public ThrowingInWriteTask(ResponseHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public void run() {
+ ContentChannel responseChannel = handler.handleResponse(
+ new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK));
+ responseChannel.close(null);
+ }
+ }
+
+
+ private static class ThrowingInWriteContentChannel implements ContentChannel {
+ @Override
+ public void write(ByteBuffer buf, CompletionHandler handler) {
+ throw new WriteException();
+ }
+
+ @Override
+ public void close(CompletionHandler handler) {
+ handler.completed();
+ }
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/handlers/WriteException.java b/application/src/test/java/com/yahoo/application/container/handlers/WriteException.java
new file mode 100644
index 00000000000..63a62340efe
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/handlers/WriteException.java
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class WriteException extends RuntimeException {
+}
diff --git a/application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java b/application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java
new file mode 100644
index 00000000000..f0ec8c25a88
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java
@@ -0,0 +1,26 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.processors;
+
+import com.yahoo.processing.Processor;
+import com.yahoo.processing.Request;
+import com.yahoo.processing.Response;
+import com.yahoo.processing.execution.Execution;
+import com.yahoo.processing.test.ProcessorLibrary;
+
+import static com.yahoo.application.container.docprocs.Rot13DocumentProcessor.rot13;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class Rot13Processor extends Processor {
+ @Override
+ public Response process(Request request, Execution execution) {
+ Object fooObj = request.properties().get("title");
+
+ Response response = new Response(request);
+ if (fooObj != null) {
+ response.data().add(new ProcessorLibrary.StringData(request, rot13(fooObj.toString())));
+ }
+ return response;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java b/application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java
new file mode 100644
index 00000000000..6e9899c7b9f
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.renderers;
+
+import com.yahoo.search.Result;
+import com.yahoo.search.rendering.Renderer;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockRenderer extends Renderer {
+ public MockRenderer() {
+ }
+
+ @Override
+ public String getEncoding() {
+ return "utf-8";
+ }
+
+ @Override
+ public String getMimeType() {
+ return "applications/xml";
+ }
+
+ @Override
+ protected void render(Writer writer, Result result) throws IOException {
+ writer.write("<mock hits=\"" + result.hits().size() + "\" />");
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java b/application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java
new file mode 100644
index 00000000000..ad65e597519
--- /dev/null
+++ b/application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.searchers;
+
+import com.yahoo.search.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.result.Hit;
+import com.yahoo.search.result.HitGroup;
+import com.yahoo.search.searchchain.Execution;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockSearcher extends Searcher {
+ @Override
+ public Result search(Query query, Execution execution) {
+ HitGroup hits = new HitGroup();
+ hits.add(new Hit("foo", query));
+ return new Result(query, hits);
+ }
+}