aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/test/java
diff options
context:
space:
mode:
authorgjoranv <gv@verizonmedia.com>2021-02-17 16:16:19 +0100
committergjoranv <gv@verizonmedia.com>2021-02-17 17:13:44 +0100
commitda183fe82e5d9eaccf3cbf03a0751cc74851ec31 (patch)
tree70cb9d97a7d2bdd5785e7512d7f88d5823e1407e /container-core/src/test/java
parenta0ae5022c689578e456eba2b5f89ac077e0b07e1 (diff)
Add java source files from the processing module.
Diffstat (limited to 'container-core/src/test/java')
-rw-r--r--container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/ChainBuilderTest.java242
-rw-r--r--container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/OrderedReadyNodesTest.java107
-rw-r--r--container-core/src/test/java/com/yahoo/component/chain/model/ChainsModelBuilderTest.java71
-rw-r--r--container-core/src/test/java/com/yahoo/processing/ResponseTestCase.java139
-rw-r--r--container-core/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java46
-rw-r--r--container-core/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java103
-rw-r--r--container-core/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java173
-rw-r--r--container-core/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java107
-rw-r--r--container-core/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java158
-rw-r--r--container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java52
-rw-r--r--container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java66
-rw-r--r--container-core/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.java61
-rw-r--r--container-core/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java99
-rw-r--r--container-core/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java141
-rw-r--r--container-core/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java44
-rw-r--r--container-core/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java60
-rw-r--r--container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java30
-rw-r--r--container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java37
-rw-r--r--container-core/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java25
-rw-r--r--container-core/src/test/java/com/yahoo/processing/test/documentation/Federator.java45
20 files changed, 1806 insertions, 0 deletions
diff --git a/container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/ChainBuilderTest.java b/container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/ChainBuilderTest.java
new file mode 100644
index 00000000000..07ac302f1f3
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/ChainBuilderTest.java
@@ -0,0 +1,242 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.component.chain.dependencies.ordering;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.chain.Chain;
+import com.yahoo.component.chain.ChainedComponent;
+import com.yahoo.component.chain.Phase;
+import com.yahoo.component.chain.dependencies.After;
+import com.yahoo.component.chain.dependencies.Before;
+import com.yahoo.component.chain.dependencies.Provides;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Tony Vaagenes
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class ChainBuilderTest {
+
+ private void addAtoG(ChainBuilder chainBuilder) throws ReflectiveOperationException {
+ List<Class<? extends ChainedComponent>> componentTypes = new ArrayList<>();
+
+ componentTypes.add(A.class);
+ componentTypes.add(B.class);
+ componentTypes.add(C.class);
+ componentTypes.add(D.class);
+ componentTypes.add(E.class);
+ componentTypes.add(F.class);
+ componentTypes.add(G.class);
+
+ permute(componentTypes);
+
+ for (Class<? extends ChainedComponent> searcherClass : componentTypes) {
+ chainBuilder.addComponent(searcherClass.getDeclaredConstructor().newInstance());
+ }
+ }
+
+
+ private void permute(List<Class<? extends ChainedComponent>> searcherTypes) {
+ for (int i=0; i<searcherTypes.size(); ++i) {
+ int j = (int) (Math.random() * searcherTypes.size());
+ Class<? extends ChainedComponent> tmp = searcherTypes.get(i);
+ searcherTypes.set(i,searcherTypes.get(j));
+ searcherTypes.set(j, tmp);
+ }
+ }
+
+ @Test
+ public void testRegular() throws Exception {
+ ChainBuilder chainBuilder = createDependencyHandler();
+
+ addAtoG(chainBuilder);
+
+ Chain<ChainedComponent> res = chainBuilder.orderNodes();
+
+ Iterator<ChainedComponent> i = res.components().iterator();
+ for (char j=0; j< 'G' - 'A'; ++j) {
+ assertEquals(String.valueOf((char)('A' + j)), name(i.next()));
+ }
+ }
+
+ @Test
+ public void testCycle() throws Exception {
+
+ ChainBuilder chainBuilder = createDependencyHandler();
+
+ addAtoG(chainBuilder);
+ chainBuilder.addComponent(new H());
+
+ boolean cycle = false;
+ try {
+ chainBuilder.orderNodes();
+ } catch (CycleDependenciesException e) {
+ cycle = true;
+ }
+ assertTrue(cycle);
+ }
+
+
+ @Test
+ public void testPhaseAndSearcher() {
+ ChainBuilder depHandler = newChainBuilder();
+ depHandler.addPhase(new Phase("phase1", set("phase2"), Collections.<String>emptySet()));
+ depHandler.addPhase(new Phase("phase2", set("phase3"), set("phase1")));
+ depHandler.addPhase(new Phase("phase3", Collections.<String>emptySet(), set("phase2", "phase1")));
+ ChainedComponent first = new First();
+ ChainedComponent second = new Second();
+
+ depHandler.addComponent(first);
+ depHandler.addComponent(second);
+ assertEquals(depHandler.orderNodes().components(), Arrays.asList(first, second));
+
+ }
+
+ @Test
+ public void testInputOrderPreservedWhenProvidesOverlap() {
+ ChainBuilder chainBuilder = newChainBuilder();
+
+ A a1 = new A();
+ C c = new C();
+ A a2 = new A();
+
+ chainBuilder.addComponent(a1);
+ chainBuilder.addComponent(c);
+ chainBuilder.addComponent(a2);
+
+ assertEquals(Arrays.asList(a1, c, a2), chainBuilder.orderNodes().components());
+ }
+
+ private ChainBuilder newChainBuilder() {
+ return new ChainBuilder(new ComponentId("test"));
+ }
+
+ private Set<String> set(String... strings) {
+ return new HashSet<>(Arrays.asList(strings));
+ }
+
+ @Before("phase1")
+ static class First extends NoopComponent {
+
+ }
+
+ @After("phase3")
+ static class Second extends NoopComponent {
+
+ }
+
+ @Test
+ public void testAfterAll1() throws Exception {
+ ChainBuilder chainBuilder = createDependencyHandler();
+ ChainedComponent afterAll1 = new AfterAll();
+ chainBuilder.addComponent(afterAll1);
+ addAtoG(chainBuilder);
+
+ List<ChainedComponent> resolution= chainBuilder.orderNodes().components();
+ assertEquals(afterAll1,resolution.get(resolution.size()-1));
+ }
+
+ @Test
+ public void testAfterAll2() throws Exception {
+ ChainBuilder chainBuilder = createDependencyHandler();
+ addAtoG(chainBuilder);
+ ChainedComponent afterAll1 = new AfterAll();
+ chainBuilder.addComponent(afterAll1);
+
+ List<ChainedComponent> resolution = chainBuilder.orderNodes().components();
+ assertEquals(afterAll1,resolution.get(resolution.size()-1));
+ }
+
+ @Test
+ public void testAfterImplicitProvides()
+ throws InstantiationException, IllegalAccessException {
+ ChainBuilder chainBuilder = createDependencyHandler();
+ ChainedComponent afterProvidesNothing=new AfterProvidesNothing();
+ ChainedComponent providesNothing=new ProvidesNothing();
+ chainBuilder.addComponent(afterProvidesNothing);
+ chainBuilder.addComponent(providesNothing);
+ List<ChainedComponent> resolution = chainBuilder.orderNodes().components();
+ assertEquals(providesNothing,resolution.get(0));
+ assertEquals(afterProvidesNothing,resolution.get(1));
+ }
+
+ private ChainBuilder createDependencyHandler() {
+ ChainBuilder chainBuilder = newChainBuilder();
+ chainBuilder.addPhase(new Phase("phase1", Collections.<String>emptySet(), Collections.<String>emptySet()));
+ chainBuilder.addPhase(new Phase("phase2", Collections.<String>emptySet(), Collections.<String>emptySet()));
+ chainBuilder.addPhase(new Phase("phase3", Collections.<String>emptySet(), Collections.<String>emptySet()));
+ return chainBuilder;
+ }
+
+ private String name(ChainedComponent searcher) {
+ return searcher.getClass().getSimpleName();
+ }
+
+ @Provides("A")
+ static class A extends NoopComponent {
+ }
+
+ @Provides("B")
+ @After("A")
+ @Before({"D", "phase1"})
+ static class B extends NoopComponent {
+ }
+
+ @Provides("C")
+ @After("phase1")
+ static class C extends NoopComponent {
+ }
+
+ @Provides("D")
+ @After({"C","A"})
+ static class D extends NoopComponent {
+ }
+
+ @Provides("E")
+ @After({"B","D"})
+ @Before("phase2")
+ static class E extends NoopComponent {
+ }
+
+ @Provides("F")
+ @After("phase2")
+ static class F extends NoopComponent {
+ }
+
+ @Provides("G")
+ @After("F")
+ static class G extends NoopComponent {
+ }
+
+ @Provides("H")
+ @Before("A")
+ @After("F")
+ static class H extends NoopComponent {
+ }
+
+ @Provides("AfterAll")
+ @After("*")
+ static class AfterAll extends NoopComponent {
+ }
+
+ static class ProvidesNothing extends NoopComponent {
+ }
+
+ @After("ProvidesNothing")
+ static class AfterProvidesNothing extends NoopComponent {
+ }
+
+ static class NoopComponent extends ChainedComponent {
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/OrderedReadyNodesTest.java b/container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/OrderedReadyNodesTest.java
new file mode 100644
index 00000000000..77729a99012
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/component/chain/dependencies/ordering/OrderedReadyNodesTest.java
@@ -0,0 +1,107 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.component.chain.dependencies.ordering;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import com.yahoo.component.chain.ChainedComponent;
+import com.yahoo.component.chain.dependencies.Dependencies;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.yahoo.component.ComponentId;
+
+
+
+/**
+ * Test for OrderedReadyNodes.
+ *
+ * @author Tony Vaagenes
+ */
+@SuppressWarnings("rawtypes")
+public class OrderedReadyNodesTest {
+
+ class ComponentA extends ChainedComponent {
+ public ComponentA(ComponentId id) {
+ super(id);
+ }
+
+ @Override
+ public Dependencies getDependencies() {
+ return new Dependencies(Arrays.asList(getId().getName()), null, null);
+ }
+ }
+
+ class ComponentB extends ComponentA {
+ public ComponentB(ComponentId id) {
+ super(id);
+ }
+ }
+
+ private OrderedReadyNodes readyNodes;
+
+ @Before
+ public void setup() {
+ readyNodes = new OrderedReadyNodes();
+ }
+
+ @Test
+ public void require_NameProviders_before_SearcherNodes() {
+ NameProvider nameProvider = createDummyNameProvider(100);
+ ComponentNode componentNode = new ComponentNode<>(createFakeComponentA("a"), 1);
+
+ addNodes(nameProvider, componentNode);
+
+ assertEquals(nameProvider, pop());
+ assertEquals(componentNode, pop());
+ }
+
+ private NameProvider createDummyNameProvider(int priority) {
+ return new NameProvider("anonymous", priority) {
+ @Override
+ protected void addNode(ComponentNode node) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ int classPriority() {
+ return 0;
+ }
+ };
+ }
+
+ @Test
+ public void require_SearcherNodes_ordered_by_insertion_order() {
+ int priority = 0;
+ ComponentNode a = new ComponentNode<>(createFakeComponentB("1"), priority++);
+ ComponentNode b = new ComponentNode<>(createFakeComponentA("2"), priority++);
+ ComponentNode c = new ComponentNode<>(createFakeComponentA("03"), priority++);
+
+ addNodes(a, b, c);
+
+ assertEquals(a, pop());
+ assertEquals(b, pop());
+ assertEquals(c, pop());
+ }
+
+ ChainedComponent createFakeComponentA(String id) {
+ return new ComponentA(ComponentId.fromString(id));
+ }
+
+ ChainedComponent createFakeComponentB(String id) {
+ return new ComponentB(ComponentId.fromString(id));
+ }
+
+
+ private void addNodes(Node... nodes) {
+ for (Node node : nodes) {
+ readyNodes.add(node);
+ }
+ }
+
+ private Node pop() {
+ return readyNodes.pop();
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/component/chain/model/ChainsModelBuilderTest.java b/container-core/src/test/java/com/yahoo/component/chain/model/ChainsModelBuilderTest.java
new file mode 100644
index 00000000000..7cef03bbb06
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/component/chain/model/ChainsModelBuilderTest.java
@@ -0,0 +1,71 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.component.chain.model;
+
+import com.yahoo.component.ComponentId;
+import com.yahoo.component.ComponentSpecification;
+import com.yahoo.container.core.ChainsConfig;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.Set;
+
+import static com.yahoo.container.core.ChainsConfig.Components;
+import static com.yahoo.container.core.ChainsConfig.Chains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author gjoranv
+ */
+public class ChainsModelBuilderTest {
+
+ @Test
+ public void components_are_added_to_componentModels() throws Exception {
+ ChainsModel model = chainsModel();
+ assertEquals(2, model.allComponents().size());
+ assertTrue(model.componentModels().containsKey(new ComponentId("componentA")));
+ }
+
+ @Test
+ public void components_are_added_to_chainSpecification() throws Exception {
+ ChainsModel model = chainsModel();
+ ChainSpecification chainSpec = model.chainSpecifications().get(new ComponentId("chain1")).model();
+ assertTrue(getComponentsByName(chainSpec.componentReferences).containsKey("componentA"));
+ }
+
+ @Test
+ public void inherited_chains_are_added_to_chainSpecification() throws Exception {
+ ChainsModel model = chainsModel();
+ ChainSpecification inheritsChain1 = model.chainSpecifications().get(new ComponentId("inheritsChain1")).model();
+ assertEquals(2, model.allChainsFlattened().size());
+ assertTrue(getComponentsByName(inheritsChain1.inheritance.chainSpecifications).containsKey("chain1"));
+ assertTrue(getComponentsByName(inheritsChain1.inheritance.excludedComponents).containsKey("componentA"));
+ }
+
+ private ChainsModel chainsModel() {
+ ChainsConfig.Builder builder = new ChainsConfig.Builder()
+ .components(new Components.Builder()
+ .id("componentA"))
+ .components(new Components.Builder()
+ .id("componentB"))
+ .chains(new Chains.Builder()
+ .id("chain1")
+ .components("componentA")
+ .components("componentB"))
+ .chains(new Chains.Builder()
+ .id("inheritsChain1")
+ .inherits("chain1")
+ .excludes("componentA"));
+ ChainsConfig config = new ChainsConfig(builder);
+
+ ChainsModel model = ChainsModelBuilder.buildFromConfig(config);
+ model.validate();
+ return model;
+ }
+
+ private static Map<String, ComponentSpecification>
+ getComponentsByName(Set<ComponentSpecification> componentSpecifications) {
+ return ChainSpecification.componentsByName(componentSpecifications);
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/ResponseTestCase.java b/container-core/src/test/java/com/yahoo/processing/ResponseTestCase.java
new file mode 100644
index 00000000000..02c6049de49
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/ResponseTestCase.java
@@ -0,0 +1,139 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing;
+
+import com.yahoo.processing.response.ArrayDataList;
+import com.yahoo.processing.response.DataList;
+import com.yahoo.processing.test.ProcessorLibrary;
+import com.yahoo.processing.test.Responses;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author bratseth
+ */
+@SuppressWarnings("unchecked")
+public class ResponseTestCase {
+
+ /**
+ * Create a nested async tree of data elements, complete it recursively and check completion order.
+ * Check the recursive toString printing along the way.
+ * List variable names ends by numbers specifying the index of the list at each level.
+ */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testRecursiveCompletionAndToString() throws InterruptedException, ExecutionException {
+ // create lists
+ Request request = new Request();
+ DataList list1 = ArrayDataList.create(request);
+ DataList list11 = ArrayDataList.create(request);
+ DataList list12 = ArrayDataList.createAsync(request);
+ DataList list13 = ArrayDataList.createAsync(request);
+ DataList list14 = ArrayDataList.create(request);
+ DataList list121 = ArrayDataList.createAsync(request);
+ DataList list122 = ArrayDataList.create(request);
+ DataList list123 = ArrayDataList.createAsync(request);
+ DataList list1231 = ArrayDataList.createAsync(request);
+ DataList list1232 = ArrayDataList.create(request);
+ // wire tree
+ list1.add(list11);
+ list1.add(list12);
+ list1.add(list13);
+ list1.add(list14);
+ list12.add(list121);
+ list12.add(list122);
+ list12.add(list123);
+ list123.add(list1231);
+ list123.add(list1232);
+ // add sync data elements
+ list1.add(new ProcessorLibrary.StringData(request,"list1"));
+ list12.add(new ProcessorLibrary.StringData(request,"list12"));
+ list14.add(new ProcessorLibrary.StringData(request,"list14"));
+ list122.add(new ProcessorLibrary.StringData(request,"list122"));
+ list1231.add(new ProcessorLibrary.StringData(request,"list1231"));
+
+ assertEqualsIgnoreObjectNumbers("Uncompleted tree, no incoming",uncompletedTreeUncompletedIncoming,Responses.recursiveToString(list1));
+
+ // provide all async incoming data
+ list12.incoming().markComplete();
+ list121.incoming().addLast(new ProcessorLibrary.StringData(request,"list121async1"));
+ list123.incoming().markComplete();
+ list1231.incoming().add(new ProcessorLibrary.StringData(request,"list13231async1"));
+ list1231.incoming().addLast(new ProcessorLibrary.StringData(request,"list1231async2"));
+ list13.incoming().add(new ProcessorLibrary.StringData(request,"list13async1"));
+ list13.incoming().addLast(new ProcessorLibrary.StringData(request,"list13async2"));
+
+ assertEqualsIgnoreObjectNumbers("Uncompleted tree, incoming complete", uncompletedTreeCompletedIncoming, Responses.recursiveToString(list1));
+
+ // complete all
+ Response.recursiveComplete(list1).get();
+ assertEqualsIgnoreObjectNumbers("Completed tree", completedTree, Responses.recursiveToString(list1));
+ }
+
+ private void assertEqualsIgnoreObjectNumbers(String explanation,String expected,String actual) {
+ assertEquals(explanation,expected,removeObjectNumbers(actual));
+ }
+
+ /** Removes all object numbers (occurrences of @hexnumber) */
+ private String removeObjectNumbers(String s) {
+ return s.replaceAll("@[0-9a-f]+","");
+ }
+
+ private static final String uncompletedTreeUncompletedIncoming=
+ "com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: incomplete, data []]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: incomplete, data []]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " list122\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: incomplete, data []]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: incomplete, data []]\n" +
+ " list1231\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " list12\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: incomplete, data []]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " list14\n" +
+ " list1\n";
+
+ private static final String uncompletedTreeCompletedIncoming=
+ "com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: complete, data []]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: complete, data [list121async1]]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " list122\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: complete, data []]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: complete, data [list13231async1, list1231async2]]\n" +
+ " list1231\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " list12\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, incoming: complete, data [list13async1, list13async2]]\n" +
+ " com.yahoo.processing.response.ArrayDataList [incomplete, (no incoming)]\n" +
+ " list14\n" +
+ " list1\n";
+
+ private static final String completedTree=
+ "com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " list121async1\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " list122\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " list1231\n" +
+ " list13231async1\n" +
+ " list1231async2\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " list12\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " list13async1\n" +
+ " list13async2\n" +
+ " com.yahoo.processing.response.ArrayDataList [completed]\n" +
+ " list14\n" +
+ " list1\n";
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java b/container-core/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java
new file mode 100644
index 00000000000..b821461fdc6
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java
@@ -0,0 +1,46 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.execution.test;
+
+import com.yahoo.component.chain.Chain;
+import com.yahoo.processing.Processor;
+import com.yahoo.processing.Request;
+import com.yahoo.processing.Response;
+import com.yahoo.processing.execution.Execution;
+import org.junit.Test;
+
+import static com.yahoo.processing.test.ProcessorLibrary.*;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author bratseth
+ */
+public class AsyncExecutionTestCase {
+
+ /** Execute a processing chain which forks off into multiple threads */
+ @Test
+ public void testAsyncExecution() {
+ // Create a chain
+ Chain<Processor> chain=new Chain<>(new CombineData(),new BlockingSplitter(2),new Get6DataItems(), new DataSource());
+
+ // Execute it
+ Request request=new Request();
+ request.properties().set("appendage",1);
+ Response response=Execution.createRoot(chain,0,Execution.Environment.createEmpty()).process(request);
+
+ // Verify the result
+ assertEquals(6*2-1,response.data().asList().size());
+ assertEquals("first.2, third.2",response.data().get(0).toString());
+ assertEquals("second.2",response.data().get(1).toString());
+ assertEquals("first.3",response.data().get(2).toString());
+ assertEquals("second.3",response.data().get(3).toString());
+ assertEquals("third.3",response.data().get(4).toString());
+ // from the parallel execution
+ assertEquals("first.2",response.data().get(5).toString());
+ assertEquals("second.2",response.data().get(6).toString());
+ assertEquals("third.2",response.data().get(7).toString());
+ assertEquals("first.3",response.data().get(8).toString());
+ assertEquals("second.3",response.data().get(9).toString());
+ assertEquals("third.3",response.data().get(10).toString());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java b/container-core/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java
new file mode 100644
index 00000000000..9c4d4de47dc
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java
@@ -0,0 +1,103 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.execution.test;
+
+import com.yahoo.component.chain.Chain;
+import com.yahoo.processing.Processor;
+import com.yahoo.processing.execution.Execution;
+import com.yahoo.processing.test.ProcessorLibrary;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author bratseth
+ */
+public class ExecutionContextTestCase {
+
+ private final Chain<Processor> chain = new Chain<Processor>(new ProcessorLibrary.DataSource());
+
+ /** Tests combined use of trace messages, context values and access log entries */
+ @Test
+ public void testtrace() {
+ Execution execution1=Execution.createRoot(chain,2,Execution.Environment.createEmpty());
+ execution1.trace().setProperty("a","a1");
+ execution1.trace().logValue("a","a1");
+ execution1.trace().trace("root 1", 2);
+ execution1.trace().setProperty("a","a2");
+ execution1.trace().setProperty("b","b1");
+ execution1.trace().logValue("a","a2");
+ execution1.trace().logValue("b","b1");
+
+ Execution execution2=new Execution(chain,execution1);
+ execution2.trace().setProperty("b","b2");
+ execution2.trace().logValue("b","b2");
+ execution2.trace().trace(" child-1 1", 2);
+ execution2.trace().setProperty("b", "b3");
+ execution2.trace().logValue("b","b3");
+
+ execution1.trace().setProperty("b","b4");
+ execution1.trace().logValue("b","b4");
+
+ Execution execution3=new Execution(chain,execution1);
+ execution3.trace().setProperty("b","b5");
+ execution3.trace().setProperty("c","c1");
+ execution3.trace().logValue("b","b5");
+ execution3.trace().logValue("c","c1");
+ execution3.trace().trace(" child-2 1", 2);
+
+ execution2.trace().setProperty("c","c2");
+ execution2.trace().logValue("c","c2");
+
+ execution1.trace().trace("root 2", 2);
+ execution3.trace().setProperty("d", "d1");
+ execution1.trace().logValue("d","d1");
+
+ execution2.trace().trace(" child-1 2", 2);
+ execution2.trace().setProperty("c", "c3");
+ execution2.trace().logValue("c","c3");
+
+ execution1.trace().setProperty("c","c4");
+ execution1.trace().logValue("c","c4");
+
+ Iterator<String> traceIterator=execution1.trace().traceNode().root().descendants(String.class).iterator();
+ assertEquals("root 1",traceIterator.next());
+ assertEquals(" child-1 1",traceIterator.next());
+ assertEquals(" child-1 2",traceIterator.next());
+ assertEquals(" child-2 1",traceIterator.next());
+ assertEquals("root 2",traceIterator.next());
+ assertFalse(traceIterator.hasNext());
+
+ // Verify context variables
+ assertEquals("a2", execution1.trace().getProperty("a"));
+ assertEquals("b5", execution1.trace().getProperty("b"));
+ assertEquals("c4", execution1.trace().getProperty("c"));
+ assertEquals("d1", execution1.trace().getProperty("d"));
+ assertNull(execution1.trace().getProperty("e"));
+
+ // Verify access log
+ Set<String> logValues=new HashSet<>();
+ for (Iterator<Execution.Trace.LogValue> logValueIterator=execution1.trace().logValueIterator(); logValueIterator.hasNext(); )
+ logValues.add(logValueIterator.next().toString());
+ assertEquals(12,logValues.size());
+ assertTrue(logValues.contains("a=a1"));
+ assertTrue(logValues.contains("a=a2"));
+ assertTrue(logValues.contains("b=b1"));
+ assertTrue(logValues.contains("b=b2"));
+ assertTrue(logValues.contains("b=b3"));
+ assertTrue(logValues.contains("b=b4"));
+ assertTrue(logValues.contains("b=b5"));
+ assertTrue(logValues.contains("c=c1"));
+ assertTrue(logValues.contains("c=c2"));
+ assertTrue(logValues.contains("d=d1"));
+ assertTrue(logValues.contains("c=c3"));
+ assertTrue(logValues.contains("c=c4"));
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java b/container-core/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java
new file mode 100644
index 00000000000..c22e34515bf
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java
@@ -0,0 +1,173 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.execution.test;
+
+import com.yahoo.component.chain.Chain;
+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.response.DataList;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static com.yahoo.processing.test.ProcessorLibrary.*;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests scenarios where a data producer returns a promise of some future data rather than the data itself.
+ * As no processor waits for the data it is returned all the way to the caller.
+ *
+ * @author bratseth
+ */
+public class FutureDataTestCase {
+
+ /** Run a chain which ends in a processor which returns a response containing future data. */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testFutureDataPassThrough() throws InterruptedException, ExecutionException, TimeoutException {
+ // Set up
+ FutureDataSource futureDataSource=new FutureDataSource();
+ Chain<Processor> chain=new Chain<>(new DataCounter(),futureDataSource);
+
+ // Execute
+ Request request=new Request();
+ Response response=Execution.createRoot(chain,0,Execution.Environment.createEmpty()).process(request); // Urk ...
+
+ // Verify the result prior to completion of delayed data
+ assertEquals(1,response.data().asList().size());
+ assertEquals("Data count: 0",response.data().get(0).toString());
+
+ // complete delayed data
+ assertEquals("Delayed data was requested once", 1, futureDataSource.incomingData.size());
+ futureDataSource.incomingData.get(0).add(new StringData(request, "d1"));
+ futureDataSource.incomingData.get(0).addLast(new StringData(request, "d2"));
+ assertEquals("New data is not visible because we haven't asked for it", 1, response.data().asList().size());
+ response.data().complete().get(1000, TimeUnit.MILLISECONDS);
+ assertEquals("Now the data is available", 3, response.data().asList().size());
+ assertEquals("d1",response.data().get(1).toString().toString());
+ assertEquals("d2",response.data().get(2).toString().toString());
+ }
+
+ /** Federate to one source which returns data immediately and one who return future data */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testFederateSyncAndAsyncData() throws InterruptedException, ExecutionException, TimeoutException {
+ // Set up
+ FutureDataSource futureDataSource=new FutureDataSource();
+ Chain<Processor> chain=new Chain<>(new DataCounter(),new Federator(new Chain<>(new DataSource()),new Chain<>(futureDataSource)));
+
+ // Execute
+ Request request=new Request();
+ request.properties().set("appendage",1);
+ Response response=Execution.createRoot(chain,0,Execution.Environment.createEmpty()).process(request);
+
+ // Verify the result prior to completion of delayed data
+ assertEquals(3,response.data().asList().size()); // The sync data list + the (currently empty) future data list) + the data count
+ DataList syncData=(DataList)response.data().get(0);
+ DataList asyncData=(DataList)response.data().get(1);
+ StringData countData=(StringData)response.data().get(2);
+
+ assertEquals("The sync data is available",3,syncData.asList().size());
+ assertEquals( "first.1",syncData.get(0).toString());
+ assertEquals("second.1", syncData.get(1).toString());
+ assertEquals( "third.1",syncData.get(2).toString());
+ assertEquals("No async data yet",0,asyncData.asList().size());
+ assertEquals("The data counter has run and accessed the sync data","Data count: 3",countData.toString());
+
+ // complete async data
+ futureDataSource.incomingData.get(0).add(new StringData(request, "d1"));
+ futureDataSource.incomingData.get(0).addLast(new StringData(request, "d2"));
+ assertEquals("New data is not visible because we haven't asked for it", 0, asyncData.asList().size());
+ asyncData.complete().get(1000, TimeUnit.MILLISECONDS);
+ assertEquals("Now the data is available", 2, asyncData.asList().size());
+ assertEquals("d1",asyncData.get(0).toString().toString());
+ assertEquals("d2", asyncData.get(1).toString().toString());
+ }
+
+ /** Register a chain which will be called when some async data is available */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testAsyncDataProcessing() throws InterruptedException, ExecutionException, TimeoutException {
+ // Set up
+ FutureDataSource futureDataSource=new FutureDataSource();
+ Chain<Processor> asyncChain=new Chain<Processor>(new DataCounter());
+ Chain<Processor> chain=new Chain<>(new AsyncDataProcessingInitiator(asyncChain),futureDataSource);
+
+ // Execute
+ Request request=new Request();
+ Response response=Execution.createRoot(chain,0,Execution.Environment.createEmpty()).process(request);
+
+ // Verify the result prior to completion of delayed data
+ assertEquals("No data yet",0,response.data().asList().size());
+
+ // complete async data
+ futureDataSource.incomingData.get(0).add(new StringData(request, "d1"));
+ assertEquals("New data is not visible because it is not complete", 0, response.data().asList().size());
+ futureDataSource.incomingData.get(0).addLast(new StringData(request, "d2"));
+ assertEquals("Not visible because it has not been synced yet", 0, response.data().asList().size());
+ response.data().complete().get(1000, TimeUnit.MILLISECONDS);
+ assertEquals("Now the data as well as the count is available", 3, response.data().asList().size());
+ assertEquals("d1",response.data().get(0).toString().toString());
+ assertEquals("d2",response.data().get(1).toString().toString());
+ assertEquals("Data count: 2",response.data().get(2).toString());
+ }
+
+ /**
+ * Register a chain which federates over three sources, two of which are future.
+ * When the first of the futures are done one additional chain is to be run.
+ * When both are done another chain is to be run.
+ */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testAsyncDataProcessingOfFederatedResult() throws InterruptedException, ExecutionException, TimeoutException {
+ // Set up
+ // Source 1 (async with completion chain)
+ FutureDataSource futureSource1=new FutureDataSource();
+ Chain<Processor> asyncChainSource1=new Chain<Processor>(new DataCounter("source1"));
+ Chain<Processor> chainSource1=new Chain<>(new AsyncDataProcessingInitiator(asyncChainSource1),futureSource1);
+ // Source 2 (async source)
+ FutureDataSource futureSource2=new FutureDataSource();
+ Chain<Processor> chainSource2=new Chain<Processor>(futureSource2);
+ // Source 3 (sync source)
+ Chain<Processor> chainSource3=new Chain<Processor>(new DataSource());
+ // Main chain federating to the above - not waiting for source 1 and 2 but invoking asyncMain when both are complete
+ Chain<Processor> asyncMain=new Chain<Processor>(new DataCounter("main"));
+ Chain<Processor> main=new Chain<>(new AsyncDataProcessingInitiator(asyncMain),new Federator(chainSource1,chainSource2,chainSource3));
+
+ // Execute
+ Request request=new Request();
+ Response response=Execution.createRoot(main,0,Execution.Environment.createEmpty()).process(request);
+
+ // Verify the result prior to completion of delayed data
+ assertEquals("We have the sync data plus placeholders for the async lists",3,response.data().asList().size());
+ DataList source1Data=((DataList)response.data().get(0));
+ DataList source2Data=((DataList)response.data().get(1));
+ DataList source3Data=((DataList)response.data().get(2));
+
+ assertEquals("No data yet",0,source1Data.asList().size());
+ assertEquals("No data yet",0,source2Data.asList().size());
+ assertEquals(3,source3Data.asList().size());
+
+ // complete async data in source1
+ futureSource1.incomingData.get(0).addLast(new StringData(request,"source1Data"));
+ assertEquals("Not visible yet", 0, source1Data.asList().size());
+ source1Data.complete().get(1000, TimeUnit.MILLISECONDS);
+ assertEquals(2, source1Data.asList().size());
+ assertEquals("source1Data",source1Data.get(0).toString());
+ assertEquals("Completion listener chain on this has run", "[source1] Data count: 1", source1Data.get(1).toString());
+
+ // source2 & main completion
+ assertEquals("Main completion listener has not run", 3, response.data().asList().size());
+ futureSource2.incomingData.get(0).addLast(new StringData(request, "source2Data"));
+ assertEquals("Main completion listener has not run", 3, response.data().asList().size());
+
+ Response.recursiveComplete(response.data()).get();
+ assertEquals("Main completion listener has run", 4, response.data().asList().size());
+ assertEquals("The main data counter saw all sync data, but not source2 data as it executes after this",
+ "[main] Data count: " + (2 + 0 + 3), response.data().get(3).toString());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java b/container-core/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java
new file mode 100644
index 00000000000..6ab37f54d7b
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java
@@ -0,0 +1,107 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.execution.test;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.yahoo.component.chain.Chain;
+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.response.Data;
+import com.yahoo.processing.response.IncomingData;
+import com.yahoo.processing.test.ProcessorLibrary;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests listening on every available new piece of data in a response
+ *
+ * @author bratseth
+ */
+public class StreamingTestCase {
+
+ /** Tests adding a chain which is called every time new data is added to a data list */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testStreamingData() throws InterruptedException, ExecutionException, TimeoutException {
+ // Set up
+ StreamProcessor streamProcessor = new StreamProcessor();
+ Chain<Processor> streamProcessing = new Chain<Processor>(streamProcessor);
+ ProcessorLibrary.FutureDataSource futureDataSource=new ProcessorLibrary.FutureDataSource();
+ Chain<Processor> main=new Chain<>(new ProcessorLibrary.DataCounter(),
+ new ProcessorLibrary.StreamProcessingInitiator(streamProcessing),
+ futureDataSource);
+
+ // Execute
+ Request request=new Request();
+ Response response= Execution.createRoot(main, 0, Execution.Environment.createEmpty()).process(request);
+ IncomingData incomingData = futureDataSource.incomingData.get(0);
+
+ // State prior to receiving any additional data
+ assertEquals(1,response.data().asList().size());
+ assertEquals("Data count: 0",response.data().get(0).toString());
+ assertEquals("Add data listener invoked also for DataCounter", 1, streamProcessor.invocationCount);
+ assertEquals("Initial data count", 1, response.data().asList().size());
+
+ // add first data - we have no listener so the data is held in the incoming buffer
+ incomingData.add(new ProcessorLibrary.StringData(request, "d1"));
+ assertEquals("Data add listener not invoked as we are not listening on new data yet",1, streamProcessor.invocationCount);
+ assertEquals("New data is not consumed", 1, response.data().asList().size());
+
+ // start listening on incoming data - this is what a renderer will do
+ incomingData.addNewDataListener(new MockNewDataListener(incomingData), MoreExecutors.directExecutor());
+ assertEquals("We got a data add event for the data which was already added", 2, streamProcessor.invocationCount);
+ assertEquals("New data is consumed", 2, response.data().asList().size());
+
+ incomingData.add(new ProcessorLibrary.StringData(request, "d2"));
+ assertEquals("We are now getting data add events each time", 3, streamProcessor.invocationCount);
+ assertEquals("New data is consumed", 3, response.data().asList().size());
+
+ incomingData.addLast(new ProcessorLibrary.StringData(request, "d3"));
+ assertEquals("We are getting data add events also the last time", 4, streamProcessor.invocationCount);
+ assertEquals("New data is consumed", 4, response.data().asList().size());
+
+ response.data().complete().get(1000, TimeUnit.MILLISECONDS); // no-op here
+ assertEquals("d1",response.data().get(1).toString().toString());
+ assertEquals("d2",response.data().get(2).toString().toString());
+ assertEquals("d3",response.data().get(3).toString().toString());
+ }
+
+ private static class MockNewDataListener implements Runnable {
+
+ private final IncomingData<Data> incomingData;
+
+ public MockNewDataListener(IncomingData<Data> incomingData) {
+ this.incomingData = incomingData;
+ }
+
+ @Override
+ public void run() {
+ // consume new data
+ for (Data newData : incomingData.drain()) {
+ incomingData.getOwner().add(newData);
+ }
+ // actual rendering would go here (at this point data add listeners will have executed)
+ }
+
+ }
+
+ private static class StreamProcessor extends Processor {
+
+ int invocationCount;
+
+ @Override
+ public Response process(Request request, Execution execution) {
+ invocationCount++;
+ return execution.process(request);
+ }
+
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java b/container-core/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java
new file mode 100644
index 00000000000..382abe8d2ca
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java
@@ -0,0 +1,158 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.request;
+
+import static org.junit.Assert.*;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Splitter;
+import com.yahoo.text.Lowercase;
+
+/**
+ * Module local test of the basic property name building block.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class CompoundNameTestCase {
+
+ private static final String NAME = "com.yahoo.processing.request.CompoundNameTestCase";
+ private CompoundName cn;
+
+ @Before
+ public void setUp() throws Exception {
+ cn = new CompoundName(NAME);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public final void testLast() {
+ assertEquals(NAME.substring(NAME.lastIndexOf('.') + 1), cn.last());
+ }
+
+ @Test
+ public final void testFirst() {
+ assertEquals(NAME.substring(0, NAME.indexOf('.')), cn.first());
+ }
+
+ @Test
+ public final void testRest() {
+ assertEquals(NAME.substring(NAME.indexOf('.') + 1), cn.rest().toString());
+ }
+
+ @Test
+ public final void testRestN() {
+ assertEquals("a.b.c.d.e", new CompoundName("a.b.c.d.e").rest(0).toString());
+ assertEquals("b.c.d.e", new CompoundName("a.b.c.d.e").rest(1).toString());
+ assertEquals("c.d.e", new CompoundName("a.b.c.d.e").rest(2).toString());
+ assertEquals("d.e", new CompoundName("a.b.c.d.e").rest(3).toString());
+ assertEquals("e", new CompoundName("a.b.c.d.e").rest(4).toString());
+ assertEquals("", new CompoundName("a.b.c.d.e").rest(5).toString());
+ }
+
+ @Test
+ public final void testPrefix() {
+ assertTrue(new CompoundName("a.b.c").hasPrefix(new CompoundName("")));
+ assertTrue(new CompoundName("a.b.c").hasPrefix(new CompoundName("a")));
+ assertTrue(new CompoundName("a.b.c").hasPrefix(new CompoundName("a.b")));
+ assertTrue(new CompoundName("a.b.c").hasPrefix(new CompoundName("a.b.c")));
+
+ assertFalse(new CompoundName("a.b.c").hasPrefix(new CompoundName("a.b.c.d")));
+ assertFalse(new CompoundName("a.b.c").hasPrefix(new CompoundName("a.b.d")));
+ }
+
+ @Test
+ public final void testSize() {
+ Splitter s = Splitter.on('.');
+ Iterable<String> i = s.split(NAME);
+ int n = 0;
+ for (@SuppressWarnings("unused") String x : i) {
+ ++n;
+ }
+ assertEquals(n, cn.size());
+ }
+
+ @Test
+ public final void testGet() {
+ String s = cn.get(0);
+ assertEquals(NAME.substring(0, NAME.indexOf('.')), s);
+ }
+
+ @Test
+ public final void testIsCompound() {
+ assertTrue(cn.isCompound());
+ }
+
+ @Test
+ public final void testIsEmpty() {
+ assertFalse(cn.isEmpty());
+ }
+
+ @Test
+ public final void testAsList() {
+ List<String> l = cn.asList();
+ Splitter peoplesFront = Splitter.on('.');
+ Iterable<String> answer = peoplesFront.split(NAME);
+ Iterator<String> expected = answer.iterator();
+ for (int i = 0; i < l.size(); ++i) {
+ assertEquals(expected.next(), l.get(i));
+ }
+ assertFalse(expected.hasNext());
+ }
+
+ @Test
+ public final void testEqualsObject() {
+ assertFalse(cn.equals(NAME));
+ assertFalse(cn.equals(null));
+ assertTrue(cn.equals(cn));
+ assertTrue(cn.equals(new CompoundName(NAME)));
+ }
+
+ @Test
+ public final void testEmptyNonEmpty() {
+ assertTrue(new CompoundName("").isEmpty());
+ assertEquals(0, new CompoundName("").size());
+ assertFalse(new CompoundName("a").isEmpty());
+ assertEquals(1, new CompoundName("a").size());
+ CompoundName empty = new CompoundName("a.b.c");
+ assertTrue(empty == empty.rest(0));
+ assertFalse(empty == empty.rest(1));
+ }
+
+ @Test
+ public final void testGetLowerCasedName() {
+ assertEquals(Lowercase.toLowerCase(NAME), cn.getLowerCasedName());
+ }
+
+ @Test
+ public void testAppend() {
+ assertEquals(new CompoundName("a.b.c.d"), new CompoundName("").append(new CompoundName("a.b.c.d")));
+ assertEquals(new CompoundName("a.b.c.d"), new CompoundName("a").append(new CompoundName("b.c.d")));
+ assertEquals(new CompoundName("a.b.c.d"), new CompoundName("a.b").append(new CompoundName("c.d")));
+ assertEquals(new CompoundName("a.b.c.d"), new CompoundName("a.b.c").append(new CompoundName("d")));
+ assertEquals(new CompoundName("a.b.c.d"), new CompoundName("a.b.c.d").append(new CompoundName("")));
+ }
+
+ @Test
+ public void empty_CompoundName_is_prefix_of_any_CompoundName() {
+ CompoundName empty = new CompoundName("");
+
+ assertTrue(empty.hasPrefix(empty));
+ assertTrue(new CompoundName("a").hasPrefix(empty));
+ }
+
+ @Test
+ public void whole_components_must_match_to_be_prefix() {
+ CompoundName stringPrefix = new CompoundName("a");
+ CompoundName name = new CompoundName("aa");
+
+ assertFalse(name.hasPrefix(stringPrefix));
+ }
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java b/container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java
new file mode 100644
index 00000000000..002b6bac4a8
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java
@@ -0,0 +1,52 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.request.test;
+
+import com.yahoo.processing.request.CompoundName;
+
+/**
+ * @author baldersheim
+ */
+public class CompoundNameBenchmark {
+ public void run() {
+ long result=0;
+ String strings[] = createStrings(1000);
+ // Warm-up
+ out("Warming up...");
+ for (int i=0; i<10*1000; i++)
+ result+=createCompundName(strings);
+
+ long startTime=System.currentTimeMillis();
+ out("Running...");
+ for (int i=0; i<100*1000; i++)
+ result+=createCompundName(strings);
+ out("Ignore this: " + result); // Make sure we are not fooled by optimization by creating an observable result
+ long endTime=System.currentTimeMillis();
+ out("Compoundification 1000 strings 100.000 times took " + (endTime-startTime) + " ms");
+ }
+
+ private final String [] createStrings(int num) {
+ String strings [] = new String [num];
+ for(int i=0; i < strings.length; i++) {
+ strings[i] = "this.is.a.short.compound.name." + i;
+ }
+ return strings;
+ }
+
+ private final int createCompundName(String [] strings) {
+ int retval = 0;
+ for (int i=0; i < strings.length; i++) {
+ CompoundName n = new CompoundName(strings[i]);
+ retval += n.size();
+ }
+ return retval;
+ }
+
+ private void out(String string) {
+ System.out.println(string);
+ }
+
+ public static void main(String[] args) {
+ new CompoundNameBenchmark().run();
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java b/container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java
new file mode 100644
index 00000000000..f5033565786
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java
@@ -0,0 +1,66 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.request.test;
+
+import com.yahoo.processing.request.CompoundName;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * @author bratseth
+ */
+public class CompoundNameTestCase {
+
+ @Test
+ public void testFirstRest() {
+ assertEquals(CompoundName.empty, CompoundName.empty.rest());
+
+ CompoundName n=new CompoundName("on.two.three");
+ assertEquals("on", n.first());
+ assertEquals("two.three", n.rest().toString());
+ n=n.rest();
+ assertEquals("two", n.first());
+ assertEquals("three", n.rest().toString());
+ n=n.rest();
+ assertEquals("three", n.first());
+ assertEquals("", n.rest().toString());
+ n=n.rest();
+ assertEquals("", n.first());
+ assertEquals("", n.rest().toString());
+ n=n.rest();
+ assertEquals("", n.first());
+ assertEquals("", n.rest().toString());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ CompoundName n1 = new CompoundName("venn.d.a");
+ CompoundName n2 = new CompoundName(n1.asList());
+ assertEquals(n1.hashCode(), n2.hashCode());
+ assertEquals(n1, n2);
+ }
+
+ @Test
+ public void testAppend() {
+ assertEquals("a",new CompoundName("a").append("").toString());
+ assertEquals("a",new CompoundName("").append("a").toString());
+ assertEquals("a.b",new CompoundName("a").append("b").toString());
+
+ CompoundName name = new CompoundName("a.b");
+ assertEquals("a.b.c",name.append("c").toString());
+ assertEquals("a.b.d",name.append("d").toString());
+ }
+
+ @Test
+ public void testEmpty() {
+ CompoundName empty=new CompoundName("");
+ assertEquals("", empty.toString());
+ assertEquals(0, empty.asList().size());
+ }
+
+ @Test
+ public void testAsList() {
+ assertEquals("[one]", new CompoundName("one").asList().toString());
+ assertEquals("[one, two, three]", new CompoundName("one.two.three").asList().toString());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.java b/container-core/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.java
new file mode 100644
index 00000000000..70f4a7720ee
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.java
@@ -0,0 +1,61 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.request.test;
+
+import com.yahoo.processing.request.ErrorMessage;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/**
+ * @author bratseth
+ */
+public class ErrorMessageTestCase {
+
+ @Test
+ public void testToString() {
+ assertEquals("message",new ErrorMessage("message").toString());
+ assertEquals("message: hello",new ErrorMessage("message",new Exception("hello")).toString());
+ assertEquals("message: detail",new ErrorMessage("message","detail").toString());
+ assertEquals("37: message: detail",new ErrorMessage(37,"message","detail").toString());
+ assertEquals("message: detail: hello",new ErrorMessage("message","detail",new Exception("hello")).toString());
+ assertEquals("message: detail: hello: world",new ErrorMessage("message","detail",new Exception("hello",new Exception("world"))).toString());
+ assertEquals("message: detail: hello: Exception",new ErrorMessage("message","detail",new Exception("hello",new Exception())).toString());
+ assertEquals("message: detail: hello",new ErrorMessage("message","detail",new Exception(new Exception("hello"))).toString());
+ assertEquals("message: detail: java.lang.Exception: Exception",new ErrorMessage("message","detail",new Exception(new Exception())).toString());
+ }
+
+ @Test
+ public void testAccessors() {
+ ErrorMessage m = new ErrorMessage(37,"message","detail",new Exception("hello"));
+ assertEquals(37,m.getCode());
+ assertEquals("message",m.getMessage());
+ assertEquals("detail",m.getDetailedMessage());
+ assertEquals("hello",m.getCause().getMessage());
+ }
+
+ @Test
+ public void testEquality() {
+ assertEquals(new ErrorMessage(37,"message","detail",new Exception("hello")),
+ new ErrorMessage(37,"message","detail",new Exception("hello")));
+ assertEquals(new ErrorMessage("message","detail",new Exception("hello")),
+ new ErrorMessage("message","detail",new Exception("hello")));
+ assertEquals(new ErrorMessage("message",new Exception("hello")),
+ new ErrorMessage("message",new Exception("hello")));
+ assertEquals(new ErrorMessage("message"),
+ new ErrorMessage("message"));
+ assertEquals(new ErrorMessage("message",new Exception()),
+ new ErrorMessage("message"));
+ assertNotEquals(new ErrorMessage("message"),
+ new ErrorMessage("message","detail"));
+ assertNotEquals(new ErrorMessage(37,"message"),
+ new ErrorMessage("message"));
+ assertNotEquals(new ErrorMessage(37,"message"),
+ new ErrorMessage(38,"message"));
+ assertNotEquals(new ErrorMessage("message","detail1"),
+ new ErrorMessage("message","detail2"));
+ assertNotEquals(new ErrorMessage("message1"),
+ new ErrorMessage("message2"));
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java b/container-core/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java
new file mode 100644
index 00000000000..3c351d341d0
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java
@@ -0,0 +1,99 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.request.test;
+
+import com.yahoo.processing.request.properties.PropertyMap;
+import com.yahoo.processing.request.properties.PublicCloneable;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author bratseth
+ */
+public class PropertyMapTestCase {
+
+ @Test
+ public void testObjectCloning() {
+ PropertyMap map = new PropertyMap();
+ map.set("clonable", new ClonableObject());
+ map.set("publicClonable", new PublicClonableObject());
+ map.set("nonclonable", new NonClonableObject());
+ map.set("clonableArray", new ClonableObject[] {new ClonableObject()});
+ map.set("publicClonableArray", new ClonableObject[] {new ClonableObject()});
+ map.set("nonclonableArray", new NonClonableObject[] {new NonClonableObject()});
+ map.set("clonableList", Collections.singletonList(new ClonableObject()));
+ map.set("nonclonableList", Collections.singletonList(new NonClonableObject()));
+ assertNotNull(map.get("clonable"));
+ assertNotNull(map.get("nonclonable"));
+
+ PropertyMap mapClone=map.clone();
+ assertTrue(map.get("clonable") != mapClone.get("clonable"));
+ assertTrue(map.get("publicClonable")!= mapClone.get("publicClonable"));
+ assertTrue(map.get("nonclonable") == mapClone.get("nonclonable"));
+
+ assertTrue(map.get("clonableArray") != mapClone.get("clonableArray"));
+ assertTrue(first(map.get("clonableArray")) != first(mapClone.get("clonableArray")));
+ assertTrue(map.get("publicClonableArray") != mapClone.get("publicClonableArray"));
+ assertTrue(first(map.get("publicClonableArray")) != first(mapClone.get("publicClonableArray")));
+ assertTrue(first(map.get("nonclonableArray")) == first(mapClone.get("nonclonableArray")));
+ }
+
+ @Test
+ public void testArrayCloning() {
+ PropertyMap map = new PropertyMap();
+ byte[] byteArray = new byte[] {2, 4, 7};
+ map.set("byteArray", byteArray);
+
+ PropertyMap mapClone = map.clone();
+ assertArrayEquals(byteArray, (byte[])mapClone.get("byteArray"));
+ assertTrue("Array was cloned", mapClone.get("byteArray") != byteArray);
+ }
+
+ private Object first(Object object) {
+ if (object instanceof Object[])
+ return ((Object[])object)[0];
+ if (object instanceof List)
+ return ((List<?>)object).get(0);
+ throw new IllegalArgumentException();
+ }
+
+ public static class ClonableObject implements Cloneable {
+
+ @Override
+ public ClonableObject clone() {
+ try {
+ return (ClonableObject)super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ }
+
+ public static class PublicClonableObject implements PublicCloneable<PublicClonableObject> {
+
+ @Override
+ public PublicClonableObject clone() {
+ try {
+ return (PublicClonableObject)super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ }
+
+ private static class NonClonableObject {
+
+ }
+
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java b/container-core/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java
new file mode 100644
index 00000000000..032fdd71f88
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java
@@ -0,0 +1,141 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.request.test;
+
+import com.yahoo.processing.Request;
+import com.yahoo.processing.request.CompoundName;
+import com.yahoo.processing.request.ErrorMessage;
+import com.yahoo.processing.request.Properties;
+import com.yahoo.processing.request.properties.PropertyMap;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests using requests
+ *
+ * @author bratseth
+ */
+public class RequestTestCase {
+
+ private static final double delta = 0.0000000001;
+
+ @Test
+ public void testProperties() {
+ Properties p = new PropertyMap();
+ p.set("a", "a1");
+ Request r = new Request(p);
+ r.properties().set("b", "b1");
+ assertEquals(2, r.properties().listProperties().size());
+ assertEquals("a1", r.properties().get("a"));
+
+ assertEquals("b1", r.properties().get("b"));
+ assertEquals("b1", r.properties().get("b", "default"));
+ assertEquals("default", r.properties().get("c", "default"));
+ assertNull(r.properties().get("c"));
+ assertEquals("b1", r.properties().get(new CompoundName("b")));
+ assertEquals("b1", r.properties().get(new CompoundName("b"), "default"));
+ assertEquals("default", r.properties().get(new CompoundName("c"), "default"));
+ assertNull(r.properties().get(new CompoundName("c")));
+
+ assertEquals("b1", r.properties().getString("b"));
+ assertEquals("b1", r.properties().getString("b","default"));
+ assertEquals("default", r.properties().getString("c","default"));
+ assertEquals(null, r.properties().getString("c"));
+ assertEquals("b1", r.properties().getString(new CompoundName("b")));
+ assertEquals("b1", r.properties().getString(new CompoundName("b"),"default"));
+ assertEquals("default", r.properties().getString(new CompoundName("c"),"default"));
+ assertEquals(null, r.properties().getString(new CompoundName("c")));
+
+ r.properties().set("i",7);
+ assertEquals(7, (int)r.properties().getInteger("i"));
+ assertEquals(7, (int)r.properties().getInteger("i",3));
+ assertEquals(3, (int)r.properties().getInteger("n",3));
+ assertNull(r.properties().getInteger("n"));
+ assertEquals(7, (int)r.properties().getInteger(new CompoundName("i")));
+ assertEquals(7, (int)r.properties().getInteger(new CompoundName("i"),3));
+ assertEquals(3, (int)r.properties().getInteger(new CompoundName("n"),3));
+ assertNull(r.properties().getInteger("n"));
+
+ r.properties().set(new CompoundName("l"), 7);
+ assertEquals(7, (long) r.properties().getLong("l"));
+ assertEquals(7, (long)r.properties().getLong("l",3l));
+ assertEquals(3, (long)r.properties().getLong("m",3l));
+ assertNull(r.properties().getInteger("m"));
+ assertEquals(7, (long)r.properties().getLong(new CompoundName("l")));
+ assertEquals(7, (long)r.properties().getLong(new CompoundName("l"),3l));
+ assertEquals(3, (long)r.properties().getLong(new CompoundName("m"),3l));
+ assertNull(r.properties().getInteger("m"));
+
+ r.properties().set("d", 7.3);
+ assertEquals(7.3, r.properties().getDouble("d"), delta);
+ assertEquals(7.3, r.properties().getDouble("d",3.4d), delta);
+ assertEquals(3.4, r.properties().getDouble("f",3.4d), delta);
+ assertNull(r.properties().getDouble("f"));
+ assertEquals(7.3, r.properties().getDouble(new CompoundName("d")), delta);
+ assertEquals(7.3, r.properties().getDouble(new CompoundName("d"),3.4d), delta);
+ assertEquals(3.4, r.properties().getDouble(new CompoundName("f"),3.4d), delta);
+ assertNull(r.properties().getDouble("f"));
+
+ r.properties().set("o",true);
+ assertEquals(true, r.properties().getBoolean("o"));
+ assertEquals(true, r.properties().getBoolean("o",true));
+ assertEquals(true, r.properties().getBoolean("g",true));
+ assertEquals(false, r.properties().getBoolean("g"));
+ assertEquals(true, r.properties().getBoolean(new CompoundName("o")));
+ assertEquals(true, r.properties().getBoolean(new CompoundName("o"),true));
+ assertEquals(true, r.properties().getBoolean(new CompoundName("g"),true));
+ assertEquals(false, r.properties().getBoolean("g"));
+
+ r.properties().set(new CompoundName("x.y"), "x1.y1");
+ r.properties().set("x.z", "x1.z1");
+
+ assertEquals(8, r.properties().listProperties().size());
+ assertEquals(0, r.properties().listProperties("a").size());
+ assertEquals(0, r.properties().listProperties(new CompoundName("a")).size());
+ assertEquals(0, r.properties().listProperties(new CompoundName("none")).size());
+ assertEquals(2, r.properties().listProperties(new CompoundName("x")).size());
+ assertEquals(2, r.properties().listProperties("x").size());
+ }
+
+ @Test
+ public void testErrorMessages() {
+ Request r = new Request();
+ r.errors().add(new ErrorMessage("foo"));
+ r.errors().add(new ErrorMessage("bar"));
+ assertEquals(2,r.errors().size());
+ assertEquals("foo", r.errors().get(0).getMessage());
+ assertEquals("bar", r.errors().get(1).getMessage());
+ }
+
+ @Test
+ public void testCloning() {
+ Request request = new Request();
+ request.properties().set("a","a1");
+ request.properties().set("b","b1");
+ request.errors().add(new ErrorMessage("foo"));
+ request.errors().add(new ErrorMessage("bar"));
+ Request rcloned = request.clone();
+ rcloned.properties().set("c", "c1");
+ rcloned.errors().add(new ErrorMessage("baz"));
+ request.properties().set("d", "d1");
+ request.errors().add(new ErrorMessage("boz"));
+
+ assertEquals("a1", request.properties().get("a"));
+ assertEquals("a1", rcloned.properties().get("a"));
+ assertEquals("b1", request.properties().get("b"));
+ assertEquals("b1", rcloned.properties().get("b"));
+ assertEquals(null, request.properties().get("c"));
+ assertEquals("c1", rcloned.properties().get("c"));
+ assertEquals("d1", request.properties().get("d"));
+ assertEquals(null, rcloned.properties().get("d"));
+
+ assertEquals(3, request.errors().size());
+ assertEquals(1, rcloned.errors().size());
+ assertEquals("foo",request.errors().get(0).getMessage());
+ assertEquals("bar",request.errors().get(1).getMessage());
+ assertEquals("boz",request.errors().get(2).getMessage());
+ assertEquals("baz",rcloned.errors().get(0).getMessage());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java b/container-core/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java
new file mode 100644
index 00000000000..ca8fb377f09
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java
@@ -0,0 +1,44 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.yahoo.component.chain.Chain;
+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.documentation.AsyncDataProcessingInitiator;
+import com.yahoo.processing.test.documentation.AsyncDataProducer;
+import com.yahoo.processing.test.documentation.ExampleProcessor;
+import com.yahoo.processing.test.documentation.Federator;
+
+/**
+ * See to it we can actually run the examples in the doc.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class DocumentationTestCase {
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public final void test() {
+ Processor p = new ExampleProcessor();
+ Chain<Processor> basic = new Chain<>(p);
+ Processor initiator = new AsyncDataProcessingInitiator(basic);
+ Chain<Processor> postProcessing = new Chain<>(initiator);
+ Execution e = Execution.createRoot(postProcessing, 0, Execution.Environment.createEmpty());
+ Response r = e.process(new Request());
+ // just adds a listener to the result returned from basic
+ assertEquals(0, r.data().asList().size());
+ Processor producer = new AsyncDataProducer();
+ Chain<Processor> asyncChain = new Chain<>(producer);
+ Processor federator = new Federator(basic, asyncChain);
+ e = Execution.createRoot(federator, 0, Execution.Environment.createEmpty());
+ r = e.process(new Request());
+ assertEquals(2, r.data().asList().size());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java b/container-core/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java
new file mode 100644
index 00000000000..77c2ca7d10d
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java
@@ -0,0 +1,60 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.test;
+
+import com.yahoo.component.chain.Chain;
+import com.yahoo.processing.Processor;
+import com.yahoo.processing.Request;
+import com.yahoo.processing.Response;
+import com.yahoo.processing.execution.Execution;
+import org.junit.Test;
+
+import static com.yahoo.processing.test.ProcessorLibrary.*;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the basic of the processing framework
+ */
+public class ProcessingTestCase {
+
+ /** Execute three simple processors doing some phony processing */
+ @Test
+ public void testChainedProcessing1() {
+ // Create a chain
+ Chain<Processor> chain=new Chain<>(new CombineData(),new Get6DataItems(), new DataSource());
+
+ // Execute it
+ Request request=new Request();
+ request.properties().set("appendage",1);
+ Response response=Execution.createRoot(chain,0,Execution.Environment.createEmpty()).process(request);
+
+ // Verify the result
+ assertEquals(6-1,response.data().asList().size());
+ assertEquals("first.2, third.2",response.data().get(0).toString());
+ assertEquals("second.2",response.data().get(1).toString());
+ assertEquals("first.3",response.data().get(2).toString());
+ assertEquals("second.3",response.data().get(3).toString());
+ assertEquals("third.3",response.data().get(4).toString());
+ }
+
+ /** Execute the same processors in a different order */
+ @Test
+ public void testChainedProcessing2() {
+ // Create a chain
+ Chain<Processor> chain=new Chain<>(new Get6DataItems(),new CombineData(), new DataSource());
+
+ // Execute it
+ Request request=new Request();
+ request.properties().set("appendage",1);
+ Response response=Execution.createRoot(chain,0,Execution.Environment.createEmpty()).process(request);
+
+ // Check the result
+ assertEquals(6,response.data().asList().size());
+ assertEquals("first.2, third.2",response.data().get(0).toString());
+ assertEquals("second.2",response.data().get(1).toString());
+ assertEquals("first.4, third.4",response.data().get(2).toString());
+ assertEquals("second.4",response.data().get(3).toString());
+ assertEquals("first.6, third.6",response.data().get(4).toString());
+ assertEquals("second.6",response.data().get(5).toString());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java b/container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java
new file mode 100644
index 00000000000..0de20b962d7
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java
@@ -0,0 +1,30 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.test.documentation;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.yahoo.component.chain.Chain;
+import com.yahoo.processing.*;
+import com.yahoo.processing.execution.*;
+
+/**
+ * A processor which registers a listener on the future completion of
+ * asynchronously arriving data to perform another chain at that point.
+ */
+public class AsyncDataProcessingInitiator extends Processor {
+
+ private final Chain<Processor> asyncChain;
+
+ public AsyncDataProcessingInitiator(Chain<Processor> asyncChain) {
+ this.asyncChain=asyncChain;
+ }
+
+ @Override
+ public Response process(Request request, Execution execution) {
+ Response response=execution.process(request);
+ response.data().complete().addListener(new RunnableExecution(request,
+ new ExecutionWithResponse(asyncChain, response, execution)),
+ MoreExecutors.directExecutor());
+ return response;
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java b/container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java
new file mode 100644
index 00000000000..c6c9c0d785b
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java
@@ -0,0 +1,37 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.test.documentation;
+
+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.response.ArrayDataList;
+import com.yahoo.processing.response.DataList;
+import com.yahoo.processing.response.IncomingData;
+import com.yahoo.processing.test.ProcessorLibrary.StringData;
+
+/**
+ * A data producer which producer data which will receive asynchronously.
+ * This is not a realistic, thread safe implementation as only the incoming data
+ * from the last created incoming data can be completed.
+ */
+public class AsyncDataProducer extends Processor {
+
+ private IncomingData incomingData;
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Response process(Request request, Execution execution) {
+ DataList dataList = ArrayDataList.createAsync(request); // Default implementation
+ incomingData=dataList.incoming();
+ return new Response(dataList);
+ }
+
+ /** Called by some other data producing thread, later */
+ @SuppressWarnings("unchecked")
+ public void completeLateData() {
+ incomingData.addLast(new StringData(incomingData.getOwner().request(),
+ "A late hello, world!"));
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java b/container-core/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java
new file mode 100644
index 00000000000..6a1e0cbdd58
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java
@@ -0,0 +1,25 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.test.documentation;
+
+import com.yahoo.processing.*;
+import com.yahoo.processing.execution.Execution;
+import com.yahoo.processing.test.ProcessorLibrary.StringData;
+
+public class ExampleProcessor extends Processor {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Response process(Request request, Execution execution) {
+ // Process the Request:
+ request.properties().set("foo","bar");
+
+ // Pass it down the chain to get a response
+ Response response=execution.process(request);
+
+ // process the response
+ response.data().add(new StringData(request,"Hello, world!"));
+
+ return response;
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/processing/test/documentation/Federator.java b/container-core/src/test/java/com/yahoo/processing/test/documentation/Federator.java
new file mode 100644
index 00000000000..079c4912fd9
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/processing/test/documentation/Federator.java
@@ -0,0 +1,45 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.test.documentation;
+
+import com.yahoo.component.chain.Chain;
+import com.yahoo.processing.Processor;
+import com.yahoo.processing.Request;
+import com.yahoo.processing.Response;
+import com.yahoo.processing.execution.AsyncExecution;
+import com.yahoo.processing.execution.Execution;
+import com.yahoo.processing.response.FutureResponse;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Call a number of chains in parallel
+ */
+public class Federator extends Processor {
+
+ private final List<Chain<? extends Processor>> chains;
+
+ @SafeVarargs
+ @SuppressWarnings("varargs")
+ public Federator(Chain<? extends Processor> ... chains) {
+ this.chains = Arrays.asList(chains);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Response process(Request request, Execution execution) {
+ List<FutureResponse> futureResponses=new ArrayList<>(chains.size());
+ for (Chain<? extends Processor> chain : chains) {
+ futureResponses.add(new AsyncExecution(chain,execution).process(request));
+ }
+ Response response=execution.process(request);
+ AsyncExecution.waitForAll(futureResponses,1000);
+ for (FutureResponse futureResponse : futureResponses) {
+ Response federatedResponse=futureResponse.get();
+ response.data().add(federatedResponse.data());
+ response.mergeWith(federatedResponse);
+ }
+ return response;
+ }
+}