diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /processing/src/test |
Publish
Diffstat (limited to 'processing/src/test')
17 files changed, 1349 insertions, 0 deletions
diff --git a/processing/src/test/java/com/yahoo/processing/ResponseTestCase.java b/processing/src/test/java/com/yahoo/processing/ResponseTestCase.java new file mode 100644 index 00000000000..4578299adff --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/ResponseTestCase.java @@ -0,0 +1,139 @@ +// Copyright 2016 Yahoo Inc. 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/processing/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java b/processing/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java new file mode 100644 index 00000000000..bdf04859151 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/execution/test/AsyncExecutionTestCase.java @@ -0,0 +1,46 @@ +// Copyright 2016 Yahoo Inc. 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 <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +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/processing/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java b/processing/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java new file mode 100644 index 00000000000..f5b3121f2f8 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/execution/test/ExecutionContextTestCase.java @@ -0,0 +1,96 @@ +// Copyright 2016 Yahoo Inc. 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 java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class ExecutionContextTestCase extends junit.framework.TestCase { + + private Chain<Processor> chain=new Chain<Processor>(new ProcessorLibrary.DataSource()); + + /** Tests combined use of trace messages, context values and access log entries */ + 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/processing/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java b/processing/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java new file mode 100644 index 00000000000..09d7c38322f --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/execution/test/FutureDataTestCase.java @@ -0,0 +1,173 @@ +// Copyright 2016 Yahoo Inc. 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 <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +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/processing/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java b/processing/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java new file mode 100644 index 00000000000..1ce4e293104 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/execution/test/StreamingTestCase.java @@ -0,0 +1,107 @@ +// Copyright 2016 Yahoo Inc. 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 <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +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.sameThreadExecutor()); + 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/processing/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java b/processing/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java new file mode 100644 index 00000000000..0d180bb6dbb --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/request/CompoundNameTestCase.java @@ -0,0 +1,158 @@ +// Copyright 2016 Yahoo Inc. 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/processing/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java b/processing/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java new file mode 100644 index 00000000000..6c512c82903 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/request/test/CompoundNameBenchmark.java @@ -0,0 +1,52 @@ +// Copyright 2016 Yahoo Inc. 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 balder + */ +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/processing/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java b/processing/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java new file mode 100644 index 00000000000..647000c5f88 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/request/test/CompoundNameTestCase.java @@ -0,0 +1,66 @@ +// Copyright 2016 Yahoo Inc. 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 <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +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/processing/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.java b/processing/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.java new file mode 100644 index 00000000000..82fd25f9754 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/request/test/ErrorMessageTestCase.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.processing.request.test; + +import com.yahoo.processing.Request; +import com.yahoo.processing.request.ErrorMessage; +import org.junit.Test; + +/** + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class ErrorMessageTestCase extends junit.framework.TestCase { + + @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")); + assertFalse(new ErrorMessage("message").equals(new ErrorMessage("message","detail"))); + assertFalse(new ErrorMessage(37,"message").equals(new ErrorMessage("message"))); + assertFalse(new ErrorMessage(37,"message").equals(new ErrorMessage(38,"message"))); + assertFalse(new ErrorMessage("message","detail1").equals(new ErrorMessage("message","detail2"))); + assertFalse(new ErrorMessage("message1").equals(new ErrorMessage("message2"))); + } + +} diff --git a/processing/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java b/processing/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java new file mode 100644 index 00000000000..61ef75c55a2 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/request/test/PropertyMapTestCase.java @@ -0,0 +1,81 @@ +// Copyright 2016 Yahoo Inc. 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 java.util.Collections; +import java.util.List; + +/** + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class PropertyMapTestCase extends junit.framework.TestCase { + + public void testCloning() { + 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"))); + } + + 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/processing/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java b/processing/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java new file mode 100644 index 00000000000..7045ea1efbd --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/request/test/RequestTestCase.java @@ -0,0 +1,137 @@ +// Copyright 2016 Yahoo Inc. 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; + +/** + * Tests using requests + * + * @author bratseth + */ +public class RequestTestCase extends junit.framework.TestCase { + + @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")); + assertEquals(7.3,r.properties().getDouble("d",3.4d)); + assertEquals(3.4,r.properties().getDouble("f",3.4d)); + assertNull(r.properties().getDouble("f")); + assertEquals(7.3,r.properties().getDouble(new CompoundName("d"))); + assertEquals(7.3,r.properties().getDouble(new CompoundName("d"),3.4d)); + assertEquals(3.4,r.properties().getDouble(new CompoundName("f"),3.4d)); + 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/processing/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java b/processing/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java new file mode 100644 index 00000000000..ea51e9079cc --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/test/DocumentationTestCase.java @@ -0,0 +1,44 @@ +// Copyright 2016 Yahoo Inc. 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/processing/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java b/processing/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java new file mode 100644 index 00000000000..4f306773c2a --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/test/ProcessingTestCase.java @@ -0,0 +1,60 @@ +// Copyright 2016 Yahoo Inc. 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/processing/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java b/processing/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java new file mode 100644 index 00000000000..afda8a7fe96 --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProcessingInitiator.java @@ -0,0 +1,30 @@ +// Copyright 2016 Yahoo Inc. 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.sameThreadExecutor()); + return response; + } + +} diff --git a/processing/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java b/processing/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java new file mode 100644 index 00000000000..f2a51e240cc --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/test/documentation/AsyncDataProducer.java @@ -0,0 +1,37 @@ +// Copyright 2016 Yahoo Inc. 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/processing/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java b/processing/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java new file mode 100644 index 00000000000..c87d508676d --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/test/documentation/ExampleProcessor.java @@ -0,0 +1,25 @@ +// Copyright 2016 Yahoo Inc. 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/processing/src/test/java/com/yahoo/processing/test/documentation/Federator.java b/processing/src/test/java/com/yahoo/processing/test/documentation/Federator.java new file mode 100644 index 00000000000..c69bdf0c85c --- /dev/null +++ b/processing/src/test/java/com/yahoo/processing/test/documentation/Federator.java @@ -0,0 +1,44 @@ +// Copyright 2016 Yahoo Inc. 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 + 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; + } +} |