summaryrefslogtreecommitdiffstats
path: root/container-core/src/test/java/com/yahoo/processing/rendering/AsynchronousSectionedRendererTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'container-core/src/test/java/com/yahoo/processing/rendering/AsynchronousSectionedRendererTest.java')
-rw-r--r--container-core/src/test/java/com/yahoo/processing/rendering/AsynchronousSectionedRendererTest.java435
1 files changed, 435 insertions, 0 deletions
diff --git a/container-core/src/test/java/com/yahoo/processing/rendering/AsynchronousSectionedRendererTest.java b/container-core/src/test/java/com/yahoo/processing/rendering/AsynchronousSectionedRendererTest.java
new file mode 100644
index 00000000000..b8efd8562b1
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/rendering/AsynchronousSectionedRendererTest.java
@@ -0,0 +1,435 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.rendering;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.yahoo.component.provider.ListenableFreezableClass;
+import com.yahoo.container.jdisc.ContentChannelOutputStream;
+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.request.ErrorMessage;
+import com.yahoo.processing.response.ArrayDataList;
+import com.yahoo.processing.response.Data;
+import com.yahoo.processing.response.DataList;
+import com.yahoo.processing.response.IncomingData;
+import com.yahoo.text.Utf8;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.*;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class AsynchronousSectionedRendererTest {
+
+ private static final Charset CHARSET = Utf8.getCharset();
+
+ @Test
+ public void testAsyncSectionedRenderer() throws IOException, InterruptedException {
+ StringDataList dataList = createDataListWithStrangeStrings();
+
+ TestRenderer renderer = new TestRenderer();
+ renderer.init();
+
+ String str = render(renderer, dataList);
+
+ assertThat(str,
+ equalTo(" beginResponse beginList[f\\o\"o, [b/a\br, f\f\no\ro\tbar\u0005]] dataf\\o\"o beginList[b/a\br, " +
+ "f\f\no\ro\tbar\u0005] datab/a\br dataf\f\no\ro\tbar\u0005 endList[b/a\br, f\f\no\ro\tbar\u0005] endList[f\\o\"o, [b/a\br, f\f\no\ro\tbar\u0005]] endResponse"));
+ }
+
+ @Test
+ public void testEmptyProcessingRendering() throws IOException, InterruptedException {
+ Request request = new Request();
+ DataList dataList = ArrayDataList.create(request);
+
+ assertThat(render(dataList),
+ equalTo("{\"datalist\":[" +
+ "]}"));
+ }
+
+ @Test
+ public void testProcessingRendering() throws IOException, InterruptedException {
+ StringDataList dataList = createDataListWithStrangeStrings();
+
+ assertThat(render(dataList),
+ equalTo("{\"datalist\":[" +
+ "{\"data\":\"f\\\\o\\\"o\"}," +
+ "{\"datalist\":[" +
+ "{\"data\":\"b/a\\br\"}," +
+ "{\"data\":\"f\\f\\no\\ro\\tbar\\u0005\"}" +
+ "]}" +
+ "]}"));
+ }
+
+ @Test
+ public void testProcessingRenderingWithErrors() throws IOException, InterruptedException {
+ StringDataList dataList = createDataList();
+
+ // Add errors
+ dataList.request().errors().add(new ErrorMessage("m1","d1"));
+ dataList.request().errors().add(new ErrorMessage("m2","d2"));
+
+ assertThat(render(dataList),
+ equalTo("{\"errors\":[" +
+ "\"m1: d1\"," +
+ "\"m2: d2\"" +
+ "]," +
+ "\"datalist\":[" +
+ "{\"data\":\"l1\"}," +
+ "{\"datalist\":[" +
+ "{\"data\":\"l11\"}," +
+ "{\"data\":\"l12\"}" +
+ "]}" +
+ "]}"));
+ }
+
+ @Test
+ public void testProcessingRenderingWithStackTraces() throws IOException, InterruptedException {
+ Exception exception=null;
+ // Create thrown exception
+ try {
+ throw new RuntimeException("Thrown");
+ }
+ catch (RuntimeException e) {
+ exception=e;
+ }
+
+ StringDataList dataList = createDataList();
+
+ // Add errors
+ dataList.request().errors().add(new ErrorMessage("m1","d1",exception));
+ dataList.request().errors().add(new ErrorMessage("m2","d2"));
+
+ assertEquals(
+ "{\"errors\":[" +
+ "{" +
+ "\"error\":\"m1: d1: Thrown\"," +
+ "\"stacktrace\":\"java.lang.RuntimeException: Thrown\\n\\tat com.yahoo.processing.rendering.AsynchronousSectionedRendererTest.",
+ render(dataList).substring(0,157));
+ }
+
+ @Test
+ public void testProcessingRenderingWithClonedErrorRequest() throws IOException, InterruptedException {
+ StringDataList dataList = createDataList();
+
+ // Add errors
+ dataList.request().errors().add(new ErrorMessage("m1","d1"));
+ dataList.request().errors().add(new ErrorMessage("m2","d2"));
+ dataList.add(new StringDataList(dataList.request().clone())); // Cloning a request which contains errors
+ // ... should not cause repetition of those errors
+
+ assertThat(render(dataList),
+ equalTo("{\"errors\":[" +
+ "\"m1: d1\"," +
+ "\"m2: d2\"" +
+ "]," +
+ "\"datalist\":[" +
+ "{\"data\":\"l1\"}," +
+ "{\"datalist\":[" +
+ "{\"data\":\"l11\"}," +
+ "{\"data\":\"l12\"}" +
+ "]}," +
+ "{\"datalist\":[]}" +
+ "]}"));
+ }
+
+ @Test
+ public void testProcessingRenderingWithClonedErrorRequestContainingNewErrors() throws IOException, InterruptedException {
+ StringDataList dataList = createDataList();
+
+ // Add errors
+ dataList.request().errors().add(new ErrorMessage("m1","d1"));
+ dataList.request().errors().add(new ErrorMessage("m2","d2"));
+ dataList.add(new StringDataList(dataList.request().clone())); // Cloning a request containing errors
+ // and adding new errors to it
+ dataList.asList().get(2).request().errors().add(new ErrorMessage("m3","d3"));
+
+ assertThat(render(dataList),
+ equalTo("{\"errors\":[" +
+ "\"m1: d1\"," +
+ "\"m2: d2\"" +
+ "]," +
+ "\"datalist\":[" +
+ "{\"data\":\"l1\"}," +
+ "{\"datalist\":[" +
+ "{\"data\":\"l11\"}," +
+ "{\"data\":\"l12\"}" +
+ "]}," +
+ "{\"errors\":[" +
+ "\"m3: d3\"" +
+ "]," +
+ "\"datalist\":[]}" +
+ "]}"));
+ }
+
+ public StringDataList createDataList() {
+ Request request = new Request();
+ StringDataList dataList = new StringDataList(request);
+ dataList.add(new StringDataItem(request, "l1"));
+ StringDataList secondLevel = new StringDataList(request);
+ secondLevel.add(new StringDataItem(request, "l11"));
+ secondLevel.add(new StringDataItem(request, "l12"));
+ dataList.add(secondLevel);
+ return dataList;
+ }
+
+ public StringDataList createDataListWithStrangeStrings() {
+ Request request = new Request();
+ StringDataList dataList = new StringDataList(request);
+ dataList.add(new StringDataItem(request, "f\\o\"o"));
+ StringDataList secondLevel = new StringDataList(request);
+ secondLevel.add(new StringDataItem(request, "b/a\br"));
+ secondLevel.add(new StringDataItem(request, "f\f\no\ro\tbar\u0005"));
+ dataList.add(secondLevel);
+ return dataList;
+ }
+
+ public String render(DataList data) throws InterruptedException, IOException {
+ ProcessingRenderer renderer = new ProcessingRenderer();
+ renderer.init();
+ return render(renderer, data);
+ }
+
+ @SuppressWarnings("unchecked")
+ public String render(Renderer renderer, DataList data) throws InterruptedException, IOException {
+ TestContentChannel contentChannel = new TestContentChannel();
+
+ Execution execution = Execution.createRoot(new NoopProcessor(), 0, null);
+
+ final ContentChannelOutputStream stream = new ContentChannelOutputStream(contentChannel);
+ ListenableFuture result = renderer.render(stream, new Response(data), execution, null);
+
+ int waitCounter = 1000;
+ while (!result.isDone()) {
+ Thread.sleep(60);
+ --waitCounter;
+ if (waitCounter < 0) {
+ throw new IllegalStateException();
+ }
+ }
+
+ stream.close();
+ contentChannel.close(null);
+
+ String str = "";
+ for (ByteBuffer buf : contentChannel.getBuffers()) {
+ str += Utf8.toString(buf);
+ }
+ return str;
+ }
+
+ private static class TestRenderer extends AsynchronousSectionedRenderer<Response> {
+
+ private OutputStream stream;
+
+ private StringDataList checkInstanceList(DataList<?> list) {
+ if (!(list instanceof StringDataList)) {
+ throw new IllegalArgumentException();
+ }
+ return (StringDataList) list;
+ }
+
+ private StringData checkInstanceData(Data data) {
+ if (!(data instanceof StringData)) {
+ throw new IllegalArgumentException();
+ }
+ return (StringData) data;
+ }
+
+
+ @Override
+ public void beginResponse(OutputStream stream) {
+ this.stream = stream;
+ try {
+ stream.write(" beginResponse".getBytes(CHARSET));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void beginList(DataList<?> list) {
+ StringDataList stringDataList = checkInstanceList(list);
+ try {
+ stream.write((" beginList" + stringDataList.getString()).getBytes(CHARSET));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void data(Data data) {
+ StringData stringData = checkInstanceData(data);
+ try {
+ stream.write((" data" + stringData.getString()).getBytes(CHARSET));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void endList(DataList<?> list) {
+ StringDataList stringDataList = checkInstanceList(list);
+ try {
+ stream.write((" endList" + stringDataList.getString()).getBytes(CHARSET));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void endResponse() {
+ try {
+ stream.write(" endResponse".getBytes(CHARSET));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getEncoding() {
+ return CHARSET.name();
+ }
+
+ @Override
+ public String getMimeType() {
+ return "text/plain";
+ }
+ }
+
+ private abstract class StringData extends ListenableFreezableClass implements Data {
+ private final Request request;
+
+ private StringData(Request request) {
+ this.request = request;
+ }
+
+ @Override
+ public Request request() {
+ return request;
+ }
+
+ public abstract String getString();
+
+ @Override
+ public String toString() {
+ return getString();
+ }
+ }
+
+ private class StringDataItem extends StringData {
+
+ private final String string;
+
+ private StringDataItem(Request request, String string) {
+ super(request);
+ this.string = string;
+ }
+
+ @Override
+ public String getString() {
+ return string;
+ }
+ }
+
+ private class StringDataList extends StringData implements DataList<StringData> {
+
+ private final ArrayList<StringData> list = new ArrayList<>();
+
+ private final IncomingData incomingData;
+
+ private StringDataList(Request request) {
+ super(request);
+ incomingData = new IncomingData.NullIncomingData<>(this);
+ }
+
+ @Override
+ public StringData add(StringData data) {
+ list.add(data);
+ return data;
+ }
+
+ @Override
+ public StringData get(int index) {
+ return list.get(index);
+ }
+
+ @Override
+ public List<StringData> asList() {
+ return list;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public IncomingData<StringData> incoming() {
+ return incomingData;
+ }
+
+ @Override
+ public void addDataListener(Runnable runnable) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public ListenableFuture<DataList<StringData>> complete() {
+ return new ListenableFuture<DataList<StringData>>() {
+ @Override
+ public void addListener(Runnable runnable, Executor executor) {
+ }
+
+ @Override
+ public boolean cancel(boolean b) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public DataList<StringData> get() throws InterruptedException, ExecutionException {
+ return StringDataList.this;
+ }
+
+ @Override
+ public DataList<StringData> get(long l, TimeUnit timeUnit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return StringDataList.this;
+ }
+ };
+ }
+
+ @Override
+ public String getString() {
+ return list.toString();
+ }
+ }
+
+ private static class NoopProcessor extends Processor {
+
+ @Override
+ public Response process(Request request, Execution execution) {
+ return execution.process(request);
+ }
+
+ }
+
+}